This commit is contained in:
eric
2026-03-29 00:34:53 -05:00
parent e0b3ac43fc
commit af927c6dca
8 changed files with 1281 additions and 21 deletions

View File

@@ -71,6 +71,7 @@ export function LiveYoutubeUnmannedView({
const [proUrlDraft, setProUrlDraft] = useState("");
const [urlAutoRefresh, setUrlAutoRefresh] = useState(true);
const [urlReloading, setUrlReloading] = useState(false);
const [urlOptimizing, setUrlOptimizing] = useState(false);
const [urlFetchNote, setUrlFetchNote] = useState<string | null>(null);
const urlListDirtyRef = useRef(false);
@@ -260,6 +261,32 @@ export function LiveYoutubeUnmannedView({
await saveYoutubeIniToServer(body);
};
const applyYoutubeBalancedPreset = async () => {
setYtIniLoading(true);
setYtIniStatus(null);
try {
const source = youtubeIniText || buildYoutubeIniFromFields(ytFields);
const data = await fetchJson<{ message?: string; content?: string }>("/config_optimize", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
root: "app-config",
path: "youtube.ini",
content: source,
action: "youtube_balanced_preset",
}),
});
const next = data.content ?? source;
setYoutubeIniText(next);
setYtFields(parseYoutubeIniFields(next));
setYtIniStatus(data.message || t("已应用 YouTube 预设", "Applied YouTube preset"));
} catch (e) {
setYtIniStatus((e as Error).message);
} finally {
setYtIniLoading(false);
}
};
const syncFieldsFromText = () => {
setYtFields(parseYoutubeIniFields(youtubeIniText));
setYtIniStatus(t("已从下方原文同步到表单", "Synced from raw"));
@@ -292,6 +319,36 @@ export function LiveYoutubeUnmannedView({
}
};
const dedupeUrlDraft = async () => {
const source = mode === "single" ? urlConfig : proUrlDraft;
setUrlOptimizing(true);
setUrlFetchNote(null);
try {
const data = await fetchJson<{ message?: string; content?: string }>("/config_optimize", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
root: "app-config",
path: "URL_config.ini",
content: source,
action: "dedupe_urls",
}),
});
const next = data.content ?? source;
if (mode === "single") setUrlConfig(next);
else {
setProUrlDraft(next);
if (active) updateActive({ urlLines: next });
}
urlListDirtyRef.current = true;
setUrlFetchNote(data.message || t("已去重当前 URL 列表", "Deduplicated current URL list"));
} catch (e) {
setUrlFetchNote((e as Error).message);
} finally {
setUrlOptimizing(false);
}
};
const pushProStreamKeyToServer = async () => {
if (!active) return;
const base = parseYoutubeIniFields(youtubeIniText || buildYoutubeIniFromFields(ytFields));
@@ -306,7 +363,7 @@ export function LiveYoutubeUnmannedView({
updateActive({ streamKey: f.key });
};
const lock = busy || ytIniLoading || urlReloading;
const lock = busy || ytIniLoading || urlReloading || urlOptimizing;
const urlToolbar = (
<div className="mt-2 flex flex-wrap items-center gap-4 text-xs text-zinc-400">
@@ -327,6 +384,14 @@ export function LiveYoutubeUnmannedView({
>
{t("手动重新读取", "Reload from server")}
</button>
<button
type="button"
disabled={lock}
onClick={() => void dedupeUrlDraft()}
className="rounded-lg border border-cyan-500/20 bg-cyan-500/10 px-3 py-1.5 text-cyan-100 hover:bg-cyan-500/15"
>
{urlOptimizing ? t("去重中...", "Deduplicating...") : t("智能去重", "Deduplicate")}
</button>
</div>
);
@@ -564,6 +629,14 @@ export function LiveYoutubeUnmannedView({
{ytIniLoading ? <Loader2 className="h-4 w-4 animate-spin" /> : null}
{t("保存 youtube.ini", "Save youtube.ini")}
</button>
<button
type="button"
disabled={lock}
onClick={() => void applyYoutubeBalancedPreset()}
className="rounded-xl border border-cyan-500/20 bg-cyan-500/10 px-4 py-2 text-sm text-cyan-100"
>
{t("平衡预设", "Balanced preset")}
</button>
<button
type="button"
disabled={lock}