kopuz

Your next music player

Kopuz-org
Rust1.7k92 forksMITupdated 1 day ago
visit the demogit clone https://github.com/Kopuz-org/kopuz.gitKopuz-org/kopuz
Kopuz Logo

Kopuz

Kopuz is a modern, lightweight, music player application built with Rust and the Dioxus framework. It provides a clean and responsive interface for managing and enjoying your local music collection.

Discord Rust Build Kopuz

English | Türkçe | Português de Portugal | മലയാളം

About the Name

The kopuz is an ancient Turkic string instrument and is often considered the ancestor of many Central Asian lutes. It was traditionally used by bards and shamans.

The Kyrgyz komuz is not the same instrument, but likely a descendant of the kopuz. The Kazakh kobyz is also related, though it is bowed rather than plucked. In contrast, the Tuvan/Yakut xomus (jaw harp) is unrelated, despite the similar name.

In Turkic legend, the kopuz is linked to Dede Korkut, a legendary bard, though this is mythological rather than historical.

Overview

Kopuz allows you to scan your local directories for audio files, stream from your Jellyfin or Subsonic (Navidrome, etc.) server, or connect YouTube Music, SoundCloud, or Spotify as a streaming backend, automatically organizing everything into a browsable library. You can navigate by artists, albums, genres, or explore your custom playlists. The application is built for performance and desktop integration, utilizing the power of Rust.

Library, playlists, favorites, and settings are stored in a local SQLite database (kopuz.db); the UI reads it live so changes show up immediately. Each media source carries its own credentials and its own favorites.

Features

  • Theming: Includes dynamic theming support to customize the visual appearance. You can also build your own custom theme from scratch with full color variable control.
  • Native Integration: Integrates with system media controls on Linux (MPRIS), macOS (Now Playing / Remote Command Center), and Windows (System Media Transport Controls).
  • Mini-Player: A compact player overlay you can toggle from the bottom bar for a smaller now-playing view.
  • Minimize to Tray: Optionally close to a system tray icon instead of quitting, so playback keeps running in the background. Toggle in Settings. Requires the appindicator library on Linux (see Installation notes).
  • Discord RPC: Embedded RPC included!!!
  • Multiple Backends: Stream from your Jellyfin or Subsonic-compatible server (Navidrome works great), connect YouTube Music, SoundCloud, or Spotify, or just point it at a local folder. Mix and match as you like. Every source is exposed through one unified MediaSource layer, and the UI adapts to each source's capabilities (search, downloads, radio, discover, favorites sync, etc.) rather than hardcoding per-service behavior.
  • YouTube Music: Full streaming backend with a Spotify-style Discover page (recommended songs, playlists, albums, artists, and moods), rich artist profiles (banner, top songs, albums, singles, related artists), album/playlist browsing, and mix radio ("start radio" from any track). Sign in with your account for your library, Liked Music, and playlists - or run it anonymously (no sign-in) for browse, search, and playback of public tracks. See YouTube Music Setup.
  • SoundCloud: Streaming backend with search, track playback (progressive MP3 and Go+ AAC/HLS), your Liked tracks as favorites, read-only playlists, and like/unlike. Added via a one-time browser sign-in in an isolated profile. See SoundCloud Setup.
  • Spotify: Your saved tracks, albums, and playlists, a Discover page, search, and scrobbling, with playback through Spotify's official Web Playback SDK or any Spotify Connect device you own. Needs Premium and a one-time app setup. See Spotify Setup.
  • Lyrics Support: Enjoy real-time synced and plain lyrics, complete with auto-scrolling to follow along with your music.
  • Favorites: Star tracks locally or sync favorites with your Jellyfin/Subsonic server.
  • Playlists: Create and manage your own playlists, add individual tracks or whole albums at once, and sync playlists to your server.
  • Genre Browsing: Browse your library by genre for both local and server music.
  • File-Type Badges: Local tracks show a small format badge (MP3, FLAC, WAV, etc.) in track rows so you can see the source format at a glance.
  • Search: Search across artists, albums, and tracks with real-time results, plus a quick search overlay you can pop open from anywhere to jump straight to what you're looking for.
  • Custom UI Fonts: Bring your own font for the interface and make Kopuz look the way you want.
  • Listening Logs: Tracks play counts locally so you can see what you actually listen to most.
  • Scrobbling: Scrobble to ListenBrainz. For Jellyfin users, jellyfin-plugin-listenbrainz is recommended if you use multiple clients.
  • Language Support: UI available in Arabic, Brazilian Portuguese, Dutch, English, European Portuguese, Filipino, French, German, Greek, Hebrew, Hungarian, Indonesian, Italian, Japanese, Korean, Malayalam, Polish, Romanian, Russian, Simplified Chinese, Spanish, Swedish, Tamil, Toki Pona, Turkish, Ukrainian, Vietnamese, and Sitelen Pona with a streamlined experience for adding new languages.
  • High Performance: Heavy background processing and an optimized library scanner ensure the app opens instantly, runs smoothly, and skips previously indexed files quickly.
  • Auto-Cleanup: Automatically removes missing or deleted tracks from your library when rescanning.
  • Smooth Navigation: Enjoy a polished interface where scroll positions reset properly as you browse different views and pages.
  • Reduce Animations: Accessibility setting to tone down motion effects if you prefer a calmer UI.
  • Equalizer: Built-in 10-band equalizer with presets and custom settings to fine-tune your sound.
  • Crossfade: Blend track transitions for smoother automatic playback between songs on native desktop builds. Browser playback currently uses normal track switching.
  • Channel Mode: Switch between Stereo, Mono, Left only, Right only, and Swap L/R output modes.
  • yt-dlp Integration: Download audio directly from YouTube and other supported sites via yt-dlp. Choose your output format (Best Audio, MP3, FLAC, Opus, WAV, or MP4 video). FLAC is not recommended since yt-dlp remuxes lossy audio rather than decoding from a lossless source. Supports SponsorBlock, chapter splitting, cookies, rate limiting, and more. Requires yt-dlp installed on your system.
  • Metadata Settings: A dedicated Metadata section in Settings lets you control how artist images are sourced. Choose between Album Cover (uses the first album artwork as the artist photo, default) or Artist Photo (fetches actual artist images directly from your Jellyfin or Subsonic server). When switching to Artist Photo mode, images are fetched from the server in the background as soon as you open the Artists page. If an artist has no dedicated photo on your server, their first album cover is used as a fallback so nothing ever shows blank.

