s
This commit is contained in:
@@ -357,9 +357,11 @@ export default function LiveControlApp() {
|
||||
const targetProc = currentProcRef.current.trim();
|
||||
const requestId = ++urlConfigRequestSeqRef.current;
|
||||
try {
|
||||
const data = await fetchJson<{ content?: string }>(
|
||||
`/get_url_config?process=${encodeURIComponent(targetProc)}`,
|
||||
);
|
||||
const params = new URLSearchParams({
|
||||
process: targetProc,
|
||||
_ts: String(Date.now()),
|
||||
});
|
||||
const data = await fetchJson<{ content?: string }>(`/get_url_config?${params}`);
|
||||
if (requestId !== urlConfigRequestSeqRef.current || targetProc !== currentProcRef.current.trim()) return;
|
||||
setUrlConfig(data.content || "");
|
||||
} catch {
|
||||
@@ -443,6 +445,7 @@ export default function LiveControlApp() {
|
||||
const proc = (processOverride ?? currentProc).trim();
|
||||
if (!proc) return;
|
||||
const content = overrideContent ?? urlConfig;
|
||||
const requestId = ++urlConfigRequestSeqRef.current;
|
||||
setBusy("save:url");
|
||||
try {
|
||||
const params = new URLSearchParams({ process: proc });
|
||||
@@ -451,6 +454,9 @@ export default function LiveControlApp() {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ content }),
|
||||
});
|
||||
if (requestId === urlConfigRequestSeqRef.current && proc === currentProcRef.current.trim()) {
|
||||
setUrlConfig(content);
|
||||
}
|
||||
notify(t("URL 配置已保存", "URL config saved"));
|
||||
} catch (e) {
|
||||
notify((e as Error).message);
|
||||
|
||||
@@ -167,9 +167,11 @@ export function LiveTiktokHdmiView({
|
||||
const pullUrlConfigFromServer = useCallback(async () => {
|
||||
if (!currentProc) return;
|
||||
try {
|
||||
const data = await fetchJson<{ content?: string }>(
|
||||
`/get_url_config?process=${encodeURIComponent(currentProc)}`,
|
||||
);
|
||||
const params = new URLSearchParams({
|
||||
process: currentProc,
|
||||
_ts: String(Date.now()),
|
||||
});
|
||||
const data = await fetchJson<{ content?: string }>(`/get_url_config?${params}`);
|
||||
applyUrlDraft(data.content ?? "");
|
||||
} catch {
|
||||
/* ignore */
|
||||
@@ -249,9 +251,11 @@ export function LiveTiktokHdmiView({
|
||||
setUrlFetchNote(null);
|
||||
urlListDirtyRef.current = false;
|
||||
try {
|
||||
const data = await fetchJson<{ content?: string }>(
|
||||
`/get_url_config?process=${encodeURIComponent(currentProc)}`,
|
||||
);
|
||||
const params = new URLSearchParams({
|
||||
process: currentProc,
|
||||
_ts: String(Date.now()),
|
||||
});
|
||||
const data = await fetchJson<{ content?: string }>(`/get_url_config?${params}`);
|
||||
applyUrlDraft(data.content ?? "");
|
||||
setUrlFetchNote(t("已从服务器读取 URL_config", "Reloaded URL_config from server"));
|
||||
} catch (e) {
|
||||
|
||||
@@ -404,9 +404,11 @@ export function LiveYoutubeUnmannedView({
|
||||
if (!targetProc) return;
|
||||
const requestId = ++urlRequestSeqRef.current;
|
||||
try {
|
||||
const data = await fetchJson<{ content?: string }>(
|
||||
`/get_url_config?process=${encodeURIComponent(targetProc)}`,
|
||||
);
|
||||
const params = new URLSearchParams({
|
||||
process: targetProc,
|
||||
_ts: String(Date.now()),
|
||||
});
|
||||
const data = await fetchJson<{ content?: string }>(`/get_url_config?${params}`);
|
||||
if (requestId !== urlRequestSeqRef.current || targetProc !== currentProcRef.current.trim()) return;
|
||||
const c = data.content ?? "";
|
||||
if (urlListDirtyRef.current) return;
|
||||
@@ -448,9 +450,11 @@ export function LiveYoutubeUnmannedView({
|
||||
urlListDirtyRef.current = false;
|
||||
const requestId = ++urlRequestSeqRef.current;
|
||||
try {
|
||||
const data = await fetchJson<{ content?: string }>(
|
||||
`/get_url_config?process=${encodeURIComponent(targetProc)}`,
|
||||
);
|
||||
const params = new URLSearchParams({
|
||||
process: targetProc,
|
||||
_ts: String(Date.now()),
|
||||
});
|
||||
const data = await fetchJson<{ content?: string }>(`/get_url_config?${params}`);
|
||||
if (requestId !== urlRequestSeqRef.current || targetProc !== currentProcRef.current.trim()) return;
|
||||
const c = data.content ?? "";
|
||||
if (urlListDirtyRef.current) {
|
||||
@@ -503,7 +507,7 @@ export function LiveYoutubeUnmannedView({
|
||||
void (async () => {
|
||||
try {
|
||||
const remote = await fetchYoutubeProChannelsRemote(fetchJson);
|
||||
if (remote.fetched && !cancelled) {
|
||||
if (remote.fetched && remote.channels.length && !cancelled) {
|
||||
setChannels(remote.channels);
|
||||
saveYoutubeProChannels(remote.channels);
|
||||
return;
|
||||
@@ -538,7 +542,13 @@ export function LiveYoutubeUnmannedView({
|
||||
}, [fetchJson]);
|
||||
|
||||
useEffect(() => {
|
||||
if (channels.length && !activeCh) setActiveCh(channels[0].id);
|
||||
if (!channels.length) {
|
||||
if (activeCh) setActiveCh("");
|
||||
return;
|
||||
}
|
||||
if (!activeCh || !channels.some((row) => row.id === activeCh)) {
|
||||
setActiveCh(channels[0].id);
|
||||
}
|
||||
}, [channels, activeCh]);
|
||||
|
||||
const rowForPm2 = useCallback((pm2: string) => liveProcesses.find((p) => p.pm2 === pm2), [liveProcesses]);
|
||||
@@ -546,26 +556,18 @@ export function LiveYoutubeUnmannedView({
|
||||
(pm2: string) => rowForPm2(pm2)?.process_status,
|
||||
[rowForPm2],
|
||||
);
|
||||
const currentProcRow = rowForPm2(currentProc);
|
||||
const currentProcLooksYoutube = useMemo(
|
||||
() => !!currentProc && (/youtube/i.test(currentProc) || /youtube/i.test(currentProcRow?.script ?? "") || /youtube/i.test(currentProcRow?.label ?? "")),
|
||||
[currentProc, currentProcRow],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (mode !== "single" || !preferredSingleProc) return;
|
||||
if (!singleProcessOptions.some((entry) => entry.pm2 === currentProc)) {
|
||||
setCurrentProc(preferredSingleProc);
|
||||
return;
|
||||
}
|
||||
const currentEntry = singleProcessOptions.find((entry) => entry.pm2 === currentProc);
|
||||
const currentRow = rowForPm2(currentProc);
|
||||
const preferredEntry = singleProcessOptions.find((entry) => entry.pm2 === preferredSingleProc);
|
||||
const preferredRow = rowForPm2(preferredSingleProc);
|
||||
if (
|
||||
currentProc !== preferredSingleProc &&
|
||||
currentEntry &&
|
||||
preferredEntry &&
|
||||
singleProcessPriority(preferredEntry, preferredRow) < singleProcessPriority(currentEntry, currentRow)
|
||||
) {
|
||||
if (!currentProc || !currentProcLooksYoutube) {
|
||||
setCurrentProc(preferredSingleProc);
|
||||
}
|
||||
}, [currentProc, mode, preferredSingleProc, rowForPm2, setCurrentProc, singleProcessOptions]);
|
||||
}, [currentProc, currentProcLooksYoutube, mode, preferredSingleProc, setCurrentProc]);
|
||||
|
||||
const applyChannelsLocal = useCallback((next: YoutubeProChannel[]) => {
|
||||
setChannels(next);
|
||||
@@ -773,9 +775,11 @@ export function LiveYoutubeUnmannedView({
|
||||
setYtIniLoading(true);
|
||||
setYtIniStatus(null);
|
||||
try {
|
||||
const data = await fetchJson<{ content?: string }>(
|
||||
`/get_config?process=${encodeURIComponent(targetProc)}`,
|
||||
);
|
||||
const params = new URLSearchParams({
|
||||
process: targetProc,
|
||||
_ts: String(Date.now()),
|
||||
});
|
||||
const data = await fetchJson<{ content?: string }>(`/get_config?${params}`);
|
||||
if (requestId !== ytIniRequestSeqRef.current || targetProc !== currentProcRef.current.trim()) return;
|
||||
const text = data.content ?? "";
|
||||
setYoutubeIniText(text);
|
||||
@@ -872,7 +876,7 @@ export function LiveYoutubeUnmannedView({
|
||||
}
|
||||
}, [active, notify, persistActiveLaneDraft, setCurrentProc, t]);
|
||||
|
||||
const saveYoutubeIniToServer = async (content: string, processPm2 = currentProc) => {
|
||||
const saveYoutubeIniToServer = async (content: string, processPm2 = currentProcRef.current) => {
|
||||
const targetPm2 = processPm2.trim();
|
||||
if (!targetPm2) {
|
||||
setYtIniStatus(t("请选择进程", "Pick a process"));
|
||||
@@ -911,7 +915,7 @@ export function LiveYoutubeUnmannedView({
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
root: "app-config",
|
||||
path: managedYoutubeIniRelPathForPm2(currentProc),
|
||||
path: managedYoutubeIniRelPathForPm2(currentProcRef.current),
|
||||
content: source,
|
||||
action: "youtube_balanced_preset",
|
||||
}),
|
||||
@@ -943,6 +947,7 @@ export function LiveYoutubeUnmannedView({
|
||||
try {
|
||||
setLaneSaving(true);
|
||||
setLaneStatus(null);
|
||||
urlRequestSeqRef.current += 1;
|
||||
const latestProUrlDraft = proUrlDraftRef.current;
|
||||
const saved = await persistActiveLaneDraft();
|
||||
setCurrentProc(saved.pm2);
|
||||
@@ -959,12 +964,13 @@ export function LiveYoutubeUnmannedView({
|
||||
};
|
||||
|
||||
const saveSingleUrlAndClearDirty = async () => {
|
||||
const targetPm2 = currentProc.trim();
|
||||
const targetPm2 = currentProcRef.current.trim();
|
||||
if (!targetPm2) {
|
||||
notify(t("请选择进程", "Pick a process"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
urlRequestSeqRef.current += 1;
|
||||
await Promise.resolve(onSaveUrlConfig(singleUrlDraftRef.current, targetPm2));
|
||||
urlListDirtyRef.current = false;
|
||||
} catch {
|
||||
@@ -982,7 +988,7 @@ export function LiveYoutubeUnmannedView({
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
root: "app-config",
|
||||
path: managedUrlConfigRelPathForPm2(currentProc),
|
||||
path: managedUrlConfigRelPathForPm2(currentProcRef.current),
|
||||
content: source,
|
||||
action: "dedupe_urls",
|
||||
}),
|
||||
@@ -1099,7 +1105,7 @@ export function LiveYoutubeUnmannedView({
|
||||
|
||||
const lock = !!busy || ytIniLoading || urlReloading || urlOptimizing || laneSaving;
|
||||
|
||||
const singleRow = rowForPm2(currentProc);
|
||||
const singleRow = currentProcRow;
|
||||
const singleOnline = pm2StatusOnline(statusForPm2(currentProc));
|
||||
|
||||
const urlToolbar = (
|
||||
@@ -1260,8 +1266,18 @@ export function LiveYoutubeUnmannedView({
|
||||
<select
|
||||
className="mt-2 w-full rounded-xl border border-white/10 bg-black/40 px-4 py-3 text-sm text-white"
|
||||
value={currentProc}
|
||||
onChange={(e) => setCurrentProc(e.target.value)}
|
||||
onChange={(e) => {
|
||||
const next = e.target.value;
|
||||
currentProcRef.current = next;
|
||||
setCurrentProc(next);
|
||||
}}
|
||||
>
|
||||
{currentProc && !singleProcessOptions.some((entry) => entry.pm2 === currentProc) ? (
|
||||
<option value={currentProc}>
|
||||
{currentProc}
|
||||
{currentProcRow ? ` (${businessStatusLabel(currentProcRow, t)})` : ""}
|
||||
</option>
|
||||
) : null}
|
||||
{singleProcessOptions.map((e) => (
|
||||
<option key={e.pm2} value={e.pm2}>
|
||||
{singleModeProcessLabel(e, t)} ({e.pm2})
|
||||
@@ -1343,7 +1359,10 @@ export function LiveYoutubeUnmannedView({
|
||||
<button
|
||||
type="button"
|
||||
className="min-w-0 flex-1 text-left"
|
||||
onClick={() => selectProChannel(c)}
|
||||
onClick={() => {
|
||||
currentProcRef.current = resolveChannelPm2(c);
|
||||
selectProChannel(c);
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="font-medium text-zinc-100">{c.name}</span>
|
||||
@@ -1573,11 +1592,11 @@ export function LiveYoutubeUnmannedView({
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
disabled={lock}
|
||||
className="inline-flex items-center gap-1 rounded-lg bg-violet-500/20 px-3 py-1.5 text-xs text-violet-100 ring-1 ring-violet-500/30"
|
||||
onClick={() => {
|
||||
<button
|
||||
type="button"
|
||||
disabled={lock}
|
||||
className="inline-flex items-center gap-1 rounded-lg bg-violet-500/20 px-3 py-1.5 text-xs text-violet-100 ring-1 ring-violet-500/30"
|
||||
onClick={() => {
|
||||
const id = newChannelId();
|
||||
const nc: YoutubeProChannel = {
|
||||
id,
|
||||
@@ -1587,11 +1606,16 @@ export function LiveYoutubeUnmannedView({
|
||||
pm2Name: defaultPm2NameForChannel(id),
|
||||
notes: "",
|
||||
};
|
||||
persistChannels([...channels, nc]);
|
||||
setActiveCh(id);
|
||||
setCurrentProc(resolveChannelPm2(nc));
|
||||
applyProUrlDraft("");
|
||||
}}
|
||||
setLaneDrafts((prev) => ({
|
||||
...prev,
|
||||
[id]: laneDraftFromChannel(nc),
|
||||
}));
|
||||
persistChannels([...channels, nc]);
|
||||
setActiveCh(id);
|
||||
currentProcRef.current = resolveChannelPm2(nc);
|
||||
setCurrentProc(currentProcRef.current);
|
||||
applyProUrlDraft("");
|
||||
}}
|
||||
>
|
||||
<Plus className="h-3.5 w-3.5" />
|
||||
{t("新增线路", "Add lane")}
|
||||
|
||||
Reference in New Issue
Block a user