's'
This commit is contained in:
116
d2ypp2/web-console/lib/youtubeProChannels.ts
Normal file
116
d2ypp2/web-console/lib/youtubeProChannels.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
export type YoutubeProChannel = {
|
||||
id: string;
|
||||
name: string;
|
||||
/** 备忘尾码(旧版) */
|
||||
streamKeySuffix?: string;
|
||||
/** 完整串流密钥或 rtmp(s) URL */
|
||||
streamKey?: string;
|
||||
/** 本线路 URL_config 草稿(多路本地编排) */
|
||||
urlLines?: string;
|
||||
/** 建议 PM2 名,如 youtube2__xxx */
|
||||
pm2Name?: string;
|
||||
notes?: string;
|
||||
};
|
||||
|
||||
const STORAGE_KEY = "live-hub-youtube-pro-v2";
|
||||
|
||||
export function parseYoutubeProChannelList(raw: unknown): YoutubeProChannel[] {
|
||||
if (!Array.isArray(raw)) return [];
|
||||
return raw
|
||||
.map((x) => migrateRow(x as Record<string, unknown>))
|
||||
.filter((c): c is YoutubeProChannel => c !== null);
|
||||
}
|
||||
|
||||
function migrateRow(x: Record<string, unknown>): YoutubeProChannel | null {
|
||||
if (!x || typeof x !== "object") return null;
|
||||
const id = String(x.id || "").trim();
|
||||
if (!id) return null;
|
||||
const name = String(x.name || "").trim() || id;
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
streamKeySuffix: typeof x.streamKeySuffix === "string" ? x.streamKeySuffix : undefined,
|
||||
streamKey: typeof x.streamKey === "string" ? x.streamKey : undefined,
|
||||
urlLines: typeof x.urlLines === "string" ? x.urlLines : undefined,
|
||||
pm2Name: typeof x.pm2Name === "string" ? x.pm2Name : undefined,
|
||||
notes: typeof x.notes === "string" ? x.notes : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function safeParse(raw: string | null): YoutubeProChannel[] {
|
||||
if (!raw) return [];
|
||||
try {
|
||||
const v = JSON.parse(raw) as unknown;
|
||||
if (!Array.isArray(v)) return [];
|
||||
return v.map((x) => migrateRow(x as Record<string, unknown>)).filter((c): c is YoutubeProChannel => c !== null);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function loadYoutubeProChannels(): YoutubeProChannel[] {
|
||||
if (typeof window === "undefined") return [];
|
||||
let raw = window.localStorage.getItem(STORAGE_KEY);
|
||||
if (!raw) {
|
||||
raw = window.localStorage.getItem("live-hub-youtube-pro-v1");
|
||||
if (raw) {
|
||||
const legacy = safeParse(raw);
|
||||
if (legacy.length) {
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(legacy));
|
||||
return legacy;
|
||||
}
|
||||
}
|
||||
}
|
||||
return safeParse(raw);
|
||||
}
|
||||
|
||||
export function saveYoutubeProChannels(channels: YoutubeProChannel[]): void {
|
||||
if (typeof window === "undefined") return;
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(channels));
|
||||
}
|
||||
|
||||
const YOUTUBE_PRO_SYNC_PATHS = ["/hub/youtube_pro_channels", "/youtube_pro_channels"] as const;
|
||||
|
||||
/** 从服务器拉多频道列表(依次尝试 /hub 与根路径,兼容反代未转发 /hub 的情况) */
|
||||
export async function fetchYoutubeProChannelsRemote(
|
||||
fetchJson: <T,>(path: string, init?: RequestInit) => Promise<T>,
|
||||
): Promise<YoutubeProChannel[]> {
|
||||
for (const path of YOUTUBE_PRO_SYNC_PATHS) {
|
||||
try {
|
||||
const data = await fetchJson<{ channels?: unknown }>(path);
|
||||
const fromServer = parseYoutubeProChannelList(data.channels);
|
||||
if (fromServer.length) return fromServer;
|
||||
} catch {
|
||||
/* try next */
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export async function pushYoutubeProChannelsRemote(
|
||||
fetchJson: <T,>(path: string, init?: RequestInit) => Promise<T>,
|
||||
channels: YoutubeProChannel[],
|
||||
): Promise<void> {
|
||||
let last: Error | null = null;
|
||||
for (const path of YOUTUBE_PRO_SYNC_PATHS) {
|
||||
try {
|
||||
await fetchJson(path, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ channels }),
|
||||
});
|
||||
return;
|
||||
} catch (e) {
|
||||
last = e instanceof Error ? e : new Error(String(e));
|
||||
}
|
||||
}
|
||||
throw last ?? new Error("youtube_pro_channels sync failed");
|
||||
}
|
||||
|
||||
export function newChannelId(): string {
|
||||
return `ch_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;
|
||||
}
|
||||
|
||||
export function defaultPm2NameForChannel(channelId: string): string {
|
||||
return `youtube2__${channelId}`;
|
||||
}
|
||||
Reference in New Issue
Block a user