Install
openclaw skills install @zhouq2039-lang/bilibili-video-parser-2Parse Bilibili video URLs to extract metadata, download and merge streams, extract key frames for visual analysis, transcribe audio, and synthesize a compreh...
openclaw skills install @zhouq2039-lang/bilibili-video-parser-2Parse and analyze Bilibili video content through a multi-stage pipeline: metadata → stream download → frame extraction + audio extraction → VLM analysis + ASR transcription → synthesized output.
{project_path}/skills/bilibili-video-parser
The simplest way to use this skill is to run the bundled script:
python3 {Skill Location}/scripts/bilibili_parser.py "https://www.bilibili.com/video/BV1q2RhB9EQC"
This runs the full pipeline and outputs a structured JSON result. See scripts/bilibili_parser.py for details.
Bilibili URL
│
├─ Stage 1: Metadata ──→ Title, UP主, Duration, Views, etc.
│
├─ Stage 2: Subtitles ──→ (If available, skip Stage 3-5 for speech)
│
├─ Stage 3: Stream Download ──→ video.m4s + audio.m4s → merged.mp4
│
├─ Stage 4a: Frame Extraction ──→ key frames → VLM analysis
│
├─ Stage 4b: Audio Extraction ──→ WAV chunks → ASR transcription
│
└─ Stage 5: Synthesis ──→ Metadata + Visual + Audio → Final output
Use Bilibili's public API. No authentication required.
curl -s "https://api.bilibili.com/x/web-interface/view?bvid=BV1q2RhB9EQC" \
-H "User-Agent: Mozilla/5.0" | python3 -c "
import json, sys
from datetime import datetime
data = json.load(sys.stdin)
if data.get('code') == 0:
info = data['data']
print('标题:', info.get('title'))
print('UP主:', info.get('owner', {}).get('name'))
print('时长:', info.get('duration'), '秒')
print('播放量:', info.get('stat', {}).get('view'))
print('点赞:', info.get('stat', {}).get('like'))
print('投币:', info.get('stat', {}).get('coin'))
print('收藏:', info.get('stat', {}).get('favorite'))
print('描述:', info.get('desc'))
print('CID:', info.get('cid'))
pubdate = datetime.fromtimestamp(info.get('pubdate', 0))
print('发布时间:', pubdate.strftime('%Y-%m-%d %H:%M:%S'))
else:
print('API错误:', data)
"
Key fields:
data.title — Video titledata.owner.name — UP主 namedata.duration — Duration in secondsdata.cid — Required for stream URL and subtitlesdata.stat.view — View countdata.stat.like — LikesURL parsing: Extract BV number from various URL formats:
https://www.bilibili.com/video/BV1q2RhB9EQChttps://www.bilibili.com/video/BV1q2RhB9EQC?t=4.6https://b23.tv/xxxxx (short URL, needs redirect resolution)Bilibili videos may have community or auto-generated subtitles. If available, this is the fastest way to get speech content — no need for ASR.
curl -s "https://api.bilibili.com/x/player/v2?bvid=BV1q2RhB9EQC&cid=<CID>" \
-H "User-Agent: Mozilla/5.0" | python3 -c "
import json, sys
data = json.load(sys.stdin)
subtitle = data.get('data', {}).get('subtitle', {})
subtitles = subtitle.get('subtitles', [])
if subtitles:
for s in subtitles:
print(f'Language: {s.get(\"lan_doc\")}, URL: {s.get(\"subtitle_url\")}')
else:
print('No subtitles available')
"
If subtitles exist, download the subtitle URL (prepend https: if it starts with //) and parse the JSON. Each entry has from, to (timestamps) and content (text).
If subtitles are available, you can skip Stages 4b (audio extraction + ASR) for speech content.
B站 videos have separate audio and video streams (m4s format). You need to download both and merge them.
curl -s "https://api.bilibili.com/x/player/playurl?bvid=BV1q2RhB9EQC&cid=<CID>&qn=16&fnver=0&fnval=16&fourk=0" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
-H "Referer: https://www.bilibili.com" | python3 -c "
import json, sys
data = json.load(sys.stdin)
if data.get('code') == 0:
d = data['data']
videos = d.get('dash', {}).get('video', [])
audios = d.get('dash', {}).get('audio', [])
if videos:
print('VIDEO_URL:', videos[0].get('baseUrl', videos[0].get('base_url', '')))
if audios:
print('AUDIO_URL:', audios[0].get('baseUrl', audios[0].get('base_url', '')))
"
Quality parameter qn:
16 — 360p (smallest, fastest download)32 — 480p64 — 720p80 — 1080pUse qn=16 for analysis purposes — smaller file, faster processing.
# Download video stream
curl -L -o video.m4s "$VIDEO_URL" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
-H "Referer: https://www.bilibili.com"
# Download audio stream
curl -L -o audio.m4s "$AUDIO_URL" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
-H "Referer: https://www.bilibili.com"
Critical: The Referer: https://www.bilibili.com header is required. Without it, CDN requests will be rejected.
ffmpeg -i video.m4s -i audio.m4s -c copy output.mp4 -y
VLM cannot process video files directly. Extract key frames as images, then analyze each frame.
# One frame every 5 seconds (adjust fps for density)
ffmpeg -i output.mp4 -vf "fps=1/5" -q:v 2 frames/frame_%03d.jpg -y
Frame rate guidance:
fps=1/5 — 1 frame per 5 seconds (good for talks/presentations)fps=1/2 — 1 frame per 2 seconds (for fast-cut videos)fps=1 — 1 frame per second (for detailed analysis)z-ai vision -p "请用中文描述这张图片的内容,包括人物、场景、屏幕上的文字等所有细节" -i frames/frame_001.jpg
Optimization tips:
ffmpeg -i output.mp4 -vn -acodec pcm_s16le -ar 16000 -ac 1 audio.wav -y
Parameters: 16kHz sample rate, mono channel, 16-bit PCM — the format ASR expects.
The ASR API has a 30-second limit. Split into 25-second segments to be safe.
ffmpeg -i audio.wav -f segment -segment_time 25 -c copy audio_chunk_%03d.wav -y
for chunk in audio_chunk_*.wav; do
z-ai asr -f "$chunk" -o "transcript_$(basename $chunk .wav).json"
done
import json, glob
full_text = ""
for f in sorted(glob.glob("transcript_*.json")):
with open(f) as fh:
data = json.load(fh)
text = data.get("text", "")
full_text += text + " "
print(full_text.strip())
Combine all extracted information into a structured output:
{
"metadata": {
"title": "...",
"author": "...",
"duration_seconds": 101,
"views": 19441,
"likes": 409,
"published_at": "2026-05-07 09:39:28"
},
"visual_analysis": {
"scenes": ["..."],
"people": ["..."],
"on_screen_text": ["..."],
"key_frames_analyzed": 5
},
"speech_transcript": "Full transcribed text...",
"summary": "Synthesized summary combining visual and audio content..."
}
| Problem | Cause | Solution |
|---|---|---|
| yt-dlp returns 412 | B站 anti-scraping | Use the API+CDN approach in Stage 3 instead |
| VLM rejects video file | VLM only accepts images | Extract frames first (Stage 4a) |
| ASR returns "时长限制0-30秒" | Audio too long | Split into 25-second chunks (Stage 4b-2) |
| CDN download fails | Missing Referer header | Add -H "Referer: https://www.bilibili.com" |
| playurl API returns error | Missing or invalid CID | Get CID from Stage 1 API first |
| No subtitles available | Video has no CC | Fall back to ASR (Stage 4b) |
| Stream URLs expired | CDN URLs are time-limited | Re-fetch from playurl API |
Referer header is mandatory for CDN downloadsAfter analysis, clean up temporary files:
rm -f video.m4s audio.m4s audio.wav output.mp4
rm -rf frames/
rm -f audio_chunk_*.wav transcript_*.json