A cross-platform terminal user interface for podcast management built with Rust.
📚 Documentation: For comprehensive architecture and design patterns, see ARCHITECTURE.md
v1.12.0 — The application has a fully working feature set for podcast subscription management, downloading, device sync, playlist management, audio playback, discovery, tagging, and optional scrobbling.
✅ Working Features:
- RSS feed subscription management with OPML import/export
- Episode browsing with comprehensive metadata
- Parallel episode downloads (configurable concurrent, default 3)
- MP3 metadata embedding (ID3 tags, artwork, track numbers, readable filenames)
- Device sync via interactive Sync buffer (F8) with dry-run preview and live progress
- Playlist management (user playlists,
Todayauto-playlist, smart playlists) - Search & filter by text, status, date range, favorites, and tags
- Download cleanup (auto on startup + manual
:clean-older-than) - Audio playback with NowPlaying buffer (F9), rodio backend, external player fallback
- Podcast discovery via PodcastIndex API (
:discover,:trending) - Favorites (
*), mark played (m) / unplayed (u), podcast tagging - 10+ themes: 4 built-in + 5 community bundled + user TOML themes with
extends - Keybinding presets (default, vim, emacs)
- Optional ListenBrainz scrobbling (disabled by default)
- Cross-platform builds (Windows x64/ARM64, Linux x64/ARM64) + NixOS flake
⏳ Not Yet Implemented:
- Episode notes
- Statistics tracking
✅ Completed Features:
- ✅ RSS Subscription Management - Subscribe to podcasts via RSS feeds
- ✅ OPML Import/Export - Non-destructive import and export of subscriptions
- ✅ Episode Management - Browse and manage episodes
- ✅ Download System - Parallel episode downloads with progress tracking and bulk cleanup
- ✅ MP3 Metadata - ID3 tags, artwork embedding, track numbers, readable folder names
- ✅ Device Sync - Sync downloads to MP3 players via interactive Sync buffer (F8) with dry-run preview and live progress
- ✅ Keyboard Navigation - Intuitive keybindings for efficient navigation (default/vim/emacs presets)
- ✅ Command Auto-completion - Intelligent command completion in minibuffer
- ✅ Buffer Management - Multiple buffers for different views
- ✅ Playlist Support - User playlists, auto-generated
Today(last 24h) playlist, and smart playlists - ✅ Search & Filter - Text search, status filter (including favorites), date range filter, tag filter
- ✅ Theme System - 4 built-in themes + 5 community themes (catppuccin-mocha, dracula, nord, gruvbox-dark, tokyo-night) + user TOML themes
- ✅ Cross-platform Build - Windows and Linux build support + NixOS flake
- ✅ Audio Playback - Play downloaded episodes with NowPlaying buffer (F9), rodio backend or external player
- ✅ Podcast Discovery - Search and browse podcasts via PodcastIndex API (
:discover,:trending) - ✅ Tagging - Organize podcasts with tags (
:tag,:untag,:filter-tag) - ✅ Favorites & Playback State - Favorite episodes (
*), mark played/unplayed (m/u) - ✅ Optional Scrobbling - ListenBrainz scrobbling support (disabled by default)
⏳ In Progress / Planned:
- ⏳ Episode Notes - Add personal notes to episodes (not yet implemented)
- ⏳ Statistics Tracking - Listen time and download statistics (not yet implemented)
Podcast TUI exposes three customization surfaces in config.json. See docs/KEYBINDINGS.md and docs/DEVICE_PROFILES.md for the full reference.
- Startup buffer — set
ui.startup_bufferto one ofhelp,podcast-list,downloads,sync,playlist-list,whats-new,now-playingto control which buffer opens on launch. Default:help. - Keybinding presets — set
keybindings.presettodefault,vim, oremacs. Per-action overrides go underkeybindings.bindings. - Device profiles — for MP3 players that show the literal filename (e.g. Innioasis Y1, generic USB DACs), define a profile under
device_profiles[]with afilename_templateand select it viaactive_device_profile. The local downloads directory is untouched; only the device-side filenames are rewritten during sync. Give a profile async_pathand switching to it points the active sync target at that device automatically. The:set-device-profileminibuffer command switches profiles at runtime.
The storage layer also caches all podcasts, episodes, and playlists in memory and persists the index to cache_index.json, making warm launches near-instant. Toggle with storage.cache_enabled (default true); rebuild with :cache-rebuild. See docs/STORAGE_DESIGN.md for benchmark numbers.
📖 Documentation:
- 📚 Complete guide: GETTING_STARTED.md - Detailed platform-specific instructions and quick start
- 🏗️ Architecture: ARCHITECTURE.md - System design and technical documentation
- Rust 1.75 or later
- Git
- Windows ARM64: Requires LLVM/Clang toolchain (see scripts/INSTALL-LLVM.md)
- Windows x64: Requires MSVC Build Tools (see scripts/INSTALL-MSVC-TOOLS.md)
- Linux: Standard build tools (gcc/clang) required
The application is currently in active development with core RSS/download features and audio playback complete.
🚧 Development Status: Pre-built binaries are available for testing core features (RSS subscriptions, downloads, audio playback, and UI).
winget install lqdev.PodcastTUI# Try it (zero commitment)
nix run github:lqdev/podcast-tui
# Install to profile
nix profile install github:lqdev/podcast-tui
# Or add to your NixOS configuration (flake-based):
# inputs.podcast-tui.url = "github:lqdev/podcast-tui";
# environment.systemPackages = [ podcast-tui.packages.${system}.default ];📖 See docs/NIX_PACKAGING.md for declarative config, Home Manager, development shell, and more.
Download the latest release for your platform from the releases page.
Windows (manual):
# Download and extract podcast-tui-vX.X.X-windows-x86_64.zip
# Run podcast-tui.exeLinux:
# Download and extract podcast-tui-vX.X.X-linux-x86_64.tar.gz
tar -xzf podcast-tui-vX.X.X-linux-x86_64.tar.gz
cd podcast-tui-vX.X.X-linux-x86_64
./podcast-tuigit clone https://github.com/lqdev/podcast-tui.git
cd podcast-tui
cargo build --release
./target/release/podcast-tui
# Optional: Install icon and desktop entry on Linux
./scripts/install-icon-linux.shLinux/macOS:
# Install build dependencies (one-time setup)
./scripts/install-build-deps.sh
# Quick local build
./scripts/build-linux.shWindows:
# Verify dependencies
.\scripts\install-build-deps.ps1
# Quick local build
.\scripts\build-windows.ps1See BUILD_SYSTEM.md for detailed build documentation and cross-platform build instructions.
- Install Docker and VS Code
- Install the Remote-Containers extension
- Clone the repository and open in VS Code
- Click "Reopen in Container" when prompted
- Run
cargo runto start the application
Podcast TUI features a custom icon combining a cassette tape and RSS feed symbol, representing the audio content and subscription management capabilities.
Linux: After installing from a release package, run the included install-icon-linux.sh script to add the application icon to your system's application menu and file manager.
Windows: The icon is automatically embedded in the executable and will appear in the taskbar, Task Manager, and file explorer.
See assets/README.md for more details about the icon design and installation.
- Start the application:
podcast-tui - Press
ato add your first podcast - Enter an RSS feed URL (try:
https://feeds.simplecast.com/54nAGcIl) - Navigate with arrow keys or Up/Down to browse episodes
- Press
Dto download episodes,F1or?for help - Press
Shift+Enteron a downloaded episode to play it
↑/↓- Move up/down←/→- Move left/rightPage Up/Page Down- Scroll by pageHome/End- Jump to top/bottomEnter- Select/activate itemSpace- Select/activate itemTab- Next bufferShift+Tab- Previous bufferCtrl+Page Up- Previous buffer (alternative)Ctrl+Page Down- Next buffer (alternative)
a- Add new podcast subscriptiond- Delete selected podcastr- Refresh selected podcast feedShift+R- Refresh all podcast feedsCtrl+r- Hard refresh (re-parse all episodes)
Enter- Open episode detail / navigate into playlistShift+D- Download episode (works in episode list and episode detail)Shift+XorX- Delete downloaded file for selected episodep- Add selected episode to a playlist*- Toggle episode as favoritem- Mark episode as playedu- Mark episode as unplayedCtrl+x- Delete ALL downloaded episodes and clean up:clean-older-than <duration>- Delete downloads older than duration (e.g.,7d,2w,1m):cleanup <duration>- Alias for clean-older-than
:playlists- Open playlist buffer:playlist-create [name]- Create playlist:playlist-delete <name>- Delete playlist:playlist-refresh- RefreshTodayauto-playlist:playlist-sync- Sync podcasts + playlists to device:smart-playlist <name>- Create a smart playlist with auto-generated rules (⚡prefix)
:discover <query>- Search for new podcasts via PodcastIndex API:trending- Browse trending podcasts
:tag <tag>- Add a tag to the selected podcast:untag <tag>- Remove a tag from the selected podcast:filter-tag <tag>- Filter podcast list by tag
:theme <name>- Switch theme- Built-in:
dark,light,high-contrast,solarized - Community:
catppuccin-mocha,dracula,nord,gruvbox-dark,tokyo-night - Custom: any name from
~/.config/podcast-tui/themes/<name>.toml
- Built-in:
F2- Switch to podcast listF3- Search (same as/)F4- Switch to downloadsF5- Refresh current bufferF6- Clear all active filtersF7- Switch to playlistsF8- Switch to sync bufferF9- Open NowPlaying bufferCtrl+b- Show buffer list / Switch bufferCtrl+k- Close current bufferCtrl+l- List all buffers
/orF3- Open search (filter by text, matches title + description):filter-status <new|downloaded|played|downloading|failed|favorited>- Filter by status:filter-date <today|12h|7d|2w|1m>- Filter by date range:filter-tag <tag>- Filter podcasts by tag:clear-filtersorF6- Clear all active filters
F1- Show helphor?- Show help:- Command promptEsc- Cancel/hide minibufferq- Quit applicationF10- Quit application
Shift+P- Toggle play/pauseShift+Enter- Play selected downloaded episodeCtrl+Left- Seek backward 10sCtrl+Right- Seek forward 10s+/=- Volume up-- Volume downF9- Open now playing buffer
See complete keybinding reference for all shortcuts.
Configuration is stored in JSON format at:
- Linux:
~/.config/podcast-tui/config.json - Windows:
%APPDATA%/podcast-tui/config.json
{
"downloads": {
"directory": "~/Downloads/Podcasts",
"concurrent_downloads": 3,
"cleanup_after_days": 30,
"sync_device_path": "/mnt/mp3player",
"sync_delete_orphans": true,
"sync_preserve_structure": true,
"sync_dry_run": false,
"sync_include_playlists": true,
"use_readable_folders": true,
"embed_id3_metadata": true,
"assign_track_numbers": true,
"download_artwork": true,
"include_episode_numbers": true,
"include_dates": false,
"max_filename_length": 150
},
"playlist": {
"today_refresh_policy": "daily",
"auto_download_on_add": true,
"download_retries": 3
},
"audio": {
"volume": 0.8,
"seek_seconds": 10,
"external_player": null,
"auto_play_next": false,
"remember_position": true
},
"ui": {
"theme": "dark",
"show_progress_bar": true,
"whats_new_episode_limit": 50
},
"discovery": {
"podcastindex_api_key": "",
"podcastindex_api_secret": "",
"max_results": 20
},
"scrobbling": {
"enabled": false,
"server_url": "https://api.listenbrainz.org",
"token": ""
}
}The device sync feature allows you to sync downloaded episodes and playlists to external MP3 players or USB devices via the interactive Sync buffer (F8):
sync_device_path: Default path to your device (can be overridden at runtime viapin the Sync buffer)sync_delete_orphans: Remove files on device that aren't on PC (default: true)sync_preserve_structure: Keep podcast folder structure on device (default: true)sync_dry_run: Preview changes without applying them (default: false)sync_include_playlists: Include playlists in sync (default: true)
Usage (via Sync buffer — F8):
Press F8 to open the Sync buffer, then:
s- Start sync to configured device pathd- Run a dry-run preview (shows what would change)p- Open directory picker to choose a different target path
The Sync buffer shows live progress during sync and a history of past sync operations.
Podcast TUI uses JSON files for data storage:
~/.local/share/podcast-tui/
├── config.json # Application configuration
├── podcasts/ # Podcast subscriptions
│ ├── {podcast-id}.json
├── episodes/ # Episode metadata and notes
│ ├── {podcast-id}/
│ │ ├── {episode-id}.json
├── playlists/ # Playlists metadata + audio copies
│ ├── Morning Commute/
│ │ ├── playlist.json
│ │ └── audio/
│ │ ├── 001-episode.mp3
│ ├── Today/
│ │ ├── playlist.json
│ │ └── audio/
└── themes/ # Optional user TOML theme files
└── my-theme.toml
This design allows for:
- Easy manual editing of data
- Simple backup (copy directory)
- Version control friendly
- Future storage backend options
The application follows a modular architecture with clear separation of concerns:
- Storage Layer - Trait-based abstraction with JSON implementation
- Domain Logic - Podcast, episode, and download management
- UI Layer - Buffer-based terminal interface using Ratatui
- Network Layer - Async HTTP with connection pooling
See ARCHITECTURE.md for comprehensive technical documentation including:
- Core architectural principles and design patterns
- Module structure and dependencies
- Storage abstraction design
- UI component patterns
- Data flow diagrams
We welcome contributions! Please see CONTRIBUTING.md for:
- Development setup instructions
- Code style guidelines (also in .github/copilot-instructions.md)
- Sprint process and project management
- Pull request requirements
- Architecture guidelines and best practices
- Testing strategy and guidelines (see docs/TESTING.md)
- PRD: Product Requirements Document
- Implementation Plan: 8-week sprint plan
- Project Board: Track progress and current sprint
- Issues: Bug reports and feature requests
- Language: Rust 2021
- TUI Framework: Ratatui + Crossterm
- Audio: Rodio
- HTTP: Reqwest
- RSS Parsing: feed-rs
- Serialization: Serde
- Async Runtime: Tokio
- Project Setup - Rust project structure, dependencies, and tooling
- Storage Layer - JSON-based storage with abstraction trait
- Data Models - Podcast, Episode, and configuration models
- Core UI Framework - TUI with Emacs-style buffers and keybindings
- Theme System - 4 built-in + 5 community themes + user TOML themes with
extends - Keybinding Presets - Default, vim, and emacs preset support
- RSS Subscription Management - Subscribe to podcasts via RSS feeds
- OPML Import/Export - Non-destructive import and export of subscriptions
- Episode Parsing - RSS feed parsing and episode extraction
- Download System - Parallel episode downloads with progress tracking
- Episode Management UI - Browse and manage episodes
- Download Cleanup - Auto-cleanup on startup + manual
:clean-older-than - Application Icon - Custom cassette+RSS icon, embedded in Windows exe
- Device Sync - Interactive Sync buffer (F8) with dry-run, directory picker, live progress
- MP3 Metadata - ID3 tags, artwork, track numbers, readable filenames
- Search & Filter - Text search, status filter (including favorites), date range filter, tag filter
- Playlist Management - User playlists + auto-generated
Todayplaylist - Smart Playlists - Rule-based playlists via
:smart-playlist - Winget Publishing - Available on Windows Package Manager
- Audio Playback - Rodio backend with NowPlaying buffer (F9) and external player fallback
- Podcast Discovery - Search and browse via PodcastIndex API (
:discover,:trending) - Tagging - Organize podcasts with custom tags
- Favorites & Playback State - Mark favorites (
*), played (m) / unplayed (u) - ListenBrainz Scrobbling - Optional listen tracking (disabled by default)
- NixOS Flake - Nix packaging for NixOS and nix-based systems
- Episode Notes - Add personal notes to episodes
- Statistics Tracking - Listen time and download statistics
- Duration Filter - Filter episodes by duration (deferred pending RSS duration data)
- SQLite storage backend option
- Plugin architecture
- Cloud synchronization (optional)
Not Yet Implemented:
⚠️ Episode notes - planned for a future release⚠️ Statistics tracking - planned for a future release⚠️ Duration filter - deferred until episode duration is reliably populated from RSS feeds
Build Requirements:
- Windows ARM64 builds require LLVM/Clang (see scripts/INSTALL-LLVM.md)
- Windows x64 builds require MSVC Build Tools
- Linux requires
libasound2-devandpkg-config
Current Limitations:
- RSS feed parsing works with most standard feeds; some non-standard audio URL formats may not parse correctly
- Terminal compatibility tested on Windows Terminal, GNOME Terminal, and similar
See GitHub Issues for current bugs and feature requests.
Licensed under the MIT License. See LICENSE for details.
- Ratatui community for excellent TUI framework
- feed-rs for robust RSS parsing
- Terminal UI community for design inspiration
- Rust community for excellent tooling and libraries
Status: 🚀 Active Development (v1.12.0)
Maintainer: @lqdev
Version: 1.12.0