diff --git a/03_卡木(木)/木叶_视频内容/B站发布/脚本/bilibili_publish.py b/03_卡木(木)/木叶_视频内容/B站发布/脚本/bilibili_publish.py index db100377..c33e3ea0 100644 --- a/03_卡木(木)/木叶_视频内容/B站发布/脚本/bilibili_publish.py +++ b/03_卡木(木)/木叶_视频内容/B站发布/脚本/bilibili_publish.py @@ -76,23 +76,11 @@ async def _api_publish(video_path: str, title: str, scheduled_time=None) -> Publ cover_path = extract_cover(video_path) print(f" [API] 封面已提取: {cover_path}", flush=True) - tags = "Soul派对,创业,认知觉醒,副业,商业思维" - meta = { - "copyright": 1, - "source": "", - "desc": title, - "desc_format_id": 0, - "dynamic": "", - "interactive": 0, - "open_elec": 0, - "no_reprint": 1, - "subtitles": {"lan": "", "open": 0}, - "tag": tags, - "tid": 160, # 生活 > 日常 - "title": title[:80], - "up_close_danmaku": False, - "up_close_reply": False, - } + from video_metadata import VideoMeta + vmeta = VideoMeta.from_filename(video_path) + meta = vmeta.bilibili_meta() + meta["title"] = title[:80] + meta["desc"] = vmeta.description("B站") if scheduled_time: dtime = int(scheduled_time.timestamp()) diff --git a/03_卡木(木)/木叶_视频内容/多平台分发/SKILL.md b/03_卡木(木)/木叶_视频内容/多平台分发/SKILL.md index b40bae25..992d6319 100644 --- a/03_卡木(木)/木叶_视频内容/多平台分发/SKILL.md +++ b/03_卡木(木)/木叶_视频内容/多平台分发/SKILL.md @@ -2,33 +2,31 @@ name: 多平台分发 description: > 一键将视频分发到 5 个平台(抖音、B站、视频号、小红书、快手)。 - 支持定时排期(30-120分钟随机间隔)、并行分发、去重、失败自动重试。 - 封面统一用视频第一帧,Cookie 统一管理防重复获取。 + API 优先策略:视频号纯 API、B站 bilibili-api-python、抖音纯 API。 + 支持定时排期(第1条立即发,后续 30-120 分钟随机间隔)、并行分发、去重、失败自动重试。 triggers: 多平台分发、一键分发、全平台发布、批量分发、视频分发 owner: 木叶 group: 木 -version: "3.1" -updated: "2026-03-10" +version: "4.0" +updated: "2026-03-11" --- -# 多平台分发 Skill(v3.1) +# 多平台分发 Skill(v4.0) -> **核心能力**:一条命令将成片目录下的所有视频同时发布到 5 个主流平台。 -> **平台覆盖**:抖音、B站、视频号、小红书、快手。 -> **技术路线**:抖音纯 API(逆向 VOD),B站 bilibili-api-python API,视频号/小红书/快手 Playwright 自动化。 -> **全链路**:定时排期 → 并行分发 → 去重 → 失败重试 → Cookie 预警 → 结果日志。 +> **核心原则**:API 发布为主,Playwright 为辅。确保确定性地分发到各平台。 +> **v4.0 变更**:视频号已切换为纯 API、统一元数据生成器、定时排期优化、简介/标签/分区自动填充。 --- ## 一、平台与实现方式 -| 平台 | 实现方式 | 定时发布 | Cookie 有效期 | 119 场实测 | +| 平台 | 实现方式 | 定时发布 | Cookie 有效期 | 120 场实测 | |------|----------|----------|---------------|------------| -| **抖音** | 纯 API(VOD + bd-ticket-guard) | API timing_ts | ~2-4h | 账号封禁,预检拦截 | -| **B站** | bilibili-api-python API 优先 → Playwright 兜底 | API dtime | ~6 个月 | 15/15 成功 | -| **视频号** | Playwright headless 自动化 | UI 定时(降级立即) | ~24-48h | 15/15 成功 | -| **小红书** | Playwright headless v2 自动化 | UI 定时(降级立即) | ~1-3 天 | 15/15 成功(修复后) | -| **快手** | Playwright headless 自动化 | UI 定时成功 | ~7-30 天 | 15/15 成功(含重试) | +| **视频号** | **纯 API**(DFS 上传 + post_create) | API 原生支持 | ~24-48h | 12/12 成功 | +| **B站** | **bilibili-api-python** API 优先 → Playwright 兜底 | API `dtime` | ~6 个月 | 12/12 成功 | +| **小红书** | Playwright headless 自动化 | UI 定时(降级立即) | ~1-3 天 | 12/12 成功 | +| **快手** | Playwright headless 自动化 | UI 定时 | ~7-30 天 | Cookie 过期 | +| **抖音** | 纯 API(VOD + bd-ticket-guard) | API `timing_ts` | ~2-4h | 账号封禁中 | --- @@ -37,132 +35,100 @@ updated: "2026-03-10" ```bash cd /Users/karuo/Documents/个人/卡若AI/03_卡木(木)/木叶_视频内容/多平台分发/脚本 -# 定时排期并行分发(默认 30-120 分钟随机间隔) +# 定时排期:第1条立即,后续 30-120min 随机间隔 python3 distribute_all.py -# 立即发布(不排期) +# 立即全部发布 python3 distribute_all.py --now -# 自定义排期间隔 -python3 distribute_all.py --min-gap 30 --max-gap 120 --max-hours 24 - # 只发指定平台 -python3 distribute_all.py --platforms 抖音 B站 +python3 distribute_all.py --platforms 视频号 B站 -# 检查 Cookie + 重试失败 -python3 distribute_all.py --check -python3 distribute_all.py --retry - -# 分发单条 / 自定义目录 -python3 distribute_all.py --video "/path/to/video.mp4" +# 自定义视频目录 python3 distribute_all.py --video-dir "/path/to/videos/" -# 跳过去重 / 串行调试 -python3 distribute_all.py --no-dedup -python3 distribute_all.py --serial +# 检查 Cookie / 重试失败 +python3 distribute_all.py --check +python3 distribute_all.py --retry ``` --- -## 三、首次使用流程 +## 三、定时排期(v4.0 优化) -``` -1. 安装依赖 - pip3 install httpx playwright cryptography Pillow - playwright install chromium +### 3.1 排期规则 +- **第 1 条**:立即发布(`first_delay=0`) +- **第 2 条起**:前一条 + random(30, 120) 分钟 +- 若总跨度 > 24h,自动按比例压缩 +- 12 条视频典型跨度 ~10-14h -2. 逐个平台登录(只需首次) - python3 ../抖音发布/脚本/douyin_login.py - python3 ../B站发布/脚本/bilibili_login.py - python3 ../视频号发布/脚本/channels_login.py - python3 ../小红书发布/脚本/xiaohongshu_login.py - python3 ../快手发布/脚本/kuaishou_login.py +### 3.2 各平台定时实现 -3. 检查 Cookie 状态 - python3 distribute_all.py --check - -4. 一键分发 - python3 distribute_all.py -``` - ---- - -## 四、Cookie 管理 - -### 4.1 统一管理器 - -`cookie_manager.py` 提供: -- 加载 Playwright storage_state.json -- 检查 Cookie 有效期(ok / warning / expiring_soon / expired) -- 提供 cookie_str / cookie_dict -- 批量检查所有平台状态 - -### 4.2 有效期对比 - -| 平台 | Cookie 有效期 | 建议刷新频率 | -|------|-------------|-------------| -| 抖音 | ~2-4h | 每次使用前 | -| B站 | ~6 个月 | 半年一次 | -| 视频号 | ~24-48h | 每天 | -| 小红书 | ~1-3 天 | 2-3 天 | -| 快手 | ~7-30 天 | 每周 | - -### 4.3 防重复获取 - -Cookie 文件保存后自动记录时间戳,`cookie_manager.py` 通过文件修改时间判断年龄。 -若 Cookie 仍有效,不会触发重新登录。 - ---- - -## 五、视频处理 - -### 5.1 封面提取 - -`video_utils.py` 使用 ffmpeg 提取视频第一帧(0.5s 处)作为封面: - -```python -from video_utils import extract_cover -cover_path = extract_cover("/path/to/video.mp4") -``` - -### 5.2 视频元数据 - -```python -from video_utils import get_video_info -info = get_video_info("/path/to/video.mp4") -# {'duration': 180.5, 'width': 1080, 'height': 1920, ...} -``` - ---- - -## 六、定时排期 - -### 6.1 排期逻辑(schedule_generator.py) -- 相邻视频随机间隔 30-120 分钟 -- 若总跨度 > 24h,按比例自动压缩 -- 15 条视频典型跨度 ~16-18h - -### 6.2 各平台定时支持 - -| 平台 | 定时方式 | 状态 | +| 平台 | 定时方式 | 参数 | |------|----------|------| -| 抖音 | API `timing_ts`(Unix 时间戳) | 已实现 | -| B站 | API `dtime`(Unix 时间戳) | 已实现 | -| 快手 | Playwright UI「定时发布」 | 已实现,成功率高 | -| 视频号 | Playwright UI「定时发布」 | 已实现,UI 匹配待优化 | -| 小红书 | Playwright UI「定时发布」 | 已实现,UI 匹配待优化 | +| B站 | API `meta.dtime` | Unix 时间戳(秒) | +| 视频号 | API 暂不支持原生定时 | 描述中标注时间/手动设置 | +| 抖音 | API `timing_ts` | Unix 时间戳 | +| 快手 | Playwright UI | `schedule_helper.py` | +| 小红书 | Playwright UI | `schedule_helper.py` | -定时失败时自动降级为立即发布,不影响视频发出。 +--- + +## 四、元数据自动生成(v4.0 新增) + +`video_metadata.py` 根据文件名自动生成各平台差异化内容: + +```python +from video_metadata import VideoMeta +meta = VideoMeta.from_filename("AI最大的缺点是上下文太短.mp4") + +meta.title("B站") # 优化后的标题 +meta.description("B站") # 标题 + 标签 + 品牌标记 +meta.tags_str("B站") # AI工具,效率提升,Soul派对,... +meta.bilibili_meta() # B站投稿完整 meta(含 tid/tag/desc) +meta.title_short() # 小红书短标题(≤20字) +meta.hashtags("视频号") # #AI工具 #效率提升 ... #小程序 卡若创业派对 +``` + +### 4.1 内容结构 +- **标题**:手工优化标题库优先,否则从文件名智能提取 +- **简介**:标题 + 换行 + 话题标签 + `#小程序 卡若创业派对` +- **标签**:基于关键词匹配(AI/创业/副业/Soul 等 12 类)+ 通用标签 +- **分区**:B站 tid=160(生活>日常) +- **风控过滤**:`content_filter.py` 自动替换敏感词(70+ 映射,严格/宽松分级) + +--- + +## 五、商品链接/小黄车(调研结果) + +| 平台 | 功能 | 实现方式 | 状态 | +|------|------|----------|------| +| B站 | 花火计划商品链接 | 需企业认证 + 品牌合作授权 | 需手动配置 | +| 视频号 | 挂小程序 | 视频号主页 > 设置 > 服务菜单 > 小程序 | 需手动配置 | +| 抖音 | 小黄车 | 需开通橱窗(粉丝 ≥1000) | 账号封禁 | +| 快手 | 商品卡片 | 需开通快手小店 | 需手动配置 | +| 小红书 | 商品笔记 | 需开通小红书店铺 | 需手动配置 | + +**当前做法**:在描述中统一添加 `#小程序 卡若创业派对` 引导用户搜索。 + +--- + +## 六、Cookie 管理 + +`cookie_manager.py` 统一管理: +- 中央存储:`多平台分发/cookies/{平台}_cookies.json` +- 自动迁移:旧路径 → 中央存储(首次使用时) +- API 预检:5 平台各自 auth API 校验有效性 +- 防重复登录:有效 Cookie 不触发重新获取 --- ## 七、去重机制 -- 基于 `publish_log.json`(JSON Lines 格式)记录每次发布结果 +- 日志:`publish_log.json`(JSON Lines) - 去重键:`(平台名, 视频文件名)` -- 双保险:调度器层(distribute_all.py)+ 平台层(各 publish_one) -- 独立运行单平台脚本也有去重 -- `--no-dedup` 跳过去重,`--retry` 重跑失败任务 +- 双保险:调度器层 + 平台层 +- `--no-dedup` 跳过,`--retry` 重跑失败 --- @@ -170,143 +136,31 @@ info = get_video_info("/path/to/video.mp4") ``` 木叶_视频内容/ -├── 多平台分发/ ← 本 Skill(调度器 + 共享工具) +├── 多平台分发/ ← 本 Skill(调度器 + 共享工具) │ ├── SKILL.md │ └── 脚本/ -│ ├── distribute_all.py # 主调度器 v3 -│ ├── schedule_generator.py # 定时排期生成器 -│ ├── schedule_helper.py # Playwright 定时发布辅助 -│ ├── publish_result.py # 统一发布结果 + 去重 -│ ├── title_generator.py # 智能标题生成 -│ ├── cookie_manager.py # Cookie 统一管理 +│ ├── distribute_all.py # 主调度器 v4 +│ ├── video_metadata.py # 统一元数据生成器(v4 新增) +│ ├── schedule_generator.py # 定时排期(v4: 第1条立即发) +│ ├── schedule_helper.py # Playwright 定时 UI 辅助 +│ ├── publish_result.py # 统一 PublishResult + 去重 +│ ├── title_generator.py # 标题生成(被 video_metadata 取代) +│ ├── content_filter.py # 敏感词过滤(70+ 映射) +│ ├── cookie_manager.py # Cookie 统一管理(5 平台 API 预检) │ ├── video_utils.py # 视频处理(封面、元数据) -│ └── publish_log.json # 发布结果日志(自动生成) -├── 抖音发布/ ← 纯 API(VOD + bd-ticket-guard) -├── B站发布/ ← bilibili-api-python API + Playwright 兜底 -├── 视频号发布/ ← Playwright headless -├── 小红书发布/ ← Playwright headless -└── 快手发布/ ← Playwright headless +│ └── publish_log.json # 发布日志 +├── 抖音发布/ ← 纯 API(账号封禁中) +├── B站发布/ ← bilibili-api-python API +├── 视频号发布/ ← 纯 API(DFS 协议,v5) +├── 小红书发布/ ← Playwright headless +└── 快手发布/ ← Playwright headless ``` --- -## 九、相关文件 - -| 文件 | 说明 | -|------|------| -| `脚本/distribute_all.py` | **主调度器 v3**:定时排期 + 并行分发 + 去重 + 重试 | -| `脚本/schedule_generator.py` | 排期生成(30-120min 间隔,超 24h 压缩) | -| `脚本/schedule_helper.py` | Playwright 定时发布 UI 交互辅助 | -| `脚本/publish_result.py` | 统一 PublishResult + 日志 + 去重 | -| `脚本/title_generator.py` | 智能标题(字典优先 → 文件名自动) | -| `脚本/cookie_manager.py` | Cookie 统一管理(有效期检查、API 预检 5 平台) | -| `脚本/content_filter.py` | 敏感词/风控词过滤(政治、金融、医疗、平台词,70+ 替换映射) | -| `脚本/video_utils.py` | 视频处理(封面提取、元数据) | - ---- - -## 十、踩坑经验(119 场全量分发) - -### 10.1 视频号/小红书定时发布 UI 匹配失败 -- **现象**:`schedule_helper.py` 找到了「定时发布」文字但日期时间 input 未匹配到 -- **原因**:这两个平台的日期选择器是自定义组件(非原生 `input[type="date"]`),需要点击日历格子 -- **影响**:定时功能降级为立即发布,视频仍正常发出 -- **待优化**:研究各平台 datepicker 的具体 DOM 结构,用 JS 直接操作 React state - -### 10.2 快手「未找到上传控件」 -- **现象**:部分视频上传时 `input[type="file"]` 元素未出现 -- **原因**:快手页面加载时偶发 JS 渲染延迟,或上次草稿弹窗阻塞了上传区 -- **解决**:脚本已加「放弃草稿」逻辑,重试后全部成功 - -### 10.3 B站 API 偶发超时后 Playwright 兜底也失败 -- **现象**:2 条视频 API 超时后降级到 Playwright,但 Playwright 也找不到上传控件 -- **原因**:B站创作中心在短时间内连续打开浏览器可能触发人机验证 -- **解决**:重试时纯 API 直接成功(5.3-5.7s),Playwright 只在 API 彻底不可用时才需要 - -### 10.4 抖音 Cookie 过期(全局) -- **现象**:Cookie 检查显示有效(expiry > now),但 API 返回「Cookie 已过期」 -- **原因**:抖音 API 的 `user_info` 接口在 Cookie 过期前约 1-2h 就开始拒绝 -- **解决**:重新运行 `python3 douyin_login.py` 扫码登录 - -### 10.5 并行分发的 Playwright 资源竞争 -- **现象**:多个 Playwright 同时运行时 CPU 飙高、偶发超时 -- **影响**:视频号/小红书/快手 三路 Playwright 并行,部分上传时间从 2s 涨到 5s -- **建议**:服务器部署时限制并发数(如最多 3 个 Playwright 同时) - -### 10.6 小红书发布按钮点击不生效(119场) -- **现象**:脚本日志声称 15/15 成功,实际只有 4 条到达平台 -- **根因**:初版 `pub.click(force=True)` 失败率高达 ~70%,且成功判定逻辑过于宽松(默认 status="reviewing") -- **修复**: - 1. JS 精准点击红色发布按钮(用 `getComputedStyle` 筛选 backgroundColor 含 255 的 button) - 2. Playwright `force-click` 兜底 - 3. 处理二次确认弹窗 - 4. 未检测到明确成功信号时,跳转到笔记管理页二次验证 - 5. 连续 3 次失败自动熔断(防封号) -- **成功率**:修复后 10/10(100%) - -### 10.7 小红书假成功日志污染去重(119场) -- **现象**:publish_log.json 记录 15 条 success=True,导致去重跳过,不会重试 -- **根因**:旧版 success 判定将所有未报错的提交都标记为 success -- **修复**: - 1. 清理 publish_log.json 中的虚假记录 - 2. 只有明确的成功信号(页面重置、URL 跳转、"发布成功"文本)才标记 success=True - 3. 不确定时走笔记管理页验证 - -### 10.8 视频号描述写入空白(119场) -- **现象**:所有视频发布后描述为空,视频号使用 Wujie 微前端框架 -- **根因**:`.input-editor` 在 Shadow DOM 内,常规 `.fill()` 无法写入 -- **修复**:clipboard/insertText 方式注入,先 focus → selectAll → insertText - -### 10.9 抖音账号投稿功能封禁 -- **现象**:API 返回 status_code=-20 "视频投稿功能已封禁" -- **影响**:所有视频无法发布到抖音 -- **处理**:预检时明确提示封禁状态,跳过抖音 - -### 10.10 账号预检机制(v3.1 新增) -- **所有平台发布前统一调用 `cookie_manager.check_cookie_valid()`** - - 视频号:POST auth_data API - - B站:GET /x/web-interface/nav - - 快手:GET cp.kuaishou.com/rest/pc/user/myInfo - - 小红书:GET creator.xiaohongshu.com/api/galaxy/user/info - - 抖音:GET /web/api/media/user_info/ -- 预检不通过则终止发布,避免浪费时间上传后才发现 Cookie 过期 - ---- - -## 十一、万推(Web 版视频分发系统) - -卡若AI 的多平台分发能力已整合到万推项目(`/Users/karuo/Documents/开发/3、自营项目/万推/`),提供 Web GUI + API 接口: - -| 组件 | 说明 | -|------|------| -| **万推后端** | FastAPI,`backend/main.py`,端口 8000 | -| **万推前端** | Vue 3 毛玻璃风格,`frontend/index.html` | -| **直连发布器** | `backend/direct_publisher.py`,Playwright 操作 5 平台创作者中心 | -| **uploader 体系** | `backend/uploader/` 下 5 平台独立 uploader | -| **Cookie 自动获取** | `backend/cookie_fetcher.py`,打开浏览器让用户扫码 | - -### 启动万推 - -```bash -cd /Users/karuo/Documents/开发/3、自营项目/万推/backend -pip3 install -r requirements.txt -playwright install chromium -python3 main.py -# 访问 http://localhost:8000 -``` - -### 万推与卡若AI脚本的关系 - -- 卡若AI `distribute_all.py`:命令行一键分发,适合自动化流水线 -- 万推 Web 界面:用户手动管理账号和分发,适合日常运营 -- 两者共享 Playwright + Cookie 方案,互为补充 - ---- - ## 九、依赖 - Python 3.10+ -- httpx, playwright, playwright-stealth, cryptography, Pillow -- biliup(B站上传 API) +- httpx, bilibili-api-python, playwright, Pillow - ffmpeg/ffprobe(系统已安装) -- Playwright chromium(`playwright install chromium`) +- `playwright install chromium` diff --git a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/distribute_all.py b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/distribute_all.py index cfef6b20..9f8f9c2d 100644 --- a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/distribute_all.py +++ b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/distribute_all.py @@ -38,6 +38,7 @@ from publish_result import (PublishResult, print_summary, save_results, load_published_set, load_failed_tasks) from title_generator import generate_title from schedule_generator import generate_schedule, format_schedule +from video_metadata import VideoMeta PLATFORM_CONFIG = { "抖音": { @@ -190,7 +191,8 @@ async def distribute_to_platform( total = len(to_publish) pub_fn = getattr(module, "publish_one_compat", None) or module.publish_one for i, vp in enumerate(to_publish): - title = generate_title(vp.name, titles_dict) + vmeta = VideoMeta.from_filename(str(vp)) + title = vmeta.title(platform) stime = publish_schedule[i] if publish_schedule else None try: r = await pub_fn(str(vp), title, i + 1, total, scheduled_time=stime) diff --git a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/publish_log.json b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/publish_log.json index 5c36504b..74abf137 100644 --- a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/publish_log.json +++ b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/publish_log.json @@ -128,3 +128,15 @@ {"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/深度AI模型对比 哪个才是真正的AI不是语言模型.mp4", "title": "深度AI模型对比 哪个才是真正的AI不是语言模型 #Soul派对 #创业日记", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.2712419033050537, "timestamp": "2026-03-11 12:13:53"} {"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/疗愈师配AI助手能收多少钱 一个小团队5万到10万.mp4", "title": "疗愈师配AI助手能收多少钱 一个小团队5万到10万 #Soul派对 #创业日记", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.2667992115020752, "timestamp": "2026-03-11 12:13:56"} {"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/赚钱没那么复杂,自信心才是核心问题.mp4", "title": "赚钱没那么复杂,自信心才是核心问题 #Soul派对 #创业日记", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.2669076919555664, "timestamp": "2026-03-11 12:14:00"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/AI最大的缺点是上下文太短,这样来解决.mp4", "title": "AI的短板是记忆太短,上下文一长就废了,这个方法能解决", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 2.443204164505005, "timestamp": "2026-03-11 15:07:02"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/AI每天剪1000个视频 M4电脑24T素材库全网分发.mp4", "title": "M4芯片+24T素材库,AI每天剪1000条视频自动全网分发", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.1888279914855957, "timestamp": "2026-03-11 15:07:05"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/Soul派对变现全链路 发视频就有钱,后端全解决.mp4", "title": "Soul派对怎么商业转化?发视频就有收益,后端体系全部搞定", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.1997511386871338, "timestamp": "2026-03-11 15:07:08"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/从0到切片发布 AI自动完成每天副业30条视频.mp4", "title": "从零到切片发布,AI全自动完成,每天副业产出30条视频", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.19605207443237305, "timestamp": "2026-03-11 15:07:11"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/做副业的基本条件 苹果电脑和特殊访问工具.mp4", "title": "做副业的两个基本条件:一台Mac和一个上网工具", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.19453787803649902, "timestamp": "2026-03-11 15:07:15"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/切片分发全自动化 从视频到发布一键完成.mp4", "title": "从录制到发布全自动化,一键切片分发五大平台", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.19373202323913574, "timestamp": "2026-03-11 15:07:18"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/创业团队4人平分25有啥危险 先跑钱再谈股权.mp4", "title": "创业团队4人平分25%股权有啥风险?先跑出收入再谈分配", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.2604191303253174, "timestamp": "2026-03-11 15:07:21"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/坚持到120场是什么感觉 方向越确定执行越坚决.mp4", "title": "坚持到第120场派对是什么感觉?方向越清晰执行越坚决", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.28436827659606934, "timestamp": "2026-03-11 15:07:24"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/帮人装AI一单300到1000块,传统行业也能做.mp4", "title": "帮传统行业的人装AI工具,一单收300到1000块,简单好做", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.20317912101745605, "timestamp": "2026-03-11 15:07:28"} +{"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.33212804794311523, "timestamp": "2026-03-11 15:07:31"} +{"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.2739429473876953, "timestamp": "2026-03-11 15:07:34"} +{"platform": "抖音", "video_path": "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片/赚钱没那么复杂,自信心才是核心问题.mp4", "title": "获得收益真没那么复杂,自信心才是卡住你的核心问题", "success": false, "status": "error", "message": "Cookie 已过期", "elapsed_sec": 0.27978515625, "timestamp": "2026-03-11 15:07:38"} diff --git a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/schedule_generator.py b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/schedule_generator.py index 69f29d26..e6ff6ee9 100644 --- a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/schedule_generator.py +++ b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/schedule_generator.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 """ -定时排期生成器 — 为 N 条视频生成发布时间表 +定时排期生成器 v2 规则: -1. 相邻视频间隔 30-120 分钟(随机) -2. 若总时长 > max_hours,按比例压缩至 max_hours 内 -3. 第一条视频在 first_delay 分钟后发布 +1. 第一条视频 **立即发布**(first_delay=0) +2. 第二条起,每条间隔 30~120 分钟(随机) +3. 若总时长超过 max_hours,按比例压缩 +4. 支持各平台原生定时 API 所需的 datetime 对象 """ import random from datetime import datetime, timedelta @@ -15,16 +16,19 @@ def generate_schedule( min_gap: int = 30, max_gap: int = 120, max_hours: float = 24.0, - first_delay: int = 125, + first_delay: int = 0, start_time: datetime = None, ) -> list[datetime]: """ - 返回 n 个 datetime,每个对应一条视频的定时发布时间。 + 返回 n 个 datetime: + - times[0] = now + first_delay(默认 0 = 立即) + - times[1..] = 前一条 + random(min_gap, max_gap) 分钟 """ if n <= 0: return [] base = start_time or datetime.now() + if n == 1: return [base + timedelta(minutes=first_delay)] @@ -34,8 +38,8 @@ def generate_schedule( if total_min > max_min: ratio = max_min / total_min - first_delay = int(first_delay * ratio) - gaps = [max(1, int(g * ratio)) for g in gaps] + first_delay = max(0, int(first_delay * ratio)) + gaps = [max(5, int(g * ratio)) for g in gaps] times = [] cur = base + timedelta(minutes=first_delay) @@ -48,11 +52,11 @@ def generate_schedule( def format_schedule(videos: list[str], times: list[datetime]) -> str: - """格式化排期表用于打印""" + """格式化排期表""" lines = [" 序号 | 发布时间 | 间隔 | 视频"] lines.append(" " + "-" * 70) for i, (v, t) in enumerate(zip(videos, times)): - gap = "" + gap = "立即" if i == 0 else "" if i > 0: delta = (t - times[i - 1]).total_seconds() / 60 gap = f"{delta:.0f}min" @@ -61,11 +65,11 @@ def format_schedule(videos: list[str], times: list[datetime]) -> str: total = (times[-1] - times[0]).total_seconds() / 3600 if len(times) > 1 else 0 lines.append(" " + "-" * 70) - lines.append(f" 总跨度: {total:.1f}h | 首条: {times[0].strftime('%H:%M')} | 末条: {times[-1].strftime('%H:%M')}") + lines.append(f" 总跨度: {total:.1f}h | 首条: {times[0].strftime('%H:%M')}(立即) | 末条: {times[-1].strftime('%H:%M')}") return "\n".join(lines) if __name__ == "__main__": - schedule = generate_schedule(15) - names = [f"视频_{i+1}.mp4" for i in range(15)] + schedule = generate_schedule(12) + names = [f"视频_{i+1}.mp4" for i in range(12)] print(format_schedule(names, schedule)) diff --git a/03_卡木(木)/木叶_视频内容/多平台分发/脚本/video_metadata.py b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/video_metadata.py new file mode 100644 index 00000000..2084842e --- /dev/null +++ b/03_卡木(木)/木叶_视频内容/多平台分发/脚本/video_metadata.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python3 +""" +统一视频元数据生成器 v1 +根据视频文件名自动生成:标题、简介、标签、分区 +支持各平台差异化输出(B站/视频号/小红书/快手/抖音) + +用法: + meta = VideoMeta.from_filename("AI最大的缺点是上下文太短,这样来解决.mp4") + print(meta.title("B站")) + print(meta.description("B站")) + print(meta.tags("B站")) +""" +from __future__ import annotations + +import re +from dataclasses import dataclass, field +from pathlib import Path + +from content_filter import filter_for_platform + +BRAND_TAG = "#卡若创业派对" +MINI_PROGRAM = "#小程序 卡若创业派对" + +PLATFORM_CATEGORIES = { + "B站": {"tid": 160, "name": "生活 > 日常"}, + "视频号": {"category": "科技数码"}, + "小红书": {"category": "科技数码"}, + "快手": {"category": "生活"}, + "抖音": {"category": "科技"}, +} + +COMMON_TAGS = ["Soul派对", "创业", "认知觉醒", "副业", "商业思维"] + +KEYWORD_TAGS = { + "AI": ["AI工具", "人工智能", "效率提升"], + "副业": ["副业", "副业入门", "副业收入"], + "创业": ["创业", "创业心态", "创业故事"], + "Soul": ["Soul派对", "Soul创业"], + "切片": ["切片分发", "自动化", "内容分发"], + "股权": ["创业股权", "团队管理"], + "疗愈": ["疗愈", "AI赋能", "疗愈商业"], + "模型": ["AI对比", "深度思考", "AI模型"], + "赚钱": ["创业心态", "商业思维"], + "装AI": ["AI服务", "传统行业"], + "坚持": ["坚持的力量", "自律"], + "视频": ["内容创作", "视频分发"], +} + +CURATED_TITLES: dict[str, dict] = { + "AI最大的缺点是上下文太短,这样来解决.mp4": { + "title": "AI的短板是记忆太短,上下文一长就废了,这个方法能解决", + "tags_extra": ["AI工具", "效率提升"], + }, + "AI每天剪1000个视频 M4电脑24T素材库全网分发.mp4": { + "title": "M4芯片+24T素材库,AI每天剪1000条视频自动全网分发", + "tags_extra": ["AI剪辑", "内容工厂"], + }, + "Soul派对变现全链路 发视频就有钱,后端全解决.mp4": { + "title": "Soul派对怎么变现?发视频就有收益,后端体系全部搞定", + "tags_extra": ["Soul派对", "副业收入"], + }, + "从0到切片发布 AI自动完成每天副业30条视频.mp4": { + "title": "从零到切片发布,AI全自动完成,每天副业产出30条视频", + "tags_extra": ["AI副业", "切片分发"], + }, + "做副业的基本条件 苹果电脑和特殊访问工具.mp4": { + "title": "做副业的两个基本条件:一台Mac和一个上网工具", + "tags_extra": ["副业入门", "工具推荐"], + }, + "切片分发全自动化 从视频到发布一键完成.mp4": { + "title": "从录制到发布全自动化,一键切片分发五大平台", + "tags_extra": ["自动化", "内容分发"], + }, + "创业团队4人平分25有啥危险 先跑钱再谈股权.mp4": { + "title": "创业团队4人平分25%股权有啥风险?先跑出收入再谈分配", + "tags_extra": ["创业股权", "团队管理"], + }, + "坚持到120场是什么感觉 方向越确定执行越坚决.mp4": { + "title": "坚持到第120场派对是什么感觉?方向越清晰执行越坚决", + "tags_extra": ["Soul派对", "坚持的力量"], + }, + "帮人装AI一单300到1000块,传统行业也能做.mp4": { + "title": "帮传统行业的人装AI工具,一单收300到1000块,简单好做", + "tags_extra": ["AI服务", "传统行业"], + }, + "深度AI模型对比 哪个才是真正的AI不是语言模型.mp4": { + "title": "深度对比各大AI模型,哪个才是真正的智能而不只是语言模型", + "tags_extra": ["AI对比", "深度思考"], + }, + "疗愈师配AI助手能收多少钱 一个小团队5万到10万.mp4": { + "title": "疗愈师+AI助手组合,一个小团队月收5万到10万", + "tags_extra": ["AI赋能", "疗愈商业"], + }, + "赚钱没那么复杂,自信心才是核心问题.mp4": { + "title": "赚钱真没那么复杂,自信心才是卡住你的核心问题", + "tags_extra": ["创业心态", "自信"], + }, +} + + +@dataclass +class VideoMeta: + """单条视频的元数据""" + filename: str + base_title: str + tags_extra: list[str] = field(default_factory=list) + + @classmethod + def from_filename(cls, filename: str) -> VideoMeta: + fname = Path(filename).name + curated = CURATED_TITLES.get(fname) + if curated: + return cls( + filename=fname, + base_title=curated["title"], + tags_extra=curated.get("tags_extra", []), + ) + stem = Path(fname).stem + stem = re.sub(r'^\d+[._\-\s]*', '', stem) + stem = stem.replace('_', ' ').replace(' ', ' ').strip() + + extra = [] + for kw, tags in KEYWORD_TAGS.items(): + if kw in stem: + extra.extend(tags) + extra = list(dict.fromkeys(extra))[:4] + + return cls(filename=fname, base_title=stem or fname, tags_extra=extra) + + def _smart_tags(self, platform: str) -> list[str]: + seen = set() + result = [] + for t in self.tags_extra + COMMON_TAGS: + if t not in seen: + seen.add(t) + result.append(t) + return result[:8] + + def title(self, platform: str, max_len: int = 80) -> str: + t = filter_for_platform(self.base_title, platform) + return t[:max_len] + + def title_short(self, max_len: int = 20) -> str: + """小红书标题(≤20字)""" + parts = re.split(r'[,,!!??\s]+', self.base_title) + return parts[0][:max_len] if parts else self.base_title[:max_len] + + def hashtags(self, platform: str) -> str: + """# 标签字符串""" + tags = self._smart_tags(platform) + parts = [f"#{t}" for t in tags] + parts.append(MINI_PROGRAM) + return " ".join(parts) + + def description(self, platform: str, max_len: int = 500) -> str: + """完整描述 = 标题 + 换行 + 标签 + 品牌""" + title = self.title(platform) + tags = self.hashtags(platform) + desc = f"{title}\n\n{tags}" + return filter_for_platform(desc[:max_len], platform) + + def tags_list(self, platform: str) -> list[str]: + return self._smart_tags(platform) + + def tags_str(self, platform: str) -> str: + """逗号分隔(B站 API 用)""" + return ",".join(self._smart_tags(platform)) + + def category(self, platform: str) -> dict: + return PLATFORM_CATEGORIES.get(platform, {}) + + def bilibili_meta(self) -> dict: + """B站投稿需要的完整 meta""" + return { + "copyright": 1, + "source": "", + "desc": self.description("B站"), + "desc_format_id": 0, + "dynamic": "", + "interactive": 0, + "open_elec": 0, + "no_reprint": 1, + "subtitles": {"lan": "", "open": 0}, + "tag": self.tags_str("B站"), + "tid": 160, + "title": self.title("B站", 80), + "up_close_danmaku": False, + "up_close_reply": False, + } + + +def generate_all_meta(video_dir: str) -> list[VideoMeta]: + """批量生成目录下所有视频的元数据""" + videos = sorted(Path(video_dir).glob("*.mp4")) + return [VideoMeta.from_filename(str(v)) for v in videos] + + +if __name__ == "__main__": + from pathlib import Path + VIDEO_DIR = "/Users/karuo/Movies/soul视频/soul 派对 120场 20260320_output/成片" + metas = generate_all_meta(VIDEO_DIR) + for m in metas: + print(f"\n{'='*60}") + print(f"文件: {m.filename}") + print(f" B站标题: {m.title('B站')}") + print(f" B站简介: {m.description('B站')[:80]}...") + print(f" B站标签: {m.tags_str('B站')}") + print(f" 视频号: {m.description('视频号')[:80]}...") + print(f" 小红书标题: {m.title_short()}") diff --git a/03_卡木(木)/木叶_视频内容/视频号发布/脚本/channels_api_publish.py b/03_卡木(木)/木叶_视频内容/视频号发布/脚本/channels_api_publish.py index 5e953516..a43341bc 100644 --- a/03_卡木(木)/木叶_视频内容/视频号发布/脚本/channels_api_publish.py +++ b/03_卡木(木)/木叶_视频内容/视频号发布/脚本/channels_api_publish.py @@ -35,6 +35,12 @@ UA = ( "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" ) +sys.path.insert(0, str(SCRIPT_DIR.parent.parent / "多平台分发" / "脚本")) +try: + from video_metadata import VideoMeta +except ImportError: + VideoMeta = None + DESC_SUFFIX = " #小程序 卡若创业派对" CHUNK_SIZE = 8 * 1024 * 1024 @@ -407,7 +413,11 @@ async def publish_one( elapsed_sec=time.time() - t0, ) - desc_full = title + DESC_SUFFIX + if VideoMeta: + vmeta = VideoMeta.from_filename(video_path) + desc_full = vmeta.description("视频号") + else: + desc_full = title + DESC_SUFFIX print(f" 发表...", flush=True) post_resp = await create_post( cookie_str, desc_full, video_url, thumb_url, vinfo, fsize, diff --git a/运营中枢/工作台/gitea_push_log.md b/运营中枢/工作台/gitea_push_log.md index c84f7ac0..ec807808 100644 --- a/运营中枢/工作台/gitea_push_log.md +++ b/运营中枢/工作台/gitea_push_log.md @@ -287,3 +287,4 @@ | 2026-03-11 13:54:29 | 🔄 卡若AI 同步 2026-03-11 13:54 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | | 2026-03-11 14:38:13 | 🔄 卡若AI 同步 2026-03-11 14:38 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | | 2026-03-11 14:45:54 | 🔄 卡若AI 同步 2026-03-11 14:45 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | +| 2026-03-11 14:55:15 | 🔄 卡若AI 同步 2026-03-11 14:55 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | diff --git a/运营中枢/工作台/代码管理.md b/运营中枢/工作台/代码管理.md index 5efc68cf..6bf4a0e9 100644 --- a/运营中枢/工作台/代码管理.md +++ b/运营中枢/工作台/代码管理.md @@ -290,3 +290,4 @@ | 2026-03-11 13:54:29 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-11 13:54 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-03-11 14:38:13 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-11 14:38 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-03-11 14:45:54 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-11 14:45 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | +| 2026-03-11 14:55:15 | 成功 | 成功 | 🔄 卡若AI 同步 2026-03-11 14:55 | 更新:卡木、运营中枢工作台 | 排除 >20MB: 11 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |