Claude Skill

playlet-douyin-feed

短剧-抖音信息源 — 每日扫描抖音短剧爆款内容,按点赞量筛选热门短剧,智能聚类题材方向后生成包含封面、互动数据与创作洞察的HTML日报。支持按题材(穿越/霸总/重生等)、达人、时间范围定向查询。⚠️查询前脚本先做输入校验:关键词需命中短剧题材词库(topic_keywords 中规定的题材名+全部相关词,如「打脸」命中逆袭题材相关词),命中后直接使用该关键词查询数据;不满足时提醒'关键词不满足查询条件'并推荐相关词,且**不发起接口请求**;查询无匹配数据或全量数据不足时先询问用户是否按推荐题材重新查询,确认后才可查询(不自动扩展、不自动降级全量)。日期

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download redfox-data-redfox-community-skills_playlet-douyin-feed-5e7b435.zip · 29 KB
Part of redfox-data/redfox-community — 66 skills

Install

skills CLI npx skills add https://github.com/redfox-data/redfox-community/tree/main/skills/playlet-douyin-feed
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install redfox-data-redfox-community@llmmart
Git git clone https://github.com/redfox-data/redfox-community.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole redfox-data/redfox-community collection as a plugin from our marketplace. Git is the plain clone.

README

短剧-抖音信息源 / playlet-douyin-feed


简介

短剧-抖音信息源是一款专为短剧创作者和MCN运营人员设计的抖音爆款内容追踪工具,每日自动扫描抖音短剧创作内容,按点赞量筛选爆款作品,智能聚类题材后生成包含封面、互动数据与创作洞察的HTML可视化日报。

核心价值

  • 每日自动追踪:每日15:00自动更新前一天数据,订阅后无需手动操作,零成本掌握行业动态
  • 智能题材聚类:内置6大题材关键词库(穿越/霸总/重生/悬疑/甜宠/逆袭),自动识别归类爆款方向
  • 深度创作洞察:自动分析爆款标题特征、核心达人榜、新兴起量信号,辅助选题决策
  • 可视化日报:深色主题HTML日报,封面图+互动数据+作品直链,美观直观

适用对象

  • 🎬 短剧创作者 — 每日追踪爆款题材与标题模式,精准把握流量风口
  • 🏢 MCN运营机构 — 追踪达人和竞品表现,提升运营决策效率
  • 📊 内容分析师 — 系统性分析题材分布与趋势变化,支撑数据驱动决策

功能特性

核心功能

  • 爆款发现:每日扫描抖音短剧内容,按点赞量筛选热门作品,精准定位高热度短剧
  • 题材聚类:智能识别穿越、霸总、重生、悬疑、甜宠、逆袭等题材方向,每日分类由内容动态决定
  • 智能查询:支持按题材、达人、关键词定向查询,数据不足时自动扩展题材批量查询,节省API额度
  • 自定义查询:可指定任意题材组合进行定向检索,灵活覆盖短剧细分方向
  • 创作洞察:分析爆款标题特征、题材趋势、达人表现,深度挖掘创作规律
  • 可视化日报:深色主题HTML日报,包含封面图、互动数据与作品直链,自动浏览器打开
  • 一键订阅:开启后每日自动产出日报,保存在本地文件夹

特色亮点

  • ⚡ 每日自动更新:15:00自动更新前一天数据,订阅后无需手动操作
  • 🏷️ 智能题材聚类:内置6大题材关键词库,自动识别归类,支持动态扩展
  • 📊 创作趋势报告:自动分析新兴起量信号、爆款标题特征、核心达人榜
  • 🎨 HTML深色主题日报:封面图+互动数据+作品直链,美观直观,自动浏览器打开

