全部技能 / 内容创作 / youtube-to-ebook
内容创作 · zarazhangrui/youtube-to-ebook

youtube-to-ebook

Transform YouTube videos into beautifully formatted ebook articles with transcripts

风险提醒:橙色 · 评估后使用AI 侦查报告
作者 zarazhangruiGitHub zarazhangrui/youtube-to-ebook ↗Stars 555许可 仓库内无 LICENSE 文件;README 自述 'MIT - Use freely, modify as needed.',但 GitHub API license=null(spdx 未识别)——元数据与仓库实际文件不一致commit 7f67fd6b55
agent 宿主通常会约束 skill 执行权限;风险提醒为 AI 侦查观点,不构成质量或安全保证。第三方 skill 仅作拆解与展示,安装使用风险自负,版权归原作者。

1实现原理 · 为什么它能做到

四个阶段串成一条本地流水线(拉视频 → 取字幕 → Claude 改写成杂志文 → 打包 EPUB 并邮件投递),main.py 只是顺序调用,不含任何分支逻辑。

main.py
from get_videos import main as fetch_videos from get_transcripts import get_transcripts_for_videos from write_articles import write_articles_for_videos from send_email import send_newsletter from video_tracker import filter_new_videos, mark_videos_processed, get_processed_count
注:run() 里依次执行 STEP 1-5,只在每步产出非空时才继续,最后一步成功后 mark_videos_processed()。这正是 SKILL.md 那张四格流程图(Fetch Videos → Get Transcripts → Write Articles → Create EPUB)的逐字实现。

视频发现走 YouTube Data API 的 uploads 播放列表而非 search(规避『search 不保证时间序』),并靠 /shorts/ URL 重定向判定短视频。

get_videos.py
# Get the 15 most recent videos from the uploads playlist # The uploads playlist is always in exact upload order (newest first)
注:配套实现:get_channel_info 用 channels().list(part="snippet,contentDetails", forHandle=handle) 取 relatedPlaylists.uploads;is_youtube_short 用 requests.head('https://www.youtube.com/shorts/{id}', allow_redirects=True) 看最终 URL 是否仍含 /shorts/。SKILL.md 把这两条列成『Known Pitfalls & Solutions』#1/#2。

字幕获取现实上依赖第三方商业 API(Supadata),不是 YouTube 官方或开源库——代码注释明确说这是为了『不被 YouTube 封』。

get_transcripts.py
This script uses the Supadata API to fetch transcripts reliably. Unlike the youtube-transcript-api library, Supadata doesn't get blocked by YouTube.
注:调用形式:requests.get(SUPADATA_TRANSCRIPT_URL, params={'url': youtube_url, 'text': 'true'}, headers={'x-api-key': SUPADATA_API_KEY}, timeout=60),端点 https://api.supadata.ai/v1/transcript;逐条 1 秒 sleep 做限速('if i < len(videos) - 1: time.sleep(1)')。

『杂志文改写』是一个写死在代码里的长 prompt:要求更换标题、面向非专业读者、解释术语、保留反直觉观点与金句,并明确要求写成不看视频也能读懂的独立文章。

