Skip to content

Commit ad847e7

Browse files
virtuecoderclaude
andcommitted
Add cross-platform support and improve defaults
- Use platformdirs for config path (macOS/Linux/Windows native locations) - Default output_dir to ~/Downloads instead of current directory - Fall back to notepad on Windows when $EDITOR is unset - Add ffmpeg to requirements docs; expand install instructions for all platforms - Document HuggingFace model cache paths per platform Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f1b0f7c commit ad847e7

5 files changed

Lines changed: 95 additions & 14 deletions

File tree

CLAUDE.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Commands
6+
7+
```bash
8+
just install # create venv and install dependencies via uv
9+
just run <url> # transcribe a YouTube URL or bare video ID
10+
just config --show # print current config
11+
just config --edit # open config in $EDITOR
12+
just models # list available Whisper model sizes
13+
```
14+
15+
There are no tests and no linter configured.
16+
17+
## Architecture
18+
19+
Two source files under `src/transcribe/`:
20+
21+
- **`cli.py`** — all CLI logic via Typer. Two commands: `run` (transcribe) and `config` (show/edit config file). The `run` command tries YouTube captions first (`fetch_youtube_captions`), falls back to Whisper (`transcribe_with_whisper`). Output path resolution order: `--output` flag → `output_dir` in config → `~/Downloads`.
22+
23+
- **`config.py`** — loads `~/.config/yt-transcribe/config.toml` (path determined by `platformdirs.user_config_dir`). Merges user TOML over hardcoded `_DEFAULTS`. Creates the file with documented defaults on `config --edit` if it doesn't exist yet.
24+
25+
## Key behaviours
26+
27+
- `faster-whisper` and `yt-dlp` are lazy imports — only loaded when captions are unavailable, so caption-only runs have no heavy dependency startup cost.
28+
- Audio is downloaded to `tempfile.TemporaryDirectory()` and deleted automatically after transcription.
29+
- `unique_path()` appends `(1)`, `(2)`, … to avoid silently overwriting existing files.
30+
- `--print` suppresses all Rich console output and writes only the transcript to stdout — safe for piping.
31+
- Config path is platform-specific via `platformdirs`: `~/Library/Application Support/yt-transcribe/` on macOS, `~/.config/yt-transcribe/` on Linux, `%LOCALAPPDATA%\yt-transcribe\` on Windows.

README.md

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,42 @@ CLI tool that extracts transcripts from YouTube videos. Fetches existing caption
55
## Requirements
66

77
- Python 3.11+
8-
- [uv](https://docs.astral.sh/uv/)`curl -LsSf https://astral.sh/uv/install.sh | sh`
9-
- [just](https://just.systems/)`brew install just`
8+
- [uv](https://docs.astral.sh/uv/)
9+
- [just](https://just.systems/)
10+
- [ffmpeg](https://ffmpeg.org/) — required when Whisper is used (no captions available)
11+
12+
### macOS
13+
14+
```bash
15+
brew install uv just ffmpeg
16+
```
17+
18+
### Linux (Debian/Ubuntu)
19+
20+
```bash
21+
# uv
22+
curl -LsSf https://astral.sh/uv/install.sh | sh
23+
24+
# just
25+
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin
26+
27+
# ffmpeg
28+
sudo apt install ffmpeg
29+
```
30+
31+
For other distros replace `apt install ffmpeg` with your package manager (`dnf`, `pacman`, etc.). The `just` binary can also be downloaded from its [GitHub releases](https://github.com/casey/just/releases).
32+
33+
### Windows
34+
35+
```powershell
36+
winget install astral-sh.uv
37+
winget install Casey.Just
38+
winget install ffmpeg
39+
```
40+
41+
Then restart your terminal so the new PATH entries take effect.
42+
43+
> **Note:** `faster-whisper` requires the [Microsoft Visual C++ Redistributable](https://aka.ms/vs/17/release/vc_redist.x64.exe) (x64). Install it if you see a DLL error on first Whisper run.
1044
1145
## Setup
1246

@@ -18,15 +52,19 @@ just install
1852
## Usage
1953

2054
```bash
21-
# Fetch captions if available, otherwise run Whisper — saves to current dir by default
55+
# Fetch captions if available, otherwise run Whisper — saves to ~/Downloads by default
2256
just run "https://youtube.com/watch?v=VIDEO_ID"
2357

2458
# Save to a specific file
2559
just run "https://youtube.com/watch?v=VIDEO_ID" --output transcript.txt
2660

2761
# Print to stdout (all status output suppressed — safe to pipe)
2862
just run "https://youtube.com/watch?v=VIDEO_ID" --print
63+
64+
# Copy to clipboard (macOS: pbcopy, Linux: xclip, Windows: clip)
2965
just run "https://youtube.com/watch?v=VIDEO_ID" --print | pbcopy
66+
just run "https://youtube.com/watch?v=VIDEO_ID" --print | xclip -selection clipboard
67+
just run "https://youtube.com/watch?v=VIDEO_ID" --print | clip
3068

3169
# Force Whisper even if captions exist
3270
just run "https://youtube.com/watch?v=VIDEO_ID" --force-whisper
@@ -43,12 +81,18 @@ just models
4381

4482
## Config
4583

46-
Defaults are stored in `~/.config/yt-transcribe/config.toml`. On first run with `just config --edit` the file is created with all options documented inline.
84+
Defaults are stored in a platform-specific config file:
85+
86+
| Platform | Path |
87+
|---|---|
88+
| macOS | `~/Library/Application Support/yt-transcribe/config.toml` |
89+
| Linux | `~/.config/yt-transcribe/config.toml` |
90+
| Windows | `%LOCALAPPDATA%\yt-transcribe\config.toml` |
4791

4892
```bash
4993
just config # show config file path
5094
just config --show # print current config
51-
just config --edit # open in $EDITOR
95+
just config --edit # open in $EDITOR (or notepad on Windows)
5296
```
5397

5498
Default config:
@@ -57,7 +101,7 @@ Default config:
57101
[defaults]
58102
model = "turbo" # tiny | base | small | medium | turbo | large-v3
59103
language = "" # empty = auto-detect per video
60-
output_dir = "" # if set, transcripts are auto-saved here (uses video title as filename)
104+
output_dir = "~/Downloads" # transcripts are auto-saved here (uses video title as filename)
61105
output_extension = "txt"
62106

63107
[whisper]
@@ -67,7 +111,7 @@ beam_size = 5 # higher = more accurate, slower (1–10)
67111
vad_filter = true # skip silent segments (recommended)
68112
```
69113

70-
**`output_dir`** — when set, every transcription is auto-saved to `<output_dir>/<video title>.<output_extension>` without needing `--output`. Useful for batch use.
114+
**`output_dir`** — when set, every transcription is auto-saved to `<output_dir>/<video title>.<output_extension>` without needing `--output`. Useful for batch use. Supports `~` expansion.
71115

72116
## Options
73117

@@ -79,13 +123,13 @@ vad_filter = true # skip silent segments (recommended)
79123
| `--language` | `-l` | auto-detect | Override language, e.g. `en`, `fr`, `de`. Omit to auto-detect — useful only when detection gets it wrong or the video has mixed-language content. |
80124
| `--force-whisper` | `-w` | off | Skip caption lookup, always use Whisper |
81125

82-
By default the transcript is **saved to a file** — to `output_dir` from config if set, otherwise to the current directory, using the video title as the filename. Use `--print` to get stdout behaviour instead.
126+
By default the transcript is **saved to `~/Downloads`** using the video title as the filename. Change `output_dir` in config to save elsewhere. Use `--print` to get stdout behaviour instead.
83127

84128
CLI flags always override config values.
85129

86130
## Whisper models
87131

88-
Model weights are downloaded from HuggingFace on first use and cached at `~/.cache/huggingface/hub/`. Subsequent runs use the cached copy — no re-download. Override the location with the `HF_HUB_CACHE` environment variable.
132+
Model weights are downloaded from HuggingFace on first use and cached at `~/.cache/huggingface/hub/` (macOS/Linux) or `%USERPROFILE%\.cache\huggingface\hub\` (Windows). Subsequent runs use the cached copy — no re-download. Override with the `HF_HUB_CACHE` environment variable.
89133

90134
| Model | Size | Speed | Accuracy |
91135
|---|---|---|---|
@@ -115,7 +159,7 @@ Model weights are downloaded from HuggingFace on first use and cached at `~/.cac
115159
Yes: done No: fallback
116160
117161
┌──────▼──────┐
118-
│ Download │ ◄── yt-dlp
162+
│ Download │ ◄── yt-dlp + ffmpeg
119163
│ audio │ best quality stream
120164
└──────┬──────┘
121165
@@ -155,3 +199,4 @@ Uses [`youtube-transcript-api`](https://github.com/jdepoix/youtube-transcript-ap
155199
| [`faster-whisper`](https://github.com/SYSTRAN/faster-whisper) | Local speech-to-text |
156200
| [`typer`](https://typer.tiangolo.com/) | CLI framework |
157201
| [`rich`](https://rich.readthedocs.io/) | Terminal output |
202+
| [`platformdirs`](https://platformdirs.readthedocs.io/) | Platform-appropriate config paths |

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dependencies = [
99
"faster-whisper>=1.2.0",
1010
"typer>=0.12.0",
1111
"rich>=13.0.0",
12+
"platformdirs>=4.0.0",
1213
]
1314

1415
[project.scripts]

src/transcribe/cli.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,9 @@ def config_cmd(
227227
if edit:
228228
import os
229229
import subprocess
230-
editor = os.environ.get("EDITOR", "nano")
230+
import sys
231+
default_editor = "notepad" if sys.platform == "win32" else "nano"
232+
editor = os.environ.get("EDITOR", default_editor)
231233
subprocess.run([editor, str(path)])
232234
elif show:
233235
_console.print(path.read_text())

src/transcribe/config.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@
77
from pathlib import Path
88
from typing import Any
99

10-
CONFIG_PATH = Path.home() / ".config" / "yt-transcribe" / "config.toml"
10+
from platformdirs import user_config_dir
11+
12+
CONFIG_PATH = Path(user_config_dir("yt-transcribe")) / "config.toml"
1113

1214
# Written on first run if the file doesn't exist yet.
1315
_DEFAULT_TOML = """\
1416
[defaults]
1517
model = "turbo" # tiny | base | small | medium | turbo | large-v3
1618
language = "" # empty = auto-detect per video
17-
output_dir = "" # if set, transcripts are auto-saved here (uses video title as filename)
19+
output_dir = "~/Downloads" # transcripts are auto-saved here (uses video title as filename)
1820
output_extension = "txt"
1921
2022
[whisper]
@@ -28,7 +30,7 @@
2830
"defaults": {
2931
"model": "turbo",
3032
"language": "",
31-
"output_dir": "",
33+
"output_dir": "~/Downloads",
3234
"output_extension": "txt",
3335
},
3436
"whisper": {

0 commit comments

Comments
 (0)