密钥获取与安全说明

  • 本技能需要使用环境变量:REDFOX_API_KEY。
  • REDFOX_API_KEY 由 红狐 hub (https://redfox.hk)提供。
  • 请前往 红狐 hub 注册账号,获取 REDFOX_API_KEY。
  • 配置设备环境变量 REDFOX_API_KEY 后使用本技能。
  • 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。
  • 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。

使用指南

直接用自然语言描述需求,无需记忆命令。

常用说法速查

意图 示例话术 效果
查询每日日报 「查询今天的短剧抖音日报」 自动判断数据可用性,生成最新日报
按题材查询 「查询穿越题材的短剧」 定向检索穿越题材爆款,生成专题日报
查询历史日期 「查询6月10日的短剧日报」 直接生成指定历史日期的日报
多题材组合 「查询穿越和霸总题材的短剧」 批量查询多个题材,自动去重合并
趋势分析 「本月短剧爆款趋势怎么样」 按月分析题材分布与增长趋势
开启订阅 「帮我开启每日短剧日报订阅」 每日自动产出日报,无需手动操作

输出示例

日报生成后将获得:

  • 📄 HTML可视化日报:深色主题,包含封面图、互动数据、作品直链,自动浏览器打开
  • 📊 题材概览表:各题材作品数量、占比、爆款亮点
  • 🔍 创作趋势分析:新兴起量信号、爆款标题特征、核心达人榜
  • 💡 跨题材对比建议:题材融合趋势与联动创作建议

使用场景

场景 角色 示例问法 收益
选题参考 短剧编剧/导演 「今天什么题材最火?帮我分析下爆款标题」 精准把握流量风口,提升作品爆款概率
运营管理 MCN运营总监 「本周穿越赛道有哪些头部达人?表现如何?」 提升运营决策效率,及时捕捉市场变化
趋势研究 内容分析师 「对比穿越和霸总题材近一个月的互动趋势」 形成数据驱动的趋势判断,支撑决策
日常追踪 短剧爱好者 「帮我开启每日订阅」 零成本追踪行业动态,省时省力

重要数据说明

  • 数据更新时间:每日15:00更新前一天的数据。15:00前最新可查为前天,15:00后为昨天
  • 数据来源:红狐Hub 抖音短剧创作数据API
  • 平台范围:固定为抖音平台短剧内容
  • 缓存策略:1小时内查询结果可复用缓存,节省API积分

Skill manifest

短剧-抖音信息源

简介

短剧-抖音信息源是一款专为短剧创作者和MCN运营人员设计的抖音爆款内容追踪工具,每日自动扫描抖音短剧创作内容,按点赞量筛选爆款作品,智能聚类题材后生成HTML可视化日报。

通过简单的自然语言指令,你可以:

  • 📊 获取每日抖音短剧爆款榜单与题材分布
  • 🏷️ 自动聚类穿越/霸总/重生/悬疑等题材方向
  • 📈 生成创作趋势分析报告(爆款标题特征、核心达人榜、新兴起量信号)
  • 🔔 开启每日订阅,日报自动产出

适用于短剧创作者选题、MCN机构运营、题材趋势研究等需要每日追踪抖音短剧热点的场景。

重要:数据每日15:00更新前一天数据(实际可能延迟,以脚本真实探活为准)。查询前脚本先做输入校验:关键词需命中短剧题材词库(topic_keywords 中规定的题材名+全部相关词,如「打脸」命中逆袭题材相关词),命中后直接使用该关键词查询数据;不满足时提醒"关键词不满足查询条件"并推荐相关词,不发起接口请求。无数据确认制:查询无匹配数据或全量数据不足时,禁止自动发起任何额外查询(禁止自动降级全量、禁止自动扩展题材),先展示推荐题材关键词并询问用户,用户确认后才可发起查询;日期超出有效查询范围时提醒并自动回退最近有数据日期,无需用户确认。

功能特性

🎯 核心功能

功能模块 能力描述 核心价值
爆款发现 从抖音短剧中按点赞量筛选热门内容 精准定位高热度短剧作品
题材聚类 自动识别题材方向(穿越/霸总/重生/悬疑等) 每天题材分类由内容动态决定
智能查询 默认查询全部短剧,关键词命中词库(题材名+相关词)直接查询,数据不足时提示推荐题材等待确认 节省接口额度,高效获取数据
自定义查询 用户可指定任意题材/达人/关键词定向查询 灵活覆盖任意短剧细分方向
创作洞察 分析爆款标题特征、题材趋势、达人表现 深度挖掘创作规律
可视化日报 深色主题HTML,封面图+互动数据+作品直链 直观展示每日短剧热点
一键订阅 --subscribe 开启每日自动产出 日报自动攒在本地文件夹

✨ 特色亮点

  • ⚡ 探活式日期预检:调用前先用轻量请求(无keyword, pageSize=1)真实探测目标日期是否有数据,替代纯本地时钟推断,自动拦截无效查询,避免浪费API额度
  • 🧭 前置输入校验:查询前先判断用户的分类/关键词是否符合短剧题材词库(题材名+全部相关词,命中后直接使用该关键词查询数据)、日期是否在有效查询范围;不满足时提醒"关键词不满足查询条件"并推荐相关分类和关键词,不发起接口请求
  • 🔄 自动回退:--latest 自动向前回退最多7天,找到最近有数据的日期再出日报,彻底告别"查到空就报错"
  • ⏸️ 无数据确认制:查询无匹配数据或全量数据不足时,不自动扩展题材、不自动降级全量,提示推荐题材关键词并等待用户确认后再查询
  • 🧠 结构化空因:空结果时明确输出原因(数据源无数据/关键词无匹配/接口异常)并给出下一步建议
  • 🏷️ 智能题材聚类:内置9大题材关键词库(穿越/霸总/重生/悬疑/甜宠/逆袭/年代/战神/古装),自动识别归类
  • 📊 创作趋势报告:自动分析新兴起量信号、爆款标题特征、核心达人榜
  • 🎨 HTML深色主题日报:封面图+互动数据+作品直链,美观直观,自动浏览器打开
  • 💰 API积分优化:批量查询+1小时缓存+探活拦截,节省接口调用额度

一键安装

前置条件

  • 已安装 Python 3 运行环境
  • 获取红狐Hub API Key

API Key 获取

数据查询接口通过请求头 X-API-KEY 鉴权,Key 从环境变量 REDFOX_API_KEY 获取。

前往 红狐Hub 官网 注册,登录后在个人中心获取,格式为 ak_xxxxxxxx。新注册用户获赠免费积分。

环境变量配置

变量名 必填 说明
REDFOX_API_KEY 是 红狐Hub API 访问密钥,格式 ak_xxxxxxxx

配置方式:

  • macOS/Linux:将 export REDFOX_API_KEY=<值> 追加到 ~/.zshrc 或 ~/.bashrc,然后 source 使其生效
  • Windows:[Environment]::SetEnvironmentVariable("REDFOX_API_KEY", "<值>", "User")(需重启终端)
  • 配置后验证:echo $REDFOX_API_KEY(macOS/Linux)或 echo %REDFOX_API_KEY%(Windows)

使用指南

基础使用

1. 查询每日短剧爆款日报

直接告诉助手你想查看的日报:

用户:查询今天的短剧抖音日报

助手:(执行 --latest,脚本自动回退定位最近有数据的日期,输出日报 + 题材概览 + 创作趋势分析)

日期预检规则:查询前先做前置校验——分类/关键词不符合短剧题材词库时提醒"关键词不满足查询条件"并推荐相关词,不发起接口请求;日期超出有效查询范围或未更新时,脚本自动向前回退获取最近时间范围数据,并明确告知"当前查询时间未更新或超过查询时间范围,已为您自动获取最近时间范围数据"。

2. 按题材定向查询

指定你关注的题材方向:

用户:查询穿越题材的短剧

助手:(生成穿越题材定向日报 + 趋势分析)

3. 查询历史日期

用户:查询6月10日的短剧日报

助手:(历史日期已有数据,直接生成日报)

高级使用

4. 多题材组合查询

python3 scripts/playlet_douyin_daily.py --topics "穿越,霸总,重生" --latest

5. 按时间范围查询

python3 scripts/playlet_douyin_daily.py \
  --start-time "2026-06-01 00:00:00" \
  --end-time "2026-06-30 23:59:59"

6. 开启每日订阅

python3 scripts/playlet_douyin_daily.py --subscribe

7. 使用缓存数据

python3 scripts/playlet_douyin_daily.py --from-cache

常用命令速查

命令 功能
--latest 生成最新一期日报(自动向前回退最多7天定位最近有数据的日期)
--date YYYY-MM-DD 生成指定日期日报(未更新时自动回退最近有数据的日期)
--topics "关键词" 自定义题材查询(逗号分隔;不满足短剧题材词时提醒+推荐,不请求接口)
--count N 扫描作品数量(默认200)
--subscribe 开启每日订阅
--unsubscribe 关闭每日订阅
--from-cache 使用缓存数据(1小时内有效)
--output-dir 自定义输出目录

完整参数说明

参数 说明 默认值
--topics 自定义题材关键词,逗号分隔。查询前先校验是否命中短剧题材词库(题材名+全部相关词,命中后直接使用该关键词查询),不满足时提醒+推荐相关词并不请求接口。默认查询全部短剧,数据不足时提示推荐题材并等待确认(不自动扩展);所有题材通过批量接口查询 短剧
--count 扫描作品数量,满足即停 200
--date 指定日期 YYYY-MM-DD(未更新或超范围时自动回退最近有数据的日期) 今天
--start-time 自定义开始时间 YYYY-MM-DD HH:MM:SS(覆盖 --date 推算) —
--end-time 自定义结束时间 YYYY-MM-DD HH:MM:SS(覆盖 --date 推算) —
--latest 自动向前回退最多7天,定位最近有数据的日期,跳过无数据区间,不扣积分 —
--output-dir 输出目录 ~/Downloads/QoderReports
--api-key 指定 API Key —
--subscribe 开启每日订阅 —
--unsubscribe 关闭每日订阅 —

工作流程

详细执行流程(日期预检规则、脚本调用、强制输出格式模板、题材聚类规则、创作趋势分析逻辑)请参阅 core_workflow.md

工作流程分为三步:

  1. 第零步 — 输入与日期预检:前置校验关键词是否命中短剧题材词库(题材名+全部相关词,命中后直接查询;不满足时提醒+推荐,不请求接口);日期按15:00规则估算起点后,用轻量请求真实探活目标日期是否有数据,无数据时自动向前回退最近有数据的日期。查询无匹配数据或数据不足时,先展示推荐题材关键词并询问用户,用户确认后才可发起查询(无数据确认制)
  2. 第一步 — 生成爆款日报:执行 playlet_douyin_daily.py 脚本,支持 --latest、--date、--topics 等参数
  3. 第二步 — 执行创作趋势分析:基于聚类结果自动分析TOP 5题材、爆款标题特征、核心达人榜,输出结构化趋势报告

自定义题材查询

除默认短剧日报外,用户可指定任意题材组合进行定向查询:

# 查询穿越题材热门短剧
python3 scripts/playlet_douyin_daily.py --topics "穿越,时空,重生"

# 查询霸总/甜宠题材
python3 scripts/playlet_douyin_daily.py --topics "霸总,甜宠,总裁,虐恋"

# 查询悬疑/反转题材
python3 scripts/playlet_douyin_daily.py --topics "悬疑,推理,反转,惊悚"

自定义查询逻辑:

  • 前置校验(v2.3):查询前先判断关键词是否命中短剧题材词库(topic_keywords 中规定的题材名+全部相关词,如「打脸」命中逆袭题材相关词、「总裁」命中霸总题材相关词)。命中后直接使用该关键词查询数据;不满足时明确提醒"关键词不满足短剧查询条件",并推荐相关分类和关键词(优先从无效词中提取题材词,再补热门题材),且不发起接口请求,引导用户改用推荐词查询
  • 无效词自动忽略:混合词场景下保留有效关键词继续查询,无效关键词自动忽略并提示;全部无效时不请求接口,直接停止并推荐相关词
  • 无数据确认制(v2.3):用户指定的关键词/题材无匹配数据时,禁止自动降级为全量查询、禁止自动扩展题材,先提示推荐题材关键词并询问用户,用户确认后才可发起查询
  • 用户提供的所有题材通过批量接口一次性查询,无需逐个调用
  • 查询结果自动去重,题材聚类、趋势分析均基于查询结果生成,与用户关注方向强关联

题材关键词速查:

题材类型 典型关键词
穿越 穿越、时空、古代、现代、回到
霸总 霸总、总裁、豪门、冷酷
重生 重生、逆袭、回到、翻盘
悬疑 悬疑、推理、反转、惊悚、谜案
甜宠 甜宠、恋爱、撒糖、甜蜜、宠溺
逆袭 逆袭、翻身、打脸、崛起

使用场景

场景一:短剧创作者选题参考

角色:短剧编剧/导演

需求:了解当前哪些题材和标题模式最容易出爆款

使用方式:

  1. 每日查询短剧爆款日报,查看题材概览
  2. 重点关注「新兴起量信号」和「爆款标题特征」
  3. 结合自身优势选择题材方向

预期收益:精准把握流量风口,提升作品爆款概率


场景二:MCN机构运营管理

角色:MCN运营总监

需求:追踪旗下达人和竞品在抖音短剧赛道的表现

使用方式:

  1. 查看「核心达人榜」了解头部达人动态
  2. 按题材定向查询,分析各赛道竞争格局
  3. 开启每日订阅,日报自动推送

预期收益:提升运营决策效率,及时捕捉市场变化


场景三:题材趋势研究

角色:内容分析师/投研人员

需求:系统性分析抖音短剧题材分布和趋势变化

使用方式:

  1. 按时间范围查询(如月度数据),观察题材占比变化
  2. 对比多个题材的互动数据和增长趋势
  3. 分析「跨题材对比建议」,发现融合机会

预期收益:形成数据驱动的趋势判断,支撑投资决策


场景四:日常内容追踪

角色:短剧爱好者/行业关注者

需求:每天快速了解抖音短剧热点,无需手动分析

使用方式:

  1. 开启订阅 --subscribe
  2. 每日自动生成日报,保存在 ~/Downloads/QoderReports/

预期收益:零成本追踪行业动态,省时省力

项目架构

目录结构

短剧-抖音信息源/
├── SKILL.md                           # Skill核心说明文档
├── scripts/
│   └── playlet_douyin_daily.py       # 日报生成脚本(含题材聚类+HTML生成)
├── references/
│   ├── core_workflow.md              # 核心工作流程+输出格式+聚类规则+趋势分析
│   └── examples.md                   # 使用示例与常见用法组合
└── assets/

技术栈

组件 技术 说明
运行环境 Python 3 脚本执行环境
数据接口 红狐Hub API 抖音短剧创作数据,RESTful接口
鉴权方式 X-API-KEY 请求头鉴权,环境变量配置
输出格式 HTML(深色主题) 自动浏览器打开,响应式设计
缓存策略 JSON本地缓存 1小时有效期,路径 ~/.workbuddy/cache/

核心模块

  • playlet_douyin_daily.py:主执行脚本,集成API调用、题材聚类、HTML日报生成、创作趋势分析,支持日期智能判断(15:00规则)、批量查询去重、浏览器自动预览

常见问答

安装相关

Q: 提示 "缺少 API Key" 怎么办?

A: 请确认已正确配置环境变量 REDFOX_API_KEY:

  1. 前往 红狐Hub 注册并获取 API Key(格式 ak_xxxxxxxx)
  2. Windows:[Environment]::SetEnvironmentVariable("REDFOX_API_KEY", "ak_xxx", "User")
  3. 重启终端后验证:echo %REDFOX_API_KEY%

Q: API Key 无效或过期?

A: 登录红狐Hub个人中心检查Key状态,确认Key未过期且账户积分充足。

使用相关

Q: 数据什么时候更新?

A: 每日15:00更新前一天的数据(实际可能延迟,以脚本真实探活为准)。15:00前最新可查为前天,15:00后为昨天。

Q: 查询时提示"数据尚未更新"?

A: 脚本会自动向前回退获取最近时间范围数据,并明确告知"当前查询时间未更新或超过查询时间范围,已为您自动获取最近时间范围数据",无需手动处理。

Q: 支持哪些题材查询?

A: 内置9大题材(穿越/霸总/重生/悬疑/甜宠/逆袭/年代/战神/古装),同时支持任意自定义关键词查询。但查询前会先校验关键词是否符合短剧题材词库,不满足时提醒+推荐相关词,不请求接口,避免浪费API额度。

Q: 如何节省API积分?

A: 使用 --from-cache 复用1小时内缓存;使用 --latest 自动回退到有数据的日期;探活式预检拦截无数据日期;关键词不满足题材词库时不发起接口请求。

故障排除

Q: 脚本报错 "UnicodeEncodeError: 'gbk' codec"?

A: Windows终端编码问题,执行前设置环境变量:$env:PYTHONIOENCODING="utf-8"

Q: HTML日报没有自动打开?

A: 确认系统默认浏览器已正确设置,日报文件始终保存在 ~/Downloads/QoderReports/ 目录下,可手动打开。

Q: API常见错误码?

错误码 说明 解决方式
1002 每页条数超过200 脚本已自动限制,无需处理
3106 缺少API Key 配置环境变量 REDFOX_API_KEY
3107 API Key无效 检查Key格式和有效性
3108 请求过于频繁 等待后重试
3109 今日调用达上限 次日再试
3201 积分不足 前往红狐Hub充值

参考文档

  • core_workflow.md — 核心执行流程、输出格式模板、日期判断逻辑、题材聚类规则、创作趋势分析逻辑
  • examples.md — 使用示例与常见用法组合
Files (redfox-community)
  • references
    • core_workflow.md 12.1 KB
      # 短剧-抖音信息源 - 核心工作流程
      
      ## 执行流程
      
      ### 第零步:输入与日期预检(必须执行,先于任何接口调用)
      
      > ⛔ **核心规则:关键词/分类不满足短剧题材词库时,不发起任何接口请求**
      >
      > 数据获取前先做两类校验(脚本层 v2.2 自动执行,Agent 侧同样遵守):
      > 1. **分类/关键词校验**:是否符合短剧题材词库(穿越/霸总/重生/甜宠/悬疑/逆袭等)
      > 2. **日期有效性校验**:格式正确、在有效查询范围
      
      **数据更新规则**:每日15:00更新前一天的数据(实际可能延迟,以脚本真实探活为准)
      - 15:00前:估算最新可用日期 = T-2(前天)——仅作初始起点
      - 15:00后:估算最新可用日期 = T-1(昨天)——仅作初始起点
      
      **执行流程(每次查询前强制执行)**:
      
      1. **关键词/分类前置校验**:判断用户输入的分类/关键词是否命中短剧题材词库(`topic_keywords` 中规定的**题材名+全部相关词**,如「打脸」命中逆袭题材相关词、「总裁」命中霸总题材相关词;**命中后直接使用该关键词查询数据**)
         - 不满足时提醒"关键词不满足查询条件",推荐相关分类和关键词,**不发起接口请求**,直接停止并引导用户改用推荐词
         - 混合词场景:保留有效关键词继续查询,无效关键词自动忽略并提示
         - 全部无效:**不请求接口**,停止并推荐相关词
      2. **日期前置校验**:判断目标日期格式是否有效、是否晚于今天;超出范围时提醒并推荐最近可用日期
      3. **真实探活**:用 `pageSize=1` 不带 keyword 的轻量请求**实际探测**目标日期是否有数据,以接口返回为准(替代纯本地时钟推断)
      4. **自动兜底**:目标日期未更新或超过查询时间范围时,自动向前回退获取最近时间范围数据,并明确告知"当前查询时间未更新或超过查询时间范围,已为您自动获取最近时间范围数据",无需用户确认
      5. `--latest` 模式同样自动向前回退(最多7天)定位最近有数据的日期
      6. 若回退 7 天内均无数据,提示用户稍后再试或联系数据源确认更新状态
      7. **无数据确认制(v2.3)**:查询无匹配数据或全量数据不足时,**禁止自动降级全量、禁止自动扩展题材**,提示推荐题材关键词并询问用户,用户确认后才可发起查询
      
      **示例对话**:
      
      ```
      用户:查询今天的短剧抖音日报
      Agent:(执行 --latest,脚本自动向前回退定位最近有数据的日期,直接生成日报)
      
      用户:查询"智能辅助驾驶"题材的短剧
      Agent:⚠️ 关键词 ['智能辅助驾驶'] 不满足短剧查询条件(短剧按题材/剧情词匹配标题,非短剧题材词大概率无结果)
             💡 推荐相关分类和关键词:穿越、霸总、重生、甜宠、悬疑、逆袭、年代、战神、古装、都市
             🛑 关键词均不满足短剧查询条件,未发起任何接口请求。请使用推荐题材词重新查询。
      
      用户:查询"打脸"题材的短剧
      Agent:(关键词命中逆袭题材相关词 → 直接使用"打脸"查询数据)
      
      用户:查询"穿越"题材无匹配数据
      Agent:📭 未查询到相关数据
             💡 推荐题材关键词:穿越、霸总、重生、悬疑、甜宠、逆袭、年代、战神、古装
             ❓ 是否按推荐题材重新查询?(确认前不发起任何额外请求)
      ```
      
      ### 第一步:生成爆款日报
      
      ```bash
      # 生成最新一期日报(--latest 自动向前回退最多7天定位最近有数据的日期,不扣积分)
      python3 scripts/playlet_douyin_daily.py --latest
      
      # 生成指定日期日报(历史日期已有数据,无需确认)
      python3 scripts/playlet_douyin_daily.py --date 2026-06-10
      
      # 自定义题材查询(关键词命中词库后直接查询该关键词;数据不足时提示推荐题材并等待确认)
      python3 scripts/playlet_douyin_daily.py --topics "穿越,霸总,重生,悬疑" --latest
      
      # 订阅 / 取消订阅
      python3 scripts/playlet_douyin_daily.py --subscribe
      python3 scripts/playlet_douyin_daily.py --unsubscribe
      ```
      
      > **查询策略**:默认查询全部短剧内容(pageSize=200),所有题材通过批量接口一次性查询,无需逐个调用。用户自定义题材时仅使用用户提供的列表,同样批量查询。**关键词校验规则(v2.3)**:关键词需命中短剧题材词库(`topic_keywords` 中规定的**题材名+全部相关词**),**命中后直接使用该关键词查询数据**;不满足时提醒+推荐,**不请求接口**。**无数据确认制**:查询无匹配数据或全量数据不足时,**禁止自动扩展题材/降级全量**,提示推荐题材关键词并等待用户确认后再查询。查询前自动探活目标日期;空结果自动重试1次。
      
      > **日期智能判断**:脚本内置 `DATA_UPDATE_HOUR = 15` 常量(每日15:00更新前一天数据),查询前先用轻量请求(pageSize=1, 无keyword)真实探测目标日期是否有数据,替代纯本地时钟推断;目标日期未更新或超过查询范围时自动向前回退最多7天定位最近有数据的日期。
      
      > **降失败率机制(v2.0增强)**:
      > - **真实探活**:查询前先用 `pageSize=1` 无 keyword 轻量请求探测目标日期,以接口返回为准(数据源实际更新可能晚于15:00),无数据立即拦截,避免空跑耗额度
      > - **自动回退**:`--latest` 从最近日期向前最多回退7天,定位第一个有数据的日期再出日报
      > - **空结果重试**:单题材查询为空/异常时自动重试1次(间隔4秒)
      > - **题材词校验**:`--topics` 传入非题材词时提示并推荐相关词,不满足时**不请求接口**;命中词库(题材名+全部相关词)后直接使用该关键词查询
      > - **结构化空因**:空结果明确输出"无数据/关键词无匹配/接口异常"及下一步建议
      > - **无数据确认制(v2.3)**:无匹配数据或全量数据不足时**不自动扩展、不自动降级**,提示推荐题材并等待用户确认
      
      ### 第二步:执行创作趋势分析
      
      日报生成后,**必须**基于聚类结果自动执行创作趋势分析:
      
      1. 读取题材聚类结果,选取TOP 5热门题材
      2. 分析每个题材的爆款数量、平均互动数据、头部作品特征
      3. 识别新兴起量题材(数量少但互动高)
      4. 输出结构化创作趋势报告
      
      生成的HTML日报保存在 `~/Downloads/QoderReports/`,自动浏览器打开。终端同步输出题材分类表格 + 创作趋势分析报告。
      
      ### 输入解析
      
      用户输入支持以下几种方式:
      
      | 输入方式 | 示例 | 解析参数 |
      |---------|------|---------|
      | 查询默认短剧日报 | "查询今天的短剧抖音日报" | `--latest` |
      | 按题材查询 | "穿越题材的短剧" | `topics="穿越"` |
      | 按时间查询 | "6月的短剧爆款" | `start_time="2026-06-01 00:00:00", end_time="2026-06-30 23:59:59"` |
      | 组合查询 | "穿越题材6月短剧" | `topics="穿越", start_time/end_time` |
      | 指定日期 | "6月10日的短剧日报" | `date="2026-06-10"` |
      
      ---
      
      ## 📊 输出格式(强制执行)
      
      > ⛔ **严格执行规则**:
      > - 以下模板是**唯一合法输出格式**,禁止任何自由发挥、省略、简化或重新组织
      > - 禁止输出模板中未定义的额外内容(如"我来帮你…""以下是…"等口语化文字)
      > - 禁止合并、跳过任何板块,即使某板块数据为"暂无"也必须保留该板块标题
      > - 日报生成后,对话回复**只能**包含以下内容,不得包含其他任何文字
      
      每次运行日报后,对话输出**必须严格**按以下模板原样输出(仅替换 `{...}` 占位符):
      
      ```
      ## 短剧-抖音信息源 · {日期} 日报
      
      **扫描 {N} 部热门短剧,聚类 {M} 个题材方向**
      
      ---
      
      ### 题材概览
      
      | 题材 | 数量 | 占比 | 爆款亮点 |
      |------|------|------|---------|
      | #{题材名} | {N}部 | {X}% | 头部作品亮点描述 |
      | ... | ... | ... | ... |
      
      ---
      
      ### 创作趋势分析
      
      **一、新兴起量信号**
      
      - 🔥 **#{题材}** — 仅{N}部但均互动{X}+,描述
      (若无新兴题材,输出:暂无新兴起量信号)
      
      **二、爆款标题特征**
      
      | 特征模式 | 出现次数 | 典型案例 | 平均互动 |
      |---------|---------|---------|---------|
      | {特征1} | {N}次 | 《{标题}》 | {X}w |
      | ... | ... | ... | ... |
      (若无标题数据,输出:暂无爆款标题数据)
      
      **三、核心达人榜**
      
      | 达人 | 作品数 | 总赞 | 代表作 |
      |------|--------|------|--------|
      | @{达人} | {N}部 | {X}w | 《{作品}》 |
      | ... | ... | ... | ... |
      (若无达人数据,输出:暂无核心达人数据)
      
      **四、题材趋势报告**
      
      **题材**:#{题材1}
      **作品数**:{N}部
      **平均点赞**:{X}w
      **头部作品**:《{标题}》-{点赞}w
      
      **题材特征**:{描述该题材的共性特征}
      **创作建议**:{针对该题材的创作建议}
      
      **五、#{题材2}**
      
      (同上格式)
      
      **六、#{题材3}**
      
      (同上格式)
      
      **七、跨题材对比建议**
      
      - **{题材}** — 建议同步关注{相关题材}的联动创作,观察题材融合趋势
      (若无建议,输出:暂无跨题材对比建议)
      
      ---
      
      **日报地址**:{HTML文件绝对路径}
      
      > 数据说明:每日15:00更新昨天的数据
      ```
      
      > 以上格式为**强制规范**,所有字段不可省略,板块标题(一、二、三、四、五、六、七)必须保留。若某模块无数据则在该板块内标注"暂无",不得删除板块本身。
      
      ---
      
      ## 参数说明
      
      ### topics(题材类型)
      
      热门题材包括但不限于:
      
      | 题材 | 说明 | 典型关键词 |
      |------|------|-----------|
      | 穿越 | 穿越时空题材 | 穿越、时空、古代、现代 |
      | 霸总 | 霸道总裁题材 | 霸总、总裁、豪门 |
      | 重生 | 重生逆袭题材 | 重生、回到、逆袭 |
      | 悬疑 | 悬疑推理题材 | 悬疑、推理、反转、惊悚 |
      | 甜宠 | 甜蜜宠溺题材 | 甜宠、恋爱、撒糖 |
      | 逆袭 | 逆袭成长题材 | 逆袭、翻身、打脸 |
      
      ### 平台固定为抖音
      
      本skill专注于抖音平台短剧内容,platform固定为 `1`(抖音)。
      
      ## 常见错误处理
      
      | 错误码 | 错误信息 | 处理方式 |
      |--------|---------|---------|
      | 1002 | 每页条数不能超过200 | 自动限制 page_size ≤ 200 |
      | 3106 | 缺少 API Key | 提示用户配置 REDFOX_API_KEY |
      | 3107 | API Key 无效 | 检查 Key 格式和有效性 |
      | 3108 | 请求过于频繁 | 等待后重试 |
      | 3109 | 今日调用次数达上限 | 提示用户次日再试 |
      | 3201 | 积分不足 | 提示用户充值积分 |
      
      ## 缓存机制
      
      - 缓存路径:`~/.workbuddy/cache/playlet_douyin_data.json`
      - 缓存时间:1 小时
      - 使用 `--from-cache` 参数可使用缓存数据
      
      ## 日期处理逻辑
      
      当用户提到时间时,自动转换为 `yyyy-MM-dd HH:mm:ss` 格式:
      
      | 用户输入 | 转换结果 |
      |---------|---------|
      | "今天" | `startTime=今日00:00:00, endTime=今日23:59:59` |
      | "昨天" | `startTime=昨日00:00:00, endTime=昨日23:59:59` |
      | "本周" | `startTime=周一00:00:00, endTime=周日23:59:59` |
      | "6月" | `startTime=2026-06-01 00:00:00, endTime=2026-06-30 23:59:59` |
      | "近7天" | `startTime=7天前, endTime=当前时间` |
      
      ## 题材聚类规则
      
      脚本自动根据作品标题和标签进行题材聚类:
      
      1. **关键词匹配**:根据题材关键词库匹配作品题材
      2. **多标签处理**:一部作品可能属于多个题材,按主题材归类
      3. **未知题材**:无法识别的归类为"其他"
      4. **动态调整**:每日题材分类由当日内容动态决定,不固化
      
      **内置题材关键词库**:
      
      | 题材 | 关键词 |
      |------|--------|
      | 穿越 | 穿越、时空、古代、现代、回到 |
      | 霸总 | 霸总、总裁、豪门、冷酷 |
      | 重生 | 重生、逆袭、回到、翻盘 |
      | 悬疑 | 悬疑、推理、反转、惊悚、谜案 |
      | 甜宠 | 甜宠、恋爱、撒糖、甜蜜、宠溺 |
      | 逆袭 | 逆袭、翻身、打脸、崛起 |
      
      ## 创作趋势分析逻辑
      
      **新兴起量信号识别**:
      - 题材作品数 < 10 但平均互动 > 5w
      - 或单部作品互动 > 20w
      
      **爆款标题特征提取**:
      - 统计高频词汇(前20个)
      - 识别标题模式(如"重生之XX"、"XX霸总"等)
      - 计算各模式的平均互动数据
      
      **核心达人评选**:
      - 按作品数排序(≥3部)
      - 同作品数按总赞排序
      - 展示TOP 10达人
      
    • examples.md 3.6 KB
      # 短剧-抖音信息源 - 使用示例
      
      ## 示例 1:查询今日短剧爆款日报
      
      ```bash
      python3 scripts/playlet_douyin_daily.py --latest
      ```
      
      **预期输出**:
      ```
      ## 短剧-抖音信息源 · 2026-06-17 日报
      
      **扫描 186 部热门短剧,聚类 8 个题材方向**
      
      ---
      
      ### 题材概览
      
      | 题材 | 数量 | 占比 | 爆款亮点 |
      |------|------|------|---------|
      | #穿越 | 45部 | 24.2% | 《重生之穿越大明》点赞52.3w |
      | #霸总 | 38部 | 20.4% | 《霸总的替身娇妻》点赞48.1w |
      | ... | ... | ... | ... |
      ```
      
      ---
      
      ## 示例 2:查询穿越题材短剧
      
      ```bash
      python3 scripts/playlet_douyin_daily.py --topics "穿越"
      ```
      
      **说明**:专注查询穿越题材的短剧内容
      
      ---
      
      ## 示例 3:查询多个题材组合
      
      ```bash
      python3 scripts/playlet_douyin_daily.py --topics "穿越,霸总,重生"
      ```
      
      **说明**:同时查询三个热门题材的短剧,自动去重
      
      ---
      
      ## 示例 4:查询6月份的短剧内容
      
      ```bash
      python3 scripts/playlet_douyin_daily.py \
        --start-time "2026-06-01 00:00:00" \
        --end-time "2026-06-30 23:59:59"
      ```
      
      ---
      
      ## 示例 5:组合查询(穿越题材+6月)
      
      ```bash
      python3 scripts/playlet_douyin_daily.py \
        --topics "穿越" \
        --start-time "2026-06-01 00:00:00" \
        --end-time "2026-06-17 23:59:59" \
        --count 100
      ```
      
      **说明**:
      - 同时使用题材、时间范围筛选
      - `count=100` 表示扫描100部作品
      
      ---
      
      ## 示例 6:使用缓存数据
      
      ```bash
      python3 scripts/playlet_douyin_daily.py --from-cache
      ```
      
      **说明**:如果1小时内有缓存,直接使用缓存数据,节省API积分
      
      ---
      
      ## 示例 7:查询指定日期
      
      ```bash
      python3 scripts/playlet_douyin_daily.py --date 2026-06-10
      ```
      
      **说明**:查询历史日期数据,无需用户确认
      
      ---
      
      ## 示例 8:开启每日订阅
      
      ```bash
      python3 scripts/playlet_douyin_daily.py --subscribe
      ```
      
      **说明**:开启后每日自动生成日报,保存在 `~/Downloads/QoderReports/`
      
      ---
      
      ## 示例 9:查询悬疑题材短剧
      
      ```bash
      python3 scripts/playlet_douyin_daily.py --topics "悬疑,推理,反转"
      ```
      
      **说明**:查询悬疑类短剧,包含相关关键词
      
      ---
      
      ## 示例 10:查询甜宠题材短剧
      
      ```bash
      python3 scripts/playlet_douyin_daily.py --topics "甜宠,恋爱,撒糖"
      ```
      
      **说明**:查询甜宠类短剧
      
      ---
      
      ## 题材参数速查
      
      | 题材类型 | 典型关键词 | 示例命令 |
      |---------|-----------|---------|
      | 穿越 | 穿越、时空、古代、现代 | `--topics "穿越"` |
      | 霸总 | 霸总、总裁、豪门 | `--topics "霸总"` |
      | 重生 | 重生、回到、逆袭 | `--topics "重生"` |
      | 悬疑 | 悬疑、推理、反转、惊悚 | `--topics "悬疑"` |
      | 甜宠 | 甜宠、恋爱、撒糖 | `--topics "甜宠"` |
      | 逆袭 | 逆袭、翻身、打脸 | `--topics "逆袭"` |
      
      ---
      
      ## 常见用法组合
      
      ### 创作者日常使用
      ```bash
      # 查看今日短剧爆款
      python3 scripts/playlet_douyin_daily.py --latest
      ```
      
      ### 创作者选题参考
      ```bash
      # 查看特定题材的热门作品
      python3 scripts/playlet_douyin_daily.py --topics "穿越,重生" --count 100
      ```
      
      ### 运营人员趋势分析
      ```bash
      # 查看本月短剧趋势
      python3 scripts/playlet_douyin_daily.py \
        --start-time "2026-06-01 00:00:00" \
        --end-time "2026-06-30 23:59:59" \
        --count 200
      ```
      
      ### 题材对比分析
      ```bash
      # 对比穿越和霸总题材
      python3 scripts/playlet_douyin_daily.py --topics "穿越,霸总" --count 150
      ```
      
      ---
      
      ## 数据说明
      
      - **数据更新**:每日15:00更新前一天的数据
      - **数据来源**:红狐Hub 抖音短剧创作数据API
      - **平台**:固定为抖音(platform=1)
      - **内容类型**:固定为短剧(msgType="短剧")
      - **缓存策略**:1小时内可复用缓存
      
  • scripts
    • playlet_douyin_daily.py 31.5 KB
      #!/usr/bin/env python3
      # -*- coding: utf-8 -*-
      """
      短剧-抖音信息源日报生成脚本 (增强版 v2.3)
      =========================================
      每日扫描抖音短剧爆款内容,智能聚类题材后生成HTML日报
      
      v2.3 调整说明(相关词命中直查 + 无数据确认制):
      - 【相关词命中直查】关键词校验匹配 topic_keywords 中规定的**题材名+全部相关词**
        (如「打脸」命中逆袭题材相关词、「总裁」命中霸总题材相关词、「宠妻/替身」命中
        霸总相关词等),命中后**直接使用该关键词查询数据**(不替换题材、不降级全量)。
      - 【无数据确认制】查询无匹配数据或全量数据不足时,**禁止自动发起任何额外查询**
        (禁止自动降级全量、禁止自动扩展题材)。脚本停止并输出推荐题材关键词,
        由 Agent 询问用户是否按推荐词重新查询,得到用户确认后才可发起查询。
      - 全量查询数据不足(小于 AUTO_EXPAND_THRESHOLD)时仅提示推荐题材并等待确认,不自动扩展。
      
      v2.2 调整说明(同步自"短剧-B站信息源" v2.2 增强 + 用户新规则):
      - 【前置校验】查询前先判断用户的输入条件:
        1) 分类/关键词是否符合短剧题材词库(穿越/霸总/重生/甜宠/悬疑等)
        2) 日期是否在有效查询范围(格式正确、不晚于今天)
      - 【不满足不请求接口】关键词/分类不满足短剧题材词库时,明确提醒"关键词不满足
        查询条件"并推荐相关分类和关键词,**不发起任何接口请求**(避免浪费API额度),
        直接停止并引导用户改用推荐词重新查询。
      - 【日期兜底】用户查询的日期未更新或超过查询时间范围时,自动向前回退获取最近
        时间范围数据,并明确告知"当前查询时间未更新或超过查询时间范围,已为您自动
        获取最近时间范围数据"。
      
      v2.0 增强说明(同步自B站版,针对"数据查询结果为空"问题的降失败率机制):
      1. 【P0-日期探活】查询前先用 pageSize=1、不带 keyword 的轻量请求探测目标日期
         是否有数据;无数据立即拦截,避免盲目消耗多题材查询额度。
      2. 【P0-自动回退】--latest 从最近日期向前最多回退 FALLBACK_DAYS(默认7) 天,
         找到第一个有数据的日期再生成日报;输出中明确标注实际数据日期。
      3. 【P1-确认制题材补充】全量查询数据不足(小于 AUTO_EXPAND_THRESHOLD)时,
         仅提示推荐题材并等待用户确认,确认前不自动扩展题材(v2.3 确认制)。
      4. 【P1-空结果重试】单题材查询为空/异常时,间隔 RETRY_INTERVAL 秒重试 1 次。
      5. 【P1-题材词校验】--topics 传入明显非题材词时给出提示(不阻断,仅提醒)。
      6. 【P2-结构化空因】每次空结果输出原因分类:无数据 / 关键词无匹配 / API异常。
      7. 【P2-防御式解析】兼容 {"code":2000,"data":{"list":[...]}} 与直出 list 两种
         格式,防止服务端调整响应结构时脚本静默失效。
      
      用法与原版完全兼容:
          python3 playlet_douyin_daily.py --latest
          python3 playlet_douyin_daily.py --date 2026-08-05
          python3 playlet_douyin_daily.py --topics "穿越,霸总" --latest
      """
      
      import argparse
      import json
      import os
      import sys
      import time
      import webbrowser
      from datetime import datetime, timedelta
      from urllib import request, error
      
      
      # ============ 配置 ============
      API_BASE_URL = "https://redfox.hk/story/api/parseWork/queryPlayletMsgs"
      CACHE_DIR = os.path.expanduser("~/.workbuddy/cache")
      CACHE_FILE = os.path.join(CACHE_DIR, "playlet_douyin_data.json")
      OUTPUT_DIR = os.path.expanduser("~/Downloads/QoderReports")
      DATA_UPDATE_HOUR = 15       # 数据源声称的更新时刻(仅作提示参考,不再作为唯一依据)
      FALLBACK_DAYS = 7           # --latest 自动回退的最大天数
      RETRY_TIMES = 1             # 空结果/异常重试次数
      RETRY_INTERVAL = 4          # 重试间隔(秒)
      REQUEST_TIMEOUT = 30        # 单次请求超时(秒)
      AUTO_EXPAND_THRESHOLD = 100 # 全量结果少于该值时提示用户确认是否扩展题材(不自动扩展)
      AUTO_EXPAND_TOPICS = ["穿越", "霸总", "重生", "悬疑", "甜宠", "逆袭"]  # 推荐扩展顺序(仅供提示,需用户确认后才查询)
      HOT_TOPICS = ["穿越", "霸总", "重生", "悬疑", "甜宠", "逆袭", "年代", "战神", "古装"]  # 推荐题材顺序
      
      # 短剧题材词库:用于 --topics 输入校验提示
      # 规则:关键词需命中 topic_keywords 中规定的题材名+全部相关词(如「打脸」命中逆袭题材相关词),命中后直接用该关键词查询数据
      TOPIC_KEYWORDS_THESAURUS = {
          "穿越": ["穿越", "时空", "古代", "现代", "回到", "大宋", "北宋", "南宋", "唐朝", "明朝", "清朝"],
          "霸总": ["霸总", "总裁", "豪门", "冷酷", "宠妻", "娇妻", "替身"],
          "重生": ["重生", "逆袭", "回到", "翻盘", "重来", "再生"],
          "悬疑": ["悬疑", "推理", "反转", "惊悚", "谜案", "秘密", "真相"],
          "甜宠": ["甜宠", "恋爱", "撒糖", "甜蜜", "宠溺", "甜甜"],
          "逆袭": ["逆袭", "翻身", "打脸", "崛起", "反击", "报复"],
          "年代": ["年代", "八零", "九零", "七零", "六零"],
          "战神": ["战神", "龙王", "兵王", "高手"],
          "古装": ["古装", "宫廷", "皇后", "贵妃", "王爷", "世子"],
      }
      # 全部有效词集合(题材名 + 各题材相关词 + 扩展词),用于关键词前置校验
      TOPIC_THESAURUS = {
          "穿越", "霸总", "重生", "悬疑", "甜宠", "逆袭", "年代", "战神",
          "古装", "总裁", "豪门", "复仇", "惊悚", "推理", "反转", "爽文",
          "科幻", "玄幻", "修仙", "都市", "职场", "萌宝", "萌娃", "亲子",
          "离婚", "闪婚", "替身", "虐恋", "先婚后爱", "双重生",
      }
      for _t, _kws in TOPIC_KEYWORDS_THESAURUS.items():
          TOPIC_THESAURUS.add(_t)
          TOPIC_THESAURUS.update(_kws)
      
      
      # ============ 工具函数 ============
      def get_api_key():
          """从环境变量获取 API Key"""
          api_key = os.environ.get("REDFOX_API_KEY")
          if not api_key:
              print("❌ 错误:未找到 REDFOX_API_KEY 环境变量")
              print("请先配置:export REDFOX_API_KEY=<你的apikey>")
              sys.exit(1)
          return api_key
      
      
      def calculate_latest_date():
          """按15:00规则估算最新可用日期(仅作初始起点,实际以探活为准)"""
          now = datetime.now()
          if now.hour < DATA_UPDATE_HOUR:
              return (now - timedelta(days=2)).strftime("%Y-%m-%d")
          else:
              return (now - timedelta(days=1)).strftime("%Y-%m-%d")
      
      
      def validate_date(date_str):
          """旧接口保留:基于本地时钟的日期校验(新逻辑改走 probe_date)"""
          latest_date = calculate_latest_date()
          target_date = datetime.strptime(date_str, "%Y-%m-%d")
          latest = datetime.strptime(latest_date, "%Y-%m-%d")
          return target_date <= latest, latest_date
      
      
      def check_topics(topics):
          """
          v2.2 前置校验:判断用户输入的分类/关键词是否符合短剧题材词库。
          返回 (有效词列表, 无效词列表, 推荐词列表)
          推荐逻辑:优先从无效词中提取包含的题材词,再补充热门题材词。
          """
          hot_topics = ["穿越", "霸总", "重生", "甜宠", "悬疑", "逆袭",
                        "年代", "战神", "古装", "都市", "科幻"]
          valid, invalid = [], []
          for t in topics:
              if t in TOPIC_THESAURUS or t == "短剧":
                  valid.append(t)
              else:
                  invalid.append(t)
          recommends = []
          for t in invalid:
              # 无效词若包含题材词(如"穿越重生"含"穿越""重生"),优先推荐
              contained = [w for w in TOPIC_THESAURUS if w in t or t in w]
              for c in contained:
                  if c not in recommends:
                      recommends.append(c)
          for h in hot_topics:
              if h not in recommends:
                  recommends.append(h)
          return valid, invalid, recommends
      
      
      def check_date(date_str):
          """
          v2.2 前置校验:判断日期是否在有效查询范围(格式正确、不晚于今天)。
          返回 (是否有效, 提示信息, 推荐日期或None)
          """
          try:
              d = datetime.strptime(date_str, "%Y-%m-%d")
          except ValueError:
              return False, f"日期格式无效:{date_str}(应为 YYYY-MM-DD)", None
          today = datetime.now().date()
          if d.date() > today:
              latest = calculate_latest_date()
              return False, f"日期 {date_str} 超出有效查询范围(晚于今天,数据每日15:00更新前一天)", latest
          return True, "", None
      
      
      def parse_response(result):
          """
          防御式响应解析(加固,非修复):兼容两种格式
          格式A(API实际完整响应): {"code":2000,"data":{"list":[...],"total":M},"msg":"..."}
          格式B(兜底/直出):      {"list":[...], "pageNum":1, "pages":N, "total":M}
          返回: (items列表, error_msg或None)
          """
          if not isinstance(result, dict):
              return [], "响应非JSON对象"
          # 格式A:code/data 包装(当前 API 实际格式)
          if result.get("code") == 2000:
              data = result.get("data") or {}
              return data.get("list") or [], None
          # 显式业务错误
          code = result.get("code")
          if code is not None:
              msg = result.get("msg")
              return [], f"API业务错误 code={code} msg={msg}"
          # 格式B:直出 list(兜底)
          if "list" in result:
              return result.get("list") or [], None
          return [], None
      
      
      def http_post(payload, api_key):
          """执行一次 POST 请求,返回原始响应 dict(网络/HTTP 层异常向上抛)"""
          data = json.dumps(payload).encode('utf-8')
          req = request.Request(
              API_BASE_URL,
              data=data,
              headers={
                  "Content-Type": "application/json",
                  "X-API-KEY": api_key
              },
              method="POST"
          )
          with request.urlopen(req, timeout=REQUEST_TIMEOUT) as response:
              return json.loads(response.read().decode('utf-8'))
      
      
      def build_payload(start_time, end_time, keyword=None, page_size=200):
          """构建请求体;keyword 为 None 时表示全量查询(不带 keyword 字段)"""
          payload = {
              "msgType": "短剧",
              "platform": 1,  # 1=抖音
              "source": "短剧抖音信息源-GitHub",
              "pageNum": 1,
              "pageSize": page_size,
              "startTime": start_time,
              "endTime": end_time,
          }
          if keyword:
              payload["keyword"] = keyword
          return payload
      
      
      def probe_date_available(api_key, start_time, end_time):
          """
          探活:pageSize=1 + 不带 keyword 的轻量请求,确认该日期是否有数据。
          返回 (bool, info_str);False 说明该日期数据源无任何数据(未更新/缺失)。
          成本:每次探测约 1 次接口额度,远低于无脑全量查询。
          """
          payload = build_payload(start_time, end_time, keyword=None, page_size=1)
          try:
              result = http_post(payload, api_key)
              items, err = parse_response(result)
              if err:
                  print(f"  ⚠️ 探活请求异常: {err}")
                  return False, "probe_error"
              if items:
                  return True, "ok"
              return False, "no_data"
          except Exception as e:
              print(f"  ⚠️ 探活请求失败: {e}")
              return False, "probe_fail"
      
      
      def _fetch_topic_once(api_key, payload, topic):
          """单题材单次查询(含重试),返回 (items, api_error: bool)"""
          for attempt in range(RETRY_TIMES + 1):
              try:
                  result = http_post(payload, api_key)
                  items, err = parse_response(result)
                  if err:
                      if attempt < RETRY_TIMES:
                          time.sleep(RETRY_INTERVAL)
                      continue
                  return items, False  # 解析成功(可能为空 list,但非异常)
              except Exception as e:
                  if attempt < RETRY_TIMES:
                      print(f"  ⚠️ 题材 {topic} 第{attempt+1}次请求失败({e}),{RETRY_INTERVAL}s后重试...")
                      time.sleep(RETRY_INTERVAL)
                  else:
                      print(f"❌ 查询题材 {topic} 失败:{str(e)}")
          return [], True
      
      
      # ============ 数据获取 ============
      def fetch_playlet_data(
          topics=None,
          start_time=None,
          end_time=None,
          count=200,
          use_cache=False,
      ):
          """
          调用 API 查询抖音短剧数据(增强版)
      
          Args:
              topics: 题材列表(逗号分隔),None/空 → 全量查询,数据不足时自动扩展题材
              start_time / end_time: 查询时间窗
              count: 扫描作品数量
              use_cache: 是否使用缓存
      
          Returns:
              (items, meta) 其中 meta 含 reason 字段用于结构化空因:
                  reason in {"ok", "no_data", "probe_fail", "probe_error",
                             "keyword_no_match", "api_error"}
          """
          if use_cache:
              cached_data = load_cache()
              if cached_data:
                  print("📦 使用缓存数据")
                  return cached_data, {"reason": "ok", "note": "cache"}
      
          if not start_time or not end_time:
              latest_date = calculate_latest_date()
              start_time = f"{latest_date} 00:00:00"
              end_time = f"{latest_date} 23:59:59"
      
          api_key = get_api_key()
          meta = {"reason": "ok", "probed": False, "date": start_time[:10]}
      
          # ---- P0-2 探活:先确认该日期数据源是否有数据 ----
          available, info = probe_date_available(api_key, start_time, end_time)
          meta["probed"] = True
          if not available:
              meta["reason"] = "no_data" if info == "no_data" else info
              print(f"📭 日期 {start_time[:10]} 数据源无数据({info}),跳过查询以避免浪费额度")
              return [], meta
      
          # ---- 确定查询题材序列 ----
          # 用户指定题材 → 仅用用户列表(文档规则:自定义时不用扩展列表)
          # 未指定 → 全量查询;数据不足时自动按 AUTO_EXPAND_TOPICS 扩展
          if topics:
              query_topics = list(topics)
              auto_expand = False
          else:
              query_topics = [None]
              auto_expand = True
      
          all_items = []
          api_errors = 0
          expanded = False
      
          for topic in query_topics:
              keyword = None if topic is None or topic == "短剧" else topic
              payload = build_payload(start_time, end_time, keyword=keyword,
                                      page_size=min(count, 200))
              items, had_error = _fetch_topic_once(api_key, payload, topic or "全量")
              if had_error:
                  api_errors += 1
              if items:
                  all_items.extend(items)
      
              # ---- 确认制(v2.3):全量数据不足时不再自动扩展题材 ----
              # 仅提示推荐题材并等待用户确认,确认前不发起任何额外请求
              if auto_expand:
                  unique_ids = {it.get("photoId") for it in all_items if it.get("photoId")}
                  if len(unique_ids) < min(count, AUTO_EXPAND_THRESHOLD):
                      expanded = True
                      print(f"  ⚠️ 全量数据不足({len(unique_ids)}条 < {AUTO_EXPAND_THRESHOLD}),不自动扩展题材")
                      print(f"  💡 推荐题材: {'、'.join(AUTO_EXPAND_TOPICS)}")
                      print(f"  ❓ 请确认是否按推荐题材扩展查询(使用 --topics 重新查询),确认前不会发起任何额外请求")
                      meta["reason"] = "need_confirm_expand"
      
          # 去重(基于photoId)
          seen = set()
          unique_items = []
          for item in all_items:
              item_id = item.get("photoId")
              if item_id and item_id not in seen:
                  seen.add(item_id)
                  unique_items.append(item)
      
          # 按点赞量排序
          unique_items.sort(key=lambda x: x.get("likeCount", 0), reverse=True)
      
          if not unique_items:
              # 探活通过但实际查询为空 → 大概率是关键词无匹配
              if topics and topics != [None]:
                  meta["reason"] = "keyword_no_match"
              else:
                  meta["reason"] = "no_data"
              return [], meta
      
          # 保存缓存
          save_cache(unique_items)
          meta["reason"] = "ok"
          return unique_items[:count], meta
      
      
      # ============ 题材聚类 ============
      def cluster_by_topic(items):
          """按题材聚类作品(词库与 TOPIC_KEYWORDS_THESAURUS 保持一致)"""
          topic_keywords = dict(TOPIC_KEYWORDS_THESAURUS)
          clusters = {}
          for item in items:
              title = item.get("title", "")
              matched_topics = []
              for topic, keywords in topic_keywords.items():
                  if any(kw in title for kw in keywords):
                      matched_topics.append(topic)
              matched_topic = matched_topics[0] if matched_topics else "其他"
              clusters.setdefault(matched_topic, []).append(item)
          return clusters
      
      
      # ============ HTML 日报 ============
      def format_number(num):
          """格式化数字(万→w)"""
          if num is None:
              return "0"
          if num >= 10000:
              return f"{num/10000:.1f}w"
          return str(num)
      
      
      def generate_html_report(items, clusters, date_str):
          """生成HTML日报(抖音粉 #FB7299)"""
          os.makedirs(OUTPUT_DIR, exist_ok=True)
          html_file = os.path.join(OUTPUT_DIR, f"短剧抖音日报_{date_str}.html")
      
          try:
              dt_obj = datetime.strptime(date_str, "%Y-%m-%d")
              weekdays = ["一", "二", "三", "四", "五", "六", "日"]
              date_cn = f"{dt_obj.year}年{dt_obj.month}月{dt_obj.day}日 星期{weekdays[dt_obj.weekday()]}"
          except ValueError:
              date_cn = date_str
      
          total_count = len(items)
          topic_count = len(clusters)
          total_likes = sum(item.get("likeCount", 0) for item in items)
          avg_likes = total_likes / total_count if total_count > 0 else 0
      
          category_cards = ""
          for i, (topic, topic_items) in enumerate(
                  sorted(clusters.items(), key=lambda x: len(x[1]), reverse=True), 1):
              articles_html = ""
              for item in topic_items[:5]:
                  title = item.get("title", "无标题")
                  author = item.get("userName", "")
                  cover = item.get("coverUrl") or ""
                  url = item.get("url") or ""
                  photo_id = item.get("photoId") or ""
      
                  raw_shares = item.get("shareCount", 0) or 0
                  raw_likes = item.get("likeCount", 0) or 0
                  raw_comments = item.get("commentCount", 0) or 0
      
                  metrics_parts = []
                  if raw_shares > 0:
                      metrics_parts.append(f'<span class="metric">🔗 {format_number(raw_shares)}</span>')
                  if raw_likes > 0:
                      metrics_parts.append(f'<span class="metric">👍 {format_number(raw_likes)}</span>')
                  if raw_comments > 0:
                      metrics_parts.append(f'<span class="metric">💬 {format_number(raw_comments)}</span>')
                  metrics_html = ''.join(metrics_parts)
      
                  cover_html = ""
                  if cover:
                      cover_html = f'<img class="article-cover" src="{cover}" alt="" loading="lazy">'
      
                  if url:
                      title_html = f'<a href="{url}" target="_blank" class="article-title">{title}</a>'
                  elif photo_id:
                      title_html = f'<a href="https://www.douyin.com/video/{photo_id}" target="_blank" class="article-title">{title}</a>'
                  else:
                      title_html = f'<span class="article-title">{title}</span>'
      
                  articles_html += f'''
                      <div class="article-item">
                          {cover_html}
                          <div class="article-info">
                              {title_html}
                              <div class="article-meta">
                                  <span class="author">{author}</span>
                                  <span class="metrics">
                                      {metrics_html}
                                  </span>
                              </div>
                          </div>
                      </div>'''
      
              category_cards += f'''
              <div class="category-card reveal">
                  <div class="card-header">
                      <span class="card-number">{i:02d}</span>
                      <h3 class="card-category">#{topic}</h3>
                      <span class="card-count">{len(topic_items)} 部</span>
                  </div>
                  <div class="card-body">{articles_html}
                  </div>
              </div>'''
      
          timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
          html_content = f'''<!DOCTYPE html>
      <html lang="zh-CN">
      <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>短剧-抖音信息源 - {date_str}</title>
      <style>
      * {{ margin: 0; padding: 0; box-sizing: border-box; }}
      body {{ font-family: -apple-system, sans-serif; background: #1a1a1a; color: #e8e4df; padding: 2rem; }}
      .header {{ text-align: center; padding: 2rem 0; }}
      .header h1 {{ font-size: 2rem; color: #FB7299; }}
      .header p {{ color: #9a9590; margin-top: 0.5rem; }}
      .stats {{ display: flex; justify-content: center; gap: 2rem; padding: 1rem; margin: 1rem 0; }}
      .stat-item {{ text-align: center; }}
      .stat-value {{ font-size: 1.5rem; font-weight: bold; color: #FB7299; }}
      .stat-label {{ font-size: 0.8rem; color: #9a9590; }}
      .cards {{ display: grid; grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); gap: 1.5rem; max-width: 1200px; margin: 2rem auto; }}
      .category-card {{ background: #2d2d2d; border-radius: 12px; padding: 1.5rem; }}
      .card-header {{ display: flex; align-items: center; gap: 0.8rem; margin-bottom: 1rem; padding-bottom: 0.8rem; border-bottom: 1px solid #3d3d3d; }}
      .card-number {{ font-size: 1.5rem; font-weight: bold; color: #FB7299; }}
      .card-category {{ flex: 1; font-size: 1.1rem; }}
      .card-count {{ color: #9a9590; font-size: 0.9rem; }}
      .article-item {{ padding: 0.6rem 0; border-bottom: 1px solid #3d3d3d; display: flex; gap: 0.8rem; }}
      .article-item:last-child {{ border-bottom: none; }}
      .article-cover {{ width: 60px; height: 60px; border-radius: 6px; object-fit: cover; flex-shrink: 0; }}
      .article-info {{ flex: 1; min-width: 0; }}
      .article-title {{ color: #e8e4df; font-size: 0.9rem; line-height: 1.4; display: block; }}
      .article-title:hover {{ color: #FB7299; text-decoration: underline; cursor: pointer; }}
      a.article-title {{ text-decoration: none; }}
      a.article-title:hover {{ color: #FB7299; text-decoration: underline; }}
      .article-meta {{ display: flex; justify-content: space-between; margin-top: 0.3rem; font-size: 0.75rem; color: #9a9590; }}
      .metrics {{ display: flex; gap: 0.8rem; }}
      .footer {{ text-align: center; padding: 2rem; color: #666; font-size: 0.8rem; }}
      .reveal {{ animation: fadeIn 0.5s ease-in; }}
      @keyframes fadeIn {{ from {{ opacity: 0; transform: translateY(20px); }} to {{ opacity: 1; transform: translateY(0); }} }}
      </style>
      </head>
      <body>
      <div class="header">
          <h1>🎬 短剧-抖音信息源</h1>
          <p>{date_cn} | 共 {total_count} 部热门短剧</p>
      </div>
      <div class="stats">
          <div class="stat-item"><div class="stat-value">{topic_count}</div><div class="stat-label">题材</div></div>
          <div class="stat-item"><div class="stat-value">{total_count}</div><div class="stat-label">短剧</div></div>
          <div class="stat-item"><div class="stat-value">{format_number(int(avg_likes))}</div><div class="stat-label">平均点赞</div></div>
          <div class="stat-item"><div class="stat-value">{format_number(total_likes)}</div><div class="stat-label">总点赞</div></div>
      </div>
      <div class="cards">{category_cards}</div>
      <div class="footer">Generated at {timestamp} by 短剧-抖音信息源 Skill<br>数据说明:每日15:00更新前一天的数据 | 数据来源:红狐Hub</div>
      </body>
      </html>'''
      
          with open(html_file, 'w', encoding='utf-8') as f:
              f.write(html_content)
          return html_file
      
      
      # ============ 缓存 ============
      def load_cache():
          if not os.path.exists(CACHE_FILE):
              return None
          try:
              with open(CACHE_FILE, 'r', encoding='utf-8') as f:
                  cache_data = json.load(f)
                  if time.time() - cache_data.get("timestamp", 0) < 3600:
                      return cache_data.get("items")
          except Exception:
              pass
          return None
      
      
      def save_cache(items):
          os.makedirs(CACHE_DIR, exist_ok=True)
          cache_data = {"timestamp": time.time(), "items": items}
          try:
              with open(CACHE_FILE, 'w', encoding='utf-8') as f:
                  json.dump(cache_data, f, ensure_ascii=False, indent=2)
          except Exception:
              pass
      
      
      # ============ 主流程 ============
      def find_latest_available_date(api_key, max_fallback=FALLBACK_DAYS):
          """
          P0-2 自动回退:从最近日期开始向前探测,返回第一个有数据的日期。
          返回 (date_str, found: bool)
          """
          latest = calculate_latest_date()
          cursor = datetime.strptime(latest, "%Y-%m-%d")
          for i in range(max_fallback + 1):
              d = (cursor - timedelta(days=i)).strftime("%Y-%m-%d")
              print(f"  🔎 探测 {d} ...", end="")
              ok, info = probe_date_available(
                  api_key, f"{d} 00:00:00", f"{d} 23:59:59"
              )
              print(" 有数据" if ok else f" 无数据({info})")
              if ok:
                  return d, True
          return latest, False
      
      
      def main():
          global OUTPUT_DIR
          parser = argparse.ArgumentParser(description="短剧-抖音信息源日报生成工具 (v2.2增强版)")
          parser.add_argument("--topics", type=str, help="题材关键词,逗号分隔;不满足短剧题材词时提醒并推荐,不请求接口")
          parser.add_argument("--count", type=int, default=200, help="扫描作品数量")
          parser.add_argument("--date", type=str, help="指定日期 YYYY-MM-DD;超出有效范围时提醒并自动回退最近有数据的日期")
          parser.add_argument("--start-time", type=str, help="开始时间 YYYY-MM-DD HH:MM:SS")
          parser.add_argument("--end-time", type=str, help="结束时间 YYYY-MM-DD HH:MM:SS")
          parser.add_argument("--latest", action="store_true", help="使用最新有数据的日期(自动回退)")
          parser.add_argument("--output-dir", type=str, default=OUTPUT_DIR, help="输出目录")
          parser.add_argument("--api-key", type=str, help="指定 API Key")
          parser.add_argument("--subscribe", action="store_true", help="开启每日订阅")
          parser.add_argument("--unsubscribe", action="store_true", help="关闭每日订阅")
          parser.add_argument("--from-cache", action="store_true", help="使用缓存数据")
          args = parser.parse_args()
      
          if args.subscribe:
              print("✅ 已开启每日订阅,日报将自动保存至:", OUTPUT_DIR)
              return
          if args.unsubscribe:
              print("✅ 已关闭每日订阅")
              return
      
          if args.output_dir:
              OUTPUT_DIR = args.output_dir
      
          api_key = args.api_key or get_api_key()
      
          # ---- 前置校验:关键词/分类是否符合短剧题材词库(不满足则不请求接口)----
          topics = None
          if args.topics:
              raw_topics = [t.strip() for t in args.topics.split(",") if t.strip()]
              valid_topics, invalid_topics, recommends = check_topics(raw_topics)
              if invalid_topics:
                  print(f"⚠️ 关键词 {invalid_topics} 不满足短剧查询条件(短剧按题材/剧情词匹配标题,非短剧题材词大概率无结果)")
                  print(f"💡 推荐相关分类和关键词:{'、'.join(recommends[:10])}")
                  if valid_topics:
                      print(f"✅ 已保留有效关键词 {valid_topics} 继续查询,无效关键词已自动忽略")
                      topics = valid_topics
                  else:
                      print("🛑 关键词均不满足短剧查询条件,未发起任何接口请求。请使用推荐题材词重新查询。")
                      return
              else:
                  topics = valid_topics
      
          # ---- 确定查询日期 ----
          if args.start_time:
              start_time = args.start_time
              date_str = args.start_time[:10]
              end_time = args.end_time or f"{date_str} 23:59:59"
          elif args.latest:
              # P0-2 自动回退:探测最近有数据的日期
              print(f"🔎 --latest: 自动寻找最近有数据的日期(最多回退{FALLBACK_DAYS}天)...")
              date_str, found = find_latest_available_date(api_key)
              if not found:
                  print(f"📭 最近 {FALLBACK_DAYS} 天内均无数据,请稍后再试或联系数据源确认更新状态")
                  return
              start_time = f"{date_str} 00:00:00"
              end_time = f"{date_str} 23:59:59"
              print(f"✅ 已定位最新可用日期: {date_str}")
          elif args.date:
              date_str = args.date
              # v2.2: 前置校验——日期格式与有效查询范围
              date_ok, date_msg, date_suggest = check_date(date_str)
              if not date_ok:
                  print(f"⚠️ {date_msg}")
                  if date_suggest:
                      print(f"💡 推荐查询时间范围:{date_suggest}(已为您自动获取该时间范围数据)")
              # 探活式预检(替代纯本地时钟判断)
              ok, info = probe_date_available(api_key, f"{date_str} 00:00:00", f"{date_str} 23:59:59")
              if not ok:
                  # v2.1: 日期兜底——未更新/超范围时自动回退最近有数据的日期,不再等待确认
                  print(f"⚠️ 当前查询时间 {date_str} 未更新或超过查询时间范围,已为您自动获取最近时间范围数据...")
                  new_date, found = find_latest_available_date(api_key)
                  if not found:
                      print(f"📭 最近 {FALLBACK_DAYS} 天内均无数据,请稍后再试或联系数据源确认更新状态")
                      return
                  print(f"✅ 已为您自动获取最近时间范围数据: {new_date}(原查询 {date_str} 未更新或超过查询时间范围)")
                  date_str = new_date
              start_time = f"{date_str} 00:00:00"
              end_time = f"{date_str} 23:59:59"
          else:
              date_str = calculate_latest_date()
              start_time = f"{date_str} 00:00:00"
              end_time = f"{date_str} 23:59:59"
      
          print(f"🔍 正在查询 {date_str} 的抖音短剧数据...")
      
          items, meta = fetch_playlet_data(
              topics=topics,
              start_time=start_time,
              end_time=end_time,
              count=args.count,
              use_cache=args.from_cache,
          )
      
          if not items:
              reason = meta.get("reason", "unknown")
              hint = {
                  "no_data": "数据源当日无数据(未更新或缺失)",
                  "probe_fail": "探测请求失败(网络/接口异常)",
                  "probe_error": "探测请求异常(接口返回异常)",
                  "keyword_no_match": "查询条件(题材词)在该日期无匹配作品",
                  "api_error": "接口调用异常",
              }.get(reason, "未知原因")
              print(f"📭 未查询到相关数据 [原因: {hint}]")
              if reason == "no_data":
                  print("💡 建议: 使用 --latest 自动回退到最近有数据的日期")
              # v2.3 无数据确认制:提示推荐题材并等待用户确认,不自动降级全量/不自动扩展
              print(f"💡 推荐题材关键词:{'、'.join(HOT_TOPICS)}")
              print("❓ 是否按推荐题材重新查询?请确认后使用 --topics 重新查询(确认前不发起任何额外请求)。")
              return
      
          print(f"✅ 共获取 {len(items)} 部短剧作品")
      
          clusters = cluster_by_topic(items)
          print(f"📊 聚类为 {len(clusters)} 个题材方向")
      
          html_file = generate_html_report(items, clusters, date_str)
          print(f"📄 日报已生成:{html_file}")
      
          webbrowser.open(f"file://{html_file}")
      
          print(f"\n## 短剧-抖音信息源 · {date_str} 日报\n")
          print(f"**扫描 {len(items)} 部热门短剧,聚类 {len(clusters)} 个题材方向**\n")
          print("### 题材概览\n")
          print("| 题材 | 数量 | 占比 | 爆款亮点 |")
          print("|------|------|------|---------|")
          for topic, topic_items in sorted(clusters.items(), key=lambda x: len(x[1]), reverse=True):
              top_item = topic_items[0] if topic_items else {}
              print(f"| #{topic} | {len(topic_items)}部 | {len(topic_items)/len(items)*100:.1f}% | 《{top_item.get('title', '')[:20]}》{format_number(top_item.get('likeCount', 0))}赞 |")
      
      
      if __name__ == "__main__":
          main()
      
  • README.en.md 6.1 KB
    # Short Drama - Douyin Feed / playlet-douyin-feed
    
    ---
    
    ## Overview
    
    Short Drama - Douyin Feed is a Douyin trending content tracking tool designed for short drama creators and MCN operators. It automatically scans Douyin short drama content daily, filters trending works by likes, intelligently clusters them by genre, and generates an HTML visual report with cover images, engagement data, and creative insights.
    
    **Core Value**
    
    - **Daily Auto Tracking**: Data updates automatically at 15:00 each day for the previous day's content. Subscribe once and get hands-free daily reports at zero cost.
    - **Smart Genre Clustering**: Built-in 6-genre keyword library (Time Travel / CEO Romance / Rebirth / Suspense / Sweet Romance / Comeback) with automatic classification of trending directions.
    - **In-depth Creative Insights**: Automatically analyzes trending title patterns, top creator rankings, and emerging growth signals to support content decisions.
    - **Visual Daily Report**: Dark-themed HTML report with cover images, engagement data, and direct links to works — clean and intuitive.
    
    **Target Users**
    
    - 🎬 **Short Drama Creators** — Track trending genres and title patterns daily to capture traffic opportunities.
    - 🏢 **MCN Operators** — Monitor creator and competitor performance to improve operational efficiency.
    - 📊 **Content Analysts** — Systematically analyze genre distribution and trend shifts for data-driven decisions.
    
    ---
    
    ## Features
    
    ### Core Features
    
    - **Trend Discovery**: Scans Douyin short drama content daily, filtering popular works by likes to pinpoint high-engagement content.
    - **Genre Clustering**: Intelligently identifies genres such as Time Travel, CEO Romance, Rebirth, Suspense, Sweet Romance, Comeback — classifications dynamically determined by daily content.
    - **Smart Query**: Supports targeted queries by genre, creator, and keywords. Auto-expands genre scope with batch queries when data is insufficient, saving API credits.
    - **Custom Query**: Specify any genre combination for targeted retrieval, flexibly covering niche short drama directions.
    - **Creative Insights**: Analyzes trending title patterns, genre trends, and creator performance for deep creative pattern mining.
    - **Visual Daily Report**: Dark-themed HTML report with cover images, engagement data, and direct links — auto-opens in browser.
    - **One-Click Subscription**: Enable daily automatic report generation, saved locally to your folder.
    
    ### Highlights
    
    - ⚡ **Daily Auto Update**: Previous day's data updates automatically at 15:00 — no manual effort after subscribing.
    - 🏷️ **Smart Genre Clustering**: Built-in 6-genre keyword library with auto classification and dynamic expansion support.
    - 📊 **Creative Trend Report**: Automatically analyzes emerging growth signals, trending title patterns, and top creator rankings.
    - 🎨 **Dark-Themed HTML Report**: Cover images + engagement data + direct links — clean, intuitive, and auto-opened in browser.
    
    ---
    
    ## API Key Acquisition & Security
    
    - This skill requires the environment variable: `REDFOX_API_KEY`.
    - `REDFOX_API_KEY` is provided by [RedFoxHub](https://redfox.hk/settings/api-keys?source=github) (`https://redfox.hk`).
    - Please sign up at [RedFoxHub](https://redfox.hk?source=github) to obtain your `REDFOX_API_KEY`.
    - Configure the environment variable `REDFOX_API_KEY` on your device before using this skill.
    - Before providing an API key, verify its source, applicable scope, validity period, and whether it supports reset/revocation.
    - Never hardcode or expose API keys in plaintext within code, prompts, logs, or output files.
    
    ---
    
    ## Usage Guide
    
    Simply describe your needs in natural language — no commands to memorize.
    
    ### Quick Reference
    
    | Intent | Example Phrase | Result |
    |--------|---------------|--------|
    | Daily Report | "Show me today's short drama Douyin report" | Auto-checks data availability and generates the latest report |
    | By Genre | "Search for time travel short dramas" | Targeted retrieval of time travel trending works with a themed report |
    | Historical Date | "Show me the short drama report for June 10th" | Directly generates the report for the specified historical date |
    | Multi-Genre | "Search for time travel and CEO romance short dramas" | Batch queries multiple genres with automatic deduplication |
    | Trend Analysis | "What are this month's short drama trends?" | Monthly genre distribution and growth trend analysis |
    | Subscribe | "Enable daily short drama report subscription for me" | Daily automatic report generation with no manual effort |
    
    ### Output Example
    
    Each report provides:
    
    - 📄 **HTML Visual Report**: Dark-themed, includes cover images, engagement data, and direct links — auto-opens in browser.
    - 📊 **Genre Overview Table**: Work counts, percentages, and trending highlights per genre.
    - 🔍 **Creative Trend Analysis**: Emerging growth signals, trending title patterns, and top creator rankings.
    - 💡 **Cross-Genre Recommendations**: Genre fusion trends and collaborative creative suggestions.
    
    ---
    
    ## Use Cases
    
    | Scenario | Role | Example Query | Benefit |
    |----------|------|--------------|---------|
    | Topic Inspiration | Screenwriter / Director | "What genres are trending today? Analyze trending titles for me" | Capture traffic opportunities and boost hit probability |
    | Operations Management | MCN Operations Director | "Who are the top creators in the time travel genre this week? How are they performing?" | Improve operational efficiency and catch market shifts |
    | Trend Research | Content Analyst | "Compare engagement trends between time travel and CEO romance this month" | Form data-driven trend judgments to support decisions |
    | Daily Tracking | Short Drama Enthusiast | "Enable daily subscription for me" | Track industry trends effortlessly at zero cost |
    
    ---
    
    ## Data Notes
    
    - **Update Schedule**: Data updates daily at 15:00 for the previous day's content. Before 15:00, the latest available data is from two days ago; after 15:00, it is from yesterday.
    - **Data Source**: RedFoxHub Douyin short drama content API
    - **Platform Scope**: Fixed to Douyin platform short drama content only
    - **Cache Policy**: Query results within 1 hour can be reused from cache to save API credits
    
  • README.md 5.1 KB
    # 短剧-抖音信息源 / playlet-douyin-feed
    
    ---
    
    ## 简介
    
    短剧-抖音信息源是一款专为短剧创作者和MCN运营人员设计的抖音爆款内容追踪工具,每日自动扫描抖音短剧创作内容,按点赞量筛选爆款作品,智能聚类题材后生成包含封面、互动数据与创作洞察的HTML可视化日报。
    
    **核心价值**
    
    - **每日自动追踪**:每日15:00自动更新前一天数据,订阅后无需手动操作,零成本掌握行业动态
    - **智能题材聚类**:内置6大题材关键词库(穿越/霸总/重生/悬疑/甜宠/逆袭),自动识别归类爆款方向
    - **深度创作洞察**:自动分析爆款标题特征、核心达人榜、新兴起量信号,辅助选题决策
    - **可视化日报**:深色主题HTML日报,封面图+互动数据+作品直链,美观直观
    
    **适用对象**
    
    - 🎬 **短剧创作者** — 每日追踪爆款题材与标题模式,精准把握流量风口
    - 🏢 **MCN运营机构** — 追踪达人和竞品表现,提升运营决策效率
    - 📊 **内容分析师** — 系统性分析题材分布与趋势变化,支撑数据驱动决策
    
    ---
    
    ## 功能特性
    
    ### 核心功能
    
    - **爆款发现**:每日扫描抖音短剧内容,按点赞量筛选热门作品,精准定位高热度短剧
    - **题材聚类**:智能识别穿越、霸总、重生、悬疑、甜宠、逆袭等题材方向,每日分类由内容动态决定
    - **智能查询**:支持按题材、达人、关键词定向查询,数据不足时自动扩展题材批量查询,节省API额度
    - **自定义查询**:可指定任意题材组合进行定向检索,灵活覆盖短剧细分方向
    - **创作洞察**:分析爆款标题特征、题材趋势、达人表现,深度挖掘创作规律
    - **可视化日报**:深色主题HTML日报,包含封面图、互动数据与作品直链,自动浏览器打开
    - **一键订阅**:开启后每日自动产出日报,保存在本地文件夹
    
    ### 特色亮点
    
    - ⚡ **每日自动更新**:15:00自动更新前一天数据,订阅后无需手动操作
    - 🏷️ **智能题材聚类**:内置6大题材关键词库,自动识别归类,支持动态扩展
    - 📊 **创作趋势报告**:自动分析新兴起量信号、爆款标题特征、核心达人榜
    - 🎨 **HTML深色主题日报**:封面图+互动数据+作品直链,美观直观,自动浏览器打开
    
    ---
    
    ## 密钥获取与安全说明
    
    - 本技能需要使用环境变量:`REDFOX_API_KEY`。
    - `REDFOX_API_KEY` 由 [红狐 hub](https://redfox.hk/settings/api-keys?source=github) (`https://redfox.hk`)提供。
    - 请前往 [红狐 hub](https://redfox.hk?source=github) 注册账号,获取 `REDFOX_API_KEY`。
    - 配置设备环境变量 `REDFOX_API_KEY` 后使用本技能。
    - 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。
    - 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。
    
    ---
    
    ## 使用指南
    
    直接用自然语言描述需求,无需记忆命令。
    
    ### 常用说法速查
    
    | 意图 | 示例话术 | 效果 |
    |------|---------|------|
    | 查询每日日报 | 「查询今天的短剧抖音日报」 | 自动判断数据可用性,生成最新日报 |
    | 按题材查询 | 「查询穿越题材的短剧」 | 定向检索穿越题材爆款,生成专题日报 |
    | 查询历史日期 | 「查询6月10日的短剧日报」 | 直接生成指定历史日期的日报 |
    | 多题材组合 | 「查询穿越和霸总题材的短剧」 | 批量查询多个题材,自动去重合并 |
    | 趋势分析 | 「本月短剧爆款趋势怎么样」 | 按月分析题材分布与增长趋势 |
    | 开启订阅 | 「帮我开启每日短剧日报订阅」 | 每日自动产出日报,无需手动操作 |
    
    ### 输出示例
    
    日报生成后将获得:
    
    - 📄 **HTML可视化日报**:深色主题,包含封面图、互动数据、作品直链,自动浏览器打开
    - 📊 **题材概览表**:各题材作品数量、占比、爆款亮点
    - 🔍 **创作趋势分析**:新兴起量信号、爆款标题特征、核心达人榜
    - 💡 **跨题材对比建议**:题材融合趋势与联动创作建议
    
    ---
    
    ## 使用场景
    
    | 场景 | 角色 | 示例问法 | 收益 |
    |------|------|---------|------|
    | 选题参考 | 短剧编剧/导演 | 「今天什么题材最火?帮我分析下爆款标题」 | 精准把握流量风口,提升作品爆款概率 |
    | 运营管理 | MCN运营总监 | 「本周穿越赛道有哪些头部达人?表现如何?」 | 提升运营决策效率,及时捕捉市场变化 |
    | 趋势研究 | 内容分析师 | 「对比穿越和霸总题材近一个月的互动趋势」 | 形成数据驱动的趋势判断,支撑决策 |
    | 日常追踪 | 短剧爱好者 | 「帮我开启每日订阅」 | 零成本追踪行业动态,省时省力 |
    
    ---
    
    ## 重要数据说明
    
    - **数据更新时间**:每日15:00更新前一天的数据。15:00前最新可查为前天,15:00后为昨天
    - **数据来源**:红狐Hub 抖音短剧创作数据API
    - **平台范围**:固定为抖音平台短剧内容
    - **缓存策略**:1小时内查询结果可复用缓存,节省API积分
    
  • SKILL.md 16.9 KB
    ---
    name: playlet-douyin-feed
    description: "短剧-抖音信息源 — 每日扫描抖音短剧爆款内容,按点赞量筛选热门短剧,智能聚类题材方向后生成包含封面、互动数据与创作洞察的HTML日报。支持按题材(穿越/霸总/重生等)、达人、时间范围定向查询。⚠️查询前脚本先做输入校验:关键词需命中短剧题材词库(topic_keywords 中规定的题材名+全部相关词,如「打脸」命中逆袭题材相关词),命中后直接使用该关键词查询数据;不满足时提醒'关键词不满足查询条件'并推荐相关词,且**不发起接口请求**;查询无匹配数据或全量数据不足时先询问用户是否按推荐题材重新查询,确认后才可查询(不自动扩展、不自动降级全量)。日期超出有效查询范围时提醒并自动回退最近有数据日期,无需用户确认。当用户需要短剧抖音日报、抖音短剧爆款、短剧热点、短剧创作趋势或自定义题材查询时使用。"
    ---
    
    # 短剧-抖音信息源
    
    ## 简介
    
    短剧-抖音信息源是一款专为短剧创作者和MCN运营人员设计的抖音爆款内容追踪工具,每日自动扫描抖音短剧创作内容,按点赞量筛选爆款作品,智能聚类题材后生成HTML可视化日报。
    
    通过简单的自然语言指令,你可以:
    - 📊 获取每日抖音短剧爆款榜单与题材分布
    - 🏷️ 自动聚类穿越/霸总/重生/悬疑等题材方向
    - 📈 生成创作趋势分析报告(爆款标题特征、核心达人榜、新兴起量信号)
    - 🔔 开启每日订阅,日报自动产出
    
    适用于**短剧创作者选题、MCN机构运营、题材趋势研究**等需要每日追踪抖音短剧热点的场景。
    
    > **重要**:数据每日15:00更新前一天数据(实际可能延迟,以脚本真实探活为准)。查询前脚本先做输入校验:关键词需命中短剧题材词库(`topic_keywords` 中规定的**题材名+全部相关词**,如「打脸」命中逆袭题材相关词),**命中后直接使用该关键词查询数据**;不满足时提醒"关键词不满足查询条件"并推荐相关词,**不发起接口请求**。**无数据确认制**:查询无匹配数据或全量数据不足时,禁止自动发起任何额外查询(禁止自动降级全量、禁止自动扩展题材),先展示推荐题材关键词并询问用户,用户确认后才可发起查询;日期超出有效查询范围时提醒并自动回退最近有数据日期,无需用户确认。
    
    ## 功能特性
    
    ### 🎯 核心功能
    
    | 功能模块 | 能力描述 | 核心价值 |
    |---------|---------|----------|
    | 爆款发现 | 从抖音短剧中按点赞量筛选热门内容 | 精准定位高热度短剧作品 |
    | 题材聚类 | 自动识别题材方向(穿越/霸总/重生/悬疑等) | 每天题材分类由内容动态决定 |
    | 智能查询 | 默认查询全部短剧,关键词命中词库(题材名+相关词)直接查询,数据不足时提示推荐题材等待确认 | 节省接口额度,高效获取数据 |
    | 自定义查询 | 用户可指定任意题材/达人/关键词定向查询 | 灵活覆盖任意短剧细分方向 |
    | 创作洞察 | 分析爆款标题特征、题材趋势、达人表现 | 深度挖掘创作规律 |
    | 可视化日报 | 深色主题HTML,封面图+互动数据+作品直链 | 直观展示每日短剧热点 |
    | 一键订阅 | `--subscribe` 开启每日自动产出 | 日报自动攒在本地文件夹 |
    
    ### ✨ 特色亮点
    
    - ⚡ **探活式日期预检**:调用前先用轻量请求(无keyword, pageSize=1)真实探测目标日期是否有数据,替代纯本地时钟推断,自动拦截无效查询,避免浪费API额度
    - 🧭 **前置输入校验**:查询前先判断用户的分类/关键词是否符合短剧题材词库(题材名+全部相关词,命中后**直接使用该关键词查询数据**)、日期是否在有效查询范围;**不满足时提醒"关键词不满足查询条件"并推荐相关分类和关键词,不发起接口请求**
    - 🔄 **自动回退**:`--latest` 自动向前回退最多7天,找到最近有数据的日期再出日报,彻底告别"查到空就报错"
    - ⏸️ **无数据确认制**:查询无匹配数据或全量数据不足时,**不自动扩展题材、不自动降级全量**,提示推荐题材关键词并等待用户确认后再查询
    - 🧠 **结构化空因**:空结果时明确输出原因(数据源无数据/关键词无匹配/接口异常)并给出下一步建议
    - 🏷️ **智能题材聚类**:内置9大题材关键词库(穿越/霸总/重生/悬疑/甜宠/逆袭/年代/战神/古装),自动识别归类
    - 📊 **创作趋势报告**:自动分析新兴起量信号、爆款标题特征、核心达人榜
    - 🎨 **HTML深色主题日报**:封面图+互动数据+作品直链,美观直观,自动浏览器打开
    - 💰 **API积分优化**:批量查询+1小时缓存+探活拦截,节省接口调用额度
    
    ## 一键安装
    
    ### 前置条件
    
    - 已安装 Python 3 运行环境
    - 获取红狐Hub API Key
    
    ### API Key 获取
    
    数据查询接口通过请求头 `X-API-KEY` 鉴权,Key 从环境变量 `REDFOX_API_KEY` 获取。
    
    前往 [红狐Hub 官网](https://redfox.hk?source=github) 注册,登录后在个人中心获取,格式为 `ak_xxxxxxxx`。新注册用户获赠免费积分。
    
    ### 环境变量配置
    
    | 变量名 | 必填 | 说明 |
    |--------|------|------|
    | `REDFOX_API_KEY` | 是 | 红狐Hub API 访问密钥,格式 `ak_xxxxxxxx` |
    
    **配置方式**:
    
    - **macOS/Linux**:将 `export REDFOX_API_KEY=<值>` 追加到 `~/.zshrc` 或 `~/.bashrc`,然后 `source` 使其生效
    - **Windows**:`[Environment]::SetEnvironmentVariable("REDFOX_API_KEY", "<值>", "User")`(需重启终端)
    - 配置后验证:`echo $REDFOX_API_KEY`(macOS/Linux)或 `echo %REDFOX_API_KEY%`(Windows)
    
    
    ## 使用指南
    
    ### 基础使用
    
    #### 1. 查询每日短剧爆款日报
    
    直接告诉助手你想查看的日报:
    
    > 用户:查询今天的短剧抖音日报
    >
    > 助手:(执行 `--latest`,脚本自动回退定位最近有数据的日期,输出日报 + 题材概览 + 创作趋势分析)
    
    > **日期预检规则**:查询前先做前置校验——分类/关键词不符合短剧题材词库时提醒"关键词不满足查询条件"并推荐相关词,**不发起接口请求**;日期超出有效查询范围或未更新时,脚本自动向前回退获取最近时间范围数据,并明确告知"当前查询时间未更新或超过查询时间范围,已为您自动获取最近时间范围数据"。
    
    #### 2. 按题材定向查询
    
    指定你关注的题材方向:
    
    > 用户:查询穿越题材的短剧
    >
    > 助手:(生成穿越题材定向日报 + 趋势分析)
    
    #### 3. 查询历史日期
    
    > 用户:查询6月10日的短剧日报
    >
    > 助手:(历史日期已有数据,直接生成日报)
    
    ### 高级使用
    
    #### 4. 多题材组合查询
    
    ```bash
    python3 scripts/playlet_douyin_daily.py --topics "穿越,霸总,重生" --latest
    ```
    
    #### 5. 按时间范围查询
    
    ```bash
    python3 scripts/playlet_douyin_daily.py \
      --start-time "2026-06-01 00:00:00" \
      --end-time "2026-06-30 23:59:59"
    ```
    
    #### 6. 开启每日订阅
    
    ```bash
    python3 scripts/playlet_douyin_daily.py --subscribe
    ```
    
    #### 7. 使用缓存数据
    
    ```bash
    python3 scripts/playlet_douyin_daily.py --from-cache
    ```
    
    ### 常用命令速查
    
    | 命令 | 功能 |
    |------|------|
    | `--latest` | 生成最新一期日报(自动向前回退最多7天定位最近有数据的日期) |
    | `--date YYYY-MM-DD` | 生成指定日期日报(未更新时自动回退最近有数据的日期) |
    | `--topics "关键词"` | 自定义题材查询(逗号分隔;不满足短剧题材词时提醒+推荐,不请求接口) |
    | `--count N` | 扫描作品数量(默认200) |
    | `--subscribe` | 开启每日订阅 |
    | `--unsubscribe` | 关闭每日订阅 |
    | `--from-cache` | 使用缓存数据(1小时内有效) |
    | `--output-dir` | 自定义输出目录 |
    
    ### 完整参数说明
    
    | 参数 | 说明 | 默认值 |
    |------|------|--------|
    | `--topics` | 自定义题材关键词,逗号分隔。查询前先校验是否命中短剧题材词库(题材名+全部相关词,命中后直接使用该关键词查询),不满足时提醒+推荐相关词并**不请求接口**。默认查询全部短剧,数据不足时提示推荐题材并等待确认(**不自动扩展**);所有题材通过批量接口查询 | `短剧` |
    | `--count` | 扫描作品数量,满足即停 | `200` |
    | `--date` | 指定日期 YYYY-MM-DD(未更新或超范围时自动回退最近有数据的日期) | 今天 |
    | `--start-time` | 自定义开始时间 YYYY-MM-DD HH:MM:SS(覆盖 --date 推算) | — |
    | `--end-time` | 自定义结束时间 YYYY-MM-DD HH:MM:SS(覆盖 --date 推算) | — |
    | `--latest` | 自动向前回退最多7天,定位最近有数据的日期,跳过无数据区间,不扣积分 | — |
    | `--output-dir` | 输出目录 | `~/Downloads/QoderReports` |
    | `--api-key` | 指定 API Key | — |
    | `--subscribe` | 开启每日订阅 | — |
    | `--unsubscribe` | 关闭每日订阅 | — |
    
    ### 工作流程
    
    > 详细执行流程(日期预检规则、脚本调用、强制输出格式模板、题材聚类规则、创作趋势分析逻辑)请参阅 [core_workflow.md](references/core_workflow.md)
    
    工作流程分为三步:
    
    1. **第零步 — 输入与日期预检**:前置校验关键词是否命中短剧题材词库(题材名+全部相关词,命中后直接查询;不满足时提醒+推荐,**不请求接口**);日期按15:00规则估算起点后,用轻量请求**真实探活**目标日期是否有数据,无数据时自动向前回退最近有数据的日期。**查询无匹配数据或数据不足时,先展示推荐题材关键词并询问用户,用户确认后才可发起查询(无数据确认制)**
    2. **第一步 — 生成爆款日报**:执行 `playlet_douyin_daily.py` 脚本,支持 `--latest`、`--date`、`--topics` 等参数
    3. **第二步 — 执行创作趋势分析**:基于聚类结果自动分析TOP 5题材、爆款标题特征、核心达人榜,输出结构化趋势报告
    
    ### 自定义题材查询
    
    除默认短剧日报外,用户可指定任意题材组合进行定向查询:
    
    ```bash
    # 查询穿越题材热门短剧
    python3 scripts/playlet_douyin_daily.py --topics "穿越,时空,重生"
    
    # 查询霸总/甜宠题材
    python3 scripts/playlet_douyin_daily.py --topics "霸总,甜宠,总裁,虐恋"
    
    # 查询悬疑/反转题材
    python3 scripts/playlet_douyin_daily.py --topics "悬疑,推理,反转,惊悚"
    ```
    
    **自定义查询逻辑**:
    - **前置校验(v2.3)**:查询前先判断关键词是否命中短剧题材词库(`topic_keywords` 中规定的**题材名+全部相关词**,如「打脸」命中逆袭题材相关词、「总裁」命中霸总题材相关词)。**命中后直接使用该关键词查询数据**;不满足时明确提醒"关键词不满足短剧查询条件",并**推荐相关分类和关键词**(优先从无效词中提取题材词,再补热门题材),且**不发起接口请求**,引导用户改用推荐词查询
    - **无效词自动忽略**:混合词场景下保留有效关键词继续查询,无效关键词自动忽略并提示;全部无效时**不请求接口**,直接停止并推荐相关词
    - **无数据确认制(v2.3)**:用户指定的关键词/题材无匹配数据时,**禁止自动降级为全量查询**、禁止自动扩展题材,先提示推荐题材关键词并询问用户,用户确认后才可发起查询
    - 用户提供的所有题材通过批量接口一次性查询,无需逐个调用
    - 查询结果自动去重,题材聚类、趋势分析均基于查询结果生成,与用户关注方向强关联
    
    **题材关键词速查**:
    
    | 题材类型 | 典型关键词 |
    |---------|-----------|
    | 穿越 | 穿越、时空、古代、现代、回到 |
    | 霸总 | 霸总、总裁、豪门、冷酷 |
    | 重生 | 重生、逆袭、回到、翻盘 |
    | 悬疑 | 悬疑、推理、反转、惊悚、谜案 |
    | 甜宠 | 甜宠、恋爱、撒糖、甜蜜、宠溺 |
    | 逆袭 | 逆袭、翻身、打脸、崛起 |
    
    ## 使用场景
    
    ### 场景一:短剧创作者选题参考
    
    **角色**:短剧编剧/导演
    
    **需求**:了解当前哪些题材和标题模式最容易出爆款
    
    **使用方式**:
    1. 每日查询短剧爆款日报,查看题材概览
    2. 重点关注「新兴起量信号」和「爆款标题特征」
    3. 结合自身优势选择题材方向
    
    **预期收益**:精准把握流量风口,提升作品爆款概率
    
    ---
    
    ### 场景二:MCN机构运营管理
    
    **角色**:MCN运营总监
    
    **需求**:追踪旗下达人和竞品在抖音短剧赛道的表现
    
    **使用方式**:
    1. 查看「核心达人榜」了解头部达人动态
    2. 按题材定向查询,分析各赛道竞争格局
    3. 开启每日订阅,日报自动推送
    
    **预期收益**:提升运营决策效率,及时捕捉市场变化
    
    ---
    
    ### 场景三:题材趋势研究
    
    **角色**:内容分析师/投研人员
    
    **需求**:系统性分析抖音短剧题材分布和趋势变化
    
    **使用方式**:
    1. 按时间范围查询(如月度数据),观察题材占比变化
    2. 对比多个题材的互动数据和增长趋势
    3. 分析「跨题材对比建议」,发现融合机会
    
    **预期收益**:形成数据驱动的趋势判断,支撑投资决策
    
    ---
    
    ### 场景四:日常内容追踪
    
    **角色**:短剧爱好者/行业关注者
    
    **需求**:每天快速了解抖音短剧热点,无需手动分析
    
    **使用方式**:
    1. 开启订阅 `--subscribe`
    2. 每日自动生成日报,保存在 `~/Downloads/QoderReports/`
    
    **预期收益**:零成本追踪行业动态,省时省力
    
    ## 项目架构
    
    ### 目录结构
    
    ```
    短剧-抖音信息源/
    ├── SKILL.md                           # Skill核心说明文档
    ├── scripts/
    │   └── playlet_douyin_daily.py       # 日报生成脚本(含题材聚类+HTML生成)
    ├── references/
    │   ├── core_workflow.md              # 核心工作流程+输出格式+聚类规则+趋势分析
    │   └── examples.md                   # 使用示例与常见用法组合
    └── assets/
    ```
    
    ### 技术栈
    
    | 组件 | 技术 | 说明 |
    |------|------|------|
    | 运行环境 | Python 3 | 脚本执行环境 |
    | 数据接口 | 红狐Hub API | 抖音短剧创作数据,RESTful接口 |
    | 鉴权方式 | X-API-KEY | 请求头鉴权,环境变量配置 |
    | 输出格式 | HTML(深色主题) | 自动浏览器打开,响应式设计 |
    | 缓存策略 | JSON本地缓存 | 1小时有效期,路径 `~/.workbuddy/cache/` |
    
    ### 核心模块
    
    - **playlet_douyin_daily.py**:主执行脚本,集成API调用、题材聚类、HTML日报生成、创作趋势分析,支持日期智能判断(15:00规则)、批量查询去重、浏览器自动预览
    
    ## 常见问答
    
    ### 安装相关
    
    **Q: 提示 "缺少 API Key" 怎么办?**
    
    A: 请确认已正确配置环境变量 `REDFOX_API_KEY`:
    1. 前往 [红狐Hub](https://redfox.hk?source=github) 注册并获取 API Key(格式 `ak_xxxxxxxx`)
    2. Windows:`[Environment]::SetEnvironmentVariable("REDFOX_API_KEY", "ak_xxx", "User")`
    3. 重启终端后验证:`echo %REDFOX_API_KEY%`
    
    **Q: API Key 无效或过期?**
    
    A: 登录红狐Hub个人中心检查Key状态,确认Key未过期且账户积分充足。
    
    ### 使用相关
    
    **Q: 数据什么时候更新?**
    
    A: 每日15:00更新前一天的数据(实际可能延迟,以脚本真实探活为准)。15:00前最新可查为前天,15:00后为昨天。
    
    **Q: 查询时提示"数据尚未更新"?**
    
    A: 脚本会自动向前回退获取最近时间范围数据,并明确告知"当前查询时间未更新或超过查询时间范围,已为您自动获取最近时间范围数据",无需手动处理。
    
    **Q: 支持哪些题材查询?**
    
    A: 内置9大题材(穿越/霸总/重生/悬疑/甜宠/逆袭/年代/战神/古装),同时支持任意自定义关键词查询。但查询前会先校验关键词是否符合短剧题材词库,不满足时提醒+推荐相关词,**不请求接口**,避免浪费API额度。
    
    **Q: 如何节省API积分?**
    
    A: 使用 `--from-cache` 复用1小时内缓存;使用 `--latest` 自动回退到有数据的日期;探活式预检拦截无数据日期;关键词不满足题材词库时不发起接口请求。
    
    ### 故障排除
    
    **Q: 脚本报错 "UnicodeEncodeError: 'gbk' codec"?**
    
    A: Windows终端编码问题,执行前设置环境变量:`$env:PYTHONIOENCODING="utf-8"`
    
    **Q: HTML日报没有自动打开?**
    
    A: 确认系统默认浏览器已正确设置,日报文件始终保存在 `~/Downloads/QoderReports/` 目录下,可手动打开。
    
    **Q: API常见错误码?**
    
    | 错误码 | 说明 | 解决方式 |
    |--------|------|---------|
    | 1002 | 每页条数超过200 | 脚本已自动限制,无需处理 |
    | 3106 | 缺少API Key | 配置环境变量 `REDFOX_API_KEY` |
    | 3107 | API Key无效 | 检查Key格式和有效性 |
    | 3108 | 请求过于频繁 | 等待后重试 |
    | 3109 | 今日调用达上限 | 次日再试 |
    | 3201 | 积分不足 | 前往红狐Hub充值 |
    
    ## 参考文档
    
    - [core_workflow.md](references/core_workflow.md) — 核心执行流程、输出格式模板、日期判断逻辑、题材聚类规则、创作趋势分析逻辑
    - [examples.md](references/examples.md) — 使用示例与常见用法组合
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related