Installation

Cargo (crates.io)

Install directly with Cargo:

cargo install --locked kopuz

NixOS / Nix

Run directly without installing:

nix run github:temidaradev/kopuz

Install to your profile:

nix profile add github:temidaradev/kopuz

On NixOS, using the flake:

Tip

This is recommended over nix profile as it installs Kopuz as a proper system app with icon & .desktop entry.

Add Kopuz to your flake.nix inputs:

{
  inputs.kopuz.url = "github:temidaradev/kopuz";
}

Then pass it through to your system config and add the Cachix substituter so it downloads the pre-built binary instead of compiling:

{
  nix.settings = {
    substituters      = ["https://kopuz.cachix.org" ];
    trusted-public-keys = ["kopuz.cachix.org-1:J2X3AnAYhKTJW5S3aCLoA1ckonQXVNZMQvhZA0YAufw="];
  };
}

Then install the package:

{pkgs, kopuz, ...}: let
  kopuzPkg = kopuz.packages.${pkgs.stdenv.hostPlatform.system}.default

in {
  environment.systemPackages = [kopuzPkg];
}

AUR (Arch Linux)

Install from the AUR using your preferred helper:

yay -S kopuz-bin
# or
paru -S kopuz-bin

Flatpak (Recommended)

Kopuz is soon available on Flathub. In the meantime, you can install it via our pre-built Flatpak repository, or build it yourself from source.

Option 1: Install pre-built (recommended)

flatpak install --user --or-update \
    https://kopuz-org.github.io/kopuz-flatpak/com.temidaradev.kopuz.flatpakref

Option 2: Build from source manifest

Requirements

Make sure you have Rust and the Dioxus CLI installed. We recommend installing dioxus-cli through your distro's package manager when available. If it's not packaged for your distro, you can install it via Cargo as a fallback:

cargo install --locked dioxus-cli

Then build and install Kopuz:

git clone https://github.com/temidaradev/kopuz
cd kopuz
dx build --release --package kopuz
flatpak-builder --user --install --force-clean \
  build-dir packaging/flatpak/com.temidaradev.kopuz.json
flatpak run com.temidaradev.kopuz

You can also click on the file and open it with an app provider, for example KDE Discover.

AppImage

Important

The AppImage requires webkit2gtk-4.1 and gtk3 installed on your system. Those dependencies are not bundled. The system tray icon additionally needs the appindicator library (e.g. libayatana-appindicator); without it Kopuz runs fine but shows no tray icon.

On most distros with a modern desktop environment, these are already present. You will need to install them manually if they are not yet installed.

On Arch-based distros, if the AppImage crashes with a WebKitNetworkProcess error, either run it with:

LD_LIBRARY_PATH=/usr/lib ./kopuz_*.AppImage

Or create symlinks once (requires sudo):

sudo mkdir -p /usr/libexec/webkit2gtk-4.1
sudo ln -s /usr/lib/webkit2gtk-4.1/WebKitNetworkProcess /usr/libexec/webkit2gtk-4.1/
sudo ln -s /usr/lib/webkit2gtk-4.1/WebKitWebProcess /usr/libexec/webkit2gtk-4.1/
sudo ln -s /usr/lib/webkit2gtk-4.1/WebKitGPUProcess /usr/libexec/webkit2gtk-4.1/

Build from Source

Dependencies

Using Nix

Tip

Nix is the primary means of development for Kopuz, and it is the recommended method for getting build dependencies in a pure, reproducible environment consistent across systems.

# Using Nix3 CLI
nix develop

If you are a Direnv user, use the provided .envrc:

# Using Direnv
direnv allow

Direnv is recommended if you want to keep using your usershell within the development environment.

Note

The system tray icon (used by minimize to tray) requires the appindicator library at runtime. It is included in the package dependencies below. Without it the tray icon simply won't appear and closing the window quits the app instead of hiding it - Kopuz still runs normally. The Nix dev shell already provides it.

Arch Linux Based Systems

sudo pacman -S rust cargo dioxus-cli base-devel cmake pkgconf opus alsa-lib xdotool webkit2gtk-4.1 gtk3 libsoup3 openssl libayatana-appindicator

Debian Based Systems

sudo apt install rustc cargo build-essential cmake pkg-config libopus-dev libasound2-dev libxdo-dev libwebkit2gtk-4.1-dev libgtk-3-dev libsoup-3.0-dev libssl-dev libayatana-appindicator3-1
cargo install dioxus-cli

Fedora Based Systems

sudo dnf groupinstall "Development Tools" "Development Libraries"
sudo dnf install rust cargo cmake pkgconf-pkg-config opus-devel alsa-lib-devel libxdo-devel webkit2gtk4.1-devel gtk3-devel libsoup3-devel openssl-devel libayatana-appindicator-gtk3
cargo install --locked dioxus-cli

openSUSE Based Systems

sudo zypper install rust cargo cmake pkg-config libopus-devel alsa-devel xdotool webkit2gtk3-soup2-devel gtk3-devel libsoup3-devel libopenssl-devel libayatana-appindicator3-1
cargo install --locked dioxus-cli

Developing Kopuz

Install Bazelisk, which reads the repository's pinned .bazelversion. On macOS:

brew install bazelisk

Clone the repository and build the desktop binary:

git clone https://github.com/Kopuz-org/kopuz
cd kopuz
bazel build //:kopuz

Run it with a separate debug database:

KOPUZ_DB_PATH=kopuz-debug.db bazel run //:kopuz

Build the optimized binary:

bazel build --config=release //:kopuz

The first Bazel invocation downloads the pinned Bazel and Rust toolchains plus the third-party crates recorded in Cargo.lock and Cargo.Bazel.lock. The built binary is available through the stable bazel-bin/crates/kopuz/kopuz symlink.

Test one crate or the complete workspace:

bazel test //crates/db:db_test
bazel test //:tests

