Skip to content

feat: support explicit Bilibili CLI transcripts - #541

Open
vincent-peng wants to merge 2 commits into
steipete:mainfrom
vincent-peng:codex/bilibili-cli
Open

vincent-peng wants to merge 2 commits into
steipete:mainfrom
vincent-peng:codex/bilibili-cli

Conversation

@vincent-peng

@vincent-peng vincent-peng commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Bilibili recording pages can return empty content or a landing-page description for explicit transcript requests. This adds a bounded BV recording route through the existing yt-dlp and transcription pipeline.

summarize "https://www.bilibili.com/video/BV1js411R7qA/" --video-mode transcript

Standard and language-prefixed URLs retain the selected p part. Bilibili downloads use 1 MiB HTTP chunks. Cookies are forwarded only when explicitly configured through the existing cookie environment settings.

Failed or empty acquisition raises an error before page-text fallback. Legacy description-only extracts and transcripts without a verified recording identity are refreshed. Identity validity is computed from the stored cache entry and enforced on both initial reuse and fallback, including expired and timestamp-ineligible entries. Matching expired transcript fallback remains available.

Browser caption retrieval is separate in #542. Guarded daemon downloader restrictions are unchanged.

Evidence and validation

Inspectable fixed-source CLI output, downloader revision/hashes, actual selected-part IDs/CIDs, and released-cache upgrade logs.

  • pnpm run build and pnpm run check passed: 3,922 tests passed, 43 skipped.
  • Thirteen failed-refresh regressions failed before the cache fix. The targeted transcript/cache suites now pass 79 tests.
  • Fresh built-CLI runs with --no-cache --no-media-cache passed for direct extraction, a language-prefixed URL selecting part 2, and summarization through local Whisper.cpp and cli/agy. Stock downloader failure exits 1 with empty stdout.
  • Actual released 0.25.1 and latest-stable 0.25.3 cache APIs wrote isolated SQLite files. The fixed source preserved ordinary cache hits, rejected invalid legacy Bilibili content after real downloader failure, and persisted/reopened matching part-2 metadata. Twelve compatibility checks passed; stored format remains version 2.
  • Structured Codex autoreview is clean for the cache fix.

Downloader compatibility and maintainer decision

Live success uses yt-dlp 2026.10.21 with yt-dlp/yt-dlp#17898 at 9fd15ea9ff813a78584db44457bae19023221f50. Stock 2026.10.21 still fails with Unable to extract initial state. As of 2026-10-24 the upstream repair is unmerged and no newer stable downloader release is available.

Maintainer shipping approval remains open. Recommendation: keep this PR unmerged until a supported downloader release includes the repair, unless a maintainer explicitly accepts the documented user-supplied patched setup. YT_DLP_PATH selects that executable; this PR adds no downloader dependency or installer.

@clawsweeper

clawsweeper Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added clawsweeper:intake:feature Preliminary submission classification; full source review pending. P2 Normal priority bug or improvement with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed clawsweeper:intake:feature Preliminary submission classification; full source review pending. labels Oct 24, 2026
@clawsweeper

clawsweeper Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codex review: blocked before merge.

What this changes

This PR adds explicit Bilibili BV video transcription through the existing downloader pipeline, preserving selected parts and validating cached recording identity.

Example: Extract https://www.bilibili.com/en/video/BV1js411R7qA/?p=2 with --video-mode transcript.

  • Before: Extraction could return empty content or the landing-page description.
  • After: With the documented patched downloader, the CLI returns a 2,156-character transcript identified as part 2; stock-downloader failure exits without page-text fallback.

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) Concrete CLI and persisted-cache evidence resolves the earlier proof gaps, and the cache defect is fixed; overall readiness remains limited by the unresolved downloader shipping choice.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (live_output): The contributor's fixed-source built-CLI output exercises the changed core adapter through fresh anonymous extraction, language-prefixed part-2 extraction, and summarization using patched yt-dlp and Whisper.cpp; stock yt-dlp exits 1 with empty stdout. Separate released-cache processes demonstrate ordinary-content preservation, invalid-identity rejection after real acquisition failure, and persisted part-2 reuse.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Product

