Metadata-Version: 2.4
Name: castscribe
Version: 0.1.1
Summary: Download subtitles or media from video and podcast URLs, then transcribe them to text.
Author: longbai
License-Expression: MIT
Project-URL: Homepage, https://github.com/longbai/CastScribe
Project-URL: Repository, https://github.com/longbai/CastScribe
Project-URL: Issues, https://github.com/longbai/CastScribe/issues
Keywords: transcription,yt-dlp,podcast,youtube,bilibili,whisper
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
Classifier: Topic :: Text Processing
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: yt-dlp>=2026.6.9
Provides-Extra: whisper
Requires-Dist: faster-whisper; extra == "whisper"
Provides-Extra: azure
Requires-Dist: azure-cognitiveservices-speech; extra == "azure"
Provides-Extra: aws
Requires-Dist: boto3; extra == "aws"
Provides-Extra: google
Requires-Dist: google-cloud-speech; extra == "google"
Provides-Extra: cloud
Requires-Dist: azure-cognitiveservices-speech; extra == "cloud"
Requires-Dist: boto3; extra == "cloud"
Requires-Dist: google-cloud-speech; extra == "cloud"
Provides-Extra: build
Requires-Dist: build; extra == "build"
Requires-Dist: twine; extra == "build"
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# 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 `yap` command.
- macOS before 26: use a cloud backend, or use `faster-whisper` by 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-dlp` Python package, installed automatically when installing this project
- Or `yt-dlp` CLI available on `PATH`
- optional cloud transcription SDKs: `castscribe[azure]`, `castscribe[aws]`, `castscribe[google]`, or `castscribe[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`](https://github.com/finnvoor/yap) for the default local backend.

Install `yap` with Homebrew:

```bash
brew install yap
```

Or install it with Mint:

```bash
mint install finnvoor/yap
```

Enable Dictation and install the language assets before long runs:

1. Open System Settings.
2. Go to Keyboard.
3. Enable Dictation.
4. 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`](https://github.com/SYSTRAN/faster-whisper) for the default local backend outside `yap` environments.

Install it through CastScribe's Whisper extra:

```bash
python3 -m pip install -e '.[whisper]'
```

Or install `faster-whisper` directly:

```bash
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:

```bash
python3 -m pip install "castscribe @ git+https://github.com/longbai/CastScribe.git"
```

Or install from a local checkout:

```bash
python3 -m pip install -e .
```

Then run:

```bash
castscribe --help
```

You can also run without installing:

```bash
PYTHONPATH=src python3 -m castscribe --help
```

The old script entry remains available for compatibility:

```bash
PYTHONPATH=src python3 media_to_text.py --help
```

## Usage

Single or multiple remote URLs:

```bash
castscribe "https://www.youtube.com/watch?v=..."
castscribe "https://space.bilibili.com/..." "https://www.xiaoyuzhoufm.com/episode/..."
```

URL file:

```bash
castscribe --input urls.txt
```

Channel or playlist URL:

```bash
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:

```bash
castscribe ./audio.mp3
castscribe file:///Users/me/Downloads/captions.vtt
castscribe --output transcripts ./podcast-downloads
```

SRT output:

```bash
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:

```bash
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:

```bash
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:

```bash
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`:

```bash
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:

```bash
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:

```bash
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:

```bash
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 `.txt` or `.srt`.
- With `--output DIR`, all transcript files are written directly into `DIR`.
- Remote downloads use `DIR/downloads` when `--output DIR` is set. Without `--output`, downloads are written under the current directory using the `yt-dlp` filename template.
- Existing `.txt` or `.srt` files are skipped, so channel and playlist downloads can be rerun after interruption.

## Development

Run tests:

```bash
python3 -m unittest discover -s tests
```

Build a wheel and source distribution:

```bash
python3 -m pip install -e '.[build]'
python3 -m build
python3 -m twine check dist/*
```

Install the built wheel locally:

```bash
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.
