Download subtitles or media from video and podcast URLs, then transcribe them to text.
Project description
CastScribe
CastScribe downloads subtitles or media from YouTube, Bilibili, Xiaoyuzhou, Apple Podcasts, playlists, feeds, and channel URLs, then writes transcript files.
It first tries subtitles. If subtitles are unavailable, it downloads audio. If audio-only download is unavailable, it downloads the lowest quality combined audio/video format and transcribes that.
Operating Systems
CastScribe can download media and convert existing subtitles on any system that supports Python and yt-dlp.
Local speech transcription depends on the operating system:
- macOS 26 or later: local transcription uses Apple's
yapcommand. - macOS before 26: use a cloud backend, or use
faster-whisperby running on another supported Python environment. - Linux and Windows: local transcription uses
faster-whisper.
Cloud transcription backends are available on macOS, Linux, and Windows when the matching cloud SDK and credentials are configured.
Requirements
- Python 3.10+
yt-dlpPython package, installed automatically when installing this project- Or
yt-dlpCLI available onPATH - optional cloud transcription SDKs:
castscribe[azure],castscribe[aws],castscribe[google], orcastscribe[cloud]
Local Transcription Setup
CastScribe first tries downloaded subtitles. These local transcription requirements apply only when no subtitle is available and CastScribe must transcribe audio.
macOS 26 or Later
CastScribe uses yap for the default local backend.
Install yap with Homebrew:
brew install yap
Or install it with Mint:
mint install finnvoor/yap
Enable Dictation and install the language assets before long runs:
- Open System Settings.
- Go to Keyboard.
- Enable Dictation.
- Add the language you plan to use, for example English -> United States.
yap does not provide speaker diarization. Use --backend google, --backend azure, or --backend aws when you need speaker labels.
Linux, Windows, or Non-yap Environments
CastScribe uses faster-whisper for the default local backend outside yap environments.
Install it through CastScribe's Whisper extra:
python3 -m pip install -e '.[whisper]'
Or install faster-whisper directly:
python3 -m pip install faster-whisper
The --model value is passed to faster-whisper; common choices are tiny, base, small, medium, and large-v3.
Install
Install from GitHub:
python3 -m pip install "castscribe @ git+https://github.com/longbai/CastScribe.git"
Or install from a local checkout:
python3 -m pip install -e .
Then run:
castscribe --help
You can also run without installing:
PYTHONPATH=src python3 -m castscribe --help
The old script entry remains available for compatibility:
PYTHONPATH=src python3 media_to_text.py --help
Usage
Single or multiple remote URLs:
castscribe "https://www.youtube.com/watch?v=..."
castscribe "https://space.bilibili.com/..." "https://www.xiaoyuzhoufm.com/episode/..."
URL file:
castscribe --input urls.txt
Channel or playlist URL:
castscribe --output transcripts "https://www.youtube.com/@channel/videos"
castscribe --output transcripts "https://www.youtube.com/playlist?list=..."
castscribe --locale en_US --output transcripts "https://podcasts.apple.com/cn/podcast/the-economics-of-everyday-things/id1666678354"
Local files, file:// URLs, and local directories:
castscribe ./audio.mp3
castscribe file:///Users/me/Downloads/captions.vtt
castscribe --output transcripts ./podcast-downloads
SRT output:
castscribe --format srt ./audio.mp3
castscribe --format srt --output transcripts "https://www.youtube.com/watch?v=..."
YouTube or Bilibili may require cookies when they show bot, sign-in, or HTTP 412 checks:
castscribe --cookies-from-browser chrome "https://www.youtube.com/shorts/JLV1YqS445I"
castscribe --cookies cookies.txt "https://www.youtube.com/watch?v=HjDsO0RHQHg&t=303s"
castscribe --cookies-from-browser chrome "https://space.bilibili.com/266765166/lists/2621160?type=season"
Limit a channel, playlist, or podcast feed while testing:
castscribe --playlist-items 1:2 --locale en_US --output transcripts "https://podcasts.apple.com/cn/podcast/the-economics-of-everyday-things/id1666678354"
Cloud Transcription
CastScribe only calls a transcription backend when it cannot use downloaded subtitles. The default backend is local.
Install optional cloud SDKs:
python3 -m pip install -e '.[azure]'
python3 -m pip install -e '.[aws]'
python3 -m pip install -e '.[google]'
python3 -m pip install -e '.[cloud]'
Azure Speech uses environment variables named SPEECH_KEY and ENDPOINT:
castscribe --backend azure --language en-US ./meeting.wav
AWS Transcribe uses the normal boto3 credential chain. The first version expects the media file to already be available in S3:
castscribe \
--backend aws \
--cloud-region us-west-2 \
--cloud-uri s3://your-bucket/path/audio.mp3 \
--language en-US \
./audio.mp3
Google Cloud Speech-to-Text uses Application Default Credentials:
castscribe --backend google --language en-US --min-speakers 2 --max-speakers 6 ./meeting.wav
Tencent Cloud and Alibaba Cloud are selectable but not implemented yet:
castscribe --backend tencent ./meeting.wav
castscribe --backend aliyun ./meeting.wav
Cloud credentials must not be committed to the repository. Do not pass access keys on the command line; use the provider's environment variables or default credential chain.
Behavior
The program first tries subtitles, preferring zh-Hans, zh-CN, zh, then en.
Apple Podcasts show pages are resolved through Apple's lookup API to their RSS feed before downloading episodes.
Local subtitle files are converted directly to .txt or .srt. Local media files are transcribed directly. Local directories are scanned recursively for supported subtitle and media files.
Output rules:
- Without
--output, each output is written next to the source or downloaded file with the same basename, ending in.txtor.srt. - With
--output DIR, all transcript files are written directly intoDIR. - Remote downloads use
DIR/downloadswhen--output DIRis set. Without--output, downloads are written under the current directory using theyt-dlpfilename template. - Existing
.txtor.srtfiles are skipped, so channel and playlist downloads can be rerun after interruption.
Development
Run tests:
python3 -m unittest discover -s tests
Build a wheel and source distribution:
python3 -m pip install -e '.[build]'
python3 -m build
python3 -m twine check dist/*
Install the built wheel locally:
python3 -m pip install dist/castscribe-*.whl
Release
Packages are published by GitHub Actions when a GitHub Release is published.
Create a GitHub Release in the browser with tag v0.1.1.
Do not push a separate release tag when publishing from the GitHub Release UI; the release event triggers the publish workflow.
PyPI publishing uses Trusted Publishing. Configure the PyPI project with:
- Owner:
longbai - Repository:
CastScribe - Workflow:
publish.yml - Environment:
pypi
No PyPI token is stored in the repository.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file castscribe-0.1.1.tar.gz.
File metadata
- Download URL: castscribe-0.1.1.tar.gz
- Upload date:
- Size: 24.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2b694ba302fb4e2f3cec71dcff8fa19067144ffb433bc3857cb557eb4848f3c
|
|
| MD5 |
52c771e8adc32bcd3fafd11ae4a56266
|
|
| BLAKE2b-256 |
b10d077baf2c77ba80f48b80a589cb980a2ad35293aa5f7683a049476880b6a4
|
Provenance
The following attestation bundles were made for castscribe-0.1.1.tar.gz:
Publisher:
publish.yml on longbai/CastScribe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
castscribe-0.1.1.tar.gz -
Subject digest:
d2b694ba302fb4e2f3cec71dcff8fa19067144ffb433bc3857cb557eb4848f3c - Sigstore transparency entry: 2114658715
- Sigstore integration time:
-
Permalink:
longbai/CastScribe@5dd3fed0f4349c729d4b4e50e7471ef09ff4b2d8 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/longbai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5dd3fed0f4349c729d4b4e50e7471ef09ff4b2d8 -
Trigger Event:
release
-
Statement type:
File details
Details for the file castscribe-0.1.1-py3-none-any.whl.
File metadata
- Download URL: castscribe-0.1.1-py3-none-any.whl
- Upload date:
- Size: 21.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17105c48df8bd18bde71b9ea89ff0a4a96577188810b4f394c0b267e1107c459
|
|
| MD5 |
6a8160a2c1b8f894886bcbfda866dc9a
|
|
| BLAKE2b-256 |
fb4c1bb1bad78e824a139907216fe798bf381ade52c6125ea300a554db525ea6
|
Provenance
The following attestation bundles were made for castscribe-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on longbai/CastScribe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
castscribe-0.1.1-py3-none-any.whl -
Subject digest:
17105c48df8bd18bde71b9ea89ff0a4a96577188810b4f394c0b267e1107c459 - Sigstore transparency entry: 2114659278
- Sigstore integration time:
-
Permalink:
longbai/CastScribe@5dd3fed0f4349c729d4b4e50e7471ef09ff4b2d8 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/longbai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5dd3fed0f4349c729d4b4e50e7471ef09ff4b2d8 -
Trigger Event:
release
-
Statement type: