mirror of
https://github.com/daymade/claude-code-skills.git
synced 2026-09-14 16:15:21 +08:00
feat(bilibili-source): add login-free Bilibili video data-fetch skill (#86)
Login-free fetch of comprehensive Bilibili (B站) video data in one view/detail call: title, UP follower count, tags, partition, per-part cids, live stats, and full danmaku text. Accepts BVID/av/b23.tv/URL. Ships bili-selftest.sh API-drift health-check and a WBI-signing API reference. All examples synthetic; NO-FABRICATION discipline.
This commit is contained in:
@@ -32,6 +32,25 @@
|
||||
"claude-code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bilibili-source",
|
||||
"description": "Fetch comprehensive, login-free data for any Bilibili (B站) video — title, UP name and follower count, publish date, partition, tags, per-part cids, live stats (view, like, coin, favorite, share, reply, danmaku), and full danmaku (bullet-comment) text. Use this skill whenever working with a Bilibili video and needing real, citable numbers or metadata — ingesting a Bilibili source into a knowledge base, analyzing why a video performed, verifying a creator's claimed metrics, building a case study, or any time a Bilibili view/like/favorite count is about to be written into a document — fetch it, never hand-type or estimate it. Accepts BVID, av numbers, b23.tv short links, or full URLs. Subtitles are also covered but require the user's Bilibili login.",
|
||||
"source": "./bilibili-source",
|
||||
"strict": false,
|
||||
"version": "1.0.0",
|
||||
"category": "developer-tools",
|
||||
"keywords": [
|
||||
"bilibili",
|
||||
"b站",
|
||||
"bilibili-api",
|
||||
"video-stats",
|
||||
"danmaku",
|
||||
"view-count",
|
||||
"content-analysis",
|
||||
"web-data",
|
||||
"claude-code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "capture-screen",
|
||||
"description": "Programmatic screenshot capture on macOS. Get window IDs via Swift CGWindowListCopyWindowInfo, capture specific windows with screencapture -l, and control application windows via AppleScript. Supports multi-shot workflows for capturing different sections of the same window. Use when taking automated screenshots, capturing application windows, or creating visual documentation",
|
||||
|
||||
+2
-3
@@ -91,9 +91,8 @@ recovered_deep_research/
|
||||
# OpenCLI cache
|
||||
.opencli/
|
||||
|
||||
# Eval workspaces (contain test data with personal info)
|
||||
douban-skill-workspace/
|
||||
debugging-network-issues-workspace/
|
||||
# Eval / runtime workspaces (test data, snapshots — never committed)
|
||||
*-workspace/
|
||||
.gstack/
|
||||
|
||||
# Claude Code local settings
|
||||
|
||||
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- **bilibili-source** v1.0.0: new skill — login-free fetch of comprehensive Bilibili (B站) video data in one `view/detail` call (title, UP follower count, tags, partition, per-part cids, live stats, and full danmaku text), accepting BVID / `av` number / `b23.tv` short link / full URL with the BVID-regex, multi-part-cid, and short-link edge cases all handled. Login-gated subtitles via `yt-dlp` (asks before reading browser cookies — no anonymous path exists, verified). Bundles a `bili-selftest.sh` health-check that detects API drift against a stable fixture, an API reference including the WBI request-signing algorithm, and 4 evals. All examples use synthetic/neutral data; metrics always carry a `fetched_at` timestamp (NO-FABRICATION discipline).
|
||||
- **pdf-creator** (`daymade-docs` v1.1.0): new `warm-terra-menu` theme — a warm-terra variant hardened for 2-column long-text module menus (full-column wrap removes first-column overflow; a Menlo `unicode-range` keeps CJK inline-code from rendering blank in Preview/Adobe Reader).
|
||||
- **tunnel-doctor** v1.6.0: Add "TUN Measurement Contamination" diagnostic section — while a proxy runs in TUN/global mode, common probes lie: `nc -z` shows a fabricated `0.00s` handshake (TUN completes it locally), `ping`/`remote_ip` are spoofed, and a foreign IP-geo lookup reports the proxy exit instead of the real home IP. Documents what to trust instead (`time_appconnect`/`time_starttransfer`, an in-region IP-geo source, config-decode + GUI cross-check) and adds matching trigger phrases.
|
||||
- **debugging-network-issues** v1.1.0: Add cognitive Trap 12 "Reverse-path / directional asymmetry" — A→B healthy does not imply B→A healthy; an external probe to a node only proves that node's return direction, systematically missing the user's failing outbound direction (and the congested direction is often one an external probe structurally cannot reach). Sibling to Trap 5 (probe self-verification); synced into the SKILL.md trap list; fixed a stale "All nine traps" count in the summary.
|
||||
|
||||
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
|
||||
## Repository Overview
|
||||
|
||||
This is a Claude Code skills marketplace containing 61 production-ready skills organized in a plugin marketplace structure. Most plugins expose one skill for narrow installs; suite plugins expose related skills under shared namespaces for combined installation workflows.
|
||||
This is a Claude Code skills marketplace containing 62 production-ready skills organized in a plugin marketplace structure. Most plugins expose one skill for narrow installs; suite plugins expose related skills under shared namespaces for combined installation workflows.
|
||||
|
||||
**Essential Skill**: `skill-creator` is the most important skill in this marketplace - it's a meta-skill that enables users to create their own skills. Always recommend it first for users interested in extending Claude Code.
|
||||
|
||||
@@ -153,7 +153,7 @@ If it fires, fix the issue — do NOT use `--no-verify` to bypass.
|
||||
## Marketplace Configuration
|
||||
|
||||
The marketplace is configured in `.claude-plugin/marketplace.json`:
|
||||
- Contains 43 plugin entries: single-skill plugins point `source` directly at the skill directory (no `skills` field); suite plugins (`daymade-audio`, `daymade-claude-code`, `daymade-docs`, `daymade-skill`) use explicit `skills` arrays for multi-skill routing
|
||||
- Contains 44 plugin entries: single-skill plugins point `source` directly at the skill directory (no `skills` field); suite plugins (`daymade-audio`, `daymade-claude-code`, `daymade-docs`, `daymade-skill`) use explicit `skills` arrays for multi-skill routing
|
||||
- Each plugin has: name, description, source, version, category, keywords
|
||||
- Marketplace metadata: name, owner, version
|
||||
- Single-skill plugins follow the official pattern (167/168 plugins in `anthropics/claude-plugins-official`): `source` points to skill directory, `skills` omitted
|
||||
@@ -258,6 +258,7 @@ This applies when you change ANY file under a skill directory:
|
||||
59. **benchmark-due-diligence** - Runs adversarial due-diligence on a benchmark the user envies (a founder, KOL, company, or product whose claimed success looks inflated), separating marketing bubble from real signal and mapping the validated playbook onto the user's own situation
|
||||
60. **pdf-to-html** - Converts a PDF into one self-contained, readable HTML file preserving images, tables, charts, and reading order, optionally translating it into another language while keeping every figure
|
||||
61. **terminal-screenshot** - Render a terminal CLI program's colored output to a PNG so Claude can see the real visual result (color contrast, alignment, background blocks) instead of raw ANSI codes — for verifying delta/bat/starship/lazygit color config
|
||||
62. **bilibili-source** - Fetch login-free, citable data for a Bilibili (B站) video — stats, UP fans, tags, per-part cids, and full danmaku text — via one view/detail call (accepts BVID/av/b23.tv/URL); login-gated subtitles; ships a self-test for API-drift detection
|
||||
|
||||
**Recommendation**: Always suggest `skill-creator` first for users interested in creating skills or extending Claude Code.
|
||||
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
[](./README.zh-CN.md)
|
||||
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://github.com/daymade/claude-code-skills)
|
||||
[](https://github.com/daymade/claude-code-skills)
|
||||
[](https://github.com/daymade/claude-code-skills)
|
||||
[](https://github.com/daymade/claude-code-skills)
|
||||
[](https://claude.com/code)
|
||||
[](./CONTRIBUTING.md)
|
||||
[](https://github.com/daymade/claude-code-skills/graphs/commit-activity)
|
||||
|
||||
</div>
|
||||
|
||||
Professional Claude Code skills marketplace featuring 61 production-ready skills for enhanced development workflows.
|
||||
Professional Claude Code skills marketplace featuring 62 production-ready skills for enhanced development workflows.
|
||||
|
||||
## 📑 Table of Contents
|
||||
|
||||
@@ -2497,6 +2497,39 @@ claude plugin install benchmark-due-diligence@daymade-skills
|
||||
|
||||
---
|
||||
|
||||
### 64. **bilibili-source** - Login-Free Bilibili Video Data + Danmaku Fetcher
|
||||
|
||||
Fetch real, citable data for any Bilibili (B站) video — title, UP follower count, publish date, tags, partition, per-part cids, live stats (view/like/coin/favorite/share/reply/danmaku), and full danmaku (bullet-comment) text — in one `view/detail` call, login-free. Built so engagement numbers are cheap to fetch and impossible to fake, instead of hand-typed into a doc where they rot.
|
||||
|
||||
**When to use:**
|
||||
- Ingesting a Bilibili video into a knowledge base, or building a "why did this perform" case study
|
||||
- Verifying a creator's claimed view/like/favorite numbers, or about to write any B站 metric into a document
|
||||
- Wanting the danmaku text (qualitative audience reactions), not just a reply count
|
||||
- Pasting a BVID, `av` number, `b23.tv` short link, or full URL — all normalized automatically
|
||||
|
||||
**Key features:**
|
||||
- One `bili-fetch.sh` returns full metadata + live stats + UP fans + tags + every part's cid; metrics carry a `fetched_at` timestamp because they drift in real time
|
||||
- `bili-danmaku.sh` pulls and decompresses the danmaku full text; `bili-subs.sh` handles the login-gated subtitle track (asks before touching browser cookies)
|
||||
- `bili-selftest.sh` health-check verifies every endpoint against the live API, so API drift surfaces as one clear FAIL instead of a silent wrong answer
|
||||
- NO-FABRICATION discipline: an unfetchable number is marked unverified, never estimated
|
||||
- Strips the local proxy (Bilibili is a domestic CN service), sends UA+Referer (avoids HTTP 412), retries with backoff
|
||||
- API reference includes the WBI request-signing algorithm for `space/wbi/*` extension
|
||||
|
||||
**Example usage:**
|
||||
```bash
|
||||
# Install the skill
|
||||
claude plugin install bilibili-source@daymade-skills
|
||||
|
||||
# Then ask Claude naturally
|
||||
"pull the real view/like/favorite counts for this B站 video so I can cite them"
|
||||
"这个 B站 视频弹幕里大家在说什么?"
|
||||
"grab the subtitle transcript from this bilibili video so I can summarize it"
|
||||
```
|
||||
|
||||
**Requirements**: `curl`, `jq`, `python3` (danmaku decompression). `yt-dlp` only for the login-gated subtitle path. No login for stats / metadata / danmaku.
|
||||
|
||||
---
|
||||
|
||||
## 🎬 Interactive Demo Gallery
|
||||
|
||||
Want to see all demos in one place with click-to-enlarge functionality? Check out our [interactive demo gallery](./demos/index.html) or browse the [demos directory](./demos/).
|
||||
|
||||
+36
-3
@@ -6,15 +6,15 @@
|
||||
[](./README.zh-CN.md)
|
||||
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://github.com/daymade/claude-code-skills)
|
||||
[](https://github.com/daymade/claude-code-skills)
|
||||
[](https://github.com/daymade/claude-code-skills)
|
||||
[](https://github.com/daymade/claude-code-skills)
|
||||
[](https://claude.com/code)
|
||||
[](./CONTRIBUTING.md)
|
||||
[](https://github.com/daymade/claude-code-skills/graphs/commit-activity)
|
||||
|
||||
</div>
|
||||
|
||||
专业的 Claude Code 技能市场,提供 61 个生产就绪的技能,用于增强开发工作流。
|
||||
专业的 Claude Code 技能市场,提供 62 个生产就绪的技能,用于增强开发工作流。
|
||||
|
||||
## 📑 目录
|
||||
|
||||
@@ -2539,6 +2539,39 @@ claude plugin install auto-repo-setup@daymade-skills
|
||||
|
||||
---
|
||||
|
||||
### 64. **bilibili-source** - 免登录 B站视频数据 + 弹幕抓取
|
||||
|
||||
一次 `view/detail` 调用、免登录地拉取任意 B站视频的可引用数据——标题、UP 粉丝数、发布时间、标签、分区、各分P 的 cid、实时互动数据(播放/点赞/投币/收藏/转发/评论/弹幕),以及完整弹幕全文。设计目标:让互动数字"取数便宜、无法伪造",而不是手敲进文档里慢慢烂掉。
|
||||
|
||||
**使用场景:**
|
||||
- 把 B站视频吸收进知识库,或做"它为什么火"的案例拆解
|
||||
- 核实创作者宣称的播放/点赞/收藏数,或要把任何 B站指标写进文档时
|
||||
- 想要弹幕全文(观众的定性反应),而不只是一个评论数
|
||||
- 粘贴 BVID、`av` 号、`b23.tv` 短链或完整 URL——全部自动识别
|
||||
|
||||
**主要功能:**
|
||||
- 一个 `bili-fetch.sh` 返回全量元数据 + 实时互动 + UP 粉丝 + 标签 + 每个分P 的 cid;互动数带 `fetched_at` 时间戳(因为实时漂移)
|
||||
- `bili-danmaku.sh` 拉取并解压弹幕全文;`bili-subs.sh` 处理需登录的字幕轨(动浏览器 cookie 前会先问你)
|
||||
- `bili-selftest.sh` 健康自检对着真实 API 验每个端点,API 一漂移就报一行清晰 FAIL,而非静默给错数据
|
||||
- NO-FABRICATION 纪律:拿不到的数字标"未核实",绝不估算
|
||||
- 自动剥离本地代理(B站是国内服务)、带 UA+Referer(防 HTTP 412)、失败退避重试
|
||||
- API 参考含 `space/wbi/*` 扩展所需的 WBI 签名算法
|
||||
|
||||
**示例用法:**
|
||||
```bash
|
||||
# 安装技能
|
||||
claude plugin install bilibili-source@daymade-skills
|
||||
|
||||
# 然后自然地让 Claude 做
|
||||
"把这个 B站 视频的真实播放/点赞/收藏数拉出来,我要引用"
|
||||
"这个 B站 视频弹幕里大家在说什么?"
|
||||
"帮我抓这个 bilibili 视频的字幕逐字稿做总结"
|
||||
```
|
||||
|
||||
**要求**:`curl`、`jq`、`python3`(弹幕解压)。`yt-dlp` 仅用于需登录的字幕路径。stats/元数据/弹幕均无需登录。
|
||||
|
||||
---
|
||||
|
||||
## 🎬 交互式演示画廊
|
||||
|
||||
想要在一个地方查看所有演示并具有点击放大功能?访问我们的[交互式演示画廊](./demos/index.html)或浏览[演示目录](./demos/)。
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Security scan passed
|
||||
Scanned at: 2026-05-30T19:16:23.677752
|
||||
Tool: gitleaks + pattern-based validation
|
||||
Content hash: 930661d0365e03f3b13c11db2db0a692b2d3db369b8cc184219cfd6189486f05
|
||||
@@ -0,0 +1,4 @@
|
||||
Security scan passed
|
||||
Scanned at: 2026-06-08T00:39:32.168530
|
||||
Tool: gitleaks + pattern-based validation
|
||||
Content hash: e8f66e65cb05e73f87f2bf57af3c9bc2c81ba2c560698e35c199f99d5786cb54
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
name: bilibili-source
|
||||
description: Fetch comprehensive, login-free data for any Bilibili (B站) video — title, UP name and follower count, publish date, partition, tags, per-part cids, live stats (view, like, coin, favorite, share, reply, danmaku), and full danmaku (bullet-comment) text. Use this skill whenever working with a Bilibili video and needing real, citable numbers or metadata — ingesting a Bilibili source into a knowledge base, analyzing why a video performed, verifying a creator's claimed metrics, building a case study, or any time a Bilibili view/like/favorite count is about to be written into a document — fetch it, never hand-type or estimate it. Accepts BVID, av numbers, b23.tv short links, or full URLs. Subtitles are also covered but require the user's Bilibili login.
|
||||
---
|
||||
|
||||
# bilibili-source
|
||||
|
||||
Fetch **real, verifiable** data for a Bilibili video so you can cite it instead of guessing. Engagement numbers are the backbone of any honest "why did this do well" analysis, and hand-typed or estimated numbers are the fastest way a knowledge base rots. This skill makes the numbers cheap to fetch — so there is no excuse to invent them.
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
scripts/bili-fetch.sh BV1xxxxxxxxx
|
||||
```
|
||||
|
||||
Returns one JSON object with everything from a single `view/detail` API call:
|
||||
|
||||
```json
|
||||
{
|
||||
"bvid": "BV1xxxxxxxxx",
|
||||
"aid": 1234567890,
|
||||
"fetched_at": "2026-06-07T13:54:17Z",
|
||||
"url": "https://www.bilibili.com/video/BV1xxxxxxxxx",
|
||||
"title": "<video title>",
|
||||
"up": { "name": "<UP name>", "mid": 12345678, "fans": 45600 },
|
||||
"pubdate": "2026-01-10T00:50:47Z",
|
||||
"tname": "<partition, may be empty>",
|
||||
"tags": ["<tag>", "<tag>"],
|
||||
"videos": 1,
|
||||
"duration_s": 372,
|
||||
"stat": { "view": 48000, "like": 1200, "coin": 180, "favorite": 950,
|
||||
"share": 64, "reply": 210, "danmaku": 130 },
|
||||
"pages": [ { "cid": 12345678, "page": 1, "part": "<part title>", "duration": 372 } ]
|
||||
}
|
||||
```
|
||||
|
||||
`bili-fetch.sh` accepts any form a user might paste — **BVID, `av` number, `b23.tv` short link, or full URL** — and normalizes it. For multi-part videos it returns every part's `cid` in `pages[]` (you need the per-part cid to fetch that part's danmaku or subtitles).
|
||||
|
||||
## Scripts
|
||||
|
||||
| Script | What it does | Login |
|
||||
|--------|--------------|-------|
|
||||
| `scripts/bili-fetch.sh <ref>` | Core: full metadata + live stats (run this first) | No |
|
||||
| `scripts/bili-danmaku.sh <ref> [P]` | Danmaku (bullet-comment) full text for a part | No |
|
||||
| `scripts/bili-subs.sh <ref> [browser]` | Subtitle/transcript track | **Yes** |
|
||||
| `scripts/bili-selftest.sh` | Health-check every capability against the live API | No |
|
||||
|
||||
All three **execute** (don't read them as reference). `bili-danmaku.sh` reuses `bili-fetch.sh` to resolve the part's cid, so they must stay siblings in `scripts/`.
|
||||
|
||||
**Danmaku** are time-synced comments overlaid on the video — a Bilibili-specific signal of *where and how* viewers reacted, qualitatively richer than a flat reply count:
|
||||
|
||||
```bash
|
||||
scripts/bili-danmaku.sh BV1xxxxxxxxx # P1; add a part number for multi-part videos
|
||||
```
|
||||
|
||||
## Rules that keep the data honest
|
||||
|
||||
- **Live metrics → always cite `fetched_at`.** The same video re-fetched minutes later drifts (a view count can tick up by a few within a single session). That is not an error — it is proof the data is live. A bare "12,000 views" with no timestamp is meaningless and silently goes stale.
|
||||
- **NO FABRICATION.** If a number can't be fetched, write "未获取/未核实" — never estimate. The whole point of the skill is that the number is cheap to fetch.
|
||||
- **The scripts already handle the network quirks** so you don't reinvent them: they strip the local proxy (Bilibili is a domestic CN service that a `127.0.0.1` proxy breaks), send a browser User-Agent + Referer (avoids the occasional HTTP 412), and retry with backoff. If you call the API by hand, do the same — see [references/bilibili_api.md](references/bilibili_api.md).
|
||||
- **CJK post-processing trap.** When you later grep/sort the fetched Chinese text or filenames, `sort`/`comm` mishandle CJK collation and report false "missing"/"broken" results. Verify with `find -name` or `grep -F`, not `comm`.
|
||||
|
||||
## Subtitles require login (no bypass)
|
||||
|
||||
Stats and danmaku are login-free. **Subtitles are not.** Verified across many videos (new and old) plus anonymous cookies: the public player API returns an empty subtitle list for anonymous requests, and `yt-dlp` reports *"Subtitles are only available when logged in."* There is **no login-free path** — do not try to bypass it.
|
||||
|
||||
`bili-subs.sh` therefore needs the user's Bilibili session via browser cookies. Because it reads their logged-in session, **ask the user before running it**:
|
||||
|
||||
```bash
|
||||
scripts/bili-subs.sh BV1xxxxxxxxx chrome # or firefox / safari / edge
|
||||
```
|
||||
|
||||
The `ai-zh` track is Bilibili's AI-generated subtitle — treat it as a draft transcript (same-sound/segmentation errors), mark it as AI-ASR in whatever you produce, and don't claim it is a human-checked verbatim. If a video has no subtitle track, there is nothing to fetch — don't invent one. A SESSDATA-env API alternative is documented in the reference.
|
||||
|
||||
## Going deeper
|
||||
|
||||
For the full endpoint catalog (UP fan history, video tags, real-time viewer count, danmaku archive, the SESSDATA subtitle path), the WBI request-signing algorithm needed for `space/wbi/*` endpoints, and every gotcha with a tested command, see **[references/bilibili_api.md](references/bilibili_api.md)**.
|
||||
|
||||
## Verified status
|
||||
|
||||
- **Stats / metadata / danmaku** (`view/detail`, `relation/stat`, `dm/list.so`, `online/total`): verified login-free, 2026-06-07. Metrics re-fetched repeatedly and matched independently; danmaku count matched `stat.danmaku`.
|
||||
- **Subtitles**: confirmed login-gated, 2026-06-07 (empty for anonymous across all videos tested). Needs `yt-dlp` for the cookie path.
|
||||
|
||||
## Maintenance
|
||||
|
||||
This skill wraps a third-party API that drifts over time — fields get renamed, endpoints add WBI signing, anti-bot tightens. Before trusting it after a gap, or whenever output looks wrong, run the health-check:
|
||||
|
||||
```bash
|
||||
scripts/bili-selftest.sh
|
||||
```
|
||||
|
||||
It hits every capability (and the login-gate invariant) against a stable public fixture and prints one PASS/FAIL row per capability, so drift surfaces as a clear FAIL pointing at what broke — not a silent wrong answer. When a row fails, the endpoint paths, field names, and WBI signing needed to fix it are in [references/bilibili_api.md](references/bilibili_api.md); update the "Verified" dates above once you re-confirm.
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"skill_name": "bilibili-source",
|
||||
"evals": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "cite-real-stats-from-url",
|
||||
"prompt": "I'm writing up a short analysis of this B站 video: https://www.bilibili.com/video/BV17x411w7KC — I need the actual play count, likes and favorites to cite, not rough numbers. Can you get them?",
|
||||
"expected_behavior": [
|
||||
"Runs scripts/bili-fetch.sh with the URL instead of guessing or estimating numbers",
|
||||
"Reports view/like/favorite from the returned stat block",
|
||||
"Includes the fetched_at timestamp alongside the metrics (because they are live snapshots)",
|
||||
"Does not hand-type or round numbers as if authoritative",
|
||||
"Mentions the UP name and other metadata returned in the same call where relevant"
|
||||
],
|
||||
"files": []
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "danmaku-audience-reactions",
|
||||
"prompt": "这个 B站 视频 av170001 弹幕里大家都在说什么?我想了解观众的真实反应",
|
||||
"expected_behavior": [
|
||||
"Recognizes danmaku (bullet comments) as the relevant signal and uses scripts/bili-danmaku.sh",
|
||||
"Handles the av number input (does not require a BVID)",
|
||||
"Returns actual danmaku text, not just the danmaku count",
|
||||
"Does not fabricate comments if the fetch returns few or none"
|
||||
],
|
||||
"files": []
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "subtitles-need-login",
|
||||
"prompt": "Grab the subtitle transcript from this bilibili video BV17x411w7KC so I can summarize what it's about.",
|
||||
"expected_behavior": [
|
||||
"Knows Bilibili subtitles are login-gated and there is no anonymous path",
|
||||
"Asks the user before reading their browser cookies / using their Bilibili login",
|
||||
"Uses scripts/bili-subs.sh (or the documented SESSDATA path) rather than claiming a login-free method",
|
||||
"Treats the ai-zh track as AI-generated draft, not a verified verbatim transcript",
|
||||
"Does not invent a transcript if no subtitle track exists"
|
||||
],
|
||||
"files": []
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "multipart-and-shortlink",
|
||||
"prompt": "朋友发我一个 b23.tv 短链(类似 https://b23.tv/xxxxxxx),说是个多P的合集视频,帮我看看有几个分P、每个分P的 cid 是多少,UP 有多少粉丝",
|
||||
"expected_behavior": [
|
||||
"Passes the b23.tv short link straight to scripts/bili-fetch.sh (which expands it)",
|
||||
"Reports videos (part count) and the per-part cids from pages[], not just the top-level cid",
|
||||
"Reports the UP follower count (up.fans) from the same call",
|
||||
"Does not claim it cannot handle short links or multi-part videos"
|
||||
],
|
||||
"files": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
# Bilibili API reference
|
||||
|
||||
Endpoints, fields, and gotchas behind `bilibili-source`. Every command below was tested
|
||||
2026-06-07 (curl 8.7 / jq 1.7 / yt-dlp 2026.03). Prefix every request with the proxy-strip
|
||||
+ headers shown in [Request basics](#request-basics).
|
||||
|
||||
## Contents
|
||||
- [Request basics](#request-basics) — proxy, headers, retries
|
||||
- [Input forms](#input-forms) — BVID / av / b23.tv / URL
|
||||
- [Core endpoint: view/detail](#core-endpoint-viewdetail) — everything in one call
|
||||
- [Other login-free endpoints](#other-login-free-endpoints) — UP stats, tags, viewers, danmaku
|
||||
- [Multi-part videos](#multi-part-videos)
|
||||
- [Danmaku decompression](#danmaku-decompression)
|
||||
- [Subtitles (login required)](#subtitles-login-required) — yt-dlp and SESSDATA paths
|
||||
- [WBI signing](#wbi-signing) — only for `space/wbi/*`
|
||||
- [Gotchas](#gotchas)
|
||||
|
||||
## Request basics
|
||||
|
||||
```bash
|
||||
NP() { env -u http_proxy -u https_proxy -u all_proxy -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY "$@"; }
|
||||
UA="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/124.0 Safari/537.36"
|
||||
HDR=(-H "User-Agent: $UA" -H "Referer: https://www.bilibili.com")
|
||||
```
|
||||
|
||||
- **Proxy:** Bilibili is a domestic CN service. A local forward proxy (e.g. `127.0.0.1:1082`) makes requests hang or fail — strip proxy env per request.
|
||||
- **Headers:** UA + Referer avoid the occasional `HTTP 412`. (As of the test date a bare request often still succeeds, but the headers are a near-zero-cost defense against IP/time-windowed risk control — keep them.)
|
||||
- **Retries:** non-zero `code` such as `-412`/`-799` is transient rate-limiting; back off and retry 2–3×. For batches of many videos, add a small sleep between calls. Single-video fetches did not trip any limit across 35 rapid calls.
|
||||
|
||||
## Input forms
|
||||
|
||||
| Input | How to resolve |
|
||||
|-------|----------------|
|
||||
| `BV` + 10 chars | Use directly: `?bvid=BV...`. Anchor the regex to `BV[0-9A-Za-z]{10}` — an unanchored `BV[0-9A-Za-z]+` over-captures trailing chars. |
|
||||
| `av<number>` / bare aid | `?aid=<number>`. The API accepts `aid` and returns `bvid`, so it doubles as an av→BV converter. |
|
||||
| `b23.tv/xxxx` short link | One `curl -sI` (no `-L`); read the `Location:` header for the canonical URL, then extract BV/av. |
|
||||
|
||||
## Core endpoint: view/detail
|
||||
|
||||
`GET https://api.bilibili.com/x/web-interface/view/detail?bvid=<BV>` (or `?aid=<n>`) — returns
|
||||
everything `bilibili-source` needs in **one** call, including the partition (`tname`) and UP
|
||||
follower count that the plain `view` endpoint often leaves empty/absent.
|
||||
|
||||
```bash
|
||||
NP curl -fsSL "${HDR[@]}" "https://api.bilibili.com/x/web-interface/view/detail?bvid=BV1xxxxxxxxx" \
|
||||
| jq '.data | {title:.View.title, up:.View.owner.name, fans:.Card.card.fans,
|
||||
tname:.View.tname, tags:[.Tags[].tag_name], videos:.View.videos,
|
||||
stat:.View.stat, pages:[.View.pages[]|{cid,page,part,duration}]}'
|
||||
```
|
||||
|
||||
Key paths: `data.View` (title, aid, bvid, pubdate, duration, videos, owner{mid,name}, tname,
|
||||
pages[], stat{view,like,coin,favorite,share,reply,danmaku}); `data.Card.card.fans` (UP
|
||||
followers); `data.Tags[].tag_name`; `data.Related[]` (up to ~40 related videos).
|
||||
|
||||
## Other login-free endpoints
|
||||
|
||||
| Data | Endpoint | Notes |
|
||||
|------|----------|-------|
|
||||
| UP follower/following | `x/relation/stat?vmid=<mid>` | `data.follower`, `data.following` |
|
||||
| UP card | `x/web-interface/card?mid=<mid>` | `data.card.fans`, name, sign |
|
||||
| Video tags | `x/tag/archive/tags?bvid=<BV>` | array of `tag_name` |
|
||||
| Real-time viewers | `x/player/online/total?bvid=<BV>&cid=<cid>` | `data.total` ("1.7万+"), `data.count` (int) |
|
||||
| Danmaku (current pool) | `x/v1/dm/list.so?oid=<cid>` | raw-deflate XML — see below |
|
||||
| Player meta | `x/player/wbi/v2?bvid=<BV>&cid=<cid>` | subtitle list here is **empty when anonymous** |
|
||||
|
||||
`tname` from `view/detail` can be empty for some videos; the tags array is the reliable
|
||||
content-classification signal.
|
||||
|
||||
## Multi-part videos
|
||||
|
||||
`data.View.videos` = part count; `data.View.pages[]` lists each part as `{cid, page, part, duration}`.
|
||||
The top-level `data.View.cid` equals **part 1 only** — for danmaku/subtitles of later parts you
|
||||
must use that part's own `cid` from `pages[]`. `bili-fetch.sh` emits the full `pages[]`.
|
||||
|
||||
## Danmaku decompression
|
||||
|
||||
`x/v1/dm/list.so?oid=<cid>` returns **headerless raw DEFLATE** (not gzip). Decompress with
|
||||
zlib window bits `-15`, then each comment is `<d p="...">text</d>`:
|
||||
|
||||
```bash
|
||||
NP curl -fsSL "${HDR[@]}" "https://api.bilibili.com/x/v1/dm/list.so?oid=<cid>" \
|
||||
| python3 -c "import sys,zlib; sys.stdout.buffer.write(zlib.decompress(sys.stdin.buffer.read(),-15))" \
|
||||
| grep -oE '<d [^>]*>[^<]*</d>' | sed -E 's/<d [^>]*>//; s|</d>||'
|
||||
```
|
||||
|
||||
`list.so` returns the current rolling pool (up to a few thousand). For the **full historical
|
||||
archive** use the protobuf segment endpoint `x/v2/dm/web/seg.so?type=1&oid=<cid>&segment_index=<n>`
|
||||
(6-minute segments; needs a protobuf decoder — out of scope for the bundled scripts).
|
||||
|
||||
## Subtitles (login required)
|
||||
|
||||
There is **no anonymous path** (verified: `player/wbi/v2` returns an empty subtitle list for
|
||||
every anonymous request tested, new videos included). Two authenticated options:
|
||||
|
||||
1. **yt-dlp + browser cookies** (what `bili-subs.sh` uses):
|
||||
```bash
|
||||
yt-dlp --skip-download --write-subs --sub-langs "ai-zh" --cookies-from-browser chrome \
|
||||
--add-header "Referer:https://www.bilibili.com" "https://www.bilibili.com/video/<BV>"
|
||||
```
|
||||
2. **SESSDATA cookie + API** (documented; verify on first use with a real login — the empty-list
|
||||
behavior above was only confirmable while logged out):
|
||||
```bash
|
||||
NP curl -fsSL "${HDR[@]}" -b "SESSDATA=<your_sessdata>" \
|
||||
"https://api.bilibili.com/x/player/wbi/v2?bvid=<BV>&cid=<cid>" \
|
||||
| jq '.data.subtitle.subtitles[] | {lan, url:.subtitle_url}'
|
||||
# then download the .subtitle_url JSON (json3 format: body[].content)
|
||||
```
|
||||
|
||||
`ai-zh` is AI-generated — same-sound/segmentation errors; mark output as AI-ASR, never as verbatim.
|
||||
|
||||
## WBI signing
|
||||
|
||||
Needed **only** for `space/wbi/*` endpoints (e.g. listing a UP's videos via
|
||||
`space/wbi/arc/search`). None of the endpoints used by the bundled scripts require it. The
|
||||
algorithm, verified end-to-end while logged out:
|
||||
|
||||
1. `GET x/web-interface/nav` (works anonymously) → `data.wbi_img.img_url` and `sub_url`; the
|
||||
filename stems are `img_key` and `sub_key`.
|
||||
2. `mixin_key` = concatenate `img_key + sub_key`, then reorder by a fixed 64-index table and
|
||||
take the first 32 chars.
|
||||
3. Add `wts=<unix-seconds>` to your params, sort keys, URL-encode (drop `!'()*`), then
|
||||
`w_rid = md5(sorted_query + mixin_key)`. Send params + `wts` + `w_rid`.
|
||||
|
||||
Gotcha: `space/wbi/*` also needs an **anonymous `buvid3`** cookie (get it login-free from
|
||||
`x/frontend/finger/spi` → `data.b_3`), or it still returns `-352` even with a valid signature.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **`code != 0` is the real error channel**, not just HTTP status. Always check `.code == 0`; surface `.message`.
|
||||
- **Metrics are live snapshots** — emit a fetch timestamp with every stat.
|
||||
- **`-352` risk-control** usually means missing WBI signature or `buvid3`, not a bad request.
|
||||
- **CJK collation** — `sort`/`comm` give false negatives on Chinese strings; verify membership with `grep -F` / `find -name`.
|
||||
- **No login-free subtitles** — settle it once: the empty array from `player/wbi/v2` is the ceiling.
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
# bili-danmaku.sh — Fetch a Bilibili video's danmaku (bullet comments) as plain text. Login-free.
|
||||
#
|
||||
# Danmaku are time-synced comments overlaid on the video — a Bilibili-specific signal of
|
||||
# WHERE and HOW viewers reacted (spikes of "前方高能", agreement, jokes). Unlike a flat
|
||||
# reply count, the danmaku text itself is qualitative audience data.
|
||||
#
|
||||
# Output: one danmaku per line to stdout; a count line to stderr.
|
||||
#
|
||||
# Usage:
|
||||
# bili-danmaku.sh <BVID | av | b23.tv | URL> [P-number] # resolves the part's cid (default P1)
|
||||
# bili-danmaku.sh --cid <CID> # if you already have a cid
|
||||
#
|
||||
# Deps: curl, jq, python3 (raw-deflate decompression). Resolution reuses bili-fetch.sh.
|
||||
#
|
||||
# Note: x/v1/dm/list.so returns the current rolling pool (up to a few thousand). For the
|
||||
# full historical archive use the protobuf seg.so endpoint — see references/bilibili_api.md.
|
||||
set -euo pipefail
|
||||
|
||||
here="$(cd "$(dirname "$0")" && pwd)"
|
||||
UA="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36"
|
||||
|
||||
bili_curl() {
|
||||
env -u http_proxy -u https_proxy -u all_proxy -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY \
|
||||
curl -fsSL --max-time 20 --retry 3 --retry-delay 1 --retry-all-errors \
|
||||
-H "User-Agent: $UA" -H "Referer: https://www.bilibili.com" "$@"
|
||||
}
|
||||
|
||||
# Resolve cid: either given directly, or pulled from the requested part via bili-fetch.sh.
|
||||
if [ "${1:-}" = "--cid" ]; then
|
||||
cid="${2:-}"; [ -z "$cid" ] && { echo "ERROR: --cid needs a value" >&2; exit 2; }
|
||||
else
|
||||
ref="${1:-}"; [ -z "$ref" ] && { echo "usage: bili-danmaku.sh <BVID|av|URL> [P] | --cid <CID>" >&2; exit 2; }
|
||||
p="${2:-1}"
|
||||
cid=$("$here/bili-fetch.sh" "$ref" | jq -r ".pages[$((p-1))].cid // empty")
|
||||
[ -z "$cid" ] && { echo "ERROR: could not resolve cid for part $p of: $ref" >&2; exit 1; }
|
||||
fi
|
||||
|
||||
# list.so is headerless raw DEFLATE (zlib window bits -15), not gzip.
|
||||
xml=$(bili_curl "https://api.bilibili.com/x/v1/dm/list.so?oid=$cid" \
|
||||
| python3 -c "import sys,zlib; sys.stdout.buffer.write(zlib.decompress(sys.stdin.buffer.read(), -15))") \
|
||||
|| { echo "ERROR: fetch/decompress failed for cid=$cid" >&2; exit 1; }
|
||||
|
||||
# Each comment is <d p="...">text</d>; emit just the text, one per line.
|
||||
texts=$(printf '%s' "$xml" \
|
||||
| grep -oE '<d [^>]*>[^<]*</d>' \
|
||||
| sed -E 's/<d [^>]*>//; s|</d>||')
|
||||
n=$(printf '%s\n' "$texts" | grep -c . || true)
|
||||
printf '%s\n' "$texts"
|
||||
echo "[$n danmaku for cid=$cid]" >&2
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env bash
|
||||
# bili-fetch.sh — Fetch comprehensive, login-free metadata + LIVE stats for a Bilibili video.
|
||||
#
|
||||
# One call to web-interface/view/detail returns: title, UP (name/mid/follower count),
|
||||
# publish date, partition, tags, per-part cids, and the full stat block
|
||||
# (view/like/coin/favorite/share/reply/danmaku). Output is ONE JSON object to stdout.
|
||||
#
|
||||
# Engagement metrics are LIVE snapshots — they drift minute to minute — so the JSON
|
||||
# carries `fetched_at`. Always cite a metric WITH that timestamp; a bare count goes
|
||||
# stale silently. If a number can't be fetched, write "未获取/未核实" — never estimate.
|
||||
#
|
||||
# Accepts any form a user might paste:
|
||||
# bili-fetch.sh BV1xxxxxxxxx # BVID
|
||||
# bili-fetch.sh av170001 # av number
|
||||
# bili-fetch.sh "https://b23.tv/xxxxxxx" # short link (auto-expanded)
|
||||
# bili-fetch.sh "https://www.bilibili.com/video/BV1xxxxxxxxx"
|
||||
#
|
||||
# Deps: curl, jq. No login required.
|
||||
set -euo pipefail
|
||||
|
||||
UA="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36"
|
||||
|
||||
# Bilibili is a domestic CN service: a local forward proxy (e.g. 127.0.0.1:1082)
|
||||
# breaks the call, so strip inherited proxy env for the request only. A browser
|
||||
# User-Agent + Referer avoids the occasional HTTP 412 anti-bot response. Retry a
|
||||
# few times with backoff to ride out transient -412/-799/network blips.
|
||||
bili_curl() {
|
||||
env -u http_proxy -u https_proxy -u all_proxy -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY \
|
||||
curl -fsSL --max-time 20 --retry 3 --retry-delay 1 --retry-all-errors \
|
||||
-H "User-Agent: $UA" -H "Referer: https://www.bilibili.com" "$@"
|
||||
}
|
||||
|
||||
usage() { echo "usage: bili-fetch.sh <BVID | av-number | b23.tv-link | bilibili-video-URL>" >&2; exit 2; }
|
||||
|
||||
input="${1:-}"; [ -z "$input" ] && usage
|
||||
|
||||
# 1) Expand b23.tv short links to their canonical URL (single 302 hop).
|
||||
if printf '%s' "$input" | grep -qi 'b23\.tv'; then
|
||||
loc=$(env -u http_proxy -u https_proxy -u all_proxy -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY \
|
||||
curl -sI --max-time 15 -H "User-Agent: $UA" "$input" \
|
||||
| tr -d '\r' | awk 'tolower($1)=="location:"{print $2; exit}')
|
||||
[ -n "$loc" ] && input="$loc"
|
||||
fi
|
||||
|
||||
# 2) Resolve to an API query param. BVID is a fixed BV + 10 chars — anchor the length
|
||||
# so a longer surrounding string can't be over-captured. Fall back to an av/aid number.
|
||||
bvid=$(printf '%s' "$input" | grep -oE 'BV[0-9A-Za-z]{10}' | head -1 || true)
|
||||
if [ -n "$bvid" ]; then
|
||||
q="bvid=$bvid"
|
||||
else
|
||||
aid=$(printf '%s' "$input" | grep -oiE 'av[0-9]+|[0-9]{6,}' | grep -oE '[0-9]+' | head -1 || true)
|
||||
[ -z "$aid" ] && { echo "ERROR: no BVID or av-number found in: $input" >&2; usage; }
|
||||
q="aid=$aid"
|
||||
fi
|
||||
|
||||
ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
json=$(bili_curl "https://api.bilibili.com/x/web-interface/view/detail?$q") \
|
||||
|| { echo "ERROR: request failed ($q) — proxy or network?" >&2; exit 1; }
|
||||
|
||||
code=$(printf '%s' "$json" | jq -r '.code')
|
||||
if [ "$code" != "0" ]; then
|
||||
echo "ERROR: bilibili API code=$code msg=$(printf '%s' "$json" | jq -r '.message // "?"')" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Multi-part videos: every part has its own cid (subtitles/danmaku are fetched per cid),
|
||||
# so emit the full pages[] — not just the top-level cid, which is only part 1.
|
||||
printf '%s' "$json" | jq --arg ts "$ts" '
|
||||
.data as $d | $d.View as $v | {
|
||||
bvid: $v.bvid,
|
||||
aid: $v.aid,
|
||||
fetched_at: $ts,
|
||||
url: ("https://www.bilibili.com/video/" + $v.bvid),
|
||||
title: $v.title,
|
||||
up: { name: $v.owner.name, mid: $v.owner.mid, fans: $d.Card.card.fans },
|
||||
pubdate: ($v.pubdate | todate),
|
||||
tname: $v.tname,
|
||||
tags: [ $d.Tags[]?.tag_name ],
|
||||
videos: $v.videos,
|
||||
duration_s: $v.duration,
|
||||
stat: { view: $v.stat.view, like: $v.stat.like, coin: $v.stat.coin,
|
||||
favorite: $v.stat.favorite, share: $v.stat.share,
|
||||
reply: $v.stat.reply, danmaku: $v.stat.danmaku },
|
||||
pages: [ $v.pages[] | { cid, page, part, duration } ]
|
||||
}'
|
||||
Executable
+82
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env bash
|
||||
# bili-selftest.sh — Health-check for bilibili-source against the LIVE Bilibili API.
|
||||
#
|
||||
# Why this exists: this skill wraps a third-party API that changes over time (fields get
|
||||
# renamed, endpoints add WBI signing, anti-bot tightens). Without a self-test, drift shows
|
||||
# up as a silent wrong answer in production. Run this after Bilibili changes something, or
|
||||
# periodically, and API drift surfaces as one clear FAIL row pointing at what broke.
|
||||
#
|
||||
# It asserts SHAPE (fields exist, right types) and documented INVARIANTS — never exact
|
||||
# values, since engagement numbers drift by design.
|
||||
#
|
||||
# Usage: bili-selftest.sh
|
||||
# Deps: curl, jq, python3 (same as the scripts under test).
|
||||
# Exit: 0 = all green; 1 = drift detected (see failing rows + references/bilibili_api.md).
|
||||
set -uo pipefail # deliberately NOT -e: run every check and report, don't abort on first fail
|
||||
|
||||
here="$(cd "$(dirname "$0")" && pwd)"
|
||||
FIXTURE="av170001" # AZIS classic — old, stable, public, multi-part (10 P). A neutral fixture.
|
||||
|
||||
pass=0; fail=0
|
||||
ok() { printf " ✅ %s\n" "$1"; pass=$((pass+1)); }
|
||||
bad() { printf " ❌ %s\n" "$1"; fail=$((fail+1)); }
|
||||
|
||||
UA="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36"
|
||||
np_curl() {
|
||||
env -u http_proxy -u https_proxy -u all_proxy -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY \
|
||||
curl -fsSL --max-time 20 -H "User-Agent: $UA" -H "Referer: https://www.bilibili.com" "$@"
|
||||
}
|
||||
|
||||
echo "bilibili-source self-test (fixture: $FIXTURE)"
|
||||
|
||||
# 1) Core fetch — av input normalizes; view/detail returns the documented shape.
|
||||
J=$("$here/bili-fetch.sh" "$FIXTURE" 2>/dev/null || true)
|
||||
CID=""
|
||||
if [ -z "$J" ]; then
|
||||
bad "fetch: returned nothing (endpoint or input-normalization broke)"
|
||||
else
|
||||
echo "$J" | jq -e '.bvid | startswith("BV")' >/dev/null 2>&1 && ok "fetch: av → BVID resolved" || bad "fetch: bvid missing (av-resolution or view/detail broke)"
|
||||
echo "$J" | jq -e '.stat.view | type=="number"' >/dev/null 2>&1 && ok "fetch: stat.view is a number" || bad "fetch: stat.view missing/renamed"
|
||||
echo "$J" | jq -e '.up.fans | type=="number"' >/dev/null 2>&1 && ok "fetch: up.fans present (Card.card.fans path)" || bad "fetch: up.fans missing (view/detail Card path drifted)"
|
||||
echo "$J" | jq -e '(.tags|type)=="array"' >/dev/null 2>&1 && ok "fetch: tags is an array" || bad "fetch: tags missing/renamed"
|
||||
echo "$J" | jq -e '(.pages|length) == .videos' >/dev/null 2>&1 && ok "fetch: pages[] complete (length == videos)" || bad "fetch: pages[] count != videos (multi-P parsing drifted)"
|
||||
echo "$J" | jq -e '(.pages|length) > 1' >/dev/null 2>&1 && ok "fetch: multi-part fixture returned >1 part" || bad "fetch: fixture no longer multi-part (pick a new FIXTURE)"
|
||||
CID=$(echo "$J" | jq -r '.pages[0].cid // empty')
|
||||
fi
|
||||
|
||||
# 2) Full-URL input still normalizes.
|
||||
echo "$("$here/bili-fetch.sh" "https://www.bilibili.com/video/BV17x411w7KC" 2>/dev/null)" \
|
||||
| jq -e '.bvid=="BV17x411w7KC"' >/dev/null 2>&1 && ok "fetch: full-URL input normalized" || bad "fetch: URL normalization broke"
|
||||
|
||||
# 3) Danmaku — list.so decompresses and yields a line count.
|
||||
if [ -n "$CID" ]; then
|
||||
DM=$("$here/bili-danmaku.sh" --cid "$CID" 2>/dev/null | grep -c . || true)
|
||||
case "$DM" in
|
||||
''|*[!0-9]*) bad "danmaku: decompression/parse broke (non-numeric result)";;
|
||||
*) ok "danmaku: list.so decompressed ($DM lines)";;
|
||||
esac
|
||||
else
|
||||
bad "danmaku: skipped — no cid from fetch"
|
||||
fi
|
||||
|
||||
# 4) Login-gate invariant — anonymous subtitle list must stay EMPTY (the documented ceiling).
|
||||
# A non-empty anonymous list means Bilibili opened subtitles up: update SKILL.md if so.
|
||||
if [ -n "$CID" ]; then
|
||||
SUBS=$(np_curl "https://api.bilibili.com/x/player/wbi/v2?aid=170001&cid=$CID" 2>/dev/null \
|
||||
| jq -r '.data.subtitle.subtitles | length' 2>/dev/null || echo ERR)
|
||||
case "$SUBS" in
|
||||
0) ok "subtitles: still login-gated (anonymous list empty, as documented)";;
|
||||
ERR) bad "subtitles: player/wbi/v2 call failed (endpoint drifted)";;
|
||||
*) bad "subtitles: anonymous list NON-empty ($SUBS) — login-gate changed, update docs";;
|
||||
esac
|
||||
else
|
||||
bad "subtitles: skipped — no cid from fetch"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
printf "Result: %d passed, %d failed\n" "$pass" "$fail"
|
||||
if [ "$fail" -eq 0 ]; then
|
||||
echo "✅ All capabilities healthy."; exit 0
|
||||
else
|
||||
echo "❌ Drift detected — see failing rows; consult references/bilibili_api.md."; exit 1
|
||||
fi
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
# bili-subs.sh — Download a Bilibili video's subtitle/transcript track. REQUIRES LOGIN.
|
||||
#
|
||||
# Important, verified 2026-06: Bilibili subtitles are gated behind login. The public
|
||||
# player API (player/wbi/v2) returns an EMPTY subtitle list for anonymous requests across
|
||||
# every video tested (new and old), and yt-dlp reports "Subtitles are only available when
|
||||
# logged in." There is NO login-free path — do not try to bypass it. So this script needs
|
||||
# the user's Bilibili session, supplied as browser cookies.
|
||||
#
|
||||
# Because it reads the user's logged-in session, ASK THE USER before running it.
|
||||
#
|
||||
# Usage:
|
||||
# bili-subs.sh <BVID | av | URL> [browser] # browser: chrome (default), firefox, safari, edge
|
||||
#
|
||||
# Output: subtitle file(s) written to the current directory as <id>.<lang>.<ext> (json3/srt).
|
||||
# Deps: yt-dlp. (Alternative SESSDATA-based API path documented in references/bilibili_api.md.)
|
||||
set -euo pipefail
|
||||
|
||||
ref="${1:-}"
|
||||
browser="${2:-chrome}"
|
||||
[ -z "$ref" ] && { echo "usage: bili-subs.sh <BVID|av|URL> [browser]" >&2; exit 2; }
|
||||
|
||||
command -v yt-dlp >/dev/null 2>&1 || { echo "ERROR: yt-dlp not installed (brew install yt-dlp / pipx install yt-dlp)" >&2; exit 3; }
|
||||
|
||||
# Normalize to a watch URL (yt-dlp accepts BV/av URLs; bare IDs need wrapping).
|
||||
case "$ref" in
|
||||
http*) url="$ref" ;;
|
||||
BV*|av*|AV*) url="https://www.bilibili.com/video/$ref" ;;
|
||||
*) url="https://www.bilibili.com/video/$ref" ;;
|
||||
esac
|
||||
|
||||
UA="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36"
|
||||
|
||||
echo "Pulling subtitles via yt-dlp using your '$browser' cookies (Bilibili login required)…" >&2
|
||||
if env -u http_proxy -u https_proxy -u all_proxy -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY \
|
||||
yt-dlp --skip-download --write-subs --sub-langs "ai-zh,zh-Hans,zh-CN,zh" \
|
||||
--user-agent "$UA" --add-header "Referer:https://www.bilibili.com" \
|
||||
--cookies-from-browser "$browser" \
|
||||
-o "%(id)s.%(ext)s" "$url"; then
|
||||
echo "Done. If no subtitle file appeared, this video simply has no subtitle track." >&2
|
||||
else
|
||||
cat >&2 <<'EOF'
|
||||
ERROR: subtitle download failed. Most likely causes:
|
||||
- Not logged into bilibili.com in the chosen browser (subtitles are login-gated).
|
||||
- Browser cookie DB locked — close the browser and retry, or pass a different browser.
|
||||
- The video has no subtitle track at all (then there is nothing to fetch — do not invent one).
|
||||
Alternative: export SESSDATA and use the player/wbi/v2 API path (see references/bilibili_api.md).
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,4 @@
|
||||
Security scan passed
|
||||
Scanned at: 2026-06-07T01:06:19.687678
|
||||
Tool: gitleaks + pattern-based validation
|
||||
Content hash: b742b75949f08f3accd8d4962e81347bd14708150ac6bb89857a92c6ba89f43f
|
||||
@@ -0,0 +1,4 @@
|
||||
Security scan passed
|
||||
Scanned at: 2026-06-07T02:15:27.247618
|
||||
Tool: gitleaks + pattern-based validation
|
||||
Content hash: f6c79db20bdee839f99043b4e316ef765ca7b1b3f0650b8b998125ce50ecd8b7
|
||||
Reference in New Issue
Block a user