Kind: Feature · Worth it: Needs a maintainer decision · Fix scope: Complete
User problem: Explicit Bilibili transcript requests can yield empty content or descriptions instead of spoken content.
Reason: The CLI capability has demonstrated value, but no owner has accepted shipping support that currently depends on an unmerged downloader repair; browser-only captions remain separate.

Merge readiness

⛔ Blocked before merge - 1 item remains

Keep open: the previous cache finding is fixed and no new actionable defect was found; shipping still needs a maintainer decision about the patched downloader requirement.

Priority: P2
Reviewed head: 4f3557b331977e3577131068381d8f79b9b5820e

Before merge

  • Product call needed - The CLI capability has demonstrated value, but no owner has accepted shipping support that currently depends on an unmerged downloader repair; browser-only captions remain separate.

Findings

None.

Agent review details

How this fits together

The CLI URL flow sends explicit transcript requests into core extraction, which validates cached identity, downloads recording audio through yt-dlp, and returns transcribed speech for extraction or summarization.

flowchart TD
  A[CLI transcript request] --> B[Core URL strategy]
  B --> C[Recording identity and cache]
  C --> D[Bilibili downloader adapter]
  D --> E[Shared yt-dlp pipeline]
  E --> F[Configured transcriber]
  F --> G[Transcript or summary]
Loading

Technical review

Best possible solution:

Land the bounded CLI integration after a supported downloader repair ships or a maintainer explicitly accepts the documented patched-downloader requirement.

Do we have a high-confidence way to reproduce the issue?

Current-main routing lacks this Bilibili acquisition path, corroborating the contributor's released-CLI empty-content and description-only observations; this review executed no target code.

Is this the best way to solve the issue?

Reusing transcript strategies and the shared downloader is a focused solution; the adapter owns only Bilibili normalization, download arguments, and recording metadata.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against ac39889e961b.

Provenance checked

  • Explicit transcript routing and terminal acquisition failures keeps the original intent (fix: support Loom transcript extraction #355: Preserve the requested recording identity and prevent failed explicit transcription from becoming a landing-page summary.)
  • Transcript cache identity validation keeps the original intent (0357b72: Reject cached transcripts when the requested embedded recording identity changed or could not be verified.)
  • Shared generic media acquisition keeps the original intent (d87bc63: Commit history records unification of media source and progress ownership; the historical show encountered an unavailable old object, while current source verifies continued use of the shared owner.)

Testing

Proof path: shipped entry point.

Security

None.

Evidence

What I checked:

Likely related people:

  • Kartik: Raw commit 9d90356 adds packages/core/src/content/transcript/providers/generic.ts:57 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: medium; commits: 9d90356e0590; files: packages/core/src/content/transcript/providers/generic.ts)

Review metrics

Metric Value Why it matters
Diff composition Production +146/-4; tests +421; documentation +19 Growth is focused on recording parsing, existing pipeline integration, cache identity, and distinct acquisition and fallback regressions.

Labels

Label changes:

  • add merge-risk: 🚨 compatibility: The documented stock downloader fails the live fixture, so shipping requires an explicit decision about the supported external-tool setup.
  • add proof: sufficient: Contributor real behavior proof is sufficient.
  • add rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: ⏳ waiting on maintainer: Only explicit maintainer approvals or decisions remain; merge is still blocked.
  • remove rating: 🧂 unranked krab: Current PR rating is rating: 🦐 gold shrimp, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: ⏳ waiting on maintainer.

Label justifications:

  • P2: This adds bounded CLI site support without evidence of an urgent regression in an existing supported workflow.
  • merge-risk: 🚨 compatibility: The documented stock downloader fails the live fixture, so shipping requires an explicit decision about the supported external-tool setup.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: ⏳ waiting on maintainer: Only explicit maintainer approvals or decisions remain; merge is still blocked.
  • proof: sufficient: Contributor real behavior proof is sufficient.

