コンテンツ制作 · zarazhangrui/youtube-to-ebook

youtube-to-ebook

YouTube動画を文字起こし付きの美しく整形された電子書籍記事に変換します

L4 高権限 · 段階的認可と監査V1 コミット固定コマンド実行依存関係インストールネットワーク通信ファイル書き込みDual-pass Audit
Author zarazhangruiGitHub zarazhangrui/youtube-to-ebook ↗Stars 555License 仓库内无 LICENSE 文件;README 自述 'MIT - Use freely, modify as needed.',但 GitHub API license=null(spdx 未识别)——元数据与仓库实际文件不一致Commit 7f67fd6b55
管理レベルは Skill の能力と推奨統制措置を示すものであり、品質や安全性の認証、利用可否の判定ではありません。使用の可否はユーザーおよび管理者が決定します。Agent 環境によって制約が異なるため、自動保護を仮定しないでください。第三者製 Skill の著作権は原著作者に帰属し、利用リスクは利用者が負います。

1仕組みと動作原理

4つのステージを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にある4コマのフロー図(Fetch Videos → Get Transcripts → Write Articles → Create EPUB)をそのまま実装したものです。

動画検出にはsearch(『時系列順が保証されない』問題を回避するため)ではなくYouTube Data APIのuploadsプレイリストを使用し、/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として記載しています。

文字起こしの取得は、YouTube公式やオープンソースライブラリではなく、実質的にサードパーティの商用API(Supadata)に依存しています。コード内のコメントには『YouTubeによるBANを回避するため』と明記されています。

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件ごとに1秒のsleepを入れてレート制限を行っています('if i < len(videos) - 1: time.sleep(1)')。

