koesub
Fetch subtitles from YouTube or Bilibili, transcribe local audio/video with Whisper, and create black-background subtitled videos. Available as both a Python library and a command-line tool.
Requirements
- Python 3.11 or newer.
- FFmpeg and ffprobe on
PATHfor subtitle conversion, media decoding, duration checks, and video encoding. - Node.js on
PATHfor YouTube extraction with the configured yt-dlp runtime. - A local multilingual Whisper
.ptcheckpoint when transcription is needed. Models are not bundled with the package or downloaded automatically.
On macOS, install the external programs with:
brew install ffmpeg node
Transcription selects CUDA, MPS, or CPU according to PyTorch availability.
The video renderer currently requires macOS because it uses the system Hiragino
font at /System/Library/Fonts/ヒラギノ角ゴシック W3.ttc.
Installation
From a source checkout, install the project and its Python dependencies with uv:
uv sync
uv run koesub --help
For a standalone CLI installation from the checkout:
uv tool install .
koesub --help
To install the library into another virtual environment:
uv pip install /path/to/koesub
Use koesub COMMAND or python -m koesub COMMAND. The public Python API is
under the koesub package. Installation includes yt-dlp, Whisper, PyTorch, and
Pillow. External programs and model checkpoints remain separate requirements.
Fetch subtitles from a URL
uv run koesub fetch "https://www.youtube.com/watch?v=YE7VzlLtp-4" > subtitles.srt
uv run koesub fetch "https://www.bilibili.com/video/BV12N4y1M7rh?p=2" --lang zh-CN > subtitles.srt
uv run koesub fetch "VIDEO_URL" --lang ja \
--model-path ~/.local/share/whisper/models/large-v3-turbo.pt > subtitles.srt
fetch accepts one YouTube or Bilibili video URL and writes complete, validated
SRT content to stdout. Language/source reports, authentication retries, Whisper
progress, and errors go to stderr. It returns 0 on success, nonzero on failure,
and 130 on interruption. Failed fetches produce no SRT on stdout, although shell
redirection may create an empty destination file.
| Argument | Behavior |
|---|---|
URL |
Single YouTube/Bilibili video; Bilibili supports ?p=2. |
--lang LANG, -l LANG |
Platform subtitle language and Whisper language hint. Default: prefer the original platform language, auto-detect during transcription. |
--model-path PATH |
Local .pt checkpoint, or a directory containing exactly one checkpoint. |
--chrome-profile PROFILE |
Chrome profile used during authentication retries. |
--help, -h |
Show command help. |
The default model directory is $XDG_DATA_HOME/whisper/models, or
~/.local/share/whisper/models when XDG_DATA_HOME is unset.
Platform captions are preferred, with manual captions preferred over automatic captions in the same language; identifiable automatic translations, danmaku, and chat are excluded. If valid captions cannot be obtained, including caption selection, download, conversion, or validation failures, the command automatically downloads audio and attempts local Whisper transcription. Audio-only formats are preferred; a single media file containing audio is used when necessary. Whisper is loaded only on this fallback path, and stderr identifies the successful source.
Each download starts anonymously. Explicit authentication errors trigger Safari, then Chrome retries. Cookie values and signed URLs are sanitized in diagnostics; cookies are not exported. Unsupported inputs are rejected before network access. Temporary captions and audio are cleaned up on success, failure, or interruption.
There is no --output-dir and no transcription opt-in flag. The caller owns final
SRT persistence. Dialect/script hints such as zh-CN, zh-Hans, and en-GB map
to zh and en for Whisper. The command transcribes speech without translating
it, and recognition accuracy should be reviewed before relying on exact wording.
Transcribe a local file
uv run koesub transcribe audio.mp3 --language ja > subtitles.srt
uv run koesub transcribe video.mp4 > subtitles.srt
transcribe accepts exactly one local audio/video file. Whisper uses FFmpeg to
decode its audio stream, so no separate audio-extraction step is needed. It writes
SRT to stdout and diagnostics to stderr; empty subtitles or processing failures
return a nonzero exit status without writing SRT.
Use --language LANG / -l LANG to set the audio language; the default is automatic
detection. --model-path PATH / --model-dir PATH select a local checkpoint or
single-checkpoint directory, with the same default model directory as fetch.
Create a subtitled video
uv run koesub make-video audio.mp3 --language ja
uv run koesub make-video first.mp3 second.flac --output-dir ./outputs
uv run koesub make-video audio.mp3 --audio-codec copy --container mkv
make-video creates a 1280×720, 25 fps video with a black background and white
subtitles. It reads existing SRT from OUTPUT_DIR/srt/INPUT_STEM.srt, or transcribes
missing subtitles in memory. Batch jobs reuse one Whisper model. Duplicate input
stems are rejected before processing to prevent collisions within a batch.
Outputs are written to OUTPUT_DIR/CONTAINER/INPUT_STEM.CONTAINER; the default
directory is ./outputs and the default format is H.264/AAC in MP4. Existing
target videos are overwritten. Use --audio-codec, --video-codec, and
--container to select available FFmpeg encoders and an output container
(mp4, mkv, mov, avi, or ts). Automatic transcription accepts the same
language/model options as transcribe.
The renderer requires non-overlapping captions and checks subtitle timing before
encoding. Platform subtitle overlaps are preserved by fetch, so some fetched
subtitles require editing before use by the renderer. Generated subtitles are
not automatically persisted by make-video.
Python API
Fetch from a URL and receive an SRT string:
from koesub.fetch import fetch_srt
srt = fetch_srt("https://www.youtube.com/watch?v=YE7VzlLtp-4", language="en")
Reuse a loaded model for local files:
from koesub.transcription import load_model, transcribe_to_srt
model = load_model("/path/to/large-v3-turbo.pt")
first_srt = transcribe_to_srt(model, "first.mp3", language="ja")
second_srt = transcribe_to_srt(model, "second.flac", language="ja")
Library calls return strings and raise exceptions on failure. Callers control subtitle persistence and model reuse. Calls are sequential: inference diagnostics are redirected to stderr using process-wide stdout redirection.
Development and building
The project uses the native uv_build backend. Tests and the project skill are
included in the source distribution; the wheel contains the koesub package,
CLI entrypoint, README metadata, and MIT license.
uv sync
uv run python -m unittest discover -s tests -v
uv build --no-sources --clear
uvx twine check dist/*
Build artifacts are written to dist/ as a source distribution (.tar.gz) and
an installable wheel (.whl). Building does not publish the package. Network and
browser authentication tests use simulated services; they do not establish live
YouTube/Bilibili availability.
Video subtitle analysis skill
The project skill is at skills/video-subtitles/SKILL.md, with UI metadata at
skills/video-subtitles/agents/openai.yaml. It captures koesub's SRT stdout as a
workspace artifact and guides timestamped analysis while identifying platform
captions or local Whisper transcription.
Invoke it explicitly by providing the skill's path and a video URL:
Read and use /path/to/koesub/skills/video-subtitles/SKILL.md to fetch this video's subtitles and summarize its content with timestamps: VIDEO_URL
The repository skills/ folder is not automatically a globally registered skill.
After installation in a supported discovery location, it can be invoked as
$video-subtitles. Its policy allows automatic selection by default. The skill
is included in the source archive and remains separate from wheel installation.
License
koesub is released under the MIT License. Third-party dependencies, model checkpoints, and system fonts retain their own licenses.
Metadata
Release files for koesub 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| koesub-0.1.0.tar.gz | 30.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| koesub-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 53.9 kB
Release files / koesub-0.1.0.tar.gz
| Download URL | koesub-0.1.0.tar.gz |
|---|---|
| Size | 30.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3589c6a8d06821c17f72cd773dd0b9ff8aaad3744f8ed44137010fb29e4eb445
|
|
BLAKE2b-256 checksum How to use checksums |
f8ed52c42a3b314599f2852b0f587c2bb2ee04b0372a4b1173167289a3262088
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / koesub-0.1.0-py3-none-any.whl
| Download URL | koesub-0.1.0-py3-none-any.whl |
|---|---|
| Size | 23.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d4f256199755d51ff2f83dad1736c29fe07f2a9520ba370c70a726207cad009d
|
|
BLAKE2b-256 checksum How to use checksums |
1db187b6702c82858a4ba3493c674ca527f400477de7213469404c3796886843
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|