Rating scale

6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only. Repair and merge execution lanes are retired.

History

Review history (1 earlier review cycle)
  • reviewed 2026-10-24T04:31:19.969Z sha 57f285b :: needs real behavior proof before merge. :: [P2] Exclude rejected Bilibili cache entries from fallback

Reviewed October 24, 2026, 1:07 AM ET / 05:07 UTC (Revision 2).

Copy link
Copy Markdown
Contributor Author

Addressed the cache finding in commit 4f3557b. Bilibili identity now comes from the cached entry even when no usable initial resolution exists, and the same validity check guards fallback. Verified matching expired entries can still fall back; bypass diagnostics remain bypassed.

Thirteen regressions failed before the fix. The targeted suites now pass 79 tests, the full check passes 3,922 tests with 43 skips, and structured Codex autoreview reports no accepted/actionable findings.

Fresh built-CLI proof

Executed on 2026-10-24 from the fixed source tree. Every case uses both --no-cache and --no-media-cache; cookies are disabled. This includes fresh acquisition for the summary, rather than reuse of an earlier extract.

Runtime: Node 26.11.1, yt-dlp 2026.10.21 with yt-dlp/yt-dlp#17898 at 9fd15ea9ff813a78584db44457bae19023221f50, whisper.cpp 1.9.5 with ggml-base.en.bin, and the configured cli/agy model backend. Only the failure control uses stock yt-dlp.

pnpm run build
# YT_DLP_PATH selects the isolated patched executable for success cases.
# SUMMARIZE_WHISPER_CPP_BINARY and SUMMARIZE_WHISPER_CPP_MODEL_PATH select the local runtime.
# Both cookie environment settings are empty.
node dist/cli.js 'https://www.bilibili.com/video/BV1js411R7qA/' --video-mode transcript --json --format text --no-cache --no-media-cache --metrics off --timeout 2m --extract
node dist/cli.js 'https://www.bilibili.com/en/video/BV1js411R7qA/?p=2' --video-mode transcript --json --format text --no-cache --no-media-cache --metrics off --timeout 2m --extract
# Stock yt-dlp 2026.10.21, same extraction command as the first case: exits 1, stdout empty.
node dist/cli.js 'https://www.bilibili.com/video/BV1js411R7qA/' --video-mode transcript --json --format text --no-cache --no-media-cache --metrics off --timeout 2m --length short --language en --model cli/agy --force-summary
Actual CLI output, filtered to transcript/summary results and redacted error details
[
  {
    "case": "anonymous-extract",
    "exit": 0,
    "transcript_characters": 2158,
    "transcript_sha256": "b948f15ac101ae247e8de7966a3089c67f3892e4ae9e29fc86cad2c4e74b0e57",
    "transcript_source": "yt-dlp",
    "transcription_provider": "whisper.cpp",
    "summary_characters": 0,
    "metadata": {
      "provider": "generic",
      "kind": "video",
      "transcriptionProvider": "whisper.cpp",
      "site": "bilibili",
      "videoId": "BV1js411R7qA",
      "part": 1,
      "videoUrl": "https://www.bilibili.com/video/BV1js411R7qA/"
    },
    "transcript_excerpt": "Transcript: [MUSIC PLAYING] [APPLAUSE] I used to think of the"
  },
  {
    "case": "language-part-extract",
    "exit": 0,
    "transcript_characters": 2156,
    "transcript_sha256": "8cbf96004f6369370b77f2f13770bb3c1ac23ff9af45b61f31dd44d10ea05cf7",
    "transcript_source": "yt-dlp",
    "transcription_provider": "whisper.cpp",
    "summary_characters": 0,
    "metadata": {
      "provider": "generic",
      "kind": "video",
      "transcriptionProvider": "whisper.cpp",
      "site": "bilibili",
      "videoId": "BV1js411R7qA",
      "part": 2,
      "videoUrl": "https://www.bilibili.com/video/BV1js411R7qA/?p=2"
    }
  },
  {
    "case": "stock-downloader-failure",
    "exit": 1,
    "transcript_characters": 0,
    "transcript_sha256": null,
    "transcript_source": null,
    "transcription_provider": null,
    "summary_characters": 0,
    "metadata": null,
    "stderr": "Failed to transcribe Bilibili video (Cache bypass requested; yt-dlp transcription failed: yt-dlp failed to download audio: yt-dlp exited with code 1: ERROR: [BiliBili] 1js411R7qA: Unable to extract initial state; please report this issue on  <redacted-url> , filling out the appropriate issue template. Confirm you are on the latest version using  yt-dlp -U)\n"
  },
  {
    "case": "anonymous-summary",
    "exit": 0,
    "transcript_characters": 2158,
    "transcript_sha256": "b948f15ac101ae247e8de7966a3089c67f3892e4ae9e29fc86cad2c4e74b0e57",
    "transcript_source": "yt-dlp",
    "transcription_provider": "whisper.cpp",
    "summary_characters": 875,
    "metadata": {
      "provider": "generic",
      "kind": "video",
      "transcriptionProvider": "whisper.cpp",
      "site": "bilibili",
      "videoId": "BV1js411R7qA",
      "part": 1,
      "videoUrl": "https://www.bilibili.com/video/BV1js411R7qA/"
    },
    "summary_excerpt": "Most people unknowingly tie their shoes incorrectly because conventional instruction teaches an unbalanced knotting technique that leaves round laces prone to unraveling. After struggling to keep round nylon laces tied...",
    "llm": {
      "provider": "cli",
      "model": "cli/agy",
      "maxCompletionTokens": null,
      "strategy": "single"
    }
  }
]

Executable SHA256s:

  • Stock yt-dlp: b7af77f686e95f7845ce8d09d21957718febb8b48b30555adb9dd2292ad7bf14
  • Patched yt-dlp: 323ece17b6ed0b1d492a067ef6d4ca2b3c18e17322b0ef033f1149d5b36875df
  • Whisper model: a03779c86df3323075f5e796cb2ce5029f00ec8869eee3fdfb897afe36c6d002

The two video parts are subtitle variants of the same talk, so similar transcript lengths are expected. A fresh real downloader run observed distinct selected IDs and actual playurl CIDs:

[
  {
    "requestedPart": 1,
    "selectedId": "BV1js411R7qA_p1",
    "selectedTitle": "【TED】如何系鞋带? p01 简体中文",
    "playurlCid": "5601020"
  },
  {
    "requestedPart": 2,
    "selectedId": "BV1js411R7qA_p2",
    "selectedTitle": "【TED】如何系鞋带? p02 英文",
    "playurlCid": "5601811"
  }
]

Released-cache upgrade proof

Persistent cache compatibility proof, 2026-10-24. Both installed 0.25.1 and latest npm stable 0.25.3 wrote isolated SQLite databases through their unmodified released cache APIs. Separate PR-source processes then read the same files. Cached content was synthetic; rejected Bilibili entries used real official yt-dlp 2026.10.21 failure, with no downloader mock and no browser cookies.

