README.md (8972B)
1 <div align="center"> 2 3 # Podcast Downloader 4 ### Vibe coded Go TUI for downloading your favorites 5 6 <img src="./media/demo-podcastdownload.gif" width="800" alt="Demo of the TUI"> 7 8 </div> 9 10 --- 11 12 ## Features 13 14 - **Search by name**: Search for any podcast by name using Apple's podcast directory 15 - **Podcast Index support**: Search podcasts not in Apple's index (e.g., Radio France, European podcasts) 16 - **Unified search**: Automatically searches both Apple and Podcast Index when credentials are configured (with deduplication) 17 - **Lookup by ID**: Direct lookup using Apple Podcast ID for faster access 18 - **Interactive selection**: Browse and select specific episodes to download 19 - **Preview metadata**: View detailed podcast/episode metadata before downloading 20 - **Back navigation**: Navigate back through screens without restarting 21 - **Batch downloads**: Select multiple episodes at once with visual progress tracking 22 - **ID3 tagging**: Automatically writes ID3v2 tags (title, artist, album, track number) 23 - **Smart file naming**: Episodes are saved with track numbers for proper ordering 24 - **Resume support**: Skips already downloaded files 25 26 ## Requirements 27 28 - Go 1.21 or later 29 - [just](https://github.com/casey/just) (optional, for build commands) 30 31 ## Installation 32 33 ### Clone from GitHub 34 35 ```bash 36 git clone https://github.com/eloualiche/podcast-go.git 37 cd podcast-go 38 ``` 39 40 ### Build 41 42 Using just (recommended): 43 44 ```bash 45 just build 46 ``` 47 48 Or using go directly: 49 50 ```bash 51 go build -o podcastdownload main.go 52 ``` 53 54 ### Install globally (optional) 55 56 To use `podcastdownload` from anywhere: 57 58 ```bash 59 go install 60 ``` 61 62 Or move the binary to your PATH: 63 64 ```bash 65 sudo mv podcastdownload /usr/local/bin/ 66 ``` 67 68 ## Usage 69 70 ### Basic Commands 71 72 ```bash 73 # Search for a podcast by name (uses Apple Podcasts by default) 74 ./podcastdownload "the daily" 75 76 # Search with multiple words 77 ./podcastdownload "new york times podcast" 78 79 # Lookup by Apple Podcast ID (faster, no search step) 80 ./podcastdownload 1200361736 81 82 # Specify output directory 83 ./podcastdownload -o ~/Music "the daily" 84 ``` 85 86 ### Using Podcast Index 87 88 Some podcasts (like Radio France, many European podcasts) are not indexed by Apple Podcasts. You can search these using [Podcast Index](https://podcastindex.org/), an open podcast directory with over 4 million podcasts. 89 90 #### Setup 91 92 1. Get free API credentials at https://api.podcastindex.org (instant, no approval needed) 93 94 2. Set environment variables (**use single quotes** to preserve special characters): 95 96 ```bash 97 export PODCASTINDEX_API_KEY='your_api_key' 98 export PODCASTINDEX_API_SECRET='your_api_secret' 99 ``` 100 101 > **Important**: Many API secrets contain `$` characters. Using double quotes will cause the shell to interpret `$` as a variable, breaking authentication. Always use single quotes. 102 103 3. Add to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.) to persist: 104 105 ```bash 106 # Podcast Index API credentials 107 export PODCASTINDEX_API_KEY='your_api_key' 108 export PODCASTINDEX_API_SECRET='your_api_secret' 109 ``` 110 111 #### Usage 112 113 When Podcast Index credentials are configured, searches automatically query **both** Apple and Podcast Index, with duplicate results removed: 114 115 ```bash 116 # Unified search (searches both Apple + Podcast Index automatically) 117 ./podcastdownload "france inter" 118 119 # Force search only Podcast Index 120 ./podcastdownload --index podcastindex "france inter" 121 ./podcastdownload --index pi "radio france" # shorthand 122 123 # Force search only Apple 124 ./podcastdownload --index apple "the daily" 125 ``` 126 127 ### Finding a Podcast ID 128 129 The podcast ID can be found in any Apple Podcasts URL: 130 131 ``` 132 https://podcasts.apple.com/us/podcast/the-daily/id1200361736 133 ^^^^^^^^^^ 134 This is the ID 135 ``` 136 137 You can also: 138 1. Open Apple Podcasts app or website 139 2. Navigate to the podcast page 140 3. Copy the URL - the ID is the number after `id` 141 142 ## Workflow 143 144 ### 1. Search or Lookup 145 146 When you run the app with a search query, you'll see matching podcasts: 147 148 ``` 149 Search Results: "the daily" 150 Found 25 podcasts 151 152 ▸ The Daily The New York Times 153 The Daily Beans MSW Media 154 Daily Tech News Show Tom Merritt 155 ... 156 ``` 157 158 ### 2. Select Episodes 159 160 After choosing a podcast, browse the episode list: 161 162 ``` 163 The Daily 164 by The New York Times • 2847 episodes 165 166 ▸ ○ [ 1] The Sunday Read: 'The Kidnapping... 2024-01-07 45:32 167 ● [ 2] A Landmark satisfies Lawsuit... 2024-01-06 28:15 168 ● [ 3] The Fight Over the Future... 2024-01-05 31:42 169 ... 170 171 Showing 1-20 of 2847 • 2 selected 172 173 ↑/↓ navigate • space select • a toggle all • v preview • enter download • esc/b back • q quit 174 ``` 175 176 ### 3. Download 177 178 Selected episodes are downloaded with a progress bar: 179 180 ``` 181 Downloading... 182 183 Episode 1 of 2 184 002 - A Landmark Lawsuit.mp3 185 186 ████████████████████░░░░░░░░░░░░░░░░░░░░ 52% 187 188 ✓ 0 completed 189 ``` 190 191 ### 4. Output 192 193 Episodes are saved to a folder named after the podcast: 194 195 ``` 196 The Daily/ 197 ├── 001 - The Sunday Read.mp3 198 ├── 002 - A Landmark Lawsuit.mp3 199 └── 003 - The Fight Over the Future.mp3 200 ``` 201 202 Each file includes ID3 tags: 203 - **Title**: Episode title 204 - **Artist**: Podcast creator/network 205 - **Album**: Podcast name 206 - **Track**: Episode number 207 208 ## Keyboard Controls 209 210 ### Search Results Screen 211 212 | Key | Action | 213 |-----|--------| 214 | `↑` / `k` | Move cursor up | 215 | `↓` / `j` | Move cursor down | 216 | `Enter` | Select podcast | 217 | `v` | Preview podcast metadata | 218 | `q` / `Ctrl+C` | Quit | 219 220 ### Episode Selection Screen 221 222 | Key | Action | 223 |-----|--------| 224 | `↑` / `k` | Move cursor up | 225 | `↓` / `j` | Move cursor down | 226 | `Space` / `x` | Toggle episode selection | 227 | `a` | Select/deselect all episodes | 228 | `PgUp` | Page up | 229 | `PgDn` | Page down | 230 | `v` | Preview episode metadata | 231 | `Enter` | Start downloading selected | 232 | `Esc` / `b` | Go back to search results | 233 | `q` / `Ctrl+C` | Quit | 234 235 ### Download Screen 236 237 | Key | Action | 238 |-----|--------| 239 | `Esc` / `b` | Go back to episode selection | 240 | `q` / `Ctrl+C` | Cancel and quit | 241 242 ### Complete/Error Screen 243 244 | Key | Action | 245 |-----|--------| 246 | `Enter` / `q` | Exit | 247 | `Ctrl+C` | Exit | 248 249 ## Build Commands 250 251 Using `just`: 252 253 ```bash 254 just build # Build the binary 255 just run # Build and run 256 just clean # Remove build artifacts 257 ``` 258 259 Or use `just --list` to see all available commands. 260 261 ## Dependencies 262 263 | Library | Purpose | 264 |---------|---------| 265 | [Bubble Tea](https://github.com/charmbracelet/bubbletea) | TUI framework | 266 | [Lip Gloss](https://github.com/charmbracelet/lipgloss) | Terminal styling | 267 | [Bubbles](https://github.com/charmbracelet/bubbles) | Progress bar, spinner components | 268 | [gofeed](https://github.com/mmcdole/gofeed) | RSS/Atom feed parsing | 269 | [id3v2](https://github.com/bogem/id3v2) | MP3 ID3 tag writing | 270 271 ## How It Works 272 273 1. **Search/Lookup**: Uses Apple's iTunes Search API or Podcast Index API to find podcasts 274 2. **Feed Parsing**: Fetches and parses the podcast's RSS feed using gofeed 275 3. **Download**: Downloads MP3 files from the enclosure URLs in the RSS feed 276 4. **Tagging**: Writes ID3v2 tags to each downloaded file 277 278 ### Search Providers 279 280 | Provider | Flag | Coverage | Notes | 281 |----------|------|----------|-------| 282 | Apple Podcasts | `--index apple` (default) | Large, US-centric | No API key needed | 283 | Podcast Index | `--index podcastindex` | 4M+ podcasts, open | Free API key required | 284 285 ## Troubleshooting 286 287 ### "No RSS feed URL found" 288 289 Some podcasts don't expose their RSS feed publicly. This app requires access to the RSS feed to download episodes. 290 291 ### "No downloadable episodes found" 292 293 The podcast's RSS feed doesn't contain audio enclosures, or uses a format not recognized as audio. 294 295 ### Download seems stuck 296 297 Some podcast CDNs may be slow. The progress bar updates every 1% of download progress. For large files on slow connections, this may take a moment. 298 299 ### Podcast Index: "Authorization header doesn't match" 300 301 This usually means your API secret contains special characters that got mangled. Check: 302 303 1. **Use single quotes** when setting environment variables: 304 ```bash 305 # Wrong - $ gets interpreted as variable 306 export PODCASTINDEX_API_SECRET="secret$with$dollars" 307 308 # Correct - single quotes preserve literal value 309 export PODCASTINDEX_API_SECRET='secret$with$dollars' 310 ``` 311 312 2. **Verify your credentials** are set correctly: 313 ```bash 314 echo "Key: [$PODCASTINDEX_API_KEY]" 315 echo "Secret: [$PODCASTINDEX_API_SECRET]" 316 ``` 317 318 3. **Check for trailing whitespace** - copy credentials carefully from the email. 319 320 ### Podcast Index: "API credentials not set" 321 322 Set the environment variables before running: 323 ```bash 324 export PODCASTINDEX_API_KEY='your_key' 325 export PODCASTINDEX_API_SECRET='your_secret' 326 ``` 327 328 Get free credentials at https://api.podcastindex.org 329 330 ## License 331 332 MIT