// ———— Mock data ———— const MECHS = ["身份落差", "传承赋能", "情感共鸣", "悬念递进", "反差冲突", "认知颠覆", "群体归属", "代际传承", "弱者逆袭"]; const MECH_COLORS = { "身份落差": "accent", "传承赋能": "purple", "情感共鸣": "warning", "悬念递进": "info", "反差冲突": "danger", "认知颠覆": "success", "群体归属": "neutral", "代际传承": "purple", "弱者逆袭": "danger" }; const TYPES = ["剧情", "反转", "知识", "猎奇", "情绪", "对比"]; const STRUCTURES = ["三段式", "五段式", "悬念结构", "反转结尾"]; const PLATFORMS = [ { id: "yt", label: "YT", name: "YouTube", cls: "platform-yt" }, { id: "tt", label: "TT", name: "TikTok", cls: "platform-tt" }, { id: "bl", label: "BL", name: "B站", cls: "platform-bl" }, ]; const CHANNEL_NAMES = [ "晚风故事集", "十点剪辑工坊", "老陈说创作", "野生剧本家", "周更剪刀手", "纪录片之眼", "镜头下的生活", "分钟观察", "阿杜做内容", "小林讲剧本", "老K工作室", "三分钟剧作" ]; const VIDEO_TITLES = [ "离家十五年的父亲回来了,家里没人认识他", "凌晨三点的急诊科,医生说了这样一句话", "奶奶偷偷藏在樟木箱里的旧信,揭开了半个世纪的秘密", "外卖小哥接了一单,送到的地方让他愣住了", "她在婚礼前一天,收到了一个陌生快递", "我花三年拍了一条没人看的短片,结果转机出现在第1037天", "同一道菜,在三家店吃下来,才明白差别在哪里", "关掉所有字幕,你还能看懂短视频吗", "教练说这个动作我练了12年,今天才做对", "从月薪3000到30万,这一年我做对了什么", "他把爷爷留下的老怀表修好了,里面有张字条", "地铁上陌生人塞给我一张纸,回家我哭了一整夜", "开了8年的面馆要关门,最后一天来了意想不到的客人", "装修完发现少了一堵墙,才知道是我爸留给我的", "演了300个龙套,今天我终于有了第一句台词", "从北京回到县城,我爸说了一句让我破防的话", "一条没被刷到的爆款,我扒了它三天三夜", "摆摊的大爷每天只卖20份,理由让全网沉默", "被裁员那天,同事说的话比HR还扎心", "她妈妈把所有朋友圈都删了,只留下这一条" ]; function seeded(i) { return (x) => ((Math.sin(i * 9999 + x * 31) + 1) * 500000) | 0; } const MOCK_VIDEOS = Array.from({ length: 54 }, (_, i) => { const r = seeded(i); const plat = PLATFORMS[i % 3]; const mechCount = 2 + (r(1) % 3); const mechs = Array.from({ length: mechCount }, (_, j) => MECHS[(r(j+2) % MECHS.length)]).filter((m,j,a)=>a.indexOf(m)===j); const score = 55 + (r(5) % 40); const views = 10000 + (r(6) % 9000000); const duration = 30 + (r(7) % 540); const days = r(8) % 200; const publishedDate = new Date(Date.now() - days * 86400000); return { id: `v${(1000 + i).toString(36)}`, title: VIDEO_TITLES[i % VIDEO_TITLES.length], channel: CHANNEL_NAMES[i % CHANNEL_NAMES.length], platform: plat, mechanisms: mechs, type: TYPES[i % TYPES.length], structure: STRUCTURES[i % STRUCTURES.length], score, views, duration, publishedAt: publishedDate, thumbHue: 220 + (i * 13) % 120, radar: { "故事逻辑": 60 + (r(9) % 35), "画面质量": 55 + (r(10) % 40), "角色一致性": 50 + (r(11) % 45), "节奏匹配": 58 + (r(12) % 38), }, emotionPath: ["好奇", "认同", "冲击", "释怀", "共鸣"].slice(0, 3 + (r(13) % 3)), conflicts: [ { label: "表层信息矛盾", level: 30 + (r(14)%40) }, { label: "人物关系错位", level: 40 + (r(15)%40) }, { label: "价值观正面冲撞", level: 50 + (r(16)%45) }, { label: "身份真相揭露", level: 60 + (r(17)%35) }, ].slice(0, 3 + (r(18)%2)), transferable: [ "可复用冷开场:3秒内抛出身份反差", "情绪锚点可换:将家庭线换为师徒线", "结尾留白可替换为开放式提问", ], metricsHistory: Array.from({ length: 24 }, (_, h) => ({ t: new Date(Date.now() - (24 - h) * 3600 * 1000 / 2), v: Math.max(0, (views * (0.2 + h / 28)) * (0.85 + ((r(h+20) % 30) / 100))) | 0 })), }; }); const MOCK_JOBS = [ { id: "j-e7a2", title: "开了8年的面馆要关门,最后一天来了意想不到的客人", platform: PLATFORMS[0], status: "完成", progress: 100, createdAt: "2 分钟前" }, { id: "j-bb91", title: "地铁上陌生人塞给我一张纸,回家我哭了一整夜", platform: PLATFORMS[1], status: "分析中", progress: 72, createdAt: "刚刚" }, { id: "j-4410", title: "关键词:「传承赋能 三段式」 · 自动采集 20 条", platform: PLATFORMS[2], status: "采集中", progress: 38, createdAt: "刚刚", isKeyword: true }, { id: "j-9c3f", title: "他把爷爷留下的老怀表修好了,里面有张字条", platform: PLATFORMS[0], status: "完成", progress: 100, createdAt: "12 分钟前" }, { id: "j-2d7e", title: "https://www.tiktok.com/@user/video/730...", platform: PLATFORMS[1], status: "失败", progress: 45, createdAt: "25 分钟前", error: "HTTP 403 · 视频不可访问或已被作者删除(TikTokDownloader timeout 30s)" }, { id: "j-150a", title: "从北京回到县城,我爸说了一句让我破防的话", platform: PLATFORMS[0], status: "完成", progress: 100, createdAt: "1 小时前" }, { id: "j-88c2", title: "凌晨三点的急诊科,医生说了这样一句话", platform: PLATFORMS[2], status: "待采集", progress: 0, createdAt: "1 小时前" }, ]; const HERMES_INSIGHT = `根据知识库中 127 条语义相关视频的分析,**「传承赋能 × 三段式 × 情感共鸣」** 这一组合在近 90 天的可复刻爆款池中出现频率为 **18.3%**,高于整体基准 2.4 倍。 核心规律如下: - **冷开场 3 秒必须建立身份反差**(例如:普通外壳 / 不普通的物件、空间、关系)。命中该规律的样本 CTR 中位数 **11.2%**,未命中仅 4.6%。 - **第二幕转折锚点偏好「信物 / 手写字 / 遗留物品」**,共出现在 73% 的高分样本中;相比纯对话转折,观看完成率高出 **14 个百分点**。 - **结尾处理**:开放式提问或留白 > 明确说教。高分样本中 81% 采用前者,完播后互动率为后者的 1.9 倍。 建议优先复刻 \`v1003\`、\`v100n\`、\`v100z\` 三条样本——它们在雷达图四个维度上的方差都小于 0.12,代表该方向上执行稳定性最高。`; window.MECHS = MECHS; window.MECH_COLORS = MECH_COLORS; window.TYPES = TYPES; window.STRUCTURES = STRUCTURES; window.PLATFORMS = PLATFORMS; window.MOCK_VIDEOS = MOCK_VIDEOS; window.MOCK_JOBS = MOCK_JOBS; window.HERMES_INSIGHT = HERMES_INSIGHT; function formatViews(n) { if (n >= 10000) return (n / 10000).toFixed(n >= 100000 ? 0 : 1) + "w"; return n.toString(); } function formatDuration(sec) { const m = Math.floor(sec / 60), s = sec % 60; return `${m}:${s.toString().padStart(2,"0")}`; } function formatDate(d) { const dd = typeof d === "string" ? new Date(d) : d; return `${dd.getFullYear()}-${(dd.getMonth()+1).toString().padStart(2,"0")}-${dd.getDate().toString().padStart(2,"0")}`; } function formatDateTime(d) { const dd = typeof d === "string" ? new Date(d) : d; return `${dd.getMonth()+1}/${dd.getDate()} ${dd.getHours().toString().padStart(2,"0")}:${dd.getMinutes().toString().padStart(2,"0")}:${dd.getSeconds().toString().padStart(2,"0")}`; } window.formatViews = formatViews; window.formatDuration = formatDuration; window.formatDate = formatDate; window.formatDateTime = formatDateTime;