{"case":"ordinary_preserved","writerRelease":"0.25.1","expectedExpired":false,"requestedTimestamps":false,"textReturned":"Existing non-Bilibili transcript survives the upgrade.","source":"captionTracks","cacheStatus":"hit","identityRejected":false,"realStockDownloaderFailure":false,"passed":true}
{"case":"legacy_fresh","writerRelease":"0.25.1","expectedExpired":false,"requestedTimestamps":false,"textReturned":null,"source":null,"cacheStatus":"miss","identityRejected":true,"realStockDownloaderFailure":true,"passed":true}
{"case":"legacy_expired","writerRelease":"0.25.1","expectedExpired":true,"requestedTimestamps":false,"textReturned":null,"source":null,"cacheStatus":"miss","identityRejected":true,"realStockDownloaderFailure":true,"passed":true}
{"case":"wrong_part","writerRelease":"0.25.1","expectedExpired":false,"requestedTimestamps":false,"textReturned":null,"source":null,"cacheStatus":"miss","identityRejected":true,"realStockDownloaderFailure":true,"passed":true}
{"case":"timestamp_ineligible","writerRelease":"0.25.1","expectedExpired":false,"requestedTimestamps":true,"textReturned":null,"source":null,"cacheStatus":"miss","identityRejected":true,"realStockDownloaderFailure":true,"passed":true}
{"case":"new_matching_part_persisted_and_reopened","textReturned":"Persisted matching part-two transcript fixture.","source":"yt-dlp","cacheStatus":"hit","part":2,"passed":true}
{"stage":"upgrade-complete","writerRelease":"0.25.1","databaseFormatVersion":2,"passed":true,"cases":6}
{"case":"ordinary_preserved","writerRelease":"0.25.3","expectedExpired":false,"requestedTimestamps":false,"textReturned":"Existing non-Bilibili transcript survives the upgrade.","source":"captionTracks","cacheStatus":"hit","identityRejected":false,"realStockDownloaderFailure":false,"passed":true}
{"case":"legacy_fresh","writerRelease":"0.25.3","expectedExpired":false,"requestedTimestamps":false,"textReturned":null,"source":null,"cacheStatus":"miss","identityRejected":true,"realStockDownloaderFailure":true,"passed":true}
{"case":"legacy_expired","writerRelease":"0.25.3","expectedExpired":true,"requestedTimestamps":false,"textReturned":null,"source":null,"cacheStatus":"miss","identityRejected":true,"realStockDownloaderFailure":true,"passed":true}
{"case":"wrong_part","writerRelease":"0.25.3","expectedExpired":false,"requestedTimestamps":false,"textReturned":null,"source":null,"cacheStatus":"miss","identityRejected":true,"realStockDownloaderFailure":true,"passed":true}
{"case":"timestamp_ineligible","writerRelease":"0.25.3","expectedExpired":false,"requestedTimestamps":true,"textReturned":null,"source":null,"cacheStatus":"miss","identityRejected":true,"realStockDownloaderFailure":true,"passed":true}
{"case":"new_matching_part_persisted_and_reopened","textReturned":"Persisted matching part-two transcript fixture.","source":"yt-dlp","cacheStatus":"hit","part":2,"passed":true}
{"stage":"upgrade-complete","writerRelease":"0.25.3","databaseFormatVersion":2,"passed":true,"cases":6}

The matching part-2 case uses current writeTranscriptCache, then closes and reopens the same database before resolution. Stored format version remains 2. No user cache/configuration was read or written.

The released writer runs as plain Node importing its actual dist/esm/cache-store.js, without the repository TypeScript loader. A separate process uses the PR source to read the same SQLite file. A controlled clock seeds expired entries one day earlier, without changing release application code. This exercises real serialization and persisted state, rather than an in-memory cache stub.

Maintainer decision

The downloader shipping decision remains open. I rechecked the upstream PR and latest release: #17898 is still unmerged, and stock 2026.10.21 still fails the live fixture. My recommendation is to keep this PR unmerged until a supported downloader release contains the repair, unless a maintainer explicitly accepts the documented user-supplied patched-downloader setup. Browser caption support remains separate in #542.

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Updated head 4f3557b331977e3577131068381d8f79b9b5820e fixes rejected-cache fallback. The PR body now links fresh no-cache CLI/part-selection/summary/failure output and actual released-cache upgrade logs. The downloader shipping decision remains explicitly reserved for a maintainer.

@clawsweeper

clawsweeper Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added clawsweeper:intake:feature Preliminary submission classification; full source review pending. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on maintainer Only explicit maintainer approvals or decisions remain; merge is still blocked. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. clawsweeper:intake:feature Preliminary submission classification; full source review pending. labels Oct 24, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on maintainer Only explicit maintainer approvals or decisions remain; merge is still blocked.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant