feat: expose multi-platform and recorder controls in web v4
This commit is contained in:
@@ -35,7 +35,7 @@ const appState = {
|
||||
lastJobDetail: null
|
||||
};
|
||||
|
||||
const INTEGRATION_ORDER = ["local_model", "cutvideo", "huobao", "n8n", "asr"];
|
||||
const INTEGRATION_ORDER = ["local_model", "live_recorder", "cutvideo", "huobao", "n8n", "asr"];
|
||||
const ACTIVE_PLATFORMS = [
|
||||
{ value: "douyin", label: "抖音" },
|
||||
{ value: "xiaohongshu", label: "小红书" },
|
||||
@@ -44,50 +44,54 @@ const ACTIVE_PLATFORMS = [
|
||||
{ value: "wechat_video", label: "微信视频号" }
|
||||
];
|
||||
const ACTIVE_PLATFORM_CHIPS = ["全平台", "抖音", "小红书", "B站", "快手", "视频号"];
|
||||
function makePlatformRoutes(platform) {
|
||||
return {
|
||||
accounts: `/v2/${platform}/accounts`,
|
||||
workspace: (accountId) => `/v2/${platform}/accounts/${encodeURIComponent(accountId)}/workspace`,
|
||||
videos: (accountId) => `/v2/${platform}/accounts/${encodeURIComponent(accountId)}/videos?limit=80`,
|
||||
analyzeAccount: (accountId) => `/v2/${platform}/accounts/${encodeURIComponent(accountId)}/analysis`,
|
||||
analyzeTopVideos: (accountId) => `/v2/${platform}/accounts/${encodeURIComponent(accountId)}/videos/analyze-top`,
|
||||
similarSearches: `/v2/${platform}/similar-searches`,
|
||||
similarSearchDetail: (searchId) => `/v2/${platform}/similar-searches/${encodeURIComponent(searchId)}`,
|
||||
benchmarkLinks: (accountId) => `/v2/${platform}/accounts/${encodeURIComponent(accountId)}/benchmark-links`,
|
||||
trackingAccounts: `/v2/${platform}/tracking/accounts`,
|
||||
trackingDigest: `/v2/${platform}/tracking/digest`,
|
||||
trackingRefresh: `/v2/${platform}/tracking/refresh`,
|
||||
trackingCursor: `/v2/${platform}/tracking/cursor`,
|
||||
trackingAccountRefresh: (trackedAccountId) => `/v2/${platform}/tracking/accounts/${encodeURIComponent(trackedAccountId)}/refresh`
|
||||
};
|
||||
}
|
||||
|
||||
const PLATFORM_REGISTRY = {
|
||||
douyin: {
|
||||
label: "抖音",
|
||||
shortLabel: "抖音",
|
||||
workbenchReady: true,
|
||||
routes: {
|
||||
accounts: "/v2/douyin/accounts",
|
||||
workspace: (accountId) => `/v2/douyin/accounts/${encodeURIComponent(accountId)}/workspace`,
|
||||
videos: (accountId) => `/v2/douyin/accounts/${encodeURIComponent(accountId)}/videos?limit=80`,
|
||||
analyzeAccount: (accountId) => `/v2/douyin/accounts/${encodeURIComponent(accountId)}/analysis`,
|
||||
analyzeTopVideos: (accountId) => `/v2/douyin/accounts/${encodeURIComponent(accountId)}/videos/analyze-top`,
|
||||
similarSearches: "/v2/douyin/similar-searches",
|
||||
similarSearchDetail: (searchId) => `/v2/douyin/similar-searches/${encodeURIComponent(searchId)}`,
|
||||
benchmarkLinks: (accountId) => `/v2/douyin/accounts/${encodeURIComponent(accountId)}/benchmark-links`,
|
||||
trackingAccounts: "/v2/douyin/tracking/accounts",
|
||||
trackingDigest: "/v2/douyin/tracking/digest",
|
||||
trackingRefresh: "/v2/douyin/tracking/refresh",
|
||||
trackingCursor: "/v2/douyin/tracking/cursor",
|
||||
trackingAccountRefresh: (trackedAccountId) => `/v2/douyin/tracking/accounts/${encodeURIComponent(trackedAccountId)}/refresh`
|
||||
}
|
||||
routes: makePlatformRoutes("douyin")
|
||||
},
|
||||
xiaohongshu: {
|
||||
label: "小红书",
|
||||
shortLabel: "小红书",
|
||||
workbenchReady: false,
|
||||
pendingText: "小红书工作台待接入"
|
||||
workbenchReady: true,
|
||||
routes: makePlatformRoutes("xiaohongshu")
|
||||
},
|
||||
bilibili: {
|
||||
label: "哔哩哔哩",
|
||||
shortLabel: "B站",
|
||||
workbenchReady: false,
|
||||
pendingText: "B站工作台待接入"
|
||||
workbenchReady: true,
|
||||
routes: makePlatformRoutes("bilibili")
|
||||
},
|
||||
kuaishou: {
|
||||
label: "快手",
|
||||
shortLabel: "快手",
|
||||
workbenchReady: false,
|
||||
pendingText: "快手工作台待接入"
|
||||
workbenchReady: true,
|
||||
routes: makePlatformRoutes("kuaishou")
|
||||
},
|
||||
wechat_video: {
|
||||
label: "微信视频号",
|
||||
shortLabel: "视频号",
|
||||
workbenchReady: false,
|
||||
pendingText: "视频号工作台待接入"
|
||||
workbenchReady: true,
|
||||
routes: makePlatformRoutes("wechat_video")
|
||||
}
|
||||
};
|
||||
const INTEGRATION_META = {
|
||||
@@ -96,6 +100,11 @@ const INTEGRATION_META = {
|
||||
hint: "OpenAI-compatible",
|
||||
impacts: ["账号分析", "高分分析", "文案生成"]
|
||||
},
|
||||
live_recorder: {
|
||||
label: "直播录制",
|
||||
hint: "fnOS NAS",
|
||||
impacts: ["直播源导入", "录制控制"]
|
||||
},
|
||||
cutvideo: {
|
||||
label: "自动剪辑",
|
||||
hint: "Windows cutvideo",
|
||||
@@ -1251,6 +1260,10 @@ function getIntegrationCards() {
|
||||
`<span class="tag clickable-tag" data-action="open-preferred-model">设主模型</span>`
|
||||
].filter(Boolean).join("");
|
||||
}
|
||||
if (key === "live_recorder") {
|
||||
extra = detail.baseUrl ? `服务地址:${detail.baseUrl}` : "当前未配置 NAS 录制服务";
|
||||
actions = `<span class="tag clickable-tag" data-action="open-live-recorder">录制控制</span>`;
|
||||
}
|
||||
return {
|
||||
key,
|
||||
meta,
|
||||
@@ -1286,7 +1299,7 @@ function getIntegrationOverview() {
|
||||
? `自动链路受阻:${blockedActions.length} 项`
|
||||
: `${reachableCount}/${cards.length} 项依赖在线`;
|
||||
const subtitle = !availableCount
|
||||
? "刷新后会显示 cutvideo / huobao / n8n / ASR 的真实状态。"
|
||||
? "刷新后会显示直播录制 / cutvideo / huobao / n8n / ASR 的真实状态。"
|
||||
: blockedActions.length
|
||||
? blockedActions.join(";")
|
||||
: "AI 视频与实拍剪辑链路当前可直接发起。";
|
||||
@@ -3333,6 +3346,38 @@ function openCreateRealCutAction(defaults = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
function openLiveRecorderAction() {
|
||||
const status = getIntegrationDetail("live_recorder");
|
||||
openActionModal({
|
||||
title: "直播录制控制",
|
||||
description: status.reachable
|
||||
? "把直播间链接导入到 NAS 录制服务,必要时直接触发开始录制。"
|
||||
: "当前 NAS 录制服务不可达,先检查集成健康。",
|
||||
submitLabel: "提交到录制服务",
|
||||
fields: [
|
||||
{ name: "raw", label: "直播源", type: "textarea", rows: 4, value: "原画,https://live.kuaishou.com/u/storyforge_anchor", placeholder: "一行一条,例如:原画,https://live.kuaishou.com/u/anchor" },
|
||||
{ name: "autoStart", label: "导入后立即开始", type: "checkbox", value: true }
|
||||
],
|
||||
onSubmit: async (values) => {
|
||||
if (!values.raw?.trim()) throw new Error("请填写直播源链接");
|
||||
const imported = await storyforgeFetch("/v2/live-recorder/url-config/import", {
|
||||
method: "POST",
|
||||
body: { raw: values.raw.trim() }
|
||||
});
|
||||
let started = null;
|
||||
if (values.autoStart) {
|
||||
try {
|
||||
started = await storyforgeFetch("/v2/live-recorder/recorder/start", { method: "POST", body: {} });
|
||||
} catch (error) {
|
||||
started = { ok: false, message: error.message };
|
||||
}
|
||||
}
|
||||
rememberAction("直播录制已下发", "NAS 录制服务已接收最新直播源。", "green", { imported, started });
|
||||
await bootstrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openReviewAction(defaults = {}) {
|
||||
const project = requireSelectedProject();
|
||||
const assistants = getAssistantOptions(project.id);
|
||||
@@ -3449,6 +3494,10 @@ document.addEventListener("click", async (event) => {
|
||||
await refreshTrackingAccountsAction();
|
||||
return;
|
||||
}
|
||||
if (name === "open-live-recorder") {
|
||||
openLiveRecorderAction();
|
||||
return;
|
||||
}
|
||||
if (name === "mark-tracking-read") {
|
||||
await markTrackingDigestRead();
|
||||
rememberAction("日报已标记", "当前跟踪摘要已更新为已读,下次会从新的时间点继续汇总。", "green");
|
||||
|
||||
Reference in New Issue
Block a user