- What’s new in v0.3.0
- Features
- Screenshots
- Quick start
- Keyboard shortcuts
- Architecture
- Development
- Packaging
- Troubleshooting
- Roadmap
- FAQ
- Project docs
- Contributing
- Security
- License
- Acknowledgments
- 🎞 YouTube playlist import — fetch a full playlist, select individual videos, set format/quality/metadata, and download as a batch. Auto-creates an app playlist on completion, matched by download history (no more fuzzy title guessing).
- 🔎 Global search overlay — Ctrl+K command-palette style overlay for tracks, albums, artists, and playlists. Keyboard navigable (↑/↓/Enter/Esc).
- 🖱 Track right-click context menu — Play Now, Add to Queue (Next/Last), Add to Playlist submenu, Toggle Favorite. Available in Library, Playlist Detail, and Home.
- ☑ Playlist mass selection — checkbox per row, Select All, batch delete in a single SQLite transaction.
- ⚡ Virtualized grids — Albums, Artists, and Playlists use
@tanstack/react-virtual. Libraries with 200+ albums no longer freeze. - 🚀 Single-transaction queue — playing an album/artist/playlist fires one
queue:setIPC call instead of N sequential adds. - 🌓 Home polish — time-aware greeting, favorites section, real mosaic covers for album/artist/playlist cards.
See the full CHANGELOG.
| Feature | Description | |
|---|---|---|
| 🎵 | Local-first library | Scan any folder. Albums, artists, folders, and playlists — indexed locally with better-sqlite3 |
| ⬇ | YouTube import | Search or paste a URL, pick a video or full playlist, choose format/quality, and import via yt-dlp + FFmpeg |
| 🎚 | 10-band equalizer | Bundled presets (Flat, Bass, Treble, Vocal, Rock, …), persisted across sessions |
| 📝 | Queue & playlists | Batch queue, shuffle, repeat, right-click context, mass selection, mosaic playlist covers |
| 🔁 | Last.fm scrobbling | Authenticated session, now-playing updates, offline-tolerant scrobble queue |
| ⌨ | Linux media keys | MPRIS via dbus-next — play/pause/next/prev/seek from keyboard, tray, or lock screen |
| 🔎 | Global search | Ctrl+K command-palette overlay for tracks, albums, artists, and playlists |
| 🎨 | Editorial dark UI | Warm ink-and-gold palette, big album art, thin dividers, direct controls |
| ⚡ | Fast & virtualized | Virtualized grids keep 200+ album libraries snappy |
| 🔒 | 100% local, private | No accounts, no telemetry, no cloud sync. Your files stay on your machine |
![]() |
![]() |
| Detailed now-playing — playback stays visible while browsing. | YouTube import — search, pick, and download into your library. |
- Linux (primary target) or Windows
ffmpegon your system pathyt-dlpon your system path (optional — only for YouTube import)
curl -fsSL https://twarga.github.io/Tplayer/install.sh | bashThis downloads the latest AppImage from GitHub Releases and installs it into ~/.local/bin.
- Linux AppImage — Latest release
- Windows installer (NSIS) — Latest release
- Source — Twarga/Tplayer
- Pick a folder to scan — Tplayer indexes your library locally
- (Optional) Paste a YouTube URL in the YouTube tab to import audio
- (Optional) Connect Last.fm in Settings to scrobble
- Press Ctrl+K from anywhere to jump around
| Keys | Action |
|---|---|
| Space | Play / pause |
| Ctrl + K | Global search |
| ← / → | Seek −5s / +5s |
| ↑ / ↓ | Volume up / down |
| N / P | Next / previous track |
| S / R / F | Shuffle / repeat / favorite |
| Esc | Close dialog or overlay |
Linux media keys (play/pause/next/prev) also work globally via MPRIS.
Tplayer/
├── src/
│ ├── main/ ← Electron main: SQLite, yt-dlp pipeline, MPRIS, Last.fm scrobbler
│ ├── preload/ ← Context-isolated bridge, typed IPC to the renderer
│ ├── renderer/ ← React UI — Home, Library, Albums, Artists, Playlists, YouTube, Settings
│ └── shared/ ← Domain types + IPC channel contracts (used by both sides)
├── site/ ← GitHub Pages landing page (index.html + styles.css)
├── assets/ ← Banner, screenshots, logo, install.sh
├── docs/ ← Brand, release checklist, production readiness, repo hygiene
├── build/ ← Packaging icons (icon.png, icon.ico)
└── .github/ ← Release + Pages workflows, issue/PR templates
Three-process Electron model with typed IPC boundaries:
┌───────────────────────────────┐ ┌──────────────────────────────────┐
│ Renderer (src/renderer) │ │ Main process (src/main) │
│ React 19 · Zustand · Tailwind │◀──▶ │ Electron · yt-dlp · FFmpeg │
│ Virtualized grids · Radix UI │ IPC │ better-sqlite3 · MPRIS · Last.fm │
└───────────────────────────────┘ └──────────────────────────────────┘
▲ ▲
│ shared types (src/shared) │
└──────────────────────────────────────┘
typed contracts through preload
(src/preload)
Stack: Electron 33 · React 19 · Vite 5 · TypeScript 5 · Tailwind CSS 3 · Zustand · Radix UI · @tanstack/react-virtual · better-sqlite3 · music-metadata · yt-dlp · fluent-ffmpeg · dbus-next
- Node.js 18+ and npm
ffmpegon your system pathyt-dlpon your system path (only if you want to test YouTube import)
git clone https://github.com/Twarga/Tplayer.git
cd Tplayer
npm installnpm run dev # hot-reload dev app
npm run typecheck # tsc --noEmit
npm run build # compile main + preload + renderer
npm run lint # eslintnpm run package:linux # Linux AppImage
npm run package:win # Windows NSIS installer
npm run package:dir # Unpacked directory (fast smoke test)Artifacts land in release/. Tagged pushes (vX.Y.Z) trigger the GitHub Actions release workflow, which builds the Linux AppImage and Windows installer and publishes them to GitHub Releases.
AppImage won’t run
chmod +x Tplayer-0.x.y.AppImage
./Tplayer-0.x.y.AppImage
# if your kernel rejects the default sandbox:
./Tplayer-0.x.y.AppImage --no-sandboxYouTube import fails
yt-dlp --version # confirm yt-dlp is installed and on PATH
pip install -U yt-dlp # or your package manager's upgrade pathMedia keys don’t work (Linux)
→ Confirm MPRIS is enabled on your desktop. Quick check:
playerctl --list-all # should list 'tplayer' while the app is runningLibrary scan is slow the first time
→ Expected — first-run parses ID3/tag metadata for every file. Subsequent scans are incremental and fast.
Last.fm scrobbles don’t show up
→ Open Settings → Last.fm and confirm it shows Connected. Offline scrobbles are queued locally and flushed when the network returns.
Playback is silent on Linux
→ Tplayer uses the Chromium audio backend, so it follows the same output routing as Firefox or Chromium. Check PipeWire/PulseAudio output device.
ffmpeg not found
# Debian / Ubuntu
sudo apt install ffmpeg
# Fedora
sudo dnf install ffmpeg
# Arch
sudo pacman -S ffmpeg- Local playback, library scan, queue, EQ, favorites
- YouTube video and playlist import, download history
- Last.fm now-playing and scrobble
- MPRIS Linux integration
- Packaging — Linux AppImage + Windows NSIS
- GitHub Pages landing page + release workflow
- Virtualized grids & single-transaction queue
- macOS packaging
- Lyrics provider integration
- Smart playlists / auto-mixes
- Mobile remote control (MPRIS already exposed)
See docs/planning/remake.md for the complete roadmap and design notes.
Is Tplayer free?
Yes. Tplayer is free and open source under the MIT license. No accounts, no paid tiers, no telemetry.
Does it work offline?
Yes. Local playback, library browsing, queue, and EQ all work fully offline. Only YouTube import and Last.fm scrobbling need the network.
What audio formats are supported?
MP3, FLAC, M4A/AAC, OGG, and WAV. YouTube imports default to high-quality M4A via yt-dlp + FFmpeg.
Which operating systems are supported?
Linux is the primary target (AppImage). Windows installer ships as well. macOS packaging is planned.
Does Tplayer upload or collect my music?
No. Tplayer is fully local-first. Your files and listening history never leave your machine unless you enable Last.fm scrobbling.
Where do imported YouTube tracks live?
In the same library you already browse, tagged with source = 'youtube' in the database so the Home view can surface recent imports separately.
Why Electron and not a native toolkit?
Electron + React lets the UI move fast and stay editorial on Linux and Windows from a single codebase. Performance-critical work (scanning, queue, EQ, yt-dlp orchestration) happens in the Node main process, not the renderer.
| Document | Purpose |
|---|---|
docs/brand.md |
Public identity: voice, colors, screenshot direction |
docs/planning/remake.md |
Completed MVP work and post-MVP release plan |
docs/release-checklist.md |
Release requirements and manual testing steps |
docs/production-readiness.md |
B1–B10 production-preparation summary |
docs/repository-hygiene.md |
Ignore list and clean-repo rules |
CONTRIBUTING.md |
Local setup, commit scope, and contribution rules |
Contributions are welcome — especially around packaging, platform support, and UI polish. By participating in this project, you agree to abide by the Code of Conduct.
See CONTRIBUTING.md for setup, commit scope, and repo-hygiene rules. Before opening a pull request:
npm run typecheck
npm run buildKeep commits small and task-focused. UI changes should stay inside the warm-dark editorial direction described in docs/brand.md.
If you discover a security issue, please do not open a public issue. See SECURITY.md for the private disclosure flow.
Tplayer stands on the shoulders of an incredible open-source ecosystem. Special thanks to:
- yt-dlp — for making YouTube import reliable and respectful
- FFmpeg — for doing the media heavy lifting
- better-sqlite3 — fast, synchronous SQLite for Node
- music-metadata — for clean tag parsing
- electron-vite — for the nicest Electron DX today
- Radix UI — for accessible headless primitives
- Lucide — for the icon set
- The Last.fm, MPRIS, and D-Bus communities for keeping desktop Linux media alive
Without music, life would be a mistake.