『雑誌風リライト』はコード内にハードコードされた長文プロンプトです。タイトルの刷新、非専門家向けの表現、専門用語の解説、直感に反する見解や名言の保持を指示し、動画を観なくても読める独立した記事の作成を明確に要求しています。

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.
同じプロンプト内で、動画のタイトルと説明文を用いて文字起こしの誤字脱字を修正するよう指示しており(SKILL.md Pitfall #5)、モデルはclaude-sonnet-4-20250514、max_tokens=8000にハードコードされています。dashboard.pyの『Writing Style』パネルは、ソースファイル内のこのプロンプト記述を正規表現で直接置換します(PROMPT_FILE = PROJECT_DIR / "write_articles.py")。

成果物は正規のEPUB(ebooklibによる目次・章・CSS・読書順の生成)であり、GmailのSMTP_SSL 465番ポート経由で添付ファイルとして送信されます。同時にnewsletters/ディレクトリへHTML/EPUB/JSON形式でアーカイブされます。

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側ではこのファイルをアーティファクトとしてアップロード/ダウンロードしており(retention-days: 90)、これがCI環境で状態を保持する唯一の手段となっています。

2種類の自動化ルートがリポジトリ内に用意されています。macOSのlaunchd(plist + ログリダイレクト付きrun_newsletter.sh)と、毎週水曜日に定期実行されるGitHub Actions(4つのシークレットを使用)です。

.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では『クラウドサーバーはBANされるためローカル実行を推奨』と記載されています。

付属のStreamlitダッシュボードは『ソースコードを直接書き換える』設計です。チャンネル一覧はget_videos.py、執筆スタイルはwrite_articles.py内のプロンプト、スケジュール設定は~/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")])で行われます。したがって、ダッシュボードはmacOSかつstreamlitがインストールされている環境でのみ動作します(requirements.txtにはstreamlitの記載がありません)。

2コア機能

01チャンネルごとに最新の長編動画を一括取得し、Shortsを自動で除外
02Supadata経由でYouTubeの文字起こしテキストを安定取得(レート制限およびエラーハンドリングを含む)
03Claudeを使用して文字起こしを単体で読める雑誌風の長文記事にリライト(タイトル刷新、用語解説、名言の保持)
04目次、CSS、章構成を備えた標準EPUB電子書籍の生成
05Gmail経由での配信(EPUB添付 + スタイリングされたHTML本文)および過去ログのローカル保存
06差分処理と重複排除:processed_videos.jsonに送信済み動画を記録し、二重配信を防止
07macOSスケジュール自動化(launchd plistにより週1回の定時にローカルパイプラインを実行)
08Streamlit管理ダッシュボード:チャンネル管理、執筆スタイルの編集、パイプラインの手動実行、過去の配信ログおよび処理済み動画の閲覧

3外部依存関係

KindDependency / Endpoint
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推奨統制措置と判定根拠

安全運用のための推奨統制措置
Agent 向け
段階的・都度認可、サンドボックス推奨
管理者向け
承認フロー、ロールバック可能、全履歴監査
判定根拠(ファイルパスと抜粋)
  • scripts/: ローカルスクリプトまたはコマンドの実行: python main.py(メインパイプライン。README/SKILL.mdのエントリポイント)
  • scripts/ 或 SKILL.md: 外部言語パッケージの動的インストールの宣言あり
  • 网络端点: 外部ネットワークアクセスの宣言あり: googleapis.com YouTube Data API v3(channels.list / playlistItems.list、APIキー使用)
  • 本地工作目录: ファイル読み書きおよび成果物の出力: processed_videos.json(プロジェクトディレクトリ内で処理済み動画を記録)
監査ノート: 統一判定基準による評価:本スキルは4〜5種類の認証情報(YouTube APIキー、Anthropicキー、Supadataキー、Gmailアカウント+アプリパスワード、CIシークレット)を読み取ります。特にGmailアプリパスワードはメールボックスへの書き込み権限を持つ長期認証情報です。またワークフローは文字起こしや説明文をサードパーティ(Supadata商用API、Anthropic)へ外部送信するため、『認証情報/環境変数の読み取りを伴い、サードパーティサービスに依存する』に該当します。外部送信先は想定通りの公式・商用エンドポイントであり、TLSのダウングレードやスクレイピング対策の回避、任意のコード実行リスク(サブプロセスは固定のargvを持つpython3/launchctlのみ実行)はないため、Red評価には引き上げません。ただし、認証情報の集中管理、メール送信機能、サブプロセスによるスケジュール管理を伴うため、一般的なテキスト生成スキルよりも運用時の影響度は高くなります。

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つのモジュール、トラッカー、メール配信、EPUB生成、Streamlitコンソール、launchd/CIの二重自動化を備えたエンドツーエンドの完結性。
  • 現場の知見がコードとドキュメントに反映済み:/shorts/リダイレクトによるShorts判定、uploadsプレイリストによる時系列保証、BANを回避するための外部API経由の文字起こし取得。
  • 適切な重複排除と差分更新:メール送信成功後に初めて処理済みとしてマークされるため、二重送信や配信未記録の不整合を回避。
  • 明確な編集方針を持つ執筆プロンプト:タイトルの変更、非専門家向けの解説、用語説明、洞察や名言の維持、メタデータによる誤字脱字修正、『In this video』のような動画前提表現の明示的な禁止。
  • 充実した成果物形態:目次/CSS/読書順を備えた本格的なEPUB、HTML本文と添付ファイルが揃ったメール、アーカイブとして振り返り可能なローカル保存。
  • 最適な用途: おすすめ:フォローしている技術チャンネルを『週刊ダイジェスト』としてEPUB化し、自分宛てに配信したい個人または小規模チーム(デフォルトの宛先はGMAIL_ADDRESS自身)。4〜5個のAPIキーや認証情報を管理でき、macOS上でlaunchdを用いた定期実行を行い、外部商用API経由での文字起こし取得を許容できるユーザー。また、コード構造が明確で各ステージが疎結合なため、『YouTube → コンテンツ生成 → 電子書籍化』パイプラインのリファレンス実装としてカスタマイズしたい用途にも適しています。
    不向きな用途: 非推奨:文字起こしデータを外部の商用APIに送信できないコンプライアンス要件のある環境。ローカルの.envファイルに長期的な認証情報(Gmailアプリパスワード+各種APIキー)を保持したくないユーザー。macOS以外の環境のユーザー(plist、launchctl、READMEのトラブルシューティングがMac前提となっており、スケジュール機能が動作しません)。ドキュメント通りに進めればすぐ動くと期待している層(コードとドキュメントに多数の乖離があり、実際の実装確認が必須)。厳密な時系列順序、多言語字幕、あるいは大規模なチャンネル監視を求めるシナリオ(各チャンネルから最新1本の長編動画のみを取得する仕様であり、字幕の精度はSupadataに依存します)。
    Install Agent-Ready
    ① Verified Mirror 2026-09-15
    Option A · Direct DownloadDownload youtube-to-ebook.tar.gz
    sha256: 2f7c07f82fd3668b…
    Option B · Copy Guide for Agent
    install.json
    Agent はこの JSON ガイドから自動的にダウンロードして検証します。
    ② Upstream GitHub
    GitHub Repo ↗
    Pinned to commit 7f67fd6b55.