- 左栏三组分区 + 当前分区的节;窄屏为搜索框、分组下拉和节的横排胶囊 - 搜索框按分区、节、节别名和字段名定位,选中字段时滚到该字段并聚焦 - 旧 hash(#automation/cache、#api-keys、#library/scrape 等)replace 到新位置 - 下载器分区放下载盘水位与保种缺省值,名字与实例抽屉统一(暂停线 / 恢复线 / 保种天数) - 新增获取、整理与刮削、系统、缓存分区;API Key 并入账户与安全,快捷键移到界面 - 补上缺的入口:最小视频体积、子会话并发与层数、搜索站点;日志保留天数(已废弃)不再显示 - 缓存分区按区列出条数、占用、最近写入,单区清空二次确认;TMDB 与图片缓存并入 - 保存提示统一为「已保存「节名」」;按入库口径把相关文案改成「整理」 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
116 lines
3.9 KiB
TypeScript
116 lines
3.9 KiB
TypeScript
import { DEFAULT_SETTINGS } from "@umt/contracts";
|
||
import { secretPatch } from "~/lib/secret.ts";
|
||
import { str } from "./resource-section.tsx";
|
||
import { type CardSpec, SettingsCard, SettingsGate } from "./settings-card.tsx";
|
||
|
||
// 元数据:TMDB 接入(API Key 只写,存 secret;Base URL)与语言回退、特别篇。缓存在「缓存」分区,资源识别在「获取」分区。
|
||
|
||
const DEFAULT_LANGUAGES = DEFAULT_SETTINGS.metadata.languages;
|
||
|
||
/** "zh-CN, zh_sg en" → ["zh-CN","zh-SG","en"];不合法的项丢弃,全空回缺省 */
|
||
function parseLanguages(v: string): string[] {
|
||
const out = v
|
||
.split(/[,,、\s]+/)
|
||
.map((x) => x.trim().replace("_", "-"))
|
||
.filter(Boolean)
|
||
.map((x) => {
|
||
const [l, r] = x.split("-");
|
||
return r ? `${l!.toLowerCase()}-${r.toUpperCase()}` : l!.toLowerCase();
|
||
})
|
||
.filter((x) => /^[a-z]{2,3}(-[A-Z]{2})?$/.test(x));
|
||
return out.length ? [...new Set(out)].slice(0, 8) : [...DEFAULT_LANGUAGES];
|
||
}
|
||
|
||
export const TMDB_CARD: CardSpec = {
|
||
anchor: "tmdb",
|
||
title: "TMDB",
|
||
description: "留空时用环境变量 TMDB_API_KEY / TMDB_ACCESS_TOKEN / TMDB_BASE_URL。",
|
||
fields: [
|
||
{
|
||
name: "metadata.tmdbApiKey",
|
||
label: "API Key",
|
||
kind: "secret",
|
||
secret: { set: (v) => v["metadata.tmdbApiKey:set"] === true, clearable: true },
|
||
hint: "v3 API Key 或 v4 Read Access Token",
|
||
wide: true,
|
||
},
|
||
{
|
||
name: "metadata.tmdbBaseUrl",
|
||
label: "Base URL",
|
||
kind: "url",
|
||
placeholder: "https://api.themoviedb.org",
|
||
hint: "自建反代时填写;留空用官方地址",
|
||
wide: true,
|
||
},
|
||
],
|
||
fromSettings: (s) => ({
|
||
"metadata.tmdbApiKey": "",
|
||
"metadata.tmdbApiKey:set": !!s.metadata.tmdbApiKeySecretRef,
|
||
"metadata.tmdbBaseUrl": s.metadata.tmdbBaseUrl ?? "",
|
||
}),
|
||
toPatch: (v) => {
|
||
const key = secretPatch(v["metadata.tmdbApiKey"]);
|
||
return {
|
||
metadata: {
|
||
tmdbBaseUrl: str(v["metadata.tmdbBaseUrl"]) || null,
|
||
...(key !== undefined ? { tmdbApiKey: key } : {}),
|
||
},
|
||
};
|
||
},
|
||
};
|
||
|
||
export const LANGUAGE_CARD: CardSpec = {
|
||
anchor: "language",
|
||
title: "语言与季集",
|
||
description: "作品、季、集的标题和简介按语言回退链取值;特别篇决定是否拉取季 0。",
|
||
fields: [
|
||
{
|
||
name: "metadata.languages",
|
||
label: "语言回退链",
|
||
kind: "text",
|
||
placeholder: DEFAULT_LANGUAGES.join(", "),
|
||
hint: "标题和每集信息按顺序取第一个有值的语言,简介默认只取链里的中文;中文名取链中第一个中文值。逗号分隔,如 zh-CN, zh-SG",
|
||
wide: true,
|
||
},
|
||
{
|
||
name: "metadata.overviewNonChineseFallback",
|
||
label: "简介回退到非中文",
|
||
kind: "switch",
|
||
hint: "没有中文简介时,按回退链取英文等语言。关闭时留空,标题不受影响",
|
||
wide: true,
|
||
},
|
||
{
|
||
name: "metadata.includeSpecials",
|
||
label: "包含特别篇(季 0)",
|
||
kind: "switch",
|
||
hint: "全量刷新和元数据回刷时,TMDB 有季 0 就一并拉取每集信息。特别篇不计入正片季的总集数和订阅完成判定",
|
||
wide: true,
|
||
},
|
||
],
|
||
fromSettings: (s) => ({
|
||
"metadata.languages": (s.metadata.languages ?? DEFAULT_LANGUAGES).join(", "),
|
||
"metadata.includeSpecials": s.metadata.includeSpecials !== false,
|
||
"metadata.overviewNonChineseFallback": s.metadata.overviewNonChineseFallback === true,
|
||
}),
|
||
toPatch: (v) => ({
|
||
metadata: {
|
||
languages: parseLanguages(str(v["metadata.languages"])),
|
||
includeSpecials: v["metadata.includeSpecials"] === true,
|
||
overviewNonChineseFallback: v["metadata.overviewNonChineseFallback"] === true,
|
||
},
|
||
}),
|
||
};
|
||
|
||
export function MetadataSection() {
|
||
return (
|
||
<SettingsGate>
|
||
{(s) => (
|
||
<>
|
||
<SettingsCard section="metadata" spec={TMDB_CARD} settings={s} />
|
||
<SettingsCard section="metadata" spec={LANGUAGE_CARD} settings={s} />
|
||
</>
|
||
)}
|
||
</SettingsGate>
|
||
);
|
||
}
|