Run Clippy with warnings denied:

bazel build \
  --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect \
  --output_groups=clippy_checks \
  --@rules_rust//rust/settings:clippy_flag=-Dwarnings \
  //crates/...

Check formatting without changing files:

bazel build \
  --aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect \
  --output_groups=rustfmt_checks \
  //crates/...

Apply rustfmt:

bazel run @rules_rust//:rustfmt -- //crates/...

Cargo manifests remain the source of truth for dependency versions and features. After changing a manifest or Cargo.lock, regenerate Bazel's resolution lock:

CARGO_BAZEL_REPIN=1 bazel build //crates/config:config

Tailwind output is committed because Dioxus consumes it as an application asset. Regenerate it whenever UI classes change:

npm ci
npx @tailwindcss/cli -i ./tailwind.css \
  -o ./crates/kopuz/assets/tailwind.css

The Bazel target builds and runs the native Rust executable. Dioxus still owns desktop installers and mobile project generation, so use the existing wrapper after the Bazel build/test gates when producing those artifacts:

just build
just android-patch
just ios-build-sim

macOS

Quarantine note: If you downloaded a .dmg instead, macOS may block it. Run once to clear the quarantine flag:

xattr -d com.apple.quarantine /Applications/Kopuz.app

Where does Kopuz keep its files?

Your settings, scanned library, playlists, and favorites all live in a single SQLite database, kopuz.db, in the config directory. Album art and downloaded tracks stay on disk in the cache directory. (Debug builds use a separate kopuz-debug.db so dx serve never touches your real data. You can override the DB location with the KOPUZ_DB_PATH env var.)

On macOS:

  • ~/Library/Application Support/com.temidaradev.kopuz/kopuz.db - settings, library, playlists, favorites
  • ~/Library/Caches/com.temidaradev.kopuz/covers/ - cached album art
  • ~/Library/Caches/com.temidaradev.kopuz/offline_tracks/ - downloaded tracks

On Linux (XDG spec):

  • ~/.config/kopuz/kopuz.db - settings, library, playlists, favorites
  • ~/.cache/kopuz/covers/ - cached album art
  • ~/.cache/kopuz/offline_tracks/ - downloaded tracks

On Windows (AppData):

  • %APPDATA%\temidaradev\kopuz\config\kopuz.db - settings, library, playlists, favorites
  • %LOCALAPPDATA%\temidaradev\kopuz\cache\covers\ - cached album art
  • %LOCALAPPDATA%\temidaradev\kopuz\cache\offline_tracks\ - downloaded tracks

Note

Upgrading from an older version? On first launch Kopuz imports your existing library.json and playlists.json into kopuz.db, leaving *.json.bak backups behind. The old JSON files are no longer read after that.

If covers aren't showing or the library looks off, just delete the cache folder and hit rescan.

YouTube Music Setup

Kopuz can use YouTube Music as a streaming backend. Add it from Settings → Media servers → Add → YouTube Music.

Choosing a mode

The setup dialog offers two methods:

  • Sign in with a browser - kopuz opens the Google sign-in page in an isolated browser profile (a fresh, separate session; your normal browsing is never touched), waits for you to log in, and extracts the session cookies. Pick which installed Chromium-family browser to use (Chrome, Chromium, Brave, Edge, Vivaldi, or Helium). This unlocks your library, Liked Music, playlists, and followed artists.

  • Continue without signing in (anonymous) - no sign-in, no cookies. You can browse, search, open artist/album/playlist pages, start mix radio, and play public tracks. Liked Music, library playlists, and following/liking are disabled (those views show a "sign in to enable" prompt). Music Premium-only tracks can't be played anonymously.

Premium tracks

Music Premium-locked tracks fall back to a local yt-dlp resolve when the primary path returns UNPLAYABLE, so having yt-dlp installed helps for those. Anonymous mode can't play Premium-only content at all.

SoundCloud Setup

Kopuz can use SoundCloud as a streaming backend. Add it from Settings → Media servers → Add → SoundCloud.

There's no URL or password to type. Kopuz opens soundcloud.com/signin in an isolated browser profile (a fresh, separate session; your normal browsing is never touched), waits for you to log in, and pulls the session's oauth_token. Pick which installed Chromium-family browser to use (Chrome, Chromium, Brave, Edge, Vivaldi, or Helium).

Once signed in you get search, track playback (progressive MP3 plus Go+ AAC/HLS streams), your Liked tracks as favorites, read-only access to your playlists, and like/unlike. Removing the source cleans up its isolated profile.

Spotify Setup

Spotify works differently from every other backend in Kopuz, so it needs a bit of one-time setup. Kopuz talks to the official Web API for your library, and the audio itself is played by Spotify's official Web Playback SDK running in a browser on your machine. Kopuz drives that player and shows everything in its own UI, but it never touches the audio stream, and it never asks for your password.

Before you start

You need three things:

  • Spotify Premium. The Web Playback SDK refuses to stream on free accounts. Browsing your library still works without Premium, playback does not.

  • Your own Spotify client. Kopuz does not ship a Client ID, you create one in about two minutes and paste it in.

  • A supported browser installed. Chrome, Edge, Brave, Chromium, Vivaldi, Helium or Safari on macOS. Firefox is not usable here: the SDK has a long-standing bug in Firefox where playback dies a few seconds in, so Kopuz will not pick it.

    [!NOTE] To use the Spotify backend with Helium, you need to place the Widevine files in the correct location. See here for more information. On NixOS using hjem, this can be done by adding

    hjem.users.your-username = {
      xdg.config.files."net.imput.helium/WidevineCdm/latest-component-updated-widevine-cdm".text = ''
        {"Path":"${pkgs.widevine-cdm}/share/google/chrome/WidevineCdm"}
      '';
    };

    to your configuration.

1. Create your Spotify app

  1. Open developer.spotify.com/dashboard and log in with the account you want to listen with.

  2. Click Create app. Name and description can be anything, they are only shown to you.

  3. In Redirect URIs, add exactly this, no trailing slash:

    http://127.0.0.1:8898/callback
    

    Spotify compares this string character by character, so a typo here is the single most common reason sign-in fails.

  4. Under Which API/SDKs are you planning to use?, tick both Web API and Web Playback SDK.

  5. Save, then open the app's Settings and copy the Client ID. You never need the Client Secret, Kopuz uses PKCE instead.

  6. Go to the app's User Management and add the display name and email of every Spotify account that will sign in, including your own.

Important

A freshly created app is in Spotify's Development Mode. That means at most five listed users can sign in, the app owner needs Premium, and some API surfaces are restricted (see What Spotify limits below). This is Spotify's policy, not a Kopuz limitation.

2. Add the source in Kopuz

Go to Settings → Media servers → Add → Spotify, paste your Client ID into the Spotify Client ID field, and save.

Kopuz opens Spotify's consent page in your default browser. Approve it, and the redirect comes back to a small listener Kopuz runs on 127.0.0.1:8898 just for those few seconds. Make sure nothing else is sitting on port 8898 while you sign in. After that the source is ready, and Kopuz refreshes the token on its own from then on.

3. Playing music

There are two places your music can come out, and you can switch between them whenever you like.

The in-app player (default). The first time you hit play, Kopuz opens a small player tab in a supported browser. That tab is doing the actual playing, because DRM playback only works in a real browser. Leave it open in the background and forget about it. Everything is still controlled from Kopuz: play/pause, seek, volume, next/previous, your queue, and your system media keys. The tab closes itself when you quit Kopuz.

Browsers block sound until you interact with a page, so if the very first track sits there doing nothing, click once anywhere in that tab. Kopuz waits for that and then starts the track.

A Spotify Connect device. The device button in the bottom bar lists whatever Spotify Connect targets you have around: your phone, the desktop app, a speaker, a TV. Pick one and Kopuz sends playback straight there, with no browser tab involved at all. Progress, play state, and the current track stay in sync in Kopuz, and your OS media widget follows along too. Pick kopuz (this app) to come back to the in-app player.

Settings worth knowing