write_articles.py
- Do NOT include phrases like "In this video" - write it as a standalone article. Assume the reader has not watched the video and has zero context about it. This article is meant to be as a replacement, not complement, for watching the video.
注:同一 prompt 还要求用视频标题+描述纠正转写错误(SKILL.md Pitfall #5),模型硬编码 claude-sonnet-4-20250514、max_tokens=8000;dashboard.py 的『Writing Style』面板直接在源文件里正则替换这段 prompt(PROMPT_FILE = PROJECT_DIR / "write_articles.py")。

成品是真正的 EPUB(ebooklib 生成目录+章节+CSS+阅读顺序)并通过 Gmail SMTP_SSL 465 以附件投递,同时留档 HTML/EPUB/JSON 到 newsletters/。

send_email.py
# Create the ebook book = epub.EpubBook()
注:发送:'with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server: server.login(GMAIL_ADDRESS, GMAIL_APP_PASSWORD); server.sendmail(...)';发送成功后 save_newsletter_archive() 写 newsletters/newsletter_<ts>.{html,epub,json},再 os.remove(epub_path) 清理临时文件。文件名 youtube_digest_YYYYMMDD.epub。

去重靠本地 JSON 追踪器:已处理视频 ID 落盘 processed_videos.json,只处理未见过的新视频,且仅在邮件发送成功后才标记。

video_tracker.py
TRACKER_FILE = os.path.join(os.path.dirname(__file__), "processed_videos.json")
注:main.py 对应 'new_videos = filter_new_videos(videos)' 与 'if success: mark_videos_processed(videos_with_transcripts)';GitHub Actions 侧把该文件当 artifact 上传/下载(retention-days: 90),是它在 CI 环境里保持状态的唯一方式。

可选的两条自动化通道都写在仓库里:macOS launchd(plist + run_newsletter.sh 重定向日志)与 GitHub Actions 每周三定时跑(含 4 个 secrets)。

.github/workflows/newsletter.yml
env: YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GMAIL_ADDRESS: ${{ secrets.GMAIL_ADDRESS }} GMAIL_APP_PASSWORD: ${{ secrets.GMAIL_APP_PASSWORD }} SUPADATA_API_KEY: ${{ secrets.SUPADATA_API_KEY }}
注:触发为 'cron: \'0 15 * * 3\''(周三 15:00 UTC)+ workflow_dispatch;本机通道见 com.youtube.newsletter.plist 与 run_newsletter.sh('python3 /Users/bytedance/youtube-newsletter/main.py >> …/logs/newsletter.log 2>&1')。两条通道并存,而 SKILL.md/README 都声称『云服务器会被封,请本地跑』。

附带的 Streamlit 管理台是『直接改源码文件』的实现:频道列表改 get_videos.py、写作风格改 write_articles.py 内的 prompt、排期改 ~/Library/LaunchAgents 下的 plist 并调 launchctl 重载。

dashboard.py
CHANNELS_FILE = PROJECT_DIR / "get_videos.py" PROMPT_FILE = PROJECT_DIR / "write_articles.py"
注:排期保存走 'subprocess.run(["launchctl", "bootout", f"gui/{os.getuid()}", str(PLIST_FILE)], capture_output=True)' 与 bootstrap;按钮触发流水线走 subprocess.run(["python3", str(PROJECT_DIR / "main.py")])。这意味着 dashboard 只在 macOS 且装了 streamlit 时可用(requirements.txt 里没有 streamlit)。

2核心能力

01按频道批量抓取最新长视频并自动跳过 Shorts
02经 Supadata 稳定获取 YouTube 字幕纯文本(含限速与错误码分支)
03用 Claude 把字幕改写成可独立阅读的杂志风长文(含标题重写、术语解释、金句保留)
04生成带目录/CSS/章节的 EPUB 电子书
05Gmail 邮件投递(EPUB 作附件 + 排版好的 HTML 正文)并可留档回看
06增量处理与去重:processed_videos.json 记录已发视频,避免重复推送
07macOS 定时自动化(launchd plist,每周固定时刻跑本地管道)
08Streamlit 图形管理台:频道增删、写作风格编辑、手动跑流水线、浏览历史 newsletter 与已处理视频

3外部依赖

类型依赖
apiYouTube Data API v3(Google)
networkYouTube Shorts URL 探测(HEAD 请求判重定向)
apiSupadata 字幕 API(第三方商业服务)
apiAnthropic Claude API(文章改写)
networkGmail SMTP(投递 EPUB 附件)
packagePython 依赖(含一个已不再被代码使用的库)
packagestreamlit(dashboard 专用,未列入 requirements.txt)
clilaunchctl(macOS 排期,dashboard 内以子进程调用)
networkGoogle Fonts(dashboard 页面样式 @import,浏览器侧发起)

4风险提醒 风险提醒:橙色 · 评估后使用

风险提醒:橙色 · 评估后使用
  • 凭证面集中且含长期邮箱凭证 — 需同时提供 YouTube/Anthropic/Supadata 三把 key 与 Gmail 账号+应用专用密码;.env 若被误提交或被 CI secrets 泄露,影响面含可发信邮箱。Gmail 应用密码应单独申请、限定用途,并定期轮换。
  • 第三方内容出境 — 字幕与视频描述会发给 Supadata(商业服务,付费/配额另有条款)与 Anthropic;Supadata 还持有可代表你身份调用的 x-api-key。对敏感频道应重新评估是否接受该链路。
  • 间接 prompt injection 直通你的邮箱 — 被监控频道的字幕/描述是外部不可信文本,被原样拼进写作 prompt;生成文章会以邮件投递并留档,若视频内容含指令性文本可污染产出(属内容可信度风险,非代码执行)。
  • 文档漂移会让按文档操作者失败 — channels.txt、`python main.py --channels`、youtube-transcript-api 用法、『不要用 GitHub Actions』均与仓库实际不符;.env.example 缺 SUPADATA_API_KEY。新用户照 README 走会在字幕阶段静默失败(脚本打印 ⚠ 后返回 None)。
  • dashboard 直接改写源码与 plist — 频道/风格/排期编辑会重写 get_videos.py、write_articles.py 与 ~/Library/LaunchAgents/com.youtube.newsletter.plist 并重载 launchd;在仓库工作区内改代码可能被 git 提交或覆盖,且排期改动对本机影响持久。
  • 平台与合规边界 — YouTube 条款对自动抓取字幕(尤其经第三方中转)与再分发视频内容有约束;把整段视频内容改写为文章并邮件分发,需自行确认所监控频道的版权与授权,README/SKILL.md 未涉及此点。
风险提醒:橙色,评估后使用。按统一分档:本 skill 读取 4-5 项凭证(YouTube API key、Anthropic key、Supadata key、Gmail 账号+应用专用密码、CI secrets),其中 Gmail 应用密码是可写邮箱账户的长期凭证;工作流会把字幕与描述外发到第三方(Supadata 商业 API、Anthropic),属『涉及凭证/环境变量读取 + 依赖第三方服务』。外发对象均为可预期的官方/商业端点,无 TLS 降级、无反爬绕过、无任意代码执行面(子进程仅固定 argv 的 python3/launchctl),故不升红;但凭证集中、邮件投递与子进程排期使落地后果比普通生成类 skill 更重。

5第二遍独立确认

  • [ok] 四阶段流水线与去重逻辑 — main.py 五个 import 与方法体逐行核对;video_tracker.py 的 load/save/is_video_processed/mark_video_processed 与 processed_videos.json 路径均存在。
  • [discrepancy] 字幕来自 Supadata(而非 SKILL.md 所述 youtube-transcript-api) — 代码侧唯一实现是 Supadata(get_transcripts.py 顶部注释与 SUPADATA_TRANSCRIPT_URL 常量);但 SKILL.md『Known Pitfalls』#3 仍在教 'ytt_api = YouTubeTranscriptApi(); transcript = ytt_api.fetch(video_id)',requirements.txt 也仍列 youtube-transcript-api——全仓 .py 无一处 import youtube_transcript_api。文档描述的是已被替换掉的旧实现。
  • [discrepancy] 『云服务会被 YouTube 封,请本地跑』与仓库自带 Actions 工作流 — SKILL.md『### 6. Cloud Automation Blocked … Solution: Run automation locally on your Mac using launchd』、README『| Cloud servers blocked | Run locally, not GitHub Actions |』,但 .github/workflows/newsletter.yml 仍是完整的 ubuntu-latest 定时任务(cron '0 15 * * 3'),且注入 5 个 secrets。两种说法不能同时成立;从代码看 Supadata 正是为绕开该封锁而引入('Supadata doesn't get blocked by YouTube'),故云路径更可能是『能跑但不保证字幕』。
  • [discrepancy] 配置入口 channels.txt 与 `python main.py --channels` — SKILL.md 命令表写 '| `python main.py --channels` | Edit channel list |'、Key Files 与 README 结构表都列 channels.txt;实际 main.py 无 argparse(全仓 .py 无 argparse 引用),频道是 get_videos.py 的 CHANNELS 常量,dashboard 通过 CHANNELS_FILE = get_videos.py 改源码。channels.txt 在仓库中不存在。
  • [discrepancy] SUPADATA_API_KEY 的文档覆盖 — .env.example 只有 YOUTUBE_API_KEY / ANTHROPIC_API_KEY / GMAIL_ADDRESS / GMAIL_APP_PASSWORD 四项,而 get_transcripts.py 在无该 key 时直接返回 None(『⚠ SUPADATA_API_KEY not set in .env file』)导致流水线无字幕。文档缺失这把必需 key(CI workflow 里反而有 secrets.SUPADATA_API_KEY)。
  • [discrepancy] 许可声明一致性 — README 末段写 'MIT - Use freely, modify as needed.',但 git ls-tree 无 LICENSE 文件,gh api license=null(spdx 未识别)。已在 meta.license 如实标注冲突,未按 MIT 硬写。
  • [ok] 邮件投递与留档细节 — SMTP_SSL('smtp.gmail.com', 465) + server.login + sendmail;MIMEBase('application','epub+zip') 附件;save_newsletter_archive 写 html/epub/json 三件套;成功后 os.remove 临时 EPUB——与 SKILL.md『Email Delivery (Optional)』一致。
  • [ok] dashboard 的破坏性写回(源码/plist) — save_channels 重写 get_videos.py 的 CHANNELS 块、风格保存重写 write_articles.py 内 prompt、save_schedule 重写 ~/Library/LaunchAgents plist 并 launchctl bootout/bootstrap —— 已在 file_writes 与 injection_surface 记入;另确认 requirements.txt 未含 streamlit(README 要求单独 pip install)。

6结论

  • 是能直接跑起来的小产品而非示例:6 个模块 + tracker + 邮件投递 + EPUB 生成 + Streamlit 控制台 + launchd/CI 两条自动化,端到端闭环。
  • 踩坑经验被固化成代码与文档:Shorts 用 /shorts/ 重定向判定、视频发现改用 uploads 播放列表保序、字幕改用不被封的第三方通道。
  • 去重与增量处理做对了:只在邮件成功后才标记已处理,避免重复推送与『发了但没记』的不一致。
  • 写作 prompt 有明确编辑标准:重写标题、面向非专业读者、解释术语、保留反直觉洞见与金句、用标题/描述纠正转写错误、明确禁止『In this video』式补充性写法。
  • 产物形态完整:EPUB 有目录/CSS/阅读顺序,邮件正文与附件都在,且历史 newsletter 留档可回看。
  • 适合:适合:个人或小团队想把自己关注的技术频道『每周一读』成一本 EPUB 推给自己(默认收件人就是 GMAIL_ADDRESS 本人);愿意维护 4-5 把 key、在 macOS 上以 launchd 定时跑、并接受字幕经第三方 API 中转的用户;也适合作为『YouTube → 内容 → 电子书』流水线的参考实现来改造(代码结构清晰、阶段解耦)。
    不适合:不适合:不愿把字幕数据交给第三方商业 API 的合规场景;不希望长期凭证出现在本机 .env 的用户(Gmail 应用密码 + 三类 API key);无 macOS 的用户(plist/launchctl/README 排障路径均为 Mac 假设,排期功能不可用);期望开箱即用、照文档即可跑通的人(文档与代码多处漂移,需先核对 actual code);以及需要精确时间序/多语言字幕/大规模频道监控的场景——它只取每个频道最新一条长视频,字幕质量取决于 Supadata。
    安装 agent 直装可复制
    ① 本站镜像 更新 2026-09-15
    方式 A · 人下载镜像包下载 youtube-to-ebook.tar.gz
    sha256: 2f7c07f82fd3668b…
    方式 B · JSON 格式安装指南,复制给 agent
    安装指南
    agent 读 JSON 指南后会自动从本站下载安装,无需更多说明。
    ② 上游 GitHub · 原始来源
    能访问 GitHub?直接去上游安装(实时版,可能已更新)GitHub 原始 ↗
    本页镜像锁定 commit 7f67fd6b55;上游为实时仓库。
    来源信息 GitHub 原始
    作者 / 仓库zarazhangrui / zarazhangrui/youtube-to-ebook
    Stars555
    最近推送2026-01-28
    本 skill commit7f67fd6b55
    许可仓库内无 LICENSE 文件;README 自述 'MIT - Use freely, modify as needed.',但 GitHub API license=null(spdx 未识别)——元数据与仓库实际文件不一致
    本站信息
    收录日期2026-09-06
    分类内容创作
    侦查报告AI 侦查 · 2 遍 · 2026-09-06
    本站镜像与 GitHub 原始是不同来源:本站锁定 commit 快照经 /r2 分发;GitHub 为实时上游,内容可能已更新。
    同分类邻近