## 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
[jellyfin-plugin-listenbrainz]: https://github.com/lyarenei/jellyfin-plugin-listenbrainz
- **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](#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](#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](#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:
```bash
cargo install --locked kopuz
```
### NixOS / Nix
**Run directly without installing:**
```bash
nix run github:temidaradev/kopuz
```
**Install to your profile:**
```bash
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:
```nix
{
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
{
nix.settings = {
substituters = ["https://kopuz.cachix.org" ];
trusted-public-keys = ["kopuz.cachix.org-1:J2X3AnAYhKTJW5S3aCLoA1ckonQXVNZMQvhZA0YAufw="];
};
}
```
Then install the package:
```nix
{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:
```bash
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)
```bash
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:
```bash
cargo install --locked dioxus-cli
```
Then build and install Kopuz:
```bash
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:
```bash
LD_LIBRARY_PATH=/usr/lib ./kopuz_*.AppImage
```
Or create symlinks once (requires sudo):
```bash
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](https://nixos.org) 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.
```bash
# Using Nix3 CLI
nix develop
```
If you are a [Direnv](https://direnv.net) user, use the provided `.envrc`:
```bash
# 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**
```bash
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**
```bash
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**
```bash
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**
```bash
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](https://github.com/bazelbuild/bazelisk), which reads the
repository's pinned `.bazelversion`. On macOS:
```bash
brew install bazelisk
```
Clone the repository and build the desktop binary:
```bash
git clone https://github.com/Kopuz-org/kopuz
cd kopuz
bazel build //:kopuz
```
Run it with a separate debug database:
```bash
KOPUZ_DB_PATH=kopuz-debug.db bazel run //:kopuz
```
Build the optimized binary:
```bash
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:
```bash
bazel test //crates/db:db_test
bazel test //:tests
```
Run Clippy with warnings denied:
```bash
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:
```bash
bazel build \
--aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect \
--output_groups=rustfmt_checks \
//crates/...
```
Apply rustfmt:
```bash
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:
```bash
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:
```bash
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:
```bash
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:
```bash
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`](https://github.com/yt-dlp/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](https://github.com/imputnet/helium/issues/116#issuecomment-3668370766)
> for more information. On NixOS using
> [hjem](https://github.com/feel-co/hjem), this can be done by adding
>
> ```nix
> 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](https://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:
```text
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](#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`](https://docs.rs/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-