A desktop audiobook player built with Tauri v2 and Svelte 5. Lightweight, fast, and fully offline.
Library — browse, search, and organize your audiobooks
Player — chapter navigation, equalizer, speed control, and waveform seekbar
Settings — themes, library folders, and preferences
Mini Player — compact always-on-top window
See the Releases page for:
.exe, no installation requiredNote: Windows Defender SmartScreen may show a warning because the app is not yet code-signed. This is normal for indie software. Click "More info" then "Run anyway" to proceed. The app is fully open-source — you can inspect the code or build it yourself.
ABPlayer is a Tauri v2 desktop application with a Svelte 5 frontend and a Rust backend. All data stays local — no network calls, no accounts, no telemetry.
┌──────────────────────────────────────────────────────┐
│ Svelte 5 Frontend (WebView) │
│ │
│ ┌─────────┐ ┌──────────┐ ┌────────────────────┐ │
│ │ Library │ │ Player │ │ Mini-Player Window │ │
│ │ View │ │ View │ │ (separate WebView)│ │
│ └────┬─────┘ └────┬─────┘ └────────┬───────────┘ │
│ │ │ │ │
│ ┌────┴──────────────┴─────────────────┴───────────┐ │
│ │ Svelte Stores (reactive state) │ │
│ │ audioStore · libraryStore · positionStore │ │
│ │ userdataStore · bookmarkStore · statisticsStore │ │
│ └────────────────────┬────────────────────────────┘ │
│ │ │
│ ┌────────────┴────────────┐ │
│ │ @tauri-apps/plugin-store │ │
│ │ (JSON key-value on disk) │ │
│ └────────────┬────────────┘ │
├───────────────────────┼──────────────────────────────┤
│ Rust Backend │ │
│ │ │
│ ┌────────────────────┴────────────────────────────┐ │
│ │ Tauri Commands (IPC) │ │
│ │ read_audio_meta · audiostream:// protocol │ │
│ └─────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
Audio playback uses a custom audiostream:// protocol registered in Rust, which serves local audio files with proper CORS headers. This is necessary because the Web Audio API's createMediaElementSource requires CORS-compliant responses — the default Tauri asset protocol does not provide these.
audiostream://localhost/{encoded-file-path}
│
▼
Rust handler
├── URL-decodes the file path (percent-encoding crate)
├── Canonicalizes the path (prevents directory traversal)
├── Reads the file with Range request support (HTTP 206)
├── Sets CORS headers (Access-Control-Allow-Origin: *)
├── Sets Content-Type based on file extension
└── Returns audio bytes with Cache-Control: immutable
On the frontend, the signal chain is:
HTMLAudioElement (src = audiostream://...)
│
▼
MediaElementAudioSourceNode
│
▼
BiquadFilterNode (lowshelf, 250 Hz) ← Bass EQ
│
▼
BiquadFilterNode (peaking, 1000 Hz) ← Mid EQ
│
▼
BiquadFilterNode (highshelf, 4000 Hz) ← Treble EQ
│
▼
GainNode (per-book volume, -12 to +12 dB)
│
▼
AudioContext.destination
All EQ parameter changes use setTargetAtTime for smooth transitions that prevent audio pops.
Metadata is extracted through a two-layer approach:
Primary — Rust lofty crate (called via Tauri IPC command read_audio_meta):
CoverFront picture type first, then first available pictureMP4/M4B Chapter Extraction (custom Rust parser):
moov atom tree manually (not via lofty — lofty doesn't expose chapters)tref → chap track → stco/co64 + stsz + stts)udta → chpl atom)Fallback — JS music-metadata (frontend, first 2 MB):
Duration fallback: If the Rust crate returns 0 duration, a temporary <audio> element probes the file via the audiostream protocol to get the browser's duration reading.
The library is a flat list of BookMeta objects stored in library.json via Tauri's plugin-store.
Scanning flow:
readDir recursively finds audio files (.mp3, .m4a, .m4b, .mp4, .ogg).m4b) are always treated as single-file books.extractMetadata is called on the first track. Title, author, album, cover art, and duration are populated. Updates are batched (5 books per store update) to minimize reactive re-renders.$APPDATA/covers/ as JPEG files. The library store is updated with asset-protocol URLs. This runs once; subsequent loads skip migration.library.json.File watching: After the initial scan, a recursive file watcher monitors all library folders. New or changed files trigger a debounced rescan (2-second delay after the last change event).
All app state is persisted via @tauri-apps/plugin-store, which writes JSON files to the OS app data directory (%APPDATA%/com.abplayer.app/ on Windows).
| Store file | Contents |
|---|---|
library.json |
Book metadata, folder list, last scan timestamp |
positions.json |
Per-book playback position, track index, duration, last played timestamp |
bookmarks.json |
Named position markers per book (label, time, track index) |
statistics.json |
Listening time per day, books finished count, streaks |
userdata.json |
Per-book overrides (title, author, cover, genre, series, status, collections), user preferences (theme, sort, view mode), collection definitions |
Position saving follows a belt-and-suspenders approach:
setIntervalonCloseRequested event (more reliable than the browser's beforeunload)Resume logic: When opening a book, if a saved position exists and is past 10 seconds (or on a track beyond the first), a resume prompt is shown. Positions below 10 seconds are treated as "not started" to avoid prompting for accidental plays.
unsafe-eval, object embeds, and unauthorized origins. Only self, Google Fonts, and the custom protocols (audiostream://, asset://) are allowed.../ directory traversal attacks.src/ # Svelte 5 frontend
App.svelte # Root layout: sidebar + library/player views
MiniPlayer.svelte # Separate mini-player window
lib/
components/ # UI components (BookCard, Player, Library, etc.)
stores/ # Reactive stores
audioStore.ts # Playback state, Web Audio API, track management
libraryStore.ts # Book scanning, indexing, folder management
positionStore.ts # Position save/load, auto-save, resume prompt
userdataStore.ts # User overrides, collections, preferences
bookmarkStore.ts # Named position markers
statisticsStore.ts # Listening time tracking
sleepTimerStore.ts # Sleep timer logic
storeUtils.ts # Shared plugin-store cache
utils/
metadata.ts # Metadata extraction (Rust IPC + JS fallback)
coverStorage.ts # Base64 → disk migration, asset URL resolution
format.ts # Time formatting, progress calculation
sort.ts # Natural sort, library sort/filter
types.ts # TypeScript type definitions
themes.ts # 6 color theme presets
src-tauri/ # Rust backend
src/lib.rs # Tauri commands, audiostream protocol, MP4 parser
capabilities/
main.json # Full permissions (main window)
mini-player.json # Minimal permissions (mini-player)
tauri.conf.json # App config, CSP, bundle settings
npm install
npm run tauri dev
npm run tauri build
Outputs:
src-tauri/target/release/abplayer.exe (portable)src-tauri/target/release/bundle/nsis/ABPlayer_*_x64-setup.exe (installer)Created by jhidalgo_dev
All rights reserved. This source code is provided for viewing purposes. See the repository for download links.