diff --git a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/.feishu_tokens.json b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/.feishu_tokens.json index 2d78a169..ca7c1714 100644 --- a/02_卡人(水)/水桥_平台对接/飞书管理/脚本/.feishu_tokens.json +++ b/02_卡人(水)/水桥_平台对接/飞书管理/脚本/.feishu_tokens.json @@ -1,6 +1,6 @@ { - "access_token": "u-dFTTY7qHFbSHNUsIRa4NqClh1ez1ghohVMGaZxk0274E", - "refresh_token": "ur-ePrUCxaTV8ipHNjZ.XICSYlh3A11ghOjr0GaVwk0271J", + "access_token": "u-elJqv3tWdeGrDSyG63Up4Vlh1KzxghqXN0GaENk0260E", + "refresh_token": "ur-eNhTsg0X9f5a7kk6iFBlqDlh1e1xghOrNwGaJB40261Y", "name": "飞书用户", - "auth_time": "2026-03-20T11:02:06.903826" + "auth_time": "2026-03-20T21:02:49.258622" } \ No newline at end of file diff --git a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/publish_log.json b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/publish_log.json index c48b2273..30c7a897 100644 --- a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/publish_log.json +++ b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/publish_log.json @@ -240,3 +240,27 @@ {"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/深度AI模型对比 哪个才是真正的AI不是语言模型.mp4", "title": "深度对比各大AI模型,哪个才是真正的智能而不只是语言模型", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.21168994903564453, "timestamp": "2026-03-20 05:40:07"} {"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/疗愈师配AI助手能收多少钱 一个小团队5万到10万.mp4", "title": "疗愈师+AI助手组合,一个小团队月收5万到10万", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.18154072761535645, "timestamp": "2026-03-20 05:40:10"} {"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/赚钱没那么复杂,自信心才是核心问题.mp4", "title": "获得收益真没那么复杂,自信心才是卡住你的核心问题", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.19428515434265137, "timestamp": "2026-03-20 05:40:13"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/127场推流273万,进房4万6,流量密码就这几个.mp4", "title": "场推流273万,进房4万6,流量密码就这几个", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 4.230232000350952, "timestamp": "2026-03-20 16:37:06"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/不学AI连工作都找不到了,学历不重要,干货和AI实操最重要.mp4", "title": "不学AI连工作都找不到了,学历不重要,干货和AI实操最重要", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 2.2930691242218018, "timestamp": "2026-03-20 16:37:12"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/保镖服务后端才是真赚钱,接触高端客户赚信任关系的钱.mp4", "title": "保镖服务后端才是真获得收益,接触高端客户赚信任关系的钱", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.320681095123291, "timestamp": "2026-03-20 16:37:15"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/分对方他挣不到的钱,员工才愿意跟你干.mp4", "title": "分对方他挣不到的钱,员工才愿意跟你干", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.489332914352417, "timestamp": "2026-03-20 16:37:19"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/想拿2万工资,AI月消耗至少1000块,这是硬指标.mp4", "title": "想拿2万工资,AI月消耗至少1000块,这是硬指标", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 1.1175451278686523, "timestamp": "2026-03-20 16:37:23"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/流量端和交付端别同时扛,缺流量就做群主开派对.mp4", "title": "流量端和交付端别同时扛,缺流量就做群主开派对", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.36455702781677246, "timestamp": "2026-03-20 16:37:26"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/游戏辅助AI模型,高收益高风险,3到6个月必须收手.mp4", "title": "游戏辅助AI模型,高收益高风险,3到6个月必须收手", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.8829331398010254, "timestamp": "2026-03-20 16:37:30"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/面试三面流程 简历+测试+试岗,300份筛到2到3个人.mp4", "title": "面试三面流程 简历+测试+试岗,300份筛到2到3个人", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.15245604515075684, "timestamp": "2026-03-20 16:37:33"} +{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/127场推流273万,进房4万6,流量密码就这几个.mp4", "title": "场推流273万,进房4万6,流量密码就这几个", "success": false, "status": "failed", "message": "Playwright: 未找到上传控件", "elapsed_sec": 6.741008043289185, "timestamp": "2026-03-20 16:37:19"} +{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/不学AI连工作都找不到了,学历不重要,干货和AI实操最重要.mp4", "title": "不学AI连工作都找不到了,学历不重要,干货和AI实操最重要", "success": true, "status": "reviewing", "message": "纯API投稿成功 (7.4s)", "elapsed_sec": 7.3929102420806885, "timestamp": "2026-03-20 16:37:30"} +{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/保镖服务后端才是真赚钱,接触高端客户赚信任关系的钱.mp4", "title": "保镖服务后端才是真获得收益,接触高端客户赚信任关系的钱", "success": true, "status": "reviewing", "message": "纯API投稿成功 (2.2s)", "elapsed_sec": 2.208482027053833, "timestamp": "2026-03-20 16:37:35"} +{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/分对方他挣不到的钱,员工才愿意跟你干.mp4", "title": "分对方他挣不到的钱,员工才愿意跟你干", "success": true, "status": "reviewing", "message": "纯API投稿成功 (3.4s)", "elapsed_sec": 3.445220947265625, "timestamp": "2026-03-20 16:37:42"} +{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/想拿2万工资,AI月消耗至少1000块,这是硬指标.mp4", "title": "想拿2万工资,AI月消耗至少1000块,这是硬指标", "success": true, "status": "reviewing", "message": "纯API投稿成功 (2.0s)", "elapsed_sec": 1.978193998336792, "timestamp": "2026-03-20 16:37:47"} +{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/流量端和交付端别同时扛,缺流量就做群主开派对.mp4", "title": "流量端和交付端别同时扛,缺流量就做群主开派对", "success": true, "status": "reviewing", "message": "纯API投稿成功 (2.1s)", "elapsed_sec": 2.136302947998047, "timestamp": "2026-03-20 16:37:52"} +{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/游戏辅助AI模型,高收益高风险,3到6个月必须收手.mp4", "title": "游戏辅助AI模型,高收益高风险,3到6个月必须收手", "success": true, "status": "reviewing", "message": "纯API投稿成功 (2.5s)", "elapsed_sec": 2.5074386596679688, "timestamp": "2026-03-20 16:37:57"} +{"platform": "B站", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/面试三面流程 简历+测试+试岗,300份筛到2到3个人.mp4", "title": "面试三面流程 简历+测试+试岗,300份筛到2到3个人", "success": true, "status": "reviewing", "message": "纯API投稿成功 (2.9s)", "elapsed_sec": 2.86631178855896, "timestamp": "2026-03-20 16:38:03"} +{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/127场推流273万,进房4万6,流量密码就这几个.mp4", "title": "场推流273万,进房4万6,流量密码就这几个", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 54.56508994102478, "timestamp": "2026-03-20 16:38:24"} +{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/不学AI连工作都找不到了,学历不重要,干货和AI实操最重要.mp4", "title": "不学AI连工作都找不到了,学历不重要,干货和AI实操最重要", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 58.11296081542969, "timestamp": "2026-03-20 16:39:49"} +{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/保镖服务后端才是真赚钱,接触高端客户赚信任关系的钱.mp4", "title": "保镖服务后端才是真获得收益,接触高端客户赚信任关系的钱", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 57.71816396713257, "timestamp": "2026-03-20 16:41:13"} +{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/分对方他挣不到的钱,员工才愿意跟你干.mp4", "title": "分对方他挣不到的钱,员工才愿意跟你干", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 50.6455979347229, "timestamp": "2026-03-20 16:42:31"} +{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/想拿2万工资,AI月消耗至少1000块,这是硬指标.mp4", "title": "想拿2万工资,AI月消耗至少1000块,这是硬指标", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 49.91238975524902, "timestamp": "2026-03-20 16:43:47"} +{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/流量端和交付端别同时扛,缺流量就做群主开派对.mp4", "title": "流量端和交付端别同时扛,缺流量就做群主开派对", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 49.58630394935608, "timestamp": "2026-03-20 16:45:03"} +{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/游戏辅助AI模型,高收益高风险,3到6个月必须收手.mp4", "title": "游戏辅助AI模型,高收益高风险,3到6个月必须收手", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 49.622374057769775, "timestamp": "2026-03-20 16:46:20"} +{"platform": "小红书", "video_path": "/Users/karuo/Movies/soul视频/第127场_20260318_output/成片/面试三面流程 简历+测试+试岗,300份筛到2到3个人.mp4", "title": "面试三面流程 简历+测试+试岗,300份筛到2到3个人", "success": true, "status": "likely_published", "message": "发布按钮+确认已点击,视频可能仍在处理", "screenshot": "/tmp/xhs_result.png", "elapsed_sec": 49.556177854537964, "timestamp": "2026-03-20 16:47:36"} diff --git a/03_卡木(木)/木叶_视频内容/小红书发布/脚本/xiaohongshu_storage_state.json b/03_卡木(木)/木叶_视频内容/小红书发布/脚本/xiaohongshu_storage_state.json index 72bc19d2..a084c952 100644 --- a/03_卡木(木)/木叶_视频内容/小红书发布/脚本/xiaohongshu_storage_state.json +++ b/03_卡木(木)/木叶_视频内容/小红书发布/脚本/xiaohongshu_storage_state.json @@ -1 +1 @@ -{"cookies": [{"name": "xsecappid", "value": "ugc", "domain": ".xiaohongshu.com", "path": "/", "expires": 1805164157, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "a1", "value": "19cd7bc1c751fyh9lntys4dzu93cfib0sfovi8mcw30000273597", "domain": ".xiaohongshu.com", "path": "/", "expires": 1804681955, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "webId", "value": "6a6cd657224a4e256b12b847a79711c4", "domain": ".xiaohongshu.com", "path": "/", "expires": 1804681955, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "gid", "value": "yjSfWDSJjJMSyjSfWDSySiY6W2yivxj1I9vMlSE4fu7jqhq8Si3D83888JWq2jW8SiJjJii0", "domain": ".xiaohongshu.com", "path": "/", "expires": 1808188160.764985, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "customer-sso-sid", "value": "68c517615603922284478469rptt2mwjgogfti8m", "domain": ".xiaohongshu.com", "path": "/", "expires": 1773750762.487704, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "x-user-id-creator.xiaohongshu.com", "value": "63b3cb6f000000002502c21d", "domain": ".xiaohongshu.com", "path": "/", "expires": 1807705963.487751, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "customerClientId", "value": "172893742756372", "domain": ".xiaohongshu.com", "path": "/", "expires": 1807705963.487788, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "access-token-creator.xiaohongshu.com", "value": "customer.creator.AT-68c5176156039222844620815tecrfvr71dngizv", "domain": ".xiaohongshu.com", "path": "/", "expires": 1775737962.4878, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "galaxy_creator_session_id", "value": "6ZdVvWjMwbN5FGk6Q39utKCwjaXS4uP7UvNj", "domain": ".xiaohongshu.com", "path": "/", "expires": 1775737963.487811, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "galaxy.creator.beaker.session.id", "value": "1773145963473064900889", "domain": ".xiaohongshu.com", "path": "/", "expires": 1775737963.487825, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "acw_tc", "value": "0a0d0f5817736274379594315eccc8d5088e687da96a9f27ed434be21432e3", "domain": "creator.xiaohongshu.com", "path": "/", "expires": 1773629237.87898, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "acw_tc", "value": "0a8f06dc17736274409275985e8bf0e139f6ce6a67d4b20cce0acfbdb40a40", "domain": "edith.xiaohongshu.com", "path": "/", "expires": 1773629240.846318, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "acw_tc", "value": "0a00dc2617736276792481330ec82f480cedb4746b7896d4dd8a4e2c0cdb96", "domain": "www.xiaohongshu.com", "path": "/", "expires": 1773629479.183852, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "websectiga", "value": "88%3B6be45f388a1ee7bf611a69f3e174cae48f1ea02c0f8ec3256031b8be9c7ee", "domain": ".xiaohongshu.com", "path": "/", "expires": 1773887243, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "sec_poison_id", "value": "fea9130c-5c87-4b16-8db4-d17d9deb8eab", "domain": ".xiaohongshu.com", "path": "/", "expires": 1773628648, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "loadts", "value": "1773628157941", "domain": ".xiaohongshu.com", "path": "/", "expires": 1805164157, "httpOnly": false, "secure": false, "sameSite": "Lax"}], "origins": [{"origin": "https://creator.xiaohongshu.com", "localStorage": [{"name": "USER_INFO_FOR_BIZ", "value": "{\"userId\":\"63b3cb6f000000002502c21d\",\"userName\":\"\u5c0f\u7ea2\u85af69B40CFE\",\"userAvatar\":\"https://sns-avatar-qc.xhscdn.com/avatar/645b7c0d1d74d41345e61461.jpg?imageView2/2/w/80/format/jpg\",\"redId\":\"27206593567\",\"role\":\"creator\",\"permissions\":[\"creatorCollege\",\"creatorWiki\",\"noteInspiration\",\"creatorHome\",\"creatorData\",\"USER_GROUP\",\"creatorActivityCenter\",\"ORIGINAL_STATEMENT\"],\"zone\":\"86\",\"phone\":\"15880802661\",\"relatedUserId\":null,\"relatedUserName\":null,\"realNameVerified\":false,\"realNameVerifyLink\":\"xhsdiscover://account/realName/atomization?businessCode=capa_podcast\",\"longAudioConfig\":{\"audioMinSeconds\":600,\"audioFileMaxSize\":1024,\"audioMaxSeconds\":7200,\"hasAudio\":false,\"summaryMaxDurationSeconds\":10800,\"needRealName\":false},\"kolCoOrder\":false}"}, {"name": "uploader-permit-video-spectrum", "value": "{\"MBTI\u7597\u6108SOUL \u5e74\u8f7b\u4eba\u6d4bMBTI\uff0c40\u523060\u5c81\u8d70\u4e94\u884c\u516b\u5366.mp4-10459668\":null,\"Soul\u4e1a\u52a1\u6a21\u578b \u6d3e\u5bf9+\u5207\u7247+\u5c0f\u7a0b\u5e8f\u5168\u94fe\u8def.mp4-10740368\":null,\"Soul\u5207\u724730\u79d2\u52308\u5206\u949f AI\u534a\u5c0f\u65f6\u80fd\u526a10\u523030\u4e2a.mp4-14677795\":null,\"\u521d\u671f\u56e2\u961f\u5148\u627e\u4e24\u4e2aIS\uff0c\u6bd4\u94b1\u597d\u4f7f ENFJ\u94fe\u63a5\u4eba\uff0cENTJ\u6307\u6325.mp4-15709935\":null,\"\u5237\u7259\u542c\u4e1a\u52a1\u903b\u8f91 Soul\u5207\u7247\u53d8\u73b0\u600e\u4e48\u8dd1.mp4-11455089\":null,\"\u56fd\u5b66\u6613\u7ecf\u600e\u4e48\u5b66 \u4e24\u5c0f\u65f6\u4e03\u4e03\u516b\u516b\uff0c\u53ec\u5524\u4f5c\u8005\u5bf9\u8bdd.mp4-9598781\":null,\"\u5efa\u7acb\u4fe1\u4efb\u4e0d\u662f\u6c42\u6765\u7684 \u5356\u5916\u6302\u53d1\u90ae\u4ef6\u4e09\u4e2a\u6708\u62ff\u4e0b\u5fb7\u56fd\u603b\u4ee3.mp4-10091673\":null,\"\u61d2\u4eba\u7684\u6d3b\u6cd5 \u52a8\u4f5c\u7b80\u5355\u6709\u5229\u53ef\u56fe\u6b63\u53cd\u9988.mp4-7059868\":null,\"\u65e9\u8d77\u4e0d\u662f\u4e3a\u4e86\u5f00\u6d3e\u5bf9\uff0c\u662f\u4e0d\u5435\u8001\u5a46\u7761\u89c9.mp4-5788932\":null,\"\u7761\u7720\u4e0d\u597d\uff1f\u6bcf\u5929\u653e\u4e0b\u4e00\u4ef6\u4e8b\uff0c\u505a\u51cf\u6cd5.mp4-7022452\":null,\"\u91d1\u878dAI\u83b7\u5ba2\u4f53\u7cfb \u540e\u7aef30\u4eba\u6c89\u6dc012\u5e74\uff0c\u524d\u7aef\u4e22\u624b\u673a.mp4-13904487\":null,\"AI\u6700\u5927\u7684\u7f3a\u70b9\u662f\u4e0a\u4e0b\u6587\u592a\u77ed\uff0c\u8fd9\u6837\u6765\u89e3\u51b3.mp4-3645225\":null,\"AI\u6bcf\u5929\u526a1000\u4e2a\u89c6\u9891 M4\u7535\u811124T\u7d20\u6750\u5e93\u5168\u7f51\u5206\u53d1.mp4-13368696\":null,\"Soul\u6d3e\u5bf9\u53d8\u73b0\u5168\u94fe\u8def \u53d1\u89c6\u9891\u5c31\u6709\u94b1\uff0c\u540e\u7aef\u5168\u89e3\u51b3.mp4-10194469\":null,\"\u4ece0\u5230\u5207\u7247\u53d1\u5e03 AI\u81ea\u52a8\u5b8c\u6210\u6bcf\u5929\u526f\u4e1a30\u6761\u89c6\u9891.mp4-7241847\":null,\"\u505a\u526f\u4e1a\u7684\u57fa\u672c\u6761\u4ef6 \u82f9\u679c\u7535\u8111\u548c\u7279\u6b8a\u8bbf\u95ee\u5de5\u5177.mp4-9735083\":null,\"\u5207\u7247\u5206\u53d1\u5168\u81ea\u52a8\u5316 \u4ece\u89c6\u9891\u5230\u53d1\u5e03\u4e00\u952e\u5b8c\u6210.mp4-14798594\":null,\"\u521b\u4e1a\u56e2\u961f4\u4eba\u5e73\u520625\u6709\u5565\u5371\u9669 \u5148\u8dd1\u94b1\u518d\u8c08\u80a1\u6743.mp4-10580437\":null,\"\u575a\u6301\u5230120\u573a\u662f\u4ec0\u4e48\u611f\u89c9 \u65b9\u5411\u8d8a\u786e\u5b9a\u6267\u884c\u8d8a\u575a\u51b3.mp4-9157815\":null,\"\u5e2e\u4eba\u88c5AI\u4e00\u5355300\u52301000\u5757\uff0c\u4f20\u7edf\u884c\u4e1a\u4e5f\u80fd\u505a.mp4-10538224\":null,\"\u6df1\u5ea6AI\u6a21\u578b\u5bf9\u6bd4 \u54ea\u4e2a\u624d\u662f\u771f\u6b63\u7684AI\u4e0d\u662f\u8bed\u8a00\u6a21\u578b.mp4-8952361\":null,\"\u7597\u6108\u5e08\u914dAI\u52a9\u624b\u80fd\u6536\u591a\u5c11\u94b1 \u4e00\u4e2a\u5c0f\u56e2\u961f5\u4e07\u523010\u4e07.mp4-11958241\":null,\"\u8d5a\u94b1\u6ca1\u90a3\u4e48\u590d\u6742\uff0c\u81ea\u4fe1\u5fc3\u624d\u662f\u6838\u5fc3\u95ee\u9898.mp4-11176359\":null,\"AI\u6700\u5927\u7684\u7f3a\u70b9\u662f\u4e0a\u4e0b\u6587\u592a\u77ed\uff0c\u8fd9\u6837\u6765\u89e3\u51b3.mp4-7521601\":null,\"AI\u6bcf\u5929\u526a1000\u4e2a\u89c6\u9891 M4\u7535\u811124T\u7d20\u6750\u5e93\u5168\u7f51\u5206\u53d1.mp4-27929301\":null,\"Soul\u6d3e\u5bf9\u53d8\u73b0\u5168\u94fe\u8def \u53d1\u89c6\u9891\u5c31\u6709\u94b1\uff0c\u540e\u7aef\u5168\u89e3\u51b3.mp4-21135131\":null,\"\u4ece0\u5230\u5207\u7247\u53d1\u5e03 AI\u81ea\u52a8\u5b8c\u6210\u6bcf\u5929\u526f\u4e1a30\u6761\u89c6\u9891.mp4-15182878\":null,\"\u505a\u526f\u4e1a\u7684\u57fa\u672c\u6761\u4ef6 \u82f9\u679c\u7535\u8111\u548c\u7279\u6b8a\u8bbf\u95ee\u5de5\u5177.mp4-19575651\":null,\"\u5207\u7247\u5206\u53d1\u5168\u81ea\u52a8\u5316 \u4ece\u89c6\u9891\u5230\u53d1\u5e03\u4e00\u952e\u5b8c\u6210.mp4-30731220\":null,\"\u521b\u4e1a\u56e2\u961f4\u4eba\u5e73\u520625\u6709\u5565\u5371\u9669 \u5148\u8dd1\u94b1\u518d\u8c08\u80a1\u6743.mp4-21423166\":null,\"\u575a\u6301\u5230120\u573a\u662f\u4ec0\u4e48\u611f\u89c9 \u65b9\u5411\u8d8a\u786e\u5b9a\u6267\u884c\u8d8a\u575a\u51b3.mp4-19367933\":null,\"\u5e2e\u4eba\u88c5AI\u4e00\u5355300\u52301000\u5757\uff0c\u4f20\u7edf\u884c\u4e1a\u4e5f\u80fd\u505a.mp4-21608518\":null,\"\u6df1\u5ea6AI\u6a21\u578b\u5bf9\u6bd4 \u54ea\u4e2a\u624d\u662f\u771f\u6b63\u7684AI\u4e0d\u662f\u8bed\u8a00\u6a21\u578b.mp4-18471791\":null,\"\u7597\u6108\u5e08\u914dAI\u52a9\u624b\u80fd\u6536\u591a\u5c11\u94b1 \u4e00\u4e2a\u5c0f\u56e2\u961f5\u4e07\u523010\u4e07.mp4-24461141\":null,\"\u8d5a\u94b1\u6ca1\u90a3\u4e48\u590d\u6742\uff0c\u81ea\u4fe1\u5fc3\u624d\u662f\u6838\u5fc3\u95ee\u9898.mp4-22996736\":null,\"Cursor\u7684\u6743\u9650\u95ee\u9898\uff0c\u5b89\u5168\u9690\u60a3\u5fc5\u987b\u63d0\u524d\u8bb2\u6e05\u695a.mp4-16471860\":null,\"NFC\u78b0\u4e00\u78b0\u5f15\u6d41\uff0c\u7ebf\u4e0b\u9910\u996e\u5e97\u7528\u8fd9\u62db\u5c31\u591f\u4e86.mp4-19205962\":null,\"Skill\u548cCursor\u7684\u533a\u522b\uff0c\u4e00\u4e2a\u8d70\u5de5\u4f5c\u6d41\u4e00\u4e2a\u8d70\u5bf9\u8bdd.mp4-18869064\":null,\"Soul\u6d3e\u5bf9\u6bd4\u6296\u97f3\u7701\u529b\u592a\u591a\uff0c\u8fde\u9ea6\u673a\u5236\u662f\u6838\u5fc3\u5dee\u5f02.mp4-14633930\":null,\"\u505aAPI\u805a\u5408\u7ed9\u522b\u4eba\u5145\u503c\uff0c\u8fd9\u662f\u53e6\u4e00\u79cdAI\u53d8\u73b0\u8def\u5f84.mp4-15571922\":null,\"\u5e2e\u522b\u4eba\u88c5AI\u5de5\u5177\u5c31\u80fd\u8d5a\u94b1\uff0c\u5de5\u4f5c\u6d41\u624d\u662f\u53d8\u73b0\u5165\u53e3.mp4-17272200\":null,\"\u672c\u5730\u90e8\u7f72\u5927\u6a21\u578b\u5230\u5e95\u884c\u4e0d\u884c\uff0c\u5c0f\u4e8b\u53ef\u4ee5\u5927\u4e8b\u522b\u60f3.mp4-17798341\":null,\"\u6bcf\u4e2a\u6d88\u8d39\u8005\u90fd\u662f\u4f60\u7684\u6d41\u91cf\u5165\u53e3\uff0c\u78b0\u4e00\u78b0\u5c31\u80fd\u8fde\u63a5.mp4-18930706\":null,\"\u7535\u7ade19\u5e74\u7ecf\u9a8c\u52a0AI\uff0c\u8de8\u754c\u7ed3\u5408\u624d\u6709\u771f\u6b63\u7684\u62a4\u57ce\u6cb3.mp4-19210847\":null,\"AI\u4e00\u90e8\u52673000\u52305000\uff0c100\u90e8\u771f\u4eba\u5267\u7684\u6210\u672c\u505a100\u90e8AI\u5267.mp4-12071079\":null,\"AI\u6559\u57f9\u662f\u771f\u7684\u53ef\u4ee5\u505a\uff0c\u4f20\u7edf\u57f9\u8bad\u884c\u4e1a\u6b63\u5728\u88ab\u6539\u9020.mp4-12437677\":null,\"\u4e00\u4e2aSoul\u53f7\u4e00\u4e2a\u6708\u6323\u51e0\u4e07\u5757\uff0c\u8fd9\u4e5f\u662f\u4e2a\u5c0f\u751f\u610f.mp4-12801915\":null,\"\u4e0d\u7528\u4e0a\u4ec0\u4e48\u6a21\u5f0f\uff0c\u7ecf\u8425\u597d\u4f60\u7684\u4eba\u5c31\u591f\u4e86.mp4-12312838\":null,\"\u4e91\u8fde\u9501\u6982\u5ff5\u592a\u725b\u4e86\uff0c\u4e0d\u9700\u8981\u5f00\u5e97\u4e5f\u80fd\u505a\u54c1\u724c.mp4-12566164\":null,\"\u4f9b\u5e94\u94fe\u52a0\u54c1\u724c\u5e97\uff0c\u540e\u7aef\u624d\u662f\u771f\u6b63\u8d5a\u94b1\u7684\u5730\u65b9.mp4-12673751\":null,\"\u5356Cursor\u5b89\u88c5\u5305\u6ca1\u6709\u540e\u7aef\u4f60\u505a\u4e0d\u4e86.mp4-12722106\":null,\"\u592a\u65e9\u5f15\u5165\u8d44\u672c\u4f60\u7684\u65b9\u5411\u5c31\u4e0d\u662f\u4f60\u8bf4\u4e86\u7b97.mp4-11906382\":null,\"\u6559\u57f9\u884c\u4e1a\u516d\u4e03\u5e74\u7ecf\u9a8c \u505a\u597d\u81ea\u5df1\u672c\u5e97\u624d\u662f\u6838\u5fc3.mp4-12800709\":null,\"\u7ed9\u5b66\u6821\u514d\u8d39\u57f9\u8bad\uff0c\u540e\u7aef\u5356\u8bbe\u5907\u624d\u662f\u771f\u751f\u610f.mp4-12684441\":null}"}, {"name": "publish-uploader-history-upload-speed", "value": "[{\"speed\":[4.361158432708688],\"domain\":\"ros-upload.xiaohongshu.com\",\"timestamp\":1773628108655},{\"speed\":[3.2569974554707377],\"domain\":\"ros-upload-d4.xhscdn.com\",\"timestamp\":1773628110227}]"}, {"name": "snsWebPublishCurrentUser", "value": "63b3cb6f000000002502c21d"}, {"name": "USER_INFO", "value": "{\"user\":{\"type\":\"User\",\"value\":{\"userId\":\"63b3cb6f000000002502c21d\",\"loginUserType\":\"creator\"}}}"}, {"name": "NEW_XHS_ABTEST_REPORT_KEY", "value": "{\"6a6cd657224a4e256b12b847a79711c463b3cb6f000000002502c21d\":\"2026-03-16\"}"}, {"name": "b1b1", "value": "1"}, {"name": "score_display", "value": "1"}, {"name": "p1", "value": "114"}, {"name": "nps-userId", "value": "63b3cb6f000000002502c21d"}, {"name": "_speedList", "value": "[{\"ts\":1773627816968,\"speed\":112159634},{\"ts\":1773627816987,\"speed\":219536283.33333334},{\"ts\":1773627891054,\"speed\":83554191},{\"ts\":1773627891068,\"speed\":218034844.33333334},{\"ts\":1773627964719,\"speed\":107255965.66666667},{\"ts\":1773627964734,\"speed\":213068750},{\"ts\":1773628038685,\"speed\":115873103},{\"ts\":1773628038700,\"speed\":146894922},{\"ts\":1773628112503,\"speed\":106507606},{\"ts\":1773628112518,\"speed\":213133355.33333334}]"}, {"name": "score_timestamp", "value": "1773192526341"}, {"name": "last_tiga_update_time", "value": "1773628043286"}, {"name": "uploader-permit-image-spectrum", "value": "{\"cover.jpeg-304598\":null,\"cover.jpeg-271507\":null,\"cover.jpeg-297124\":null,\"cover.jpeg-295955\":null,\"cover.jpeg-287062\":null,\"cover.jpeg-293020\":null,\"cover.jpeg-308663\":null,\"cover.jpeg-305977\":null,\"cover.jpeg-273401\":null,\"cover.jpeg-282376\":null,\"cover.jpeg-265803\":null,\"cover.jpeg-281603\":null,\"cover.jpeg-262701\":null,\"cover.jpeg-257765\":null,\"cover.jpeg-258311\":null,\"cover.jpeg-274588\":null,\"cover.jpeg-275845\":null,\"cover.jpeg-315902\":null,\"cover.jpeg-299788\":null,\"cover.jpeg-287571\":null,\"cover.jpeg-289713\":null,\"cover.jpeg-278856\":null,\"cover.jpeg-297410\":null,\"cover.jpeg-302252\":null,\"cover.jpeg-295517\":null,\"cover.jpeg-280128\":null,\"cover.jpeg-263507\":null,\"cover.jpeg-263797\":null,\"cover.jpeg-258601\":null,\"cover.jpeg-290089\":null,\"cover.jpeg-278428\":null,\"cover.jpeg-285963\":null,\"cover.jpeg-272752\":null,\"cover.jpeg-272048\":null,\"cover.jpeg-178397\":null,\"cover.jpeg-170888\":null,\"cover.jpeg-155576\":null,\"cover.jpeg-165689\":null,\"cover.jpeg-165572\":null,\"cover.jpeg-150213\":null,\"cover.jpeg-159266\":null,\"cover.jpeg-168582\":null,\"cover.jpeg-163136\":null}"}, {"name": "sdt_source_storage_key", "value": "{\"commonPatch\":[\"/fe_api/burdock/v2/note/post\",\"/api/sns/web/v1/comment/post\",\"/api/sns/web/v1/note/like\",\"/api/sns/web/v1/note/collect\",\"/api/sns/web/v1/user/follow\",\"/api/sns/web/v1/feed\",\"/api/sns/web/v1/login/activate\",\"/api/sns/web/v1/note/metrics_report\",\"/api/redcaptcha\",\"/api/store/jpd/main\",\"/phoenix/api/strategy/getAppStrategy\",\"/web_api/sns/v2/note\"],\"signUrl\":\"https://fe-static.xhscdn.com/as/v1/f218/a15/public/04b29480233f4def5c875875b6bdc3b1.js\",\"signVersion\":\"1\",\"extraInfo\":{},\"url\":\"https://fe-static.xhscdn.com/as/v2/fp/962356ead351e7f2422eb57edff6982d.js\",\"reportUrl\":\"/api/sec/v1/shield/webprofile\",\"desVersion\":\"2\",\"validate\":false,\"xhsTokenUrl\":\"https://fe-static.xhscdn.com/as/v1/3e44/public/bf7d4e32677698655a5cadc581fd09b3.js\"}"}, {"name": "b1", "value": "I38rHdgsjopgIvesdVwgIC+oIELmBZ5e3VwXLgFTIxS3bqwErFeexd0ekncAzMFYnqthIhJeSBMDKutRI3KsYorWHPtGrbi0P9WfIi/eWc6eYqtyQApPI37ekmR6QLQ5Ii6sdneeSfqYHqwl2qt5B0DBIx+PGDV/sutkIx0sxuwr4qtiIhuaIE3e3LV0I3VTIC7e0Vtl2ADmsLveDSKsSPw5IEvsiVtJOqw8BuwfPpdeTFWOIx4TIiu6ZPwrPut5IvlaLbgs3qtxIxes1VwHIkumIkIyejgsY/WTge7eSqte/D7sDcpipedeYrDtIC6eDVw2IENsSqtlnlSuNjVtIvoekqt3cZ7sVo4gIESyIhE2QorUIxmPOzmoIicXePwFIviC2B7sDz7sxVtdIv6ed77eYjRKIi/e6WbjtuwjIhJs3oes6DveTPtNcU6eDuw5IvYgce5edqwApfOsfPw+I3lcIxmlsqtsaPwyssHbZAve3p/eDPwxICeskD0eSeHiIi7sjbos3grFIide6uw3IirktutmtutFIxeeTPtFIE/eVqtiIiOsjut3wutnsPwXICclI3Ir27lk2I5e1utCIES/IEJs0PtnpYIAO0JeYfD1IErPOPtKoqw3I3OexqtWQL5eicAs3phwIhos39Os3utscPwaICJsWPw5IigekeqLICKejd/sfPt5Ix7sxuwD4ZD3I3+vIi0eYeAsScOs3a3exqwVIC+eIhurKVwCIkrPIx0edUzbzbOeTVwEI3PiqPw2Q95sSlgeWqwBgPtrIxh8Ii8Zput+yVwbIxosiWOeVqw4gutbIhpPOutuIxrNrYIyIiNsf7vsSg5ekuw8wVtNIkFLIxOs0VtdIE5edFVYICZ="}, {"name": "_renderInfo", "value": "angle (google, vulkan 1.3.0 (swiftshader device (llvm 10.0.0) (0x0000c0de)), swiftshader driver)"}, {"name": "xhs_context_networkQuality", "value": "GOOD"}]}]} \ No newline at end of file +{"cookies": [{"name": "xsecappid", "value": "ugc", "domain": ".xiaohongshu.com", "path": "/", "expires": 1805532438, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "a1", "value": "19cd7bc1c751fyh9lntys4dzu93cfib0sfovi8mcw30000273597", "domain": ".xiaohongshu.com", "path": "/", "expires": 1804681955, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "webId", "value": "6a6cd657224a4e256b12b847a79711c4", "domain": ".xiaohongshu.com", "path": "/", "expires": 1804681955, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "gid", "value": "yjSfWDSJjJMSyjSfWDSySiY6W2yivxj1I9vMlSE4fu7jqhq8Si3D83888JWq2jW8SiJjJii0", "domain": ".xiaohongshu.com", "path": "/", "expires": 1808556441.55126, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "x-user-id-creator.xiaohongshu.com", "value": "63b3cb6f000000002502c21d", "domain": ".xiaohongshu.com", "path": "/", "expires": 1807705963.487751, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "customerClientId", "value": "172893742756372", "domain": ".xiaohongshu.com", "path": "/", "expires": 1807705963.487788, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "access-token-creator.xiaohongshu.com", "value": "customer.creator.AT-68c5176156039222844620815tecrfvr71dngizv", "domain": ".xiaohongshu.com", "path": "/", "expires": 1775737962.4878, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "galaxy_creator_session_id", "value": "6ZdVvWjMwbN5FGk6Q39utKCwjaXS4uP7UvNj", "domain": ".xiaohongshu.com", "path": "/", "expires": 1775737963.487811, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "galaxy.creator.beaker.session.id", "value": "1773145963473064900889", "domain": ".xiaohongshu.com", "path": "/", "expires": 1775737963.487825, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "acw_tc", "value": "0a0d09d017739958287583306e15d7636890a4acb2b61d0ff0a1563ded1d20", "domain": "creator.xiaohongshu.com", "path": "/", "expires": 1773997628.784966, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "acw_tc", "value": "0a0bb4b917739958336124144eb4aba36cfd61370f64cda28181f31c51c4e5", "domain": "edith.xiaohongshu.com", "path": "/", "expires": 1773997633.626556, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "acw_tc", "value": "0a00d90517739958669465543ef6dd61755232ba669d441b8f5923def2ca0e", "domain": "www.xiaohongshu.com", "path": "/", "expires": 1773997666.967963, "httpOnly": true, "secure": false, "sameSite": "Lax"}, {"name": "loadts", "value": "1773996438597", "domain": ".xiaohongshu.com", "path": "/", "expires": 1805532438, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "websectiga", "value": "9730ffafd99f2d09dc024760e253af6ab1feb0002827740b95a255ddf6847fc8", "domain": ".xiaohongshu.com", "path": "/", "expires": 1774255639, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "sec_poison_id", "value": "fddf137c-c43a-4c82-ad27-d4e5558eaf0d", "domain": ".xiaohongshu.com", "path": "/", "expires": 1773997043, "httpOnly": false, "secure": false, "sameSite": "Lax"}], "origins": [{"origin": "https://creator.xiaohongshu.com", "localStorage": [{"name": "USER_INFO_FOR_BIZ", "value": "{\"userId\":\"63b3cb6f000000002502c21d\",\"userName\":\"\u5c0f\u7ea2\u85af69B40CFE\",\"userAvatar\":\"https://sns-avatar-qc.xhscdn.com/avatar/645b7c0d1d74d41345e61461.jpg?imageView2/2/w/80/format/jpg\",\"redId\":\"27206593567\",\"role\":\"creator\",\"permissions\":[\"creatorCollege\",\"creatorWiki\",\"noteInspiration\",\"creatorHome\",\"creatorData\",\"USER_GROUP\",\"creatorActivityCenter\",\"ORIGINAL_STATEMENT\"],\"zone\":\"86\",\"phone\":\"15880802661\",\"relatedUserId\":null,\"relatedUserName\":null,\"realNameVerified\":false,\"realNameVerifyLink\":\"xhsdiscover://account/realName/atomization?businessCode=capa_podcast\",\"longAudioConfig\":{\"audioMinSeconds\":600,\"audioFileMaxSize\":1024,\"audioMaxSeconds\":7200,\"hasAudio\":false,\"summaryMaxDurationSeconds\":10800,\"needRealName\":false},\"kolCoOrder\":false}"}, {"name": "uploader-permit-video-spectrum", "value": "{\"MBTI\u7597\u6108SOUL \u5e74\u8f7b\u4eba\u6d4bMBTI\uff0c40\u523060\u5c81\u8d70\u4e94\u884c\u516b\u5366.mp4-10459668\":null,\"Soul\u4e1a\u52a1\u6a21\u578b \u6d3e\u5bf9+\u5207\u7247+\u5c0f\u7a0b\u5e8f\u5168\u94fe\u8def.mp4-10740368\":null,\"Soul\u5207\u724730\u79d2\u52308\u5206\u949f AI\u534a\u5c0f\u65f6\u80fd\u526a10\u523030\u4e2a.mp4-14677795\":null,\"\u521d\u671f\u56e2\u961f\u5148\u627e\u4e24\u4e2aIS\uff0c\u6bd4\u94b1\u597d\u4f7f ENFJ\u94fe\u63a5\u4eba\uff0cENTJ\u6307\u6325.mp4-15709935\":null,\"\u5237\u7259\u542c\u4e1a\u52a1\u903b\u8f91 Soul\u5207\u7247\u53d8\u73b0\u600e\u4e48\u8dd1.mp4-11455089\":null,\"\u56fd\u5b66\u6613\u7ecf\u600e\u4e48\u5b66 \u4e24\u5c0f\u65f6\u4e03\u4e03\u516b\u516b\uff0c\u53ec\u5524\u4f5c\u8005\u5bf9\u8bdd.mp4-9598781\":null,\"\u5efa\u7acb\u4fe1\u4efb\u4e0d\u662f\u6c42\u6765\u7684 \u5356\u5916\u6302\u53d1\u90ae\u4ef6\u4e09\u4e2a\u6708\u62ff\u4e0b\u5fb7\u56fd\u603b\u4ee3.mp4-10091673\":null,\"\u61d2\u4eba\u7684\u6d3b\u6cd5 \u52a8\u4f5c\u7b80\u5355\u6709\u5229\u53ef\u56fe\u6b63\u53cd\u9988.mp4-7059868\":null,\"\u65e9\u8d77\u4e0d\u662f\u4e3a\u4e86\u5f00\u6d3e\u5bf9\uff0c\u662f\u4e0d\u5435\u8001\u5a46\u7761\u89c9.mp4-5788932\":null,\"\u7761\u7720\u4e0d\u597d\uff1f\u6bcf\u5929\u653e\u4e0b\u4e00\u4ef6\u4e8b\uff0c\u505a\u51cf\u6cd5.mp4-7022452\":null,\"\u91d1\u878dAI\u83b7\u5ba2\u4f53\u7cfb \u540e\u7aef30\u4eba\u6c89\u6dc012\u5e74\uff0c\u524d\u7aef\u4e22\u624b\u673a.mp4-13904487\":null,\"AI\u6700\u5927\u7684\u7f3a\u70b9\u662f\u4e0a\u4e0b\u6587\u592a\u77ed\uff0c\u8fd9\u6837\u6765\u89e3\u51b3.mp4-3645225\":null,\"AI\u6bcf\u5929\u526a1000\u4e2a\u89c6\u9891 M4\u7535\u811124T\u7d20\u6750\u5e93\u5168\u7f51\u5206\u53d1.mp4-13368696\":null,\"Soul\u6d3e\u5bf9\u53d8\u73b0\u5168\u94fe\u8def \u53d1\u89c6\u9891\u5c31\u6709\u94b1\uff0c\u540e\u7aef\u5168\u89e3\u51b3.mp4-10194469\":null,\"\u4ece0\u5230\u5207\u7247\u53d1\u5e03 AI\u81ea\u52a8\u5b8c\u6210\u6bcf\u5929\u526f\u4e1a30\u6761\u89c6\u9891.mp4-7241847\":null,\"\u505a\u526f\u4e1a\u7684\u57fa\u672c\u6761\u4ef6 \u82f9\u679c\u7535\u8111\u548c\u7279\u6b8a\u8bbf\u95ee\u5de5\u5177.mp4-9735083\":null,\"\u5207\u7247\u5206\u53d1\u5168\u81ea\u52a8\u5316 \u4ece\u89c6\u9891\u5230\u53d1\u5e03\u4e00\u952e\u5b8c\u6210.mp4-14798594\":null,\"\u521b\u4e1a\u56e2\u961f4\u4eba\u5e73\u520625\u6709\u5565\u5371\u9669 \u5148\u8dd1\u94b1\u518d\u8c08\u80a1\u6743.mp4-10580437\":null,\"\u575a\u6301\u5230120\u573a\u662f\u4ec0\u4e48\u611f\u89c9 \u65b9\u5411\u8d8a\u786e\u5b9a\u6267\u884c\u8d8a\u575a\u51b3.mp4-9157815\":null,\"\u5e2e\u4eba\u88c5AI\u4e00\u5355300\u52301000\u5757\uff0c\u4f20\u7edf\u884c\u4e1a\u4e5f\u80fd\u505a.mp4-10538224\":null,\"\u6df1\u5ea6AI\u6a21\u578b\u5bf9\u6bd4 \u54ea\u4e2a\u624d\u662f\u771f\u6b63\u7684AI\u4e0d\u662f\u8bed\u8a00\u6a21\u578b.mp4-8952361\":null,\"\u7597\u6108\u5e08\u914dAI\u52a9\u624b\u80fd\u6536\u591a\u5c11\u94b1 \u4e00\u4e2a\u5c0f\u56e2\u961f5\u4e07\u523010\u4e07.mp4-11958241\":null,\"\u8d5a\u94b1\u6ca1\u90a3\u4e48\u590d\u6742\uff0c\u81ea\u4fe1\u5fc3\u624d\u662f\u6838\u5fc3\u95ee\u9898.mp4-11176359\":null,\"AI\u6700\u5927\u7684\u7f3a\u70b9\u662f\u4e0a\u4e0b\u6587\u592a\u77ed\uff0c\u8fd9\u6837\u6765\u89e3\u51b3.mp4-7521601\":null,\"AI\u6bcf\u5929\u526a1000\u4e2a\u89c6\u9891 M4\u7535\u811124T\u7d20\u6750\u5e93\u5168\u7f51\u5206\u53d1.mp4-27929301\":null,\"Soul\u6d3e\u5bf9\u53d8\u73b0\u5168\u94fe\u8def \u53d1\u89c6\u9891\u5c31\u6709\u94b1\uff0c\u540e\u7aef\u5168\u89e3\u51b3.mp4-21135131\":null,\"\u4ece0\u5230\u5207\u7247\u53d1\u5e03 AI\u81ea\u52a8\u5b8c\u6210\u6bcf\u5929\u526f\u4e1a30\u6761\u89c6\u9891.mp4-15182878\":null,\"\u505a\u526f\u4e1a\u7684\u57fa\u672c\u6761\u4ef6 \u82f9\u679c\u7535\u8111\u548c\u7279\u6b8a\u8bbf\u95ee\u5de5\u5177.mp4-19575651\":null,\"\u5207\u7247\u5206\u53d1\u5168\u81ea\u52a8\u5316 \u4ece\u89c6\u9891\u5230\u53d1\u5e03\u4e00\u952e\u5b8c\u6210.mp4-30731220\":null,\"\u521b\u4e1a\u56e2\u961f4\u4eba\u5e73\u520625\u6709\u5565\u5371\u9669 \u5148\u8dd1\u94b1\u518d\u8c08\u80a1\u6743.mp4-21423166\":null,\"\u575a\u6301\u5230120\u573a\u662f\u4ec0\u4e48\u611f\u89c9 \u65b9\u5411\u8d8a\u786e\u5b9a\u6267\u884c\u8d8a\u575a\u51b3.mp4-19367933\":null,\"\u5e2e\u4eba\u88c5AI\u4e00\u5355300\u52301000\u5757\uff0c\u4f20\u7edf\u884c\u4e1a\u4e5f\u80fd\u505a.mp4-21608518\":null,\"\u6df1\u5ea6AI\u6a21\u578b\u5bf9\u6bd4 \u54ea\u4e2a\u624d\u662f\u771f\u6b63\u7684AI\u4e0d\u662f\u8bed\u8a00\u6a21\u578b.mp4-18471791\":null,\"\u7597\u6108\u5e08\u914dAI\u52a9\u624b\u80fd\u6536\u591a\u5c11\u94b1 \u4e00\u4e2a\u5c0f\u56e2\u961f5\u4e07\u523010\u4e07.mp4-24461141\":null,\"\u8d5a\u94b1\u6ca1\u90a3\u4e48\u590d\u6742\uff0c\u81ea\u4fe1\u5fc3\u624d\u662f\u6838\u5fc3\u95ee\u9898.mp4-22996736\":null,\"Cursor\u7684\u6743\u9650\u95ee\u9898\uff0c\u5b89\u5168\u9690\u60a3\u5fc5\u987b\u63d0\u524d\u8bb2\u6e05\u695a.mp4-16471860\":null,\"NFC\u78b0\u4e00\u78b0\u5f15\u6d41\uff0c\u7ebf\u4e0b\u9910\u996e\u5e97\u7528\u8fd9\u62db\u5c31\u591f\u4e86.mp4-19205962\":null,\"Skill\u548cCursor\u7684\u533a\u522b\uff0c\u4e00\u4e2a\u8d70\u5de5\u4f5c\u6d41\u4e00\u4e2a\u8d70\u5bf9\u8bdd.mp4-18869064\":null,\"Soul\u6d3e\u5bf9\u6bd4\u6296\u97f3\u7701\u529b\u592a\u591a\uff0c\u8fde\u9ea6\u673a\u5236\u662f\u6838\u5fc3\u5dee\u5f02.mp4-14633930\":null,\"\u505aAPI\u805a\u5408\u7ed9\u522b\u4eba\u5145\u503c\uff0c\u8fd9\u662f\u53e6\u4e00\u79cdAI\u53d8\u73b0\u8def\u5f84.mp4-15571922\":null,\"\u5e2e\u522b\u4eba\u88c5AI\u5de5\u5177\u5c31\u80fd\u8d5a\u94b1\uff0c\u5de5\u4f5c\u6d41\u624d\u662f\u53d8\u73b0\u5165\u53e3.mp4-17272200\":null,\"\u672c\u5730\u90e8\u7f72\u5927\u6a21\u578b\u5230\u5e95\u884c\u4e0d\u884c\uff0c\u5c0f\u4e8b\u53ef\u4ee5\u5927\u4e8b\u522b\u60f3.mp4-17798341\":null,\"\u6bcf\u4e2a\u6d88\u8d39\u8005\u90fd\u662f\u4f60\u7684\u6d41\u91cf\u5165\u53e3\uff0c\u78b0\u4e00\u78b0\u5c31\u80fd\u8fde\u63a5.mp4-18930706\":null,\"\u7535\u7ade19\u5e74\u7ecf\u9a8c\u52a0AI\uff0c\u8de8\u754c\u7ed3\u5408\u624d\u6709\u771f\u6b63\u7684\u62a4\u57ce\u6cb3.mp4-19210847\":null,\"AI\u4e00\u90e8\u52673000\u52305000\uff0c100\u90e8\u771f\u4eba\u5267\u7684\u6210\u672c\u505a100\u90e8AI\u5267.mp4-12071079\":null,\"AI\u6559\u57f9\u662f\u771f\u7684\u53ef\u4ee5\u505a\uff0c\u4f20\u7edf\u57f9\u8bad\u884c\u4e1a\u6b63\u5728\u88ab\u6539\u9020.mp4-12437677\":null,\"\u4e00\u4e2aSoul\u53f7\u4e00\u4e2a\u6708\u6323\u51e0\u4e07\u5757\uff0c\u8fd9\u4e5f\u662f\u4e2a\u5c0f\u751f\u610f.mp4-12801915\":null,\"\u4e0d\u7528\u4e0a\u4ec0\u4e48\u6a21\u5f0f\uff0c\u7ecf\u8425\u597d\u4f60\u7684\u4eba\u5c31\u591f\u4e86.mp4-12312838\":null,\"\u4e91\u8fde\u9501\u6982\u5ff5\u592a\u725b\u4e86\uff0c\u4e0d\u9700\u8981\u5f00\u5e97\u4e5f\u80fd\u505a\u54c1\u724c.mp4-12566164\":null,\"\u4f9b\u5e94\u94fe\u52a0\u54c1\u724c\u5e97\uff0c\u540e\u7aef\u624d\u662f\u771f\u6b63\u8d5a\u94b1\u7684\u5730\u65b9.mp4-12673751\":null,\"\u5356Cursor\u5b89\u88c5\u5305\u6ca1\u6709\u540e\u7aef\u4f60\u505a\u4e0d\u4e86.mp4-12722106\":null,\"\u592a\u65e9\u5f15\u5165\u8d44\u672c\u4f60\u7684\u65b9\u5411\u5c31\u4e0d\u662f\u4f60\u8bf4\u4e86\u7b97.mp4-11906382\":null,\"\u6559\u57f9\u884c\u4e1a\u516d\u4e03\u5e74\u7ecf\u9a8c \u505a\u597d\u81ea\u5df1\u672c\u5e97\u624d\u662f\u6838\u5fc3.mp4-12800709\":null,\"\u7ed9\u5b66\u6821\u514d\u8d39\u57f9\u8bad\uff0c\u540e\u7aef\u5356\u8bbe\u5907\u624d\u662f\u771f\u751f\u610f.mp4-12684441\":null,\"127\u573a\u63a8\u6d41273\u4e07\uff0c\u8fdb\u623f4\u4e076\uff0c\u6d41\u91cf\u5bc6\u7801\u5c31\u8fd9\u51e0\u4e2a.mp4-5969598\":null,\"\u4e0d\u5b66AI\u8fde\u5de5\u4f5c\u90fd\u627e\u4e0d\u5230\u4e86\uff0c\u5b66\u5386\u4e0d\u91cd\u8981\uff0c\u5e72\u8d27\u548cAI\u5b9e\u64cd\u6700\u91cd\u8981.mp4-5891885\":null,\"\u4fdd\u9556\u670d\u52a1\u540e\u7aef\u624d\u662f\u771f\u8d5a\u94b1\uff0c\u63a5\u89e6\u9ad8\u7aef\u5ba2\u6237\u8d5a\u4fe1\u4efb\u5173\u7cfb\u7684\u94b1.mp4-1002090\":null,\"\u5206\u5bf9\u65b9\u4ed6\u6323\u4e0d\u5230\u7684\u94b1\uff0c\u5458\u5de5\u624d\u613f\u610f\u8ddf\u4f60\u5e72.mp4-5180517\":null,\"\u60f3\u62ff2\u4e07\u5de5\u8d44\uff0cAI\u6708\u6d88\u8017\u81f3\u5c111000\u5757\uff0c\u8fd9\u662f\u786c\u6307\u6807.mp4-4299806\":null,\"\u6d41\u91cf\u7aef\u548c\u4ea4\u4ed8\u7aef\u522b\u540c\u65f6\u625b\uff0c\u7f3a\u6d41\u91cf\u5c31\u505a\u7fa4\u4e3b\u5f00\u6d3e\u5bf9.mp4-2254549\":null,\"\u6e38\u620f\u8f85\u52a9AI\u6a21\u578b\uff0c\u9ad8\u6536\u76ca\u9ad8\u98ce\u9669\uff0c3\u52306\u4e2a\u6708\u5fc5\u987b\u6536\u624b.mp4-3880532\":null,\"\u9762\u8bd5\u4e09\u9762\u6d41\u7a0b \u7b80\u5386+\u6d4b\u8bd5+\u8bd5\u5c97\uff0c300\u4efd\u7b5b\u52302\u52303\u4e2a\u4eba.mp4-5001850\":null}"}, {"name": "publish-uploader-history-upload-speed", "value": "[{\"speed\":[1.6228209191759113],\"domain\":\"ros-upload.xiaohongshu.com\",\"timestamp\":1773996389881},{\"speed\":[2.1485522450692405],\"domain\":\"ros-upload-d4.xhscdn.com\",\"timestamp\":1773996392264}]"}, {"name": "snsWebPublishCurrentUser", "value": "63b3cb6f000000002502c21d"}, {"name": "USER_INFO", "value": "{\"user\":{\"type\":\"User\",\"value\":{\"userId\":\"63b3cb6f000000002502c21d\",\"loginUserType\":\"creator\"}}}"}, {"name": "NEW_XHS_ABTEST_REPORT_KEY", "value": "{\"6a6cd657224a4e256b12b847a79711c463b3cb6f000000002502c21d\":\"2026-03-20\"}"}, {"name": "b1b1", "value": "1"}, {"name": "score_display", "value": "1"}, {"name": "p1", "value": "130"}, {"name": "nps-userId", "value": "63b3cb6f000000002502c21d"}, {"name": "_speedList", "value": "[{\"ts\":1773996093124,\"speed\":51030674.333333336},{\"ts\":1773996093126,\"speed\":45066416.333333336},{\"ts\":1773996164074,\"speed\":59048666.666666664},{\"ts\":1773996164076,\"speed\":46884750},{\"ts\":1773996245352,\"speed\":68956555.33333333},{\"ts\":1773996245353,\"speed\":59470999.666666664},{\"ts\":1773996332904,\"speed\":98631083.33333333},{\"ts\":1773996332906,\"speed\":66596277.666666664},{\"ts\":1773996391259,\"speed\":80394750},{\"ts\":1773996391261,\"speed\":60695730}]"}, {"name": "score_timestamp", "value": "1773192526341"}, {"name": "last_tiga_update_time", "value": "1773996439163"}, {"name": "uploader-permit-image-spectrum", "value": "{\"cover.jpeg-304598\":null,\"cover.jpeg-271507\":null,\"cover.jpeg-297124\":null,\"cover.jpeg-295955\":null,\"cover.jpeg-287062\":null,\"cover.jpeg-293020\":null,\"cover.jpeg-308663\":null,\"cover.jpeg-305977\":null,\"cover.jpeg-273401\":null,\"cover.jpeg-282376\":null,\"cover.jpeg-265803\":null,\"cover.jpeg-281603\":null,\"cover.jpeg-262701\":null,\"cover.jpeg-257765\":null,\"cover.jpeg-258311\":null,\"cover.jpeg-274588\":null,\"cover.jpeg-275845\":null,\"cover.jpeg-315902\":null,\"cover.jpeg-299788\":null,\"cover.jpeg-287571\":null,\"cover.jpeg-289713\":null,\"cover.jpeg-278856\":null,\"cover.jpeg-297410\":null,\"cover.jpeg-302252\":null,\"cover.jpeg-295517\":null,\"cover.jpeg-280128\":null,\"cover.jpeg-263507\":null,\"cover.jpeg-263797\":null,\"cover.jpeg-258601\":null,\"cover.jpeg-290089\":null,\"cover.jpeg-278428\":null,\"cover.jpeg-285963\":null,\"cover.jpeg-272752\":null,\"cover.jpeg-272048\":null,\"cover.jpeg-178397\":null,\"cover.jpeg-170888\":null,\"cover.jpeg-155576\":null,\"cover.jpeg-165689\":null,\"cover.jpeg-165572\":null,\"cover.jpeg-150213\":null,\"cover.jpeg-159266\":null,\"cover.jpeg-168582\":null,\"cover.jpeg-163136\":null,\"cover.jpeg-42960\":null}"}, {"name": "sdt_source_storage_key", "value": "{\"reportUrl\":\"/api/sec/v1/shield/webprofile\",\"desVersion\":\"2\",\"signUrl\":\"https://fe-static.xhscdn.com/as/v1/f218/a15/public/04b29480233f4def5c875875b6bdc3b1.js\",\"signVersion\":\"1\",\"xhsTokenUrl\":\"https://fe-static.xhscdn.com/as/v1/3e44/public/bf7d4e32677698655a5cadc581fd09b3.js\",\"extraInfo\":{},\"url\":\"https://fe-static.xhscdn.com/as/v2/fp/a2a27d37e76c8a23531accea3b0956b7.js\",\"commonPatch\":[\"/fe_api/burdock/v2/note/post\",\"/api/sns/web/v1/comment/post\",\"/api/sns/web/v1/note/like\",\"/api/sns/web/v1/note/collect\",\"/api/sns/web/v1/user/follow\",\"/api/sns/web/v1/feed\",\"/api/sns/web/v1/login/activate\",\"/api/sns/web/v1/note/metrics_report\",\"/api/redcaptcha\",\"/api/store/jpd/main\",\"/phoenix/api/strategy/getAppStrategy\",\"/web_api/sns/v2/note\"],\"validate\":false}"}, {"name": "b1", "value": "I38rHdgsjopgIvesdVwgIC+oIELmBZ5e3VwXLgFTIxS3bqwErFeexd0ekncAzMFYnqthIhJeSBMDKutRI3KsYorWHPtGrbi0P9WfIi/eWc6eYqtyQApPI37ekmR6QLQ5Ii6sdneeSfqYHqwl2qt5B0DBIx+PGDV/sutkIx0sxuwr4qtiIhuaIE3e3LV0I3VTIC7e0Vtl2ADmsLveDSKsSPw5IEvsiVtJOqw8BuwfPpdeTFWOIx4TIiu6ZPwrPut5IvlaLbgs3qtxIxes1VwHIkumIkIyejgsY/WTge7eSqte/D7sDcpipedeYrDtIC6eDVw2IENsSqtlnlSuNjVtIvoekqt3cZ7sVo4gIESyIhE2QdLUIxmPOzmoIicXePwFIviC2BvsDz7sxVtdIv6ed77eYjRKIi/e6WbjtuwjIhJs3oes6DveTPtNcU6eDuw5IvYgceeeSVwRL9osfqwZI3YcIxmlsqtsaPwyssHbZAve3p/eDPwxICeskD0eSeHiIi7sjbos3grFIide6uw3IirktutmtutFIxeeTPtFIE/eVqtiIiOsjut3wutnsPwXICclI3Ir27lk2I5e1utCIES/IEJs0PtnpYIAO0JeYfD1IErPOPtKoqw3I3OexqtWQL5eicAs3phwIhos39Os3utscPwaICJsWPw5IigekeqLICKejd/sfPt5Ix7sxuwD4ZD3I3+vIi0eYeAsScOs3a3exqwVIC+eIhurKVwCIkrPIx0edUzbzbOeTVwEI3PiqPw2Q77e0WvsYutGgutaIxHNIES+ePtqrqwHI3OsfI6e1uwmpqtnIhSOZVtUI3DleAHcIkve0nH="}, {"name": "_renderInfo", "value": "angle (google, vulkan 1.3.0 (swiftshader device (llvm 10.0.0) (0x0000c0de)), swiftshader driver)"}, {"name": "xhs_context_networkQuality", "value": "EXCELLENT"}]}]} \ No newline at end of file diff --git a/03_卡木(木)/木叶_视频内容/视频切片/SKILL.md b/03_卡木(木)/木叶_视频内容/视频切片/SKILL.md index 5eb2b1c5..035bce85 100644 --- a/03_卡木(木)/木叶_视频内容/视频切片/SKILL.md +++ b/03_卡木(木)/木叶_视频内容/视频切片/SKILL.md @@ -2,9 +2,9 @@ name: 视频切片 description: Soul派对视频切片 + 快速混剪 + 切片动效包装(片头/片尾/程序化)+ 剪映思路借鉴(智能剪口播/镜头分割)。触发词含视频剪辑、切片发布、快速混剪、切片动效包装、程序化包装、片头片尾。 group: 木 -triggers: 视频剪辑、切片发布、字幕烧录、全画面标定、竖屏裁剪、飞书录屏白边、**快速混剪、混剪预告、快剪串联、切片动效包装、程序化包装、片头片尾、批量封面、视频包装**、镜头切分、场景检测 +triggers: 视频剪辑、切片发布、字幕烧录、全画面标定、竖屏裁剪、飞书录屏白边、**快速混剪、混剪预告、快剪串联、切片动效包装、程序化包装、片头片尾、批量封面、视频包装**、镜头切分、场景检测、**运营短切片、15秒切片、30秒切片、京剧梗、热点密度切片** owner: 木叶 -version: "1.4" +version: "1.5" updated: "2026-03-20" --- @@ -24,7 +24,20 @@ updated: "2026-03-20" 提取后立即繁转简+修正错误 封面+字幕(已简体)+加速10%+去语气词 ``` -**切片时长**:每段为**完整的一个片段**,时长 **30 秒~300 秒**,由该完整片段起止时间决定。**标题**用一句**刺激性观点**(见 `Soul竖屏切片_SKILL.md`)。 +**切片时长(两种模式)**: + +| 模式 | 单段时长 | 条数/场(建议) | 选题侧重 | +|------|-----------|-----------------|----------| +| **深度切片(默认)** | **30 秒~300 秒**,完整语义单元 | 6~10 | 提问→回答、整场观点 | +| **运营短切片** | **15~30 秒**(可 `--min-duration` / `--max-duration` 微调) | **20~30**(默认脚本 **24**) | **京剧/戏曲比喻梗**、**当场热点词**、强反差金句,适合抖音高密度测试 | + +运营短切片流程与深度切片相同(转录 → `identify_highlights` → `batch_clip` → `soul_enhance`),区别在 **高光 preset** 与 **prompt**:`identify_highlights.py --preset ops-short` 会在提示词中要求模型**整场均匀取点**,并优先京剧相关比喻/唱腔梗与热点表达;过滤逻辑会**丢弃**短于 15 秒或长于 30 秒的区间(深度模式只卡最短 60 秒、不卡上限)。 + +**开场 ASR 噪声**:派对录播常在开场出现同一短句循环(如「我看你不太好」),会把模型注意力锁死在前几分钟。**ops-short 默认**将送模型的文字稿与成片时间轴**从约 7:30(450 秒)之后**才开始(`--prompt-min-sec`,可改)。若你的场次正片明显更早开始,可改小该值或临时改 `long` 再人工筛 `highlights.json`。 + +**批量节奏(人工剪辑对齐)**:一场录播可先按 **15~30 条**为一轮做高光与切片,再进成片;多轮叠加时注意 `highlights.json` 备份,避免覆盖。 + +**标题**:深度模式用一句**刺激性观点**;短切片标题 **4~10 个汉字** 为宜(见 `Soul竖屏切片_SKILL.md`)。 **提问→回答 结构**:若片段内有人提问,前3秒优先展示**提问问题**,再播回答;高光识别填 `question` 且 `hook_3sec` 与之一致,成片整条去语助词。详见 `参考资料/视频结构_提问回答与高光.md`、`参考资料/高光识别提示词.md`。 @@ -54,6 +67,15 @@ cd 03_卡木(木)/木叶_视频内容/视频切片/脚本 conda activate mlx-whisper python3 soul_slice_pipeline.py --video "/path/to/soul派对会议第57场.mp4" --clips 6 +# 运营短切片(15~30 秒 × 约 24 条,京剧梗+热点优先,两目录+竖屏成片) +python3 soul_slice_pipeline.py -v "视频.mp4" -o "/path/to/场次_output" --two-folders --ops-short --prefix soul127 + +# 已转录场次仅重跑高光+切片+成片(省 MLX) +python3 soul_slice_pipeline.py -v "视频.mp4" -o "/path/to/场次_output" --two-folders --ops-short --skip-transcribe --prefix soul127 + +# 自定义条数与时长区间 +python3 soul_slice_pipeline.py -v "视频.mp4" -o "/path/to/out" --two-folders --highlight-preset ops-short -n 28 --min-clip-sec 10 --max-clip-sec 30 + # 仅重新烧录(字幕转简体后重跑增强) python3 soul_slice_pipeline.py -v "视频.mp4" -n 6 --skip-transcribe --skip-highlights --skip-clips @@ -61,6 +83,14 @@ python3 soul_slice_pipeline.py -v "视频.mp4" -n 6 --skip-transcribe --skip-hig python3 soul_slice_pipeline.py -v "视频.mp4" -n 8 --two-folders --quick-montage ``` +**分步:仅高光(运营短切片)** + +```bash +python3 identify_highlights.py -t transcript.srt -o highlights.json --preset ops-short -n 24 +# 或显式时长 + 长视频也强调京剧/热点: +python3 identify_highlights.py -t transcript.srt -o highlights.json --preset ops-short -n 26 --min-duration 15 --max-duration 30 --ops-jingju-hotspot +``` + 流程:**转录 → 字幕转简体 → 高光识别 → 批量切片 → 增强** #### 分步命令 @@ -155,6 +185,8 @@ python3 analyze_feishu_ui_crop.py "/path/to/原片.mp4" --at 0.2 将输出的 `CROP_VF` 传给:`python3 soul_enhance.py ... --vertical --crop-vf '...'`(`OVERLAY_X` 脚本会一并打印;也可用 `--overlay-x` 覆盖)。 +**全画面入画(不裁竖条)**:加 `--vertical-fit-full`,整幅 16:9 缩放入 498×1080 + 上下黑边,左右内容都可见。详见 `Soul竖屏切片_SKILL.md` 第六节 B。 + 详见:`参考资料/竖屏中段裁剪参数说明.md`、`脚本/analyze_feishu_ui_crop.py`。 **FFmpeg 一条命令(固定参数):** @@ -350,7 +382,7 @@ python3 scripts/burn_subtitles_clean.py -i enhanced.mp4 -s clean.srt -o 成片.m | **kill_ffmpeg_when_clip_done.py** | 剪辑结束后自动关掉 ffmpeg(监视剪映/PID 或立即杀) | ⭐ 按需 | | **scene_detect_to_highlights.py** | 镜头/场景检测 → highlights.json(PySceneDetect,可接 batch_clip) | ⭐⭐ | | chapter_themes_to_highlights.py | 按章节 .md 主题提取片段(本地模型→highlights.json) | ⭐⭐⭐ | -| identify_highlights.py | 高光识别(API 优先→Ollama→规则,默认 gpt-4o) | ⭐⭐ | +| identify_highlights.py | 高光识别(API→Ollama→规则;`--preset ops-short` 为 15~30 秒运营密度) | ⭐⭐ | | batch_clip.py | 批量切片 | ⭐⭐ | | one_video.py | 单视频一键成片 | ⭐⭐ | | burn_subtitles_clean.py | 字幕烧录(无阴影) | ⭐ | diff --git a/03_卡木(木)/木叶_视频内容/视频切片/Soul竖屏切片_SKILL.md b/03_卡木(木)/木叶_视频内容/视频切片/Soul竖屏切片_SKILL.md index 95de6031..cc38fd0c 100644 --- a/03_卡木(木)/木叶_视频内容/视频切片/Soul竖屏切片_SKILL.md +++ b/03_卡木(木)/木叶_视频内容/视频切片/Soul竖屏切片_SKILL.md @@ -1,7 +1,7 @@ --- name: Soul竖屏切片 description: Soul 派对视频→竖屏成片(498×1080),剪辑→成片两文件夹;竖屏裁剪以全画面 1920×1080 标定(analyze_feishu_ui_crop.py),默认深色带 crop=568@508+居中498、无右侧白边。MLX 转录→高光→batch_clip→soul_enhance(封面+字幕同步+逐字可选+去语助词+纠错+违禁词)→visual_enhance v8 可选。LTX/基因胶囊可选。 -triggers: Soul竖屏切片、视频切片、热点切片、竖屏成片、派对切片、全画面标定、竖屏裁剪、白边、飞书录屏、LTX、AI生成视频、Retake重剪、字幕优化、字幕同步、逐字字幕 +triggers: Soul竖屏切片、视频切片、热点切片、竖屏成片、派对切片、全画面标定、竖屏裁剪、全画面成片、letterbox、画面显示全、白边、飞书录屏、LTX、AI生成视频、Retake重剪、字幕优化、字幕同步、逐字字幕 owner: 木叶 group: 木 version: "1.4" @@ -72,7 +72,7 @@ python3 analyze_feishu_ui_crop.py "/path/to/全画面.jpg" |----|------| | **单段时长** | **30~300 秒**,由完整片段起止决定 | | **完整性** | 每段是一个完整话题/情节,有头有尾 | -| **标题** | **一句刺激性观点**(金句、反常识、结论句) | +| **标题** | **一句刺激性观点**,**4~6 个汉字**为宜(单行封面好读、主题一眼懂);忌长句当文件名 | | **数量** | 建议 ≤10 段/场 | | **语助词** | 识别与剪辑须符合 `参考资料/高光识别提示词.md`,成片由 soul_enhance 统一去语助词 | @@ -81,7 +81,8 @@ python3 analyze_feishu_ui_crop.py "/path/to/全画面.jpg" ## 五、成片:封面 + 字幕 + 竖屏 - **封面**:竖屏 498×1080 内**不超出界面**;**半透明质感**(背景 alpha=165);深色渐变、左上角 Soul logo;**封面显示标题 = 成片文件名 = highlights.title**(去杠、去下划线后一致,无 `:|—/_`、无序号);标题文字严格居中、多行自动换行。透明度由 `VERTICAL_COVER_ALPHA` 调节。 -- **字幕**:封面结束后才显示,**居中**在竖屏内;先尝试**单次 FFmpeg 通道**(一次 pass 完成所有字幕叠加,最快);若失败自动回退到分批模式(batch_size=40);语助词在解析阶段已由 `clean_filler_words` 去除。重新加字幕时加 `--force-burn-subs`。⚠️ 注意:当前 FFmpeg 不支持 drawtext/subtitles 滤镜,只能用 PIL 图像 overlay 方案。 +- **字幕**:封面结束后先留**约 3 秒纯画面**(无字幕),再开始叠字幕;字幕**居中**在竖屏内。先尝试**单次 FFmpeg 通道**(一次 pass 完成所有字幕叠加,最快);若失败自动回退到分批模式(batch_size=40);语助词在解析阶段已由 `clean_filler_words` 去除。重新加字幕时加 `--force-burn-subs`。⚠️ 注意:当前 FFmpeg 不支持 drawtext/subtitles 滤镜,只能用 PIL 图像 overlay 方案。(脚本常量:`SUBS_START_AFTER_COVER_SEC`,默认 3.0) +- **封面标题**:高光 `title` 建议 **4~6 个汉字**;成片内封面主标题最多显示 **6 个汉字**(超长由 `soul_enhance` 自动截断,与文件名 `--title-only` 一致)。 - **竖屏**:498×1080,crop 参数与 `参考资料/竖屏中段裁剪参数说明.md` 一致 ### ⚠️ 字幕烧录常见坑(已修复) @@ -98,14 +99,26 @@ python3 analyze_feishu_ui_crop.py "/path/to/全画面.jpg" --- -## 六、竖屏裁剪参数(成片内嵌) +## 六、竖屏输出两种模式(成片内嵌) + +### A. 竖条模式(默认,小程序无白边) + +只取横向**中间深色带**,再裁成 498 宽,适合抖音全屏铺满、不要桌面白边。 | 步骤 | 滤镜 | |------|------| | 1 | crop=568:1080:508:0(整段深色小程序主体,不含右侧桌面白边) | | 2 | crop=498:1080:35:0(568 内水平居中取 498) | -**输出**:498×1080 竖屏。 +### B. 全画面模式(`--vertical-fit-full`) + +**不裁中间竖条**:整幅 16:9 **完整入画**,等比缩放到宽度 498,**上下加黑边** 到 1080 高。左侧小程序 + 右侧人像/桌面都会在画面里,适合「画面要显示全」的成片。 + +- 封面、字幕先在 **完整横版分辨率** 上叠加(`overlay=0:0`),再整体走: + `scale=w=498:h=1080:force_original_aspect_ratio=decrease,pad=498:1080:(ow-iw)/2:(oh-ih)/2:color=black` +- 命令:在原有 `soul_enhance.py ... --vertical --title-only` 上增加 **`--vertical-fit-full`** + +**输出**:两种模式均为 **498×1080** 竖屏文件。 --- diff --git a/03_卡木(木)/木叶_视频内容/视频切片/参考资料/高光识别提示词.md b/03_卡木(木)/木叶_视频内容/视频切片/参考资料/高光识别提示词.md index 36352f05..47d3afae 100644 --- a/03_卡木(木)/木叶_视频内容/视频切片/参考资料/高光识别提示词.md +++ b/03_卡木(木)/木叶_视频内容/视频切片/参考资料/高光识别提示词.md @@ -121,7 +121,7 @@ CTA的目的是引导用户完成下一步动作。 [ { "rank": 1, - "title": "简短有力的标题(用于短视频)", + "title": "4~6 个汉字的刺激性观点(用于短视频封面/文件名,单一主题)", "start_time": "00:12:34", "end_time": "00:13:56", "duration_sec": 82, @@ -192,7 +192,7 @@ CTA的目的是引导用户完成下一步动作。 - 优先:有步骤、有起伏、起承转合清晰的片段 - 避免:大段碎碎念、断句混乱、同一句话重复多遍、长时间无信息量停顿 -- 与主题片段提取规则一致:每段为完整语义单元,时长 30~300 秒,标题为一句刺激性观点 +- 与主题片段提取规则一致:每段为完整语义单元,时长 30~300 秒;**title 固定 4~6 个汉字**,一句刺激性观点、单一主题(用于竖屏封面字) ## 文字稿格式要求 diff --git a/03_卡木(木)/木叶_视频内容/视频切片/场次稿/第127场_20260318_highlights.json b/03_卡木(木)/木叶_视频内容/视频切片/场次稿/第127场_20260318_highlights.json new file mode 100644 index 00000000..5c4d0d0b --- /dev/null +++ b/03_卡木(木)/木叶_视频内容/视频切片/场次稿/第127场_20260318_highlights.json @@ -0,0 +1,82 @@ +[ + { + "title": "高薪先看消耗", + "start_time": "00:07:30", + "end_time": "00:12:00", + "hook_3sec": "想两万月薪?先看AI月烧多少", + "question": "高薪硬指标是什么?", + "cta_ending": "今天就到这里,点个关注下次不迷路", + "transcript_excerpt": "想拿2万工资,AI月消耗至少1000块以上。消耗多,说明你是实实在在用AI在解决职业里的问题", + "reason": "TOKEN消耗硬指标" + }, + { + "title": "辅助暴利快收", + "start_time": "00:12:00", + "end_time": "00:18:00", + "hook_3sec": "游戏辅助来钱快,定性也狠", + "question": "做游戏AI能挣多少?", + "cta_ending": "今天就到这里,点个关注下次不迷路", + "transcript_excerpt": "三角洲辅助瞄准,AI训练人物识别模型。闲鱼抖音直播分销,单价500块一个人一个月", + "reason": "高收益高风险" + }, + { + "title": "保镖钱在后端", + "start_time": "00:18:00", + "end_time": "00:23:00", + "hook_3sec": "真赚的不是保镖费,是后端", + "question": "保镖怎么赚大钱?", + "cta_ending": "今天就到这里,点个关注下次不迷路", + "transcript_excerpt": "初级2万一个月,中级3万,高级4万。真正赚钱的是商务中介、介绍投资、拉业务合作", + "reason": "信任关系变现" + }, + { + "title": "别两头扛流量", + "start_time": "00:23:00", + "end_time": "00:28:00", + "hook_3sec": "流量交付同时扛,必崩", + "question": "做流量还是交付?", + "cta_ending": "今天就到这里,点个关注下次不迷路", + "transcript_excerpt": "现在这条赛道缺的是流量,不是交付。和群主合作要分钱,不分必被排挤", + "reason": "一端打穿" + }, + { + "title": "分钱分缺口", + "start_time": "00:28:00", + "end_time": "00:33:00", + "hook_3sec": "分他靠自己赚不到的那块", + "question": "招人怎么分钱?", + "cta_ending": "今天就到这里,点个关注下次不迷路", + "transcript_excerpt": "员工自己只能挣5000到8000,你给他1万,他多拿2000,他才愿意跟你干", + "reason": "分钱逻辑" + }, + { + "title": "推流就三板斧", + "start_time": "00:33:00", + "end_time": "00:38:00", + "hook_3sec": "二百七十万推流,密码就几条", + "question": "派对流量怎么来?", + "cta_ending": "今天就到这里,点个关注下次不迷路", + "transcript_excerpt": "流量密码就那几个:职场、搞钱、MBTI性格匹配,最容易共鸣", + "reason": "Soul数据复盘" + }, + { + "title": "面试三面定人", + "start_time": "00:38:00", + "end_time": "00:43:00", + "hook_3sec": "三百简历,最后只要两三个", + "question": "怎么筛人?", + "cta_ending": "今天就到这里,点个关注下次不迷路", + "transcript_excerpt": "二面线上做题、跟团队开25分钟会,看配合。三面定薪资岗位,7天试岗", + "reason": "面试流程" + }, + { + "title": "实操碾压学历", + "start_time": "00:43:00", + "end_time": "00:48:00", + "hook_3sec": "不学AI,连班都难上", + "question": null, + "cta_ending": "今天就到这里,点个关注下次不迷路", + "transcript_excerpt": "想拿2万工资,AI月消耗至少1000。这不是卡学历,是卡你有没有真在用AI干活", + "reason": "实操门槛" + } +] diff --git a/03_卡木(木)/木叶_视频内容/视频切片/脚本/identify_highlights.py b/03_卡木(木)/木叶_视频内容/视频切片/脚本/identify_highlights.py index 27c949d7..d48bedae 100644 --- a/03_卡木(木)/木叶_视频内容/视频切片/脚本/identify_highlights.py +++ b/03_卡木(木)/木叶_视频内容/视频切片/脚本/identify_highlights.py @@ -11,12 +11,19 @@ import os import re import sys from pathlib import Path +from typing import Optional OLLAMA_URL = "http://localhost:11434" DEFAULT_CTA = "关注我,每天学一招私域干货" CLIP_COUNT = 15 -MIN_DURATION = 60 # 最少 1 分钟 +MIN_DURATION = 60 # 最少 1 分钟(长切片默认) MAX_DURATION = 300 # 最多 5 分钟 +# 运营短切片默认:单场产出高密度短视频,便于抖音/热点测试 +OPS_SHORT_MIN = 15 +OPS_SHORT_MAX = 30 +OPS_SHORT_CLIPS = 24 +# 飞书/录屏开场常见 ASR 鬼畜循环,运营短切片喂给模型的文字稿从该秒之后开始(约 7:30) +OPS_SHORT_PROMPT_MIN_SEC_DEFAULT = 450.0 # API 默认模型:优先用当前可用最佳(可被 OPENAI_MODEL / OPENAI_MODELS 覆盖) DEFAULT_API_MODEL = "gpt-4o" @@ -43,18 +50,27 @@ def parse_srt_segments(srt_path: str) -> list: return segments -def fallback_highlights(transcript_path: str, clip_count: int) -> list: - """规则备用:每段 60-300 秒(1-5 分钟)""" +def fallback_highlights( + transcript_path: str, + clip_count: int, + min_dur: float = 60, + max_dur: float = 300, + start_from_sec: float = 0, +) -> list: + """规则备用:按 min_dur~max_dur 均匀切分;可从 start_from_sec 起切(运营短切片对齐正片起点)。""" segments = parse_srt_segments(transcript_path) if not segments: return [] total = segments[-1]["end_sec"] if segments else 0 - seg_dur = min(300, max(60, total / clip_count)) # 每段 1-5 分钟 + start_from_sec = max(0, min(float(start_from_sec), max(0, total - min_dur - 2))) + usable = max(0, total - start_from_sec - 2) + target = usable / max(1, clip_count) + seg_dur = min(max_dur, max(min_dur, target)) result = [] for i in range(clip_count): - start_sec = int(i * seg_dur) - end_sec = min(int(start_sec + seg_dur), int(total - 5)) - if end_sec <= start_sec + 59: # 不足 1 分钟跳过 + start_sec = int(start_from_sec + i * seg_dur) + end_sec = min(int(start_sec + seg_dur), int(total - 2)) + if end_sec <= start_sec + max(5, min_dur - 1): continue # 找该时间段内的字幕 texts = [s["text"] for s in segments if s["end_sec"] >= start_sec and s["start_sec"] <= end_sec] @@ -116,6 +132,40 @@ def srt_to_timestamped_text(srt_path: str, skip_repetitive_head: int = 150) -> s return "\n".join(f"[{s}] {t}" for s, t in out) +def srt_text_from_min_sec(srt_path: str, min_start_sec: float) -> str: + """只保留字幕开始时间 >= min_start_sec 的行,拼成带时间戳文本(削掉开场噪声再送模型)。""" + segments = parse_srt_segments(srt_path) + if not segments: + return "" + lines = [ + f"[{s['start_time']}] {s['text']}" + for s in segments + if s["start_sec"] >= min_start_sec + ] + return "\n".join(lines) + + +def _filter_start_not_before(data: list, min_start_sec: float) -> list: + """丢弃开始时间早于 min_start_sec 的片段(运营短切片防开场鬼畜)。""" + out = [] + for item in data: + if not isinstance(item, dict): + continue + st = item.get("start_time") or item.get("start") or "00:00:00" + if isinstance(st, (int, float)): + sec = float(st) + else: + sec = _parse_time_to_sec(str(st)) + if sec >= min_start_sec: + out.append(item) + else: + print( + f" 过滤过早片段: {item.get('title', '?')} (起 {sec:.0f}s < {min_start_sec:.0f}s)", + file=sys.stderr, + ) + return out + + def _sec_to_hhmmss(sec: float) -> str: """秒数转为 HH:MM:SS""" s = int(sec) @@ -140,8 +190,8 @@ def _parse_time_to_sec(t: str) -> float: return 0 -def _filter_short_clips(data: list) -> list: - """过滤掉时长 < 60 秒的切片""" +def _filter_clips_by_duration(data: list, min_sec: float, max_sec: Optional[float]) -> list: + """按时长过滤;max_sec 为 None 时不限制上限""" result = [] for item in data: if not isinstance(item, dict): @@ -149,26 +199,95 @@ def _filter_short_clips(data: list) -> list: st = item.get("start_time") or item.get("start") or "00:00:00" et = item.get("end_time") or item.get("end") or "00:01:00" dur = _parse_time_to_sec(et) - _parse_time_to_sec(st) - if dur >= 60: + ok_min = dur >= min_sec + ok_max = max_sec is None or dur <= max_sec + if ok_min and ok_max: result.append(item) else: - print(f" 过滤短片段: {item.get('title','?')} (仅{dur:.0f}秒)", file=sys.stderr) + why = [] + if not ok_min: + why.append(f"短于{min_sec:.0f}秒") + if not ok_max: + why.append(f"长于{max_sec:.0f}秒") + print( + f" 过滤片段: {item.get('title','?')} ({dur:.0f}秒, {','.join(why)})", + file=sys.stderr, + ) return result -def _build_prompt(transcript: str, clip_count: int) -> str: +def _ops_short_ai_plausible( + data: list, + min_dur: float, + max_dur: float, + min_start_sec: float, + min_count: int = 5, +) -> bool: + """运营短切片:AI 必须给出足够条数,且每条时长与起点符合窗口,否则走规则均匀切。""" + if not data or not isinstance(data, list) or len(data) < min_count: + return False + tol = 1.5 + for item in data: + if not isinstance(item, dict): + return False + st = item.get("start_time") or item.get("start") or "00:00:00" + et = item.get("end_time") or item.get("end") or "00:01:00" + if isinstance(st, (int, float)): + st = _sec_to_hhmmss(float(st)) + if isinstance(et, (int, float)): + et = _sec_to_hhmmss(float(et)) + try: + ssec = _parse_time_to_sec(str(st)) + esec = _parse_time_to_sec(str(et)) + except Exception: + return False + dur = esec - ssec + if dur < min_dur - tol or dur > max_dur + tol: + return False + if min_start_sec > 0 and ssec < min_start_sec - tol: + return False + return True + + +def _transcript_for_prompt(transcript: str, clip_count: int, min_dur: float) -> str: + """长视频多短切片时需要更大上下文,避免高光只落在开头""" + if min_dur < 45 or clip_count > 12: + cap = 120000 + else: + cap = 5000 + return transcript[:cap] if len(transcript) > cap else transcript + + +def _build_prompt( + transcript: str, + clip_count: int, + min_dur: float = 60, + max_dur: float = 300, + ops_jingju_hotspot: bool = False, +) -> str: """构建高光识别 prompt(提问→回答:有提问时 question/hook_3sec 用提问问题)""" - txt = transcript[:5000] if len(transcript) > 5000 else transcript + txt = _transcript_for_prompt(transcript, clip_count, min_dur) + dur_rule = f"每段时长必须严格在 {int(min_dur)}~{int(max_dur)} 秒之间(看时间戳相减),不要输出低于 {int(min_dur)} 秒或超过 {int(max_dur)} 秒的区间。" + extra = "" + if ops_jingju_hotspot: + extra = """ +## 运营短切片选题(优先) +- 优先剪:说话人用**京剧、戏曲、唱腔、行当、锣鼓**等做的比喻或梗(有趣、反差、易传播)。 +- 其次:当场**热点词**(平台规则、搞钱案例、AI/职场/流量等强刺激观点),一句话能当标题。 +- 仍遵守提问→回答:有提问时 question + hook_3sec 一致。 +- 标题 **4~10 个汉字**,要像抖音封面,忌长句。 +""" return f"""识别视频文字稿中的 {clip_count} 个高光片段,直接输出 JSON 数组,第一个字符必须是 [。 重要:每个话题均优先提问→回答。若某片段里有人提问(观众/连麦者问的问题),必须提取提问内容填 question,且 hook_3sec 用该提问;成片前3秒先展示提问,再播回答。 - +{dur_rule} +{extra} 示例(有提问): [{{"title":"普通人怎么敢跟ZF搞","start_time":"01:12:30","end_time":"01:15:30","question":"普通人怎么敢跟ZF搞?","hook_3sec":"普通人怎么敢跟ZF搞?","cta_ending":"{DEFAULT_CTA}","transcript_excerpt":"维权起头跑通就成生意","reason":"提问+回答完整"}}] 示例(无提问): [{{"title":"起头难","start_time":"00:05:55","end_time":"00:08:00","hook_3sec":"没人起头就起头","cta_ending":"{DEFAULT_CTA}","transcript_excerpt":"起头难跑通就能变成付费服务","reason":"核心观点"}}] -文字稿(从时间戳提取 start_time、end_time,每段 60-300 秒): +文字稿(从时间戳提取 start_time、end_time;整场均匀覆盖,不要扎堆在同一分钟): {txt} 直接输出 JSON 数组,以 [ 开头。有提问的片段必须带 question 且 hook_3sec 与 question 一致。""" @@ -279,18 +398,27 @@ def _build_api_provider_queue() -> list: return queue -def call_openai_api(transcript: str, clip_count: int, provider: dict) -> str: +def call_openai_api( + transcript: str, + clip_count: int, + provider: dict, + min_dur: float = 60, + max_dur: float = 300, + ops_jingju_hotspot: bool = False, +) -> str: """调用 OpenAI 兼容 API(Chat Completion),使用指定 base_url / api_key / model。""" try: from openai import OpenAI except ImportError: raise RuntimeError("未安装 openai 库,请执行: pip install openai") - prompt = _build_prompt(transcript, clip_count) + prompt = _build_prompt( + transcript, clip_count, min_dur, max_dur, ops_jingju_hotspot=ops_jingju_hotspot + ) system = ( "你是短视频策划师。用户会提供视频文字稿,你只输出一个 JSON 数组。" "若某片段内有人提问(观众/连麦者问的问题),必须提取提问原文填 question,且 hook_3sec 用该提问(前3秒先展示提问再回答);无提问则 hook_3sec 用金句/悬念。" "格式含 title, start_time, end_time, hook_3sec, cta_ending, transcript_excerpt, reason;有提问时加 question。" - "禁止输出任何非 JSON 内容。" + "必须严格遵守用户给出的单段时长区间(秒)。禁止输出任何非 JSON 内容。" ) client = OpenAI(api_key=provider["api_key"], base_url=provider["base_url"]) resp = client.chat.completions.create( @@ -308,15 +436,24 @@ def call_openai_api(transcript: str, clip_count: int, provider: dict) -> str: return content -def call_ollama(transcript: str, clip_count: int = CLIP_COUNT, model: str = "qwen2.5:3b") -> str: +def call_ollama( + transcript: str, + clip_count: int = CLIP_COUNT, + model: str = "qwen2.5:3b", + min_dur: float = 60, + max_dur: float = 300, + ops_jingju_hotspot: bool = False, +) -> str: """调用卡若AI本地模型(Ollama),使用 chat 接口避免对话式误判""" import requests - prompt = _build_prompt(transcript, clip_count) + prompt = _build_prompt( + transcript, clip_count, min_dur, max_dur, ops_jingju_hotspot=ops_jingju_hotspot + ) system = ( "你是短视频策划师。用户会提供视频文字稿,你只输出一个 JSON 数组。" "若某片段内有人提问(观众/连麦者问的问题),必须提取提问原文填 question,且 hook_3sec 用该提问(前3秒先展示提问再回答);无提问则 hook_3sec 用金句/悬念。" "格式含 title, start_time, end_time, hook_3sec, cta_ending, transcript_excerpt, reason;有提问时加 question。" - "禁止输出任何非 JSON 内容。" + "必须严格遵守用户给出的单段时长区间(秒)。禁止输出任何非 JSON 内容。" ) try: r = requests.post( @@ -348,14 +485,80 @@ def main(): parser = argparse.ArgumentParser(description="高光识别 - AI 分析文字稿输出 highlights.json") parser.add_argument("--transcript", "-t", required=True, help="transcript.srt 路径") parser.add_argument("--output", "-o", required=True, help="highlights.json 输出路径") - parser.add_argument("--clips", "-n", type=int, default=CLIP_COUNT, help="切片数量") + parser.add_argument("--clips", "-n", type=int, default=None, help="切片数量(默认随 preset)") + parser.add_argument( + "--preset", + choices=["long", "ops-short"], + default="long", + help="long=单场深度切片 60~300 秒;ops-short=运营短切片 15~30 秒×约24条,京剧梗+热点优先", + ) + parser.add_argument( + "--min-duration", + type=float, + default=None, + help="单段最小时长(秒),默认随 preset", + ) + parser.add_argument( + "--max-duration", + type=float, + default=None, + help="单段最大时长(秒),默认随 preset;长切片模式可不限制上限则传极大值", + ) + parser.add_argument( + "--ops-jingju-hotspot", + action="store_true", + help="在 prompt 中强调京剧比喻/唱腔梗 + 热点选题(可与 ops-short 同用)", + ) + parser.add_argument( + "--prompt-min-sec", + type=float, + default=None, + help="送模型的 SRT 从该秒之后截取;ops-short 默认 450(约7:30),long 默认 0", + ) parser.add_argument("--require-ai", action="store_true", help="必须用 AI 识别,失败则退出不兜底") args = parser.parse_args() + + if args.preset == "ops-short": + min_dur = float(args.min_duration if args.min_duration is not None else OPS_SHORT_MIN) + max_dur = float(args.max_duration if args.max_duration is not None else OPS_SHORT_MAX) + clip_n = int(args.clips if args.clips is not None else OPS_SHORT_CLIPS) + ops_focus = True # 运营短切片默认强调京剧梗+热点 + filter_max_sec = max_dur # 严格卡上限 + else: + min_dur = float(args.min_duration if args.min_duration is not None else MIN_DURATION) + max_dur = float(args.max_duration if args.max_duration is not None else MAX_DURATION) + clip_n = int(args.clips if args.clips is not None else CLIP_COUNT) + ops_focus = bool(args.ops_jingju_hotspot) + filter_max_sec = None # 与历史一致:只滤掉过短,不因略超 300 秒丢片 + prompt_min_sec = ( + float(args.prompt_min_sec) + if args.prompt_min_sec is not None + else ( + OPS_SHORT_PROMPT_MIN_SEC_DEFAULT + if args.preset == "ops-short" + else 0.0 + ) + ) transcript_path = Path(args.transcript) if not transcript_path.exists(): print(f"❌ 文字稿不存在: {transcript_path}", file=sys.stderr) sys.exit(1) - text = srt_to_timestamped_text(str(transcript_path)) + if prompt_min_sec > 0: + text = srt_text_from_min_sec(str(transcript_path), prompt_min_sec) + if len(text) < 400: + print( + "⚠️ 截断后文字稿过短,回退 srt_to_timestamped_text 全长", + file=sys.stderr, + ) + text = srt_to_timestamped_text(str(transcript_path)) + else: + print( + f" 运营短切片:送模型文字稿已从 {prompt_min_sec:.0f}s 之后截取(约 {len(text)} 字)", + flush=True, + ) + else: + text = srt_to_timestamped_text(str(transcript_path)) + fb_start = float(prompt_min_sec) if args.preset == "ops-short" else 0.0 if len(text) < 100: print("❌ 文字稿过短,请检查 SRT 格式", file=sys.stderr) sys.exit(1) @@ -366,13 +569,29 @@ def main(): for provider in api_queue: try: print(f"正在调用 API {provider.get('model', '?')} 分析高光片段...") - raw = call_openai_api(text, args.clips, provider) + raw = call_openai_api( + text, + clip_n, + provider, + min_dur=min_dur, + max_dur=max_dur, + ops_jingju_hotspot=ops_focus, + ) if not raw: raise ValueError("API 返回空") data = _parse_ai_json(raw) if data and isinstance(data, list) and len(data) > 0: - print(f" ✓ API ({provider.get('model', '?')}) 成功,识别 {len(data)} 段") - break + if args.preset == "ops-short" and not _ops_short_ai_plausible( + data, min_dur, max_dur, prompt_min_sec + ): + print( + " API 结果不符合运营短切片规则,丢弃并尝试下一通道", + file=sys.stderr, + ) + data = None + else: + print(f" ✓ API ({provider.get('model', '?')}) 成功,识别 {len(data)} 段") + break except Exception as e: print(f" API ({provider.get('model', '?')}) 失败: {e}", file=sys.stderr) if raw: @@ -388,13 +607,29 @@ def main(): for model in OLLAMA_MODELS: try: print(f"正在调用 Ollama {model} 分析高光片段...") - raw = call_ollama(text, args.clips, model) + raw = call_ollama( + text, + clip_n, + model, + min_dur=min_dur, + max_dur=max_dur, + ops_jingju_hotspot=ops_focus, + ) if not raw: raise ValueError("模型返回空") data = _parse_ai_json(raw) if data and isinstance(data, list) and len(data) > 0: - print(f" ✓ {model} 成功,识别 {len(data)} 段") - break + if args.preset == "ops-short" and not _ops_short_ai_plausible( + data, min_dur, max_dur, prompt_min_sec + ): + print( + f" {model} 结果不符合运营短切片规则,尝试下一模型", + file=sys.stderr, + ) + data = None + else: + print(f" ✓ {model} 成功,识别 {len(data)} 段") + break except Exception as e: print(f" {model} 失败: {e}", file=sys.stderr) if raw: @@ -405,14 +640,16 @@ def main(): print("❌ 必须用 AI 识别,当前无可用模型或解析失败", file=sys.stderr) sys.exit(1) print("使用规则备用切分", file=sys.stderr) - data = fallback_highlights(str(transcript_path), args.clips) + data = fallback_highlights( + str(transcript_path), clip_n, min_dur, max_dur, start_from_sec=fb_start + ) if not data: - data = fallback_highlights(str(transcript_path), args.clips) + data = fallback_highlights( + str(transcript_path), clip_n, min_dur, max_dur, start_from_sec=fb_start + ) if not isinstance(data, list): data = [data] - # 过滤短于 1 分钟的切片 - data = _filter_short_clips(data) - # 统一 start_time/end_time 为 HH:MM:SS(兼容 Ollama 返回秒数) + # 先统一时间为 HH:MM:SS,再按时长过滤(兼容模型返回数值秒) for item in data: if not isinstance(item, dict): continue @@ -422,10 +659,15 @@ def main(): et = item.get("end_time") or item.get("end") if isinstance(et, (int, float)): item["end_time"] = _sec_to_hhmmss(et) + if args.preset == "ops-short" and prompt_min_sec > 0: + data = _filter_start_not_before(data, prompt_min_sec) + data = _filter_clips_by_duration(data, min_dur, filter_max_sec) # 若 AI 返回的片段全被过滤,用规则备用 if not data and transcript_path.exists(): - print(" AI 片段时长无效,改用规则切分(1-5 分钟)", file=sys.stderr) - data = fallback_highlights(str(transcript_path), args.clips) + print(" AI 片段时长无效,改用规则切分", file=sys.stderr) + data = fallback_highlights( + str(transcript_path), clip_n, min_dur, max_dur, start_from_sec=fb_start + ) # 强制中文 print(" 确保导出名与封面为简体中文...") data = _ensure_chinese_highlights(data) diff --git a/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_enhance.py b/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_enhance.py index 8b2331d2..62e09c17 100644 --- a/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_enhance.py +++ b/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_enhance.py @@ -64,6 +64,12 @@ CROP_VF = "crop=568:1080:508:0,crop=498:1080:35:0" VERTICAL_W, VERTICAL_H = 498, 1080 OVERLAY_X = 543 # 508+35,与历史 483+60 对齐,避免封面/字幕错位 +# 竖屏「全画面入画」:不裁中间竖条;整幅横版等比缩放入 498×1080,上下黑边(letterbox) +VERTICAL_FIT_FULL_VF = ( + "scale=w=498:h=1080:force_original_aspect_ratio=decrease:flags=lanczos," + "pad=498:1080:(ow-iw)/2:(oh-ih)/2:color=black" +) + def _overlay_x_from_crop_vf(crop_vf: str): """从两段 crop 解析字幕/封面叠在横版上的 x:crop=W:1080:X:0,crop=498:1080:Y:0 → X+Y""" @@ -81,13 +87,14 @@ def build_typewriter_subtitle_images( temp_dir, out_w, out_h, - cover_duration, + subtitle_overlay_start, min_step_sec=0.05, max_steps_per_line=28, ): """ 将每条字幕拆成多帧:同一时间段内前缀逐字(逐段)变长,读起来更顺、更像跟读语音。 长句按步数上限均分字符,避免单条 concat 段过多。 + subtitle_overlay_start:最早显示字幕的时间轴(秒),须 ≥ 封面结束 + 留白。 """ sub_images = [] img_idx = 0 @@ -96,7 +103,7 @@ def build_typewriter_subtitle_images( if not safe_text or not safe_text.strip(): continue s, e = float(sub["start"]), float(sub["end"]) - s = max(s, cover_duration) + s = max(s, subtitle_overlay_start) if s >= e - 0.02: continue dur = e - s @@ -299,6 +306,11 @@ STYLE = { } } +# 字幕与语音同步的全局延迟补偿(秒);封面后留白再叠字幕;封面标题汉字上限(须在本文件先于 _limit_cover_title_cjk 定义) +SUBTITLE_DELAY_SEC = 2.0 +SUBS_START_AFTER_COVER_SEC = 3.0 +COVER_TITLE_MAX_CJK = 6 + # ============ 工具函数 ============ def get_font(font_path, size): @@ -339,6 +351,21 @@ def _normalize_title_for_display(title: str) -> str: return s +def _limit_cover_title_cjk(text: str, max_cjk: int = COVER_TITLE_MAX_CJK) -> str: + """封面标题最多保留 max_cjk 个汉字(含汉字即计数);超长截断,避免封面字过小或换行过多。""" + if not text or max_cjk <= 0: + return text or "" + out = [] + n_cjk = 0 + for ch in text: + if "\u4e00" <= ch <= "\u9fff": + n_cjk += 1 + if n_cjk > max_cjk: + break + out.append(ch) + return "".join(out).strip() + + # macOS/APFS 文件名允许的中文标点(保留刺激性标题所需的标点) _SAFE_CJK_PUNCT = set(",。?!;:·、…()【】「」《》~—·+") @@ -449,14 +476,6 @@ def _detect_clip_pts_offset(clip_path: str) -> float: return 0.0 -# 字幕与语音同步的全局延迟补偿(秒) -# batch_clip -ss input seeking 导致实际切割比请求早 0~3 秒(关键帧对齐) -# 字幕按 highlights.start_time 算相对时间,会比实际音频提前 -# 加正值延迟 = 字幕往后推 = 与声音更同步 -# 2025-03 实测:Soul派对直播视频关键帧间距 2-4 秒,补偿需约 2.0s -SUBTITLE_DELAY_SEC = 2.0 # 增大到 2.0,避免字幕超前于说话 - - def _is_noise_line(text: str) -> bool: """检测是否为噪声行(单字母、重复符号、ASR幻觉等)""" if not text: @@ -625,9 +644,9 @@ def _sec_to_srt_time(sec): return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}" -def write_clip_srt(srt_path, subtitles, cover_duration): - """写出用于烧录的 SRT(仅保留封面结束后的字幕,时间已相对片段)""" - safe_start = cover_duration + 0.3 +def write_clip_srt(srt_path, subtitles, cover_duration, subs_after_cover_sec=SUBS_START_AFTER_COVER_SEC): + """写出用于烧录的 SRT(仅保留封面结束+留白后的字幕,时间已相对片段)""" + safe_start = cover_duration + subs_after_cover_sec + 0.3 lines = [] idx = 1 for sub in subtitles: @@ -726,8 +745,11 @@ def get_video_info(video_path): '-of', 'json', video_path ] result = subprocess.run(cmd, capture_output=True, text=True) - info = json.loads(result.stdout) - stream = info['streams'][0] + info = json.loads(result.stdout or "{}") + streams = info.get("streams") or [] + if not streams: + raise ValueError(f"ffprobe 无视频流: {video_path}") + stream = streams[0] # 获取时长 cmd2 = [ @@ -909,31 +931,39 @@ def create_cover_image(hook_text, width, height, output_path, video_path=None): # ============ 字幕图片生成 ============ def create_subtitle_image(text, width, height, output_path): - """创建字幕图片(关键词加粗加大突出)。竖屏 498 宽时字号略小、保证居中且不溢出。""" + """创建字幕图片(关键词加粗加大突出)。498 竖条时居中;全幅横版时偏下居中(为 --vertical-fit-full)。""" style = STYLE['subtitle'] img = Image.new('RGBA', (width, height), (0, 0, 0, 0)) draw = ImageDraw.Draw(img) - # 竖屏窄幅时缩小字号,保证整行在画面内且居中 base_size = style['font_size'] if (width, height) == (VERTICAL_W, VERTICAL_H): base_size = min(base_size, 38) + elif height == 1080 and width >= 1280: + # 1920×1080 全画面叠字:字号略大,条带靠下,避免挡脸 + base_size = min(max(base_size, 46), 56) font = get_font(FONT_BOLD, base_size) text_w, text_h = get_text_size(draw, text, font) - while text_w > width - 80 and base_size > 24: + margin_x = 120 if width >= 1280 else 80 + while text_w > width - margin_x and base_size > 24: base_size -= 2 font = get_font(FONT_BOLD, base_size) text_w, text_h = get_text_size(draw, text, font) kw_size = base_size + style.get('keyword_size_add', 4) kw_font = get_font(FONT_HEAVY, kw_size) - # 字幕完全居中(水平+垂直正中间);竖屏时限制在界面内不超出 base_x = (width - text_w) // 2 if (width, height) == (VERTICAL_W, VERTICAL_H): pad = 24 base_x = max(pad, min(width - pad - text_w, base_x)) - base_y = (height - text_h) // 2 + base_y = (height - text_h) // 2 + elif height == 1080 and width >= 1280: + pad = 40 + base_x = max(pad, min(width - pad - text_w, base_x)) + base_y = height - text_h - 100 + else: + base_y = (height - text_h) // 2 # 背景条(不超出画布) padding = 15 @@ -1085,10 +1115,11 @@ def _parse_clip_index(filename: str) -> int: def enhance_clip(clip_path, output_path, highlight_info, temp_dir, transcript_path, force_burn_subs=False, skip_subs=False, vertical=False, - crop_vf=None, overlay_x=None, typewriter_subs=False): - """增强单个切片。vertical=True 时最后裁成竖屏 498x1080 直出成片。 - crop_vf / overlay_x:场次取景微调(先截 20% 帧对一下小程序黑框再填)。 - typewriter_subs:同一条字幕时间内前缀逐字渐显(更跟口型)。 + crop_vf=None, overlay_x=None, typewriter_subs=False, + vertical_fit_full=False): + """增强单个切片。vertical=True 时最后输出 498×1080。 + vertical_fit_full:不裁中间竖条;整幅画面等比缩放入 498×1080 + 上下黑边,前后内容都可见。 + 否则沿用 crop 竖条(全画面标定深色带)。 """ print(f" 输入: {os.path.basename(clip_path)}", flush=True) @@ -1108,17 +1139,28 @@ def enhance_clip(clip_path, output_path, highlight_info, temp_dir, transcript_pa hook_text = _normalize_title_for_display(raw_title) or raw_title or '精彩切片' # 封面文字同样做安全处理 hook_text = apply_platform_safety(hook_text) + hook_text = _limit_cover_title_cjk(hook_text, COVER_TITLE_MAX_CJK) or hook_text cover_duration = STYLE['cover']['duration'] + subtitle_overlay_start = cover_duration + SUBS_START_AFTER_COVER_SEC - # 竖屏成片:封面/字幕按 498x1080 做,叠在裁切区域,文字与字幕在竖屏上完整且居中 - out_w, out_h = (VERTICAL_W, VERTICAL_H) if vertical else (width, height) - vf_use = (crop_vf or CROP_VF).strip() if vertical else CROP_VF - ox = overlay_x - if vertical and ox is None and crop_vf: - ox = _overlay_x_from_crop_vf(crop_vf) - if vertical and ox is None: - ox = OVERLAY_X - overlay_pos = f"{int(ox)}:0" if vertical else "0:0" + # 竖屏:默认封面/字幕按 498×1080 叠在竖条上;全画面模式按原分辨率全屏叠加再整体缩放 + if vertical and vertical_fit_full: + out_w, out_h = width, height + vf_use = "" + overlay_pos = "0:0" + elif vertical: + out_w, out_h = VERTICAL_W, VERTICAL_H + vf_use = (crop_vf or CROP_VF).strip() + ox = overlay_x + if ox is None and crop_vf: + ox = _overlay_x_from_crop_vf(crop_vf) + if ox is None: + ox = OVERLAY_X + overlay_pos = f"{int(ox)}:0" + else: + out_w, out_h = width, height + vf_use = CROP_VF + overlay_pos = "0:0" # 1. 生成封面 print(f" [1/5] 封面生成中…", flush=True) @@ -1198,7 +1240,7 @@ def enhance_clip(clip_path, output_path, highlight_info, temp_dir, transcript_pa print(f" ✓ 字幕解析 ({len(subtitles)}条),将烧录为{mode}字幕", flush=True) if typewriter_subs: sub_images = build_typewriter_subtitle_images( - subtitles, temp_dir, out_w, out_h, cover_duration + subtitles, temp_dir, out_w, out_h, subtitle_overlay_start ) else: for i, sub in enumerate(subtitles): @@ -1254,10 +1296,10 @@ def enhance_clip(clip_path, output_path, highlight_info, temp_dir, transcript_pa # duration X.XXX # 最后一行不写 duration(用于循环/截断防报错) concat_lines = [] - prev_end = cover_duration # 字幕从封面结束后开始 + prev_end = subtitle_overlay_start # 字幕从「封面结束 + SUBS_START_AFTER_COVER_SEC」起 for img in sub_images: - sub_start = max(img['start'], cover_duration) + sub_start = max(img['start'], subtitle_overlay_start) sub_end = img['end'] if sub_start >= sub_end: continue @@ -1327,15 +1369,26 @@ def enhance_clip(clip_path, output_path, highlight_info, temp_dir, transcript_pa if result.stderr: print(f" {str(result.stderr)[:300]}", file=sys.stderr) - # 5.4 输出:竖屏则裁成 498x1080 直出(高光区域裁剪,成片必做) - print(f" [5/5] 竖屏裁剪中(498×1080)…", flush=True) - if vertical: + # 5.4 输出:竖屏 498×1080(竖条裁剪 或 全画面 letterbox) + print(f" [5/5] 竖屏输出(498×1080)…", flush=True) + if vertical and vertical_fit_full: + r = subprocess.run([ + 'ffmpeg', '-y', '-i', current_video, + '-vf', VERTICAL_FIT_FULL_VF, '-c:a', 'copy', output_path + ], capture_output=True, text=True) + if r.returncode == 0 and os.path.exists(output_path): + print(f" ✓ 全画面缩放+上下黑边完成(未裁中间竖条)", flush=True) + else: + print(f" ❌ 全画面缩放失败: {(r.stderr or '')[:400]}", file=sys.stderr) + shutil.copy(current_video, output_path) + print(f" ⚠ 已回退为未缩放版本", flush=True) + elif vertical: r = subprocess.run([ 'ffmpeg', '-y', '-i', current_video, '-vf', vf_use, '-c:a', 'copy', output_path ], capture_output=True, text=True) if r.returncode == 0 and os.path.exists(output_path): - print(f" ✓ 竖屏裁剪完成", flush=True) + print(f" ✓ 竖屏竖条裁剪完成", flush=True) else: print(f" ❌ 竖屏裁剪失败: {(r.stderr or '')[:300]}", file=sys.stderr) shutil.copy(current_video, output_path) @@ -1378,6 +1431,11 @@ def main(): action="store_true", help="字幕在同一条时间内前缀逐字渐显(更通顺、更跟读)", ) + parser.add_argument( + "--vertical-fit-full", + action="store_true", + help="竖屏成片不裁中间竖条:整幅 16:9 等比缩放入 498×1080,上下黑边,画面显示全;封面/字幕先叠满横版再缩放", + ) args = parser.parse_args() clips_dir = Path(args.clips) if args.clips else CLIPS_DIR @@ -1404,12 +1462,14 @@ def main(): overlay_x_arg = getattr(args, "overlay_x", -1) overlay_x_arg = None if overlay_x_arg < 0 else overlay_x_arg typewriter = getattr(args, "typewriter_subs", False) + vfit = getattr(args, "vertical_fit_full", False) print( f"功能: 封面+字幕+加速10%+去语气词" + ("+竖屏498x1080" if vertical else "") + + ("+全画面letterbox(不裁竖条)" if vertical and vfit else "") + ("+逐字字幕" if typewriter else "") ) - if vertical and crop_vf_arg: + if vertical and crop_vf_arg and not vfit: print(f"取景: --crop-vf {crop_vf_arg}") print(f"输入: {clips_dir}") print(f"输出: {output_dir}" + ("(成片,文件名=标题)" if title_only else "")) @@ -1439,6 +1499,9 @@ def main(): if getattr(args, 'title_only', False): title = (highlight_info.get('title') or highlight_info.get('hook_3sec') or clip_path.stem) + title = _limit_cover_title_cjk( + _normalize_title_for_display(str(title)) or str(title), COVER_TITLE_MAX_CJK + ) or str(title) name = sanitize_filename(title) + '.mp4' output_path = output_dir / name else: @@ -1458,6 +1521,7 @@ def main(): crop_vf=crop_vf_arg or None, overlay_x=overlay_x_arg, typewriter_subs=typewriter, + vertical_fit_full=vfit, ): success_count += 1 finally: diff --git a/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_slice_pipeline.py b/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_slice_pipeline.py index 8060f7b6..8ba556e0 100644 --- a/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_slice_pipeline.py +++ b/03_卡木(木)/木叶_视频内容/视频切片/脚本/soul_slice_pipeline.py @@ -105,7 +105,31 @@ def main(): parser = argparse.ArgumentParser(description="Soul 切片一体化流水线") parser.add_argument("--video", "-v", required=True, help="输入视频路径") parser.add_argument("--output", "-o", help="输出目录(默认:视频同目录下 视频名_output)") - parser.add_argument("--clips", "-n", type=int, default=8, help="切片数量") + parser.add_argument("--clips", "-n", type=int, default=8, help="切片数量(--ops-short 且未改 -n 时默认 24)") + parser.add_argument( + "--highlight-preset", + choices=["long", "ops-short"], + default="long", + help="高光识别 preset:long=60~300 秒深度切片;ops-short=15~30 秒运营密度切片", + ) + parser.add_argument( + "--ops-short", + action="store_true", + help="运营短切片一键:preset=ops-short,未指定 -n 时用 24 条", + ) + parser.add_argument("--min-clip-sec", type=float, default=None, help="传给 identify_highlights --min-duration") + parser.add_argument("--max-clip-sec", type=float, default=None, help="传给 identify_highlights --max-duration") + parser.add_argument( + "--ops-jingju-hotspot", + action="store_true", + help="高光 prompt 强调京剧梗+热点(可与 ops-short 同用)", + ) + parser.add_argument( + "--prompt-min-sec", + type=float, + default=None, + help="传给 identify_highlights:送模型的 SRT 从该秒之后截取(ops-short 默认 450)", + ) parser.add_argument("--skip-transcribe", action="store_true", help="跳过转录(已有 transcript.srt)") parser.add_argument("--skip-highlights", action="store_true", help="跳过高光识别(已有 highlights.json)") parser.add_argument("--skip-clips", action="store_true", help="跳过切片(已有 clips/,仅重新增强)") @@ -122,6 +146,11 @@ def main(): parser.add_argument("--montage-seconds", type=float, default=4.0, help="快速混剪每条截取秒数") args = parser.parse_args() + if getattr(args, "ops_short", False): + args.highlight_preset = "ops-short" + if args.clips == 8: + args.clips = 24 + video_path = Path(args.video).resolve() if not video_path.exists(): print(f"❌ 视频不存在: {video_path}") @@ -148,7 +177,7 @@ def main(): print("=" * 60) print(f"输入视频: {video_path}") print(f"输出目录: {base_dir}") - print(f"切片数量: {args.clips}") + print(f"切片数量: {args.clips} | 高光 preset: {args.highlight_preset}") print("=" * 60) # 0. 强制重转录时删除旧产物(含 audio 以重提完整音频) @@ -201,16 +230,26 @@ def main(): # 2. 高光识别 if not args.skip_highlights: + hl_cmd = [ + sys.executable, + str(SCRIPT_DIR / "identify_highlights.py"), + "--transcript", str(transcript_path), + "--output", str(highlights_path), + "--clips", str(args.clips), + "--preset", str(args.highlight_preset), + ] + if args.min_clip_sec is not None: + hl_cmd.extend(["--min-duration", str(args.min_clip_sec)]) + if args.max_clip_sec is not None: + hl_cmd.extend(["--max-duration", str(args.max_clip_sec)]) + if getattr(args, "ops_jingju_hotspot", False): + hl_cmd.append("--ops-jingju-hotspot") + if getattr(args, "prompt_min_sec", None) is not None: + hl_cmd.extend(["--prompt-min-sec", str(args.prompt_min_sec)]) run( - [ - sys.executable, - str(SCRIPT_DIR / "identify_highlights.py"), - "--transcript", str(transcript_path), - "--output", str(highlights_path), - "--clips", str(args.clips), - ], - "高光识别(Ollama→规则)", - timeout=180, + hl_cmd, + "高光识别(API→Ollama→规则)", + timeout=600, ) if not highlights_path.exists(): print(f"❌ 需要 highlights.json: {highlights_path}") @@ -283,7 +322,11 @@ def main(): ] if getattr(args, "skip_subs", False): enhance_cmd.append("--skip-subs") - if getattr(args, "force_burn_subs", False): + if use_two_folders: + enhance_cmd.extend(["--vertical", "--title-only"]) + if (getattr(args, "force_burn_subs", False) or use_two_folders) and not getattr( + args, "skip_subs", False + ): enhance_cmd.append("--force-burn-subs") enhance_timeout = max(900, 600 + len(clips_list) * 90) # 约 90 秒/片 ok = run(enhance_cmd, "增强处理(封面+字幕+加速)", timeout=enhance_timeout, check=False) diff --git a/SKILL_REGISTRY.md b/SKILL_REGISTRY.md index c6b9b34f..fa67eda0 100644 --- a/SKILL_REGISTRY.md +++ b/SKILL_REGISTRY.md @@ -104,7 +104,7 @@ | # | 技能 | 成员 | 触发词 | SKILL 路径 | 一句话 | |:--|:---|:---|:---|:---|:---| -| M01 | 视频切片 | 木叶 | **视频剪辑、切片发布、切片动效包装、程序化包装、片头片尾、批量封面、视频包装** | `03_卡木(木)/木叶_视频内容/视频切片/SKILL.md` | 长视频切片+字幕+发布;联动切片动效包装(片头/片尾/程序化) | +| M01 | 视频切片 | 木叶 | **视频剪辑、切片发布、切片动效包装、程序化包装、片头片尾、批量封面、视频包装、运营短切片、15秒切片、热点密度、京剧梗** | `03_卡木(木)/木叶_视频内容/视频切片/SKILL.md` | 长视频切片+字幕+发布;**ops-short 15~30 秒×20~30 条**;联动切片动效包装(片头/片尾/程序化) | | M01b | 抖音视频解析 | 木叶 | **抖音视频、抖音链接、抖音解析、抖音下载、提取抖音文案、抖音无水印** | `03_卡木(木)/木叶_视频内容/抖音视频解析/SKILL.md` | 链接→解析ID→提取文案→下载无水印视频 | | M01c | 抖音发布 | 木叶 | **抖音发布、发布到抖音、抖音登录、抖音上传、腕推抖音** | `03_卡木(木)/木叶_视频内容/抖音发布/SKILL.md` | 纯 API 视频上传+发布(VOD + bd-ticket-guard),无需浏览器 | | M01d | B站发布 | 木叶 | **B站发布、发布到B站、B站登录、B站上传、bilibili发布** | `03_卡木(木)/木叶_视频内容/B站发布/SKILL.md` | 纯 API(preupload 分片),Cookie 有效期约6个月 | diff --git a/运营中枢/工作台/gitea_push_log.md b/运营中枢/工作台/gitea_push_log.md index 219b5002..52f8e9a6 100644 --- a/运营中枢/工作台/gitea_push_log.md +++ b/运营中枢/工作台/gitea_push_log.md @@ -403,3 +403,4 @@ | 2026-03-20 11:27:30 | 🔄 卡若AI 同步 2026-03-20 11:27 | 更新:水溪整理归档、运营中枢工作台 | 排除 >20MB: 11 个 | | 2026-03-20 12:22:19 | 🔄 卡若AI 同步 2026-03-20 12:22 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 11 个 | | 2026-03-20 13:39:57 | 🔄 卡若AI 同步 2026-03-20 12:40 | 更新:水桥平台对接 | 排除 >20MB: 11 个 | +| 2026-03-20 16:08:50 | 🔄 卡若AI 同步 2026-03-20 16:08 | 更新:水桥平台对接、卡木、运营中枢工作台 | 排除 >20MB: 11 个 | diff --git a/运营中枢/工作台/scripts/s2_admin_fullpage_capture.py b/运营中枢/工作台/scripts/s2_admin_fullpage_capture.py new file mode 100644 index 00000000..8cb26411 --- /dev/null +++ b/运营中枢/工作台/scripts/s2_admin_fullpage_capture.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +""" +S2 私域管理后台:按路由批量全页截图(Playwright) + +用法(需已登录态,任选其一): + 1) 先在本机 Chrome 登录后台,再用「用户数据目录」启动浏览器: + python3 s2_admin_fullpage_capture.py --user-data-dir "$HOME/Library/Application Support/Google/Chrome" --channel chromium --headless + (无 Google Chrome.app 时用 channel chromium;有则用 --channel chrome。建议先关 Chrome 避免配置锁。) + + 2) 或使用已导出的 storage_state.json: + playwright install chromium + python3 s2_admin_fullpage_capture.py --storage-state /path/to/state.json + +默认输出:卡若Ai 报告目录下 screenshots/fullpage_playwright/ +""" +from __future__ import annotations + +import argparse +import asyncio +import re +from pathlib import Path + +import httpx + +BASE = "https://s2.siyuguanli.com/admin/static/js/app.6dd8bb884b28919ef0f9.js" +ADMIN = "https://s2.siyuguanli.com/admin/#" + +DEST_DEFAULT = Path( + "/Users/karuo/Documents/卡若Ai的文件夹/报告/S2私域管理后台_功能与接口调研/screenshots/fullpage_playwright" +) + + +def parse_routes_from_app_js(text: str) -> list[str]: + paths = re.findall(r'path:"([^"]+)"', text) + stack: list[str] = [] + out: list[str] = [] + for p in paths: + if p in ("*", "/401", "/404", "/login", "/authredirect", "/"): + continue + if p.startswith("/"): + stack = [p.rstrip("/")] + continue + if not stack: + stack = [""] + segs = [s for s in stack if s] + full = "/" + "/".join(segs + [p]) + full = re.sub(r"/+", "/", full) + out.append(full) + seen: set[str] = set() + uniq: list[str] = [] + for x in out: + if x not in seen: + seen.add(x) + uniq.append(x) + uniq = ["/home" if x == "/home/home" else x for x in uniq] + if "/home" not in uniq: + uniq.insert(0, "/home") + # 线上大屏常见 hash 为 #/datav/overview,与 bundle 内 /dataReport/datav/overview 并存 + if "/datav/overview" not in uniq: + uniq.append("/datav/overview") + return uniq + + +async def main() -> None: + ap = argparse.ArgumentParser() + ap.add_argument("--dest", type=Path, default=DEST_DEFAULT) + ap.add_argument("--storage-state", type=Path, default=None) + ap.add_argument("--user-data-dir", type=Path, default=None) + ap.add_argument("--channel", default="chrome", help="与 user-data-dir 联用:chrome | chromium") + ap.add_argument("--wait-ms", type=int, default=2500) + ap.add_argument("--max", type=int, default=0, help="仅截取前 N 条路由,0 表示全量") + ap.add_argument( + "--headless", + action="store_true", + help="与 --user-data-dir 联用:无界面跑持久化上下文(需本机已登录该 Profile)", + ) + ap.add_argument( + "--no-escape", + action="store_true", + help="不在每页加载后按 Escape(默认按一次以尝试关闭 v-modal 遮罩)", + ) + args = ap.parse_args() + + from playwright.async_api import async_playwright + + async with httpx.AsyncClient(verify=False, timeout=60) as client: + js = (await client.get(BASE)).text + routes = parse_routes_from_app_js(js) + if args.max and args.max > 0: + routes = routes[: args.max] + + args.dest.mkdir(parents=True, exist_ok=True) + + async with async_playwright() as p: + if args.user_data_dir: + browser = await p.chromium.launch_persistent_context( + user_data_dir=str(args.user_data_dir), + channel=args.channel if args.channel in ("chrome", "msedge") else None, + headless=args.headless, + viewport={"width": 1440, "height": 900}, + ) + page = browser.pages[0] if browser.pages else await browser.new_page() + else: + browser = await p.chromium.launch(headless=True) + context = await browser.new_context( + viewport={"width": 1440, "height": 900}, + storage_state=str(args.storage_state) if args.storage_state else None, + ) + page = await context.new_page() + + for i, route in enumerate(routes, 1): + url = f"{ADMIN}{route}" + name = route.strip("/").replace("/", "__") or "home" + fp = args.dest / f"{i:03d}_{name}.png" + try: + await page.goto(url, wait_until="networkidle", timeout=120000) + except Exception: + await page.goto(url, wait_until="domcontentloaded", timeout=120000) + await page.wait_for_timeout(args.wait_ms) + if not args.no_escape: + await page.keyboard.press("Escape") + await page.wait_for_timeout(300) + await page.screenshot(path=str(fp), full_page=True) + print(f"OK {i}/{len(routes)} {fp.name}") + + if args.user_data_dir: + await browser.close() + else: + await browser.close() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/运营中枢/工作台/代码管理.md b/运营中枢/工作台/代码管理.md index d88f6a2e..1426a68a 100644 --- a/运营中枢/工作台/代码管理.md +++ b/运营中枢/工作台/代码管理.md @@ -406,3 +406,4 @@ | 2026-03-20 11:27:30 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-20 11:27 | 更新:水溪整理归档、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-03-20 12:22:19 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-20 12:22 | 更新:水桥平台对接、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-03-20 13:39:57 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-20 12:40 | 更新:水桥平台对接 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | +| 2026-03-20 16:08:50 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-20 16:08 | 更新:水桥平台对接、卡木、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |