video

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Video & Podcast Digest Skill

视频&播客摘要 Skill

Send a video/podcast link → get full transcript + structured summary
发送视频/播客链接 → 获取完整转录稿 + 结构化摘要

Supported Platforms

支持的平台

PlatformTypeSubtitlesWhisper Transcription
YouTubeVideo
BilibiliVideo
X/TwitterVideo
Xiaoyuzhou (小宇宙)Podcast
Apple PodcastsPodcast
Direct links (mp3/mp4/m3u8)Any
平台类型字幕支持Whisper转录支持
YouTube视频
Bilibili视频
X/Twitter视频
小宇宙 (Xiaoyuzhou)播客
Apple Podcasts播客
直链 (mp3/mp4/m3u8)任意

Trigger

触发条件

Auto-triggered when a media URL is detected:
  • YouTube:
    youtube.com
    ,
    youtu.be
  • Bilibili:
    bilibili.com
    ,
    b23.tv
  • X/Twitter:
    x.com
    ,
    twitter.com
    (tweets with video)
  • Xiaoyuzhou:
    xiaoyuzhoufm.com
  • Apple Podcasts:
    podcasts.apple.com
  • Direct:
    .mp3
    ,
    .mp4
    ,
    .m3u8
    ,
    .m4a
    ,
    .webm
检测到媒体URL时自动触发:
  • YouTube:
    youtube.com
    ,
    youtu.be
  • Bilibili:
    bilibili.com
    ,
    b23.tv
  • X/Twitter:
    x.com
    ,
    twitter.com
    (带视频的推文)
  • 小宇宙:
    xiaoyuzhoufm.com
  • Apple Podcasts:
    podcasts.apple.com
  • 直链:
    .mp3
    ,
    .mp4
    ,
    .m3u8
    ,
    .m4a
    ,
    .webm

Pipeline

处理流程

Step 0: Detect Media Type

步骤0:检测媒体类型

URL PatternTypePipeline
xiaoyuzhoufm.com/episode/
Podcast→ Step 1b (Xiaoyuzhou)
podcasts.apple.com
Podcast→ Step 1c (Apple)
bilibili.com
,
b23.tv
Video→ Step 1d (Bilibili API)
.mp3
,
.m4a
direct link
Audio→ Step 2b (direct download)
OtherVideo→ Step 1a (subtitle extraction)
URL匹配规则类型后续流程
xiaoyuzhoufm.com/episode/
播客 → 步骤1b(小宇宙处理)
podcasts.apple.com
播客 → 步骤1c(Apple播客处理)
bilibili.com
,
b23.tv
视频 → 步骤1d(Bilibili API处理)
.mp3
,
.m4a
直链
音频 → 步骤2b(直接下载)
其他视频 → 步骤1a(字幕提取)

Step 1a: Video — Extract Subtitles

步骤1a:视频——提取字幕

bash
rm -f /tmp/media_sub*.vtt /tmp/media_audio.mp3 /tmp/media_transcript*.json /tmp/media_segment_*.mp3 2>/dev/null || true
bash
rm -f /tmp/media_sub*.vtt /tmp/media_audio.mp3 /tmp/media_transcript*.json /tmp/media_segment_*.mp3 2>/dev/null || true

YouTube (prefer English, fallback Chinese)

YouTube (优先英语字幕, fallback到中文)

yt-dlp --skip-download --write-auto-sub --sub-lang "en,zh-Hans" -o "/tmp/media_sub" "VIDEO_URL"
yt-dlp --skip-download --write-auto-sub --sub-lang "en,zh-Hans" -o "/tmp/media_sub" "VIDEO_URL"

Bilibili

Bilibili

yt-dlp --skip-download --write-auto-sub --sub-lang "zh-Hans,zh" -o "/tmp/media_sub" "VIDEO_URL"

Check for subtitles:
```bash
ls /tmp/media_sub*.vtt 2>/dev/null
  • Has subtitles → Read VTT content, skip to Step 3
  • No subtitles → Step 2a (download audio)
yt-dlp --skip-download --write-auto-sub --sub-lang "zh-Hans,zh" -o "/tmp/media_sub" "VIDEO_URL"

检查字幕是否存在:
```bash
ls /tmp/media_sub*.vtt 2>/dev/null
  • 存在字幕 → 读取VTT内容,直接跳转到步骤3
  • 无字幕 → 进入步骤2a(下载音频)

Step 1b: Xiaoyuzhou (小宇宙) — Extract Audio URL

步骤1b:小宇宙——提取音频链接

bash
AUDIO_URL=$(curl -sL -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" \
  "EPISODE_URL" \
  | grep -oE 'https://media\.xyzcdn\.net/[^"]+\.(m4a|mp3)' \
  | head -1)

echo "Audio URL: $AUDIO_URL"
curl -L -o /tmp/media_audio.mp3 "$AUDIO_URL"
→ Step 2b
bash
AUDIO_URL=$(curl -sL -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" \
  "EPISODE_URL" \
  | grep -oE 'https://media\.xyzcdn\.net/[^"]+\.(m4a|mp3)' \
  | head -1)

echo "Audio URL: $AUDIO_URL"
curl -L -o /tmp/media_audio.mp3 "$AUDIO_URL"
→ 进入步骤2b

Step 1c: Apple Podcasts — via yt-dlp

步骤1c:Apple Podcasts——通过yt-dlp处理

bash
yt-dlp -f "ba[ext=m4a]/ba/b" --extract-audio --audio-format mp3 --audio-quality 5 \
  -o "/tmp/media_audio.%(ext)s" "APPLE_PODCAST_URL"
→ Step 2b
bash
yt-dlp -f "ba[ext=m4a]/ba/b" --extract-audio --audio-format mp3 --audio-quality 5 \
  -o "/tmp/media_audio.%(ext)s" "APPLE_PODCAST_URL"
→ 进入步骤2b

Step 1d: Bilibili — API Direct Audio Stream

步骤1d:Bilibili——API直接获取音频流

bash
BV="BV1xxxxx"

curl -s "https://api.bilibili.com/x/web-interface/view?bvid=$BV" \
  -H "User-Agent: Mozilla/5.0" -H "Referer: https://www.bilibili.com/" \
  | python3 -c "import json,sys; d=json.load(sys.stdin)['data']; print(f\"Title: {d['title']}\nDuration: {d['duration']}s\nCID: {d['cid']}\")"

CID=<CID from previous step>
AUDIO_URL=$(curl -s "https://api.bilibili.com/x/player/playurl?bvid=$BV&cid=$CID&fnval=16&qn=64" \
  -H "User-Agent: Mozilla/5.0" -H "Referer: https://www.bilibili.com/" \
  | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['dash']['audio'][0]['baseUrl'])")

curl -L -o /tmp/media_audio.m4s \
  -H "User-Agent: Mozilla/5.0" -H "Referer: https://www.bilibili.com/" "$AUDIO_URL"
ffmpeg -y -i /tmp/media_audio.m4s -acodec libmp3lame -q:a 5 /tmp/media_audio.mp3
→ Step 2b
bash
BV="BV1xxxxx"

curl -s "https://api.bilibili.com/x/web-interface/view?bvid=$BV" \
  -H "User-Agent: Mozilla/5.0" -H "Referer: https://www.bilibili.com/" \
  | python3 -c "import json,sys; d=json.load(sys.stdin)['data']; print(f\"Title: {d['title']}\nDuration: {d['duration']}s\nCID: {d['cid']}\")"

CID=<CID from previous step>
AUDIO_URL=$(curl -s "https://api.bilibili.com/x/player/playurl?bvid=$BV&cid=$CID&fnval=16&qn=64" \
  -H "User-Agent: Mozilla/5.0" -H "Referer: https://www.bilibili.com/" \
  | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['dash']['audio'][0]['baseUrl'])")

curl -L -o /tmp/media_audio.m4s \
  -H "User-Agent: Mozilla/5.0" -H "Referer: https://www.bilibili.com/" "$AUDIO_URL"
ffmpeg -y -i /tmp/media_audio.m4s -acodec libmp3lame -q:a 5 /tmp/media_audio.mp3
→ 进入步骤2b

Step 2a: Video — Download Audio (when no subtitles)

步骤2a:视频——下载音频(无字幕时执行)

bash
yt-dlp --cookies-from-browser chrome -f "ba[ext=m4a]/ba/b" --extract-audio --audio-format mp3 --audio-quality 5 \
  -o "/tmp/media_audio.%(ext)s" "VIDEO_URL"
bash
yt-dlp --cookies-from-browser chrome -f "ba[ext=m4a]/ba/b" --extract-audio --audio-format mp3 --audio-quality 5 \
  -o "/tmp/media_audio.%(ext)s" "VIDEO_URL"

Step 2b: Check Audio Size & Segment

步骤2b:检查音频大小&分片

bash
FILE_SIZE=$(stat -f%z /tmp/media_audio.* 2>/dev/null || stat -c%s /tmp/media_audio.* 2>/dev/null)
echo "File size: $FILE_SIZE bytes"
  • ≤ 25MB → Step 2c (transcribe directly)
  • > 25MB → Split into 10-minute segments:
bash
DURATION=$(ffprobe -v error -show_entries format=duration -of csv=p=0 /tmp/media_audio.* | head -1)
SEGMENT_SEC=600
SEGMENTS=$(python3 -c "import math; print(math.ceil(float('$DURATION')/$SEGMENT_SEC))")

for i in $(seq 0 $((SEGMENTS-1))); do
  START=$((i * SEGMENT_SEC))
  ffmpeg -y -i /tmp/media_audio.* -ss $START -t $SEGMENT_SEC -acodec libmp3lame -q:a 5 \
    "/tmp/media_segment_${i}.mp3" 2>/dev/null
done
→ Call Step 2c for each segment sequentially (parallel triggers Groq 524 timeout)
bash
FILE_SIZE=$(stat -f%z /tmp/media_audio.* 2>/dev/null || stat -c%s /tmp/media_audio.* 2>/dev/null)
echo "File size: $FILE_SIZE bytes"
  • ≤ 25MB → 进入步骤2c(直接转录)
  • > 25MB → 切分为10分钟分片:
bash
DURATION=$(ffprobe -v error -show_entries format=duration -of csv=p=0 /tmp/media_audio.* | head -1)
SEGMENT_SEC=600
SEGMENTS=$(python3 -c "import math; print(math.ceil(float('$DURATION')/$SEGMENT_SEC))")

for i in $(seq 0 $((SEGMENTS-1))); do
  START=$((i * SEGMENT_SEC))
  ffmpeg -y -i /tmp/media_audio.* -ss $START -t $SEGMENT_SEC -acodec libmp3lame -q:a 5 \
    "/tmp/media_segment_${i}.mp3" 2>/dev/null
done
→ 按顺序为每个分片调用步骤2c(并行会触发Groq 524超时)

Step 2c: Whisper Transcription

步骤2c:Whisper转录

Prerequisite:
GROQ_API_KEY
environment variable
bash
if [ -z "$GROQ_API_KEY" ]; then
  echo "❌ GROQ_API_KEY not set. Get one at: https://console.groq.com/keys"
  exit 1
fi

curl -s -X POST "https://api.groq.com/openai/v1/audio/transcriptions" \
  -H "Authorization: Bearer $GROQ_API_KEY" \
  -H "Content-Type: multipart/form-data" \
  -F "file=@AUDIO_FILE" \
  -F "model=whisper-large-v3-turbo" \
  -F "response_format=verbose_json" \
  -F "language=zh" \
  > /tmp/media_transcript.json

python3 -c "import json; print(json.load(open('/tmp/media_transcript.json'))['text'])"
前置要求:已设置
GROQ_API_KEY
环境变量
bash
if [ -z "$GROQ_API_KEY" ]; then
  echo "❌ GROQ_API_KEY not set. Get one at: https://console.groq.com/keys"
  exit 1
fi

curl -s -X POST "https://api.groq.com/openai/v1/audio/transcriptions" \
  -H "Authorization: Bearer $GROQ_API_KEY" \
  -H "Content-Type: multipart/form-data" \
  -F "file=@AUDIO_FILE" \
  -F "model=whisper-large-v3-turbo" \
  -F "response_format=verbose_json" \
  -F "language=zh" \
  > /tmp/media_transcript.json

python3 -c "import json; print(json.load(open('/tmp/media_transcript.json'))['text'])"

Step 3: Structured Summary

步骤3:结构化摘要

Video (≤20 min):
  1. Overview (1-2 sentences)
  2. Key Points (3-5 bullet points)
  3. Notable Quotes (if any)
  4. Action Items (if applicable)
Podcast (>20 min):
  1. Overview (2-3 sentences)
  2. Chapter Summary (segmented by topic)
  3. Key Points (5-8 bullet points)
  4. Notable Quotes
  5. Action Items (if applicable)
视频(≤20分钟)
  1. 概览(1-2句话)
  2. 核心要点(3-5条要点)
  3. 值得关注的引用(如果有)
  4. 行动项(如果适用)
播客(>20分钟)
  1. 概览(2-3句话)
  2. 章节摘要(按主题分段)
  3. 核心要点(5-8条要点)
  4. 值得关注的引用
  5. 行动项(如果适用)

Error Handling

错误处理

SituationAction
No subtitles + no GROQ_API_KEYPrompt user to set API key
Audio >25MBffmpeg segment (10min/segment), transcribe sequentially
Podcast >2 hoursWarn user, confirm before proceeding
Groq 524 timeoutDo NOT parallelize — transcribe sequentially, sleep 5-8s between
Groq 429 rate limitWait for retry-after header, then retry
yt-dlp Bilibili 412Use Bilibili API instead (Step 1d)
yt-dlp YouTube bot detectionAdd
--cookies-from-browser chrome
Spotify linksNot supported (DRM protected)
场景处理方案
无字幕 + 未设置GROQ_API_KEY提示用户设置API密钥
音频大小>25MB使用ffmpeg分片(每片10分钟),按顺序转录
播客时长>2小时警告用户,确认后再继续处理
Groq返回524超时不要并行处理——按顺序转录,两次请求间隔5-8秒
Groq返回429限流等待retry-after头指定的时间后重试
yt-dlp请求Bilibili返回412改用Bilibili API处理(步骤1d)
yt-dlp请求YouTube被反爬拦截增加
--cookies-from-browser chrome
参数
Spotify链接不支持(受DRM保护)

Dependencies

依赖项

  • yt-dlp
    : video download + subtitle extraction
  • ffmpeg
    : audio conversion + segmentation
  • curl
    : audio download, API calls
  • GROQ_API_KEY
    : Whisper transcription (free at https://console.groq.com/keys)