Both of these live on the Spotify row under Settings → Media servers:

  • Spotify playback browser. Which browser gets the player tab. Automatic picks the first supported one it finds installed.
  • Spotify playback device. What Kopuz should do when Spotify is already playing somewhere else as Kopuz starts up. Other device (the default) means Kopuz adopts that session and just syncs to it instead of yanking playback away from it. This app means Kopuz always plays locally.

What you get

Saved tracks show up as favorites, saved albums as your library, and your playlists are browsable. There is a Discover page with On repeat, Jump back in, and All-time favorites, search across tracks/albums/artists, liking and unliking, and scrobbling to Last.fm, Libre.fm, and ListenBrainz like any other source.

What Spotify limits

Most of the rough edges here come from Spotify's Development Mode, not from Kopuz:

  • Search returns at most ten results per type. Development Mode apps get a hard cap on the search endpoint.
  • Playlists are read-only. You can browse and play them, but creating and editing playlists is not available for Spotify sources.
  • Playlists you only follow may look empty. Spotify only exposes the tracks of playlists you own or collaborate on. Editorial playlists (Discover Weekly, Release Radar, and friends) are off limits to third-party apps entirely.
  • No downloads, no tag editing, no radio for Spotify tracks.
  • No equalizer, crossfade, or gapless on Spotify audio. The browser owns that audio pipeline, so Kopuz's own audio features do not apply to it.

Troubleshooting

  • "Spotify playback needs Chrome, Edge, Brave, Chromium, Vivaldi, Helium, or Safari" means none of those were found. Install one, then pick it under Settings → Media servers.
  • Sign-in never completes. Check the redirect URI on your Spotify app character by character, and make sure nothing else is using port 8898.
  • Sign-in is refused for a friend's account. Add them under User Management on your app first. Development Mode allows five users total.
  • Auth errors after the app has been closed for a while. Kopuz refreshes the token on startup, so give it a moment. If it sticks, remove the Spotify source and add it again.
  • The Discover page is empty. If you signed in before updating Kopuz, your token may predate the scopes Discover needs. Sign out and back in.
  • A track plays in the tab but Kopuz looks frozen, or the other way around. Closing the player tab by hand disconnects the device. Press play again in Kopuz and it will open a fresh one.

Logs & Debugging

Kopuz logs through tracing. Most of this is reachable from the app itself - Settings → Logs has Open logs folder, Export logs, and an Enable Performance Tracing toggle - so users never need a terminal to send a useful report.

Where the files live

All files sit in the logs directory (the Open logs folder button jumps straight here):

  • Linux: ~/.cache/kopuz/logs/
  • macOS: ~/Library/Caches/com.temidaradev.kopuz/logs/
  • Windows: %LOCALAPPDATA%\temidaradev\kopuz\cache\logs\
File What it is
latest.log The current session. Span timing + events; the live log.
kopuz-<timestamp>.log Previous sessions, archived on startup (last 10 kept). A restart never erases the run before it.
crash-<timestamp>.txt Written only on a crash (Rust panic): message, backtrace, recent log tail, app/OS version.
kopuz-trace.json Performance trace - only when tracing is enabled (see below). Overwritten each run.

Timestamps are UTC YYYY-MM-DD_HH-MM-SS, so the files sort chronologically.

Triage cheat-sheet

App crashed → a crash-<timestamp>.txt is generated automatically. Ask the user for Settings → Logs → Export logs (bundles latest.log + the newest crash report into one file), or Open logs folder and grab the newest crash-*.txt.

Performance issue (freeze / slow load / stutter) → ask the user to:

  1. Settings → Logs → enable "Performance Tracing", then restart the app (the toggle warns about this - the trace recorder is set up once at startup).
  2. Reproduce the slow action.
  3. Quit the app (this flushes the trace cleanly).
  4. Settings → Logs → Open logs folder and send kopuz-trace.json (or Export logs).

Open the trace at speedscope.app or ui.perfetto.dev. Critical paths (YouTube stream resolve, browse/search/pagination, mix radio, library scan, downloads, playback transitions, per-component renders) are instrumented as named spans, and worker-thread work nests under the action that launched it, so the trace shows exactly where time goes. Turn it back off afterward - it adds overhead and grows the trace file during long sessions.

Power-user env vars

Log verbosity is controlled by env vars for terminal runs:

# Verbose (debug-level) logs for a session
KOPUZ_DEBUG=1 kopuz

# Fine-grained, per-module (overrides KOPUZ_DEBUG); standard tracing directive syntax
KOPUZ_LOG="server::ytmusic=trace,kopuz=debug" kopuz

# Deep render-tree profiling: Dioxus's own per-component render/diff spans
# (enable the trace toggle in Settings first; this just controls what's recorded)
KOPUZ_LOG="info,dioxus_core=trace" kopuz

RUST_LOG works too; KOPUZ_LOG takes precedence.

The performance trace is enabled only via Settings → Logs → Enable Performance Tracing (then restart) - there's no env var for it; the UI is the single source of truth. Off by default → zero overhead.

Debug builds add a Trigger crash button in Settings → Logs to exercise the crash-report path. It's compiled out of release builds.

Optimization

Kopuz is built to feel snappy even with large libraries. Here's what we do under the hood:

  • Skip what's already indexed - the scanner keeps a HashSet of every path it's already seen, so rescans only process new files. If you have 10,000 tracks, and then add 5 new ones, Kopuz will not re-read the other 9995. This makes a huge difference, especially on HDDs.

  • Parallel startup loading - on launch, library, config, playlists, and favorites all load in parallel with tokio::join!. Before this change, everything loaded sequentially and you'd stare at a blank window for a bit. Now it's near-instant.

  • Album art caching - cover images get extracted once and saved to disk (~/.cache/kopuz/covers/ on Linux, ~/Library/Caches/ on macOS). We also cache the macOS now-playing artwork object in memory so it doesn't re-decode the image every time the progress bar updates.

  • Lazy loading images - album covers in search results, track rows, and genre views all use loading="lazy" so we're not loading hundreds of images at once when you scroll through a big library.

  • Non-blocking I/O - all the heavy stuff (metadata parsing, file scanning, saving library state) runs on spawn_blocking threads so the UI never freezes. The main thread stays responsive even during a full library scan.

  • Smarter sorting - we use sort_by_cached_key instead of regular sort_by_key for library views, which avoids recalculating the sort key (like .to_lowercase()) on every comparison. Small thing perhaps, but it adds up with thousands of tracks.

  • HTTP caching for artwork - the custom artwork:// protocol serves images with Cache-Control: public, max-age=31536000 so the Webview doesn't re-request covers it already has.

Overall, these changes brought the rescan time down significantly and the app feels much more responsive, especially with libraries over 5000 tracks. Memory usage stays reasonable too since we're not holding decoded images in memory longer than needed.

Tech Stack

  • Dioxus: UI Framework
  • Symphonia: Audio decoding library
  • Cpal: Audio I/O library
  • Lofty: Metadata parsing
  • SQLite / sqlx: Local storage with compile-time-checked queries
  • TailwindCSS: Styling framework based on CSS

Crypto Donation

  • Solana: "2fapJYRztnTRLpJbmyEUnsuZ36AzLK2JrMmmLEfDqKpN"
  • Bitcoin: "bc1qz94yz9xvufa6hxlvjzaajgd2zyfu86arn68hu4"
  • Monero: "86mz3HxTrKyYpuvx78m6pufbXdwAnoyoZBztz6HyYrnM1XP5YVrMy9jTVRY5vzgGtkizACLpFwHEdafKTMoj6y8mAVgvWMz"
  • Ethereum: "0xa490D50470cdFf837B6663F7f6cBe50B157224e5"
  • USDT on Solana Chain: "GYmnAcrA5MbF6cUxT2m5d5cwdfr14qSY9WFYRwXxaibW"

Credits

Star History

Star History Chart

more like this

musicboy

A high-quality audio playback cartridge for Nintendo® Game Boy® and Game Boy Color®.

C50

YiMao

YiMao · 云海求片助手 — 双核心 Telegram 影视求片机器人。订阅模式:TMDB 智能搜索一键订阅 / 趣味求片模式:AI 生成五层地狱闯关,通关解锁优先求片。深度集成 MoviePilot + Emby/Jellyfin。…

Go52

search

search projects, people, and tags