logo

# ncspot [![Crates.io](https://img.shields.io/crates/v/ncspot.svg)](https://crates.io/crates/ncspot) [![Gitter](https://badges.gitter.im/ncspot/community.svg)](https://gitter.im/ncspot/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![CI](https://github.com/hrkfdn/ncspot/actions/workflows/ci.yml/badge.svg)](https://github.com/hrkfdn/ncspot/actions/workflows/ci.yml) [![Packaging status](https://repology.org/badge/vertical-allrepos/ncspot.svg)](https://repology.org/project/ncspot/versions) Download on Flathub [![ncspot](https://snapcraft.io//ncspot/badge.svg)](https://snapcraft.io/ncspot) `ncspot` is a ncurses Spotify client written in Rust using `librespot`. It is heavily inspired by ncurses MPD clients, such as ncmpc. My motivation was to provide a simple and resource friendly alternative to the official client as well as to support platforms that currently don't have a Spotify client, such as the \*BSDs. Note that `ncspot` offers features that are legally incompatible with free Spotify accounts. See [feature comparison](https://support.spotify.com/us/article/premium-plans/) and [Spotify user guidelines](https://www.spotify.com/us/legal/user-guidelines/). You **must** have an existing premium Spotify subscription to use `ncspot`. ![Search Screen](images/screenshot-thumb.png) ## Table of Contents - [ncspot](#ncspot) - [Table of Contents](#table-of-contents) - [Resource Footprint Comparison](#resource-footprint-comparison) - [Installation](#installation) - [On macOS](#on-macos) - [On Windows](#on-windows) - [On Linux](#on-linux) - [Build](#build) - [Prerequisites](#prerequisites) - [Debugging](#debugging) - [Compiling](#compiling) - [Building a Debian Package](#building-a-debian-package) - [Audio Backends](#audio-backends) - [Other Features](#other-features) - [Key Bindings](#key-bindings) - [Navigation](#navigation) - [Playback](#playback) - [Context Menus](#context-menus) - [Sharing](#sharing) - [Queue](#queue) - [Library](#library) - [Vim-Like Search Bar](#vim-like-search-bar) - [Vim-Like Commands](#vim-like-commands) - [Remote control (IPC)](#remote-control-ipc) - [Extracting info on currently playing song](#extracting-info-on-currently-playing-song) - [Configuration](#configuration) - [Custom Keybindings](#custom-keybindings) - [Proxy](#proxy) - [Theming](#theming) - [Track Formatting](#track-formatting) - [Notification Formatting](#notification-formatting) - [Cover Drawing](#cover-drawing) - [Authentication](#authentication) ## Resource Footprint Comparison Measured using `ps_mem` on Linux during playback: | Client | Private Memory | Shared Memory | Total | |---------|----------------|---------------|------------| | ncspot | 22.1 MiB | 24.1 MiB | 46.2 MiB | | Spotify | 407.3 MiB | 592.7 MiB | 1000.0 MiB | ## Installation ### On macOS `ncspot` is available via [Homebrew](https://brew.sh/): ```zsh brew install ncspot ``` ### On Windows `ncspot` is available via [Scoop](https://scoop.sh/): ```powershell scoop install ncspot ``` ### On Linux Your distribution may have packaged `ncspot` in its package repository. If so, simply install using your distribution's package manager - it is by far the easiest way. If not, you can build from source instead. See [Build](#build). In case your package manager does not perform dependency resolution, here are the runtime dependencies: - `dbus`, `libncurses`, `libssl` - `libpulse` (or `portaudio`, if built using the PortAudio backend) - `libxcb` (if built with the `clipboard` feature) - `ueberzug` (if built with the `cover` feature) ## Build ### Prerequisites - A working [Rust installation](https://www.rust-lang.org/tools/install) - Python 3 (needed for building `rust-xcb` dependency) On Linux, you also need: - `pkgconf` (or `pkg-config`) - Development headers for the [aforementioned runtime dependencies](#on-linux) - Debian and derivatives: ```sh sudo apt install libdbus-1-dev libncursesw5-dev libpulse-dev libssl-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev ``` - Fedora: ```sh sudo dnf install dbus-devel libxcb-devel ncurses-devel openssl-devel pulseaudio-libs-devel ``` - Arch and derivatives: ```sh # headers are included in the base packages sudo pacman -S dbus libpulse libxcb ncurses openssl ``` ### Debugging For debugging, you can pass a debug log filename: ```sh cargo run -- -d debug.log ``` If ncspot has crashed you can find the latest backtrace at `~/.cache/ncspot/backtrace.log`. ### Compiling Compile and install the latest release with `cargo-install`: ```sh cargo install ncspot ``` Or clone and build locally: ```sh git clone https://github.com/hrkfdn/ncspot cargo build --release ``` **You may need to manually set the audio backend on non-Linux OSes.** See [Audio Backends](#audio-backends). #### Building a Debian Package You can also use `cargo-deb` to build a Debian package ```sh cargo install cargo-deb cargo deb ``` You can find the package under `target/debian`. ### Audio Backends By default `ncspot` is built using the PulseAudio backend. To make it use the PortAudio backend (e.g. for \*BSD or macOS) or Rodio backend (e.g. for Windows), you need to compile `ncspot` with the respective features: ```sh # PortAudio (BSD/macOS) cargo build --release --no-default-features --features portaudio_backend,pancurses_backend # Rodio (Windows) cargo build --release --no-default-features --features rodio_backend,pancurses_backend ``` ### Other Features Here are some auxiliary features you may wish to enable: | Feature | Default | Description | |-------------------|---------|--------------------------------------------------------------------------------------------| | `cover` | off | Add a screen to show the album art. See [Cover Drawing](#cover-drawing). | | `mpris` | on | Control `ncspot` via dbus. See [Arch Wiki: MPRIS](https://wiki.archlinux.org/title/MPRIS). | | `notify` | on | Send a notification to show what's playing. | | `share_clipboard` | on | Ability to copy the URL of a song/playlist/etc. to system clipboard. | Consult [Cargo.toml](Cargo.toml) for the full list of supported features. ## Key Bindings The keybindings listed below are configured by default. Additionally, if you built `ncspot` with MPRIS support, you may be able to use media keys to control playback depending on your desktop environment settings. Have a look at the [configuration section](#configuration) if you want to set custom bindings. ### Navigation | Key | Command | |-------------------|-------------------------------------------------------------------------------| | ? | Show help screen. | | F1 | Queue (See [specific commands](#queue)). | | F2 | Search. | | F3 | Library (See [specific commands](#library)). | | F8 | Album Art (if built with the `cover` feature). | | / | Open a Vim-like search bar (See [specific commands](#vim-like-search-bar)). | | : | Open a Vim-like command prompt (See [specific commands](#vim-like-commands)). | | Escape | Close Vim-like search bar or command prompt. | | Q | Quit `ncspot`. | ### Playback | Key | Command | |-------------------------------|----------------------------------------------------------------| | Return | Play track or playlist. | | Space | Queue track or playlist. | | . | Play the selected item after the currently playing track. | | P | Move to the currently playing track in the queue. | | S | Save the currently playing item to your library. | | D | Remove the currently playing item from your library. | | Shift+P | Toggle playback (i.e. Play/Pause). | | Shift+S | Stop playback. | | Shift+U | Update the library cache (tracks, artists, albums, playlists). | | < | Play the previous track. | | > | Play the next track. | | F | Seek forward by 1 second. | | Shift+F | Seek forward by 10 seconds. | | B | Seek backward by 1 second. | | Shift+B | Seek backward by 10 seconds. | | - | Decrease volume by 1%. | | + | Increase volume by 1%. | | [ | Decrease volume by 5%. | | ] | Increase volume by 5%. | | R | Toggle _Repeat_ mode. | | Z | Toggle _Shuffle_ state. | ### Context Menus | Key | Command | |-------------------------------|-----------------------------------------------------------------------------------------------------------| | O | Open a detail view or context for the **selected item**. | | Shift+O | Open a context menu for the **currently playing track**. | | A | Open the **album view** for the selected item. | | Shift+A | Open the **artist view** for the selected item. | | M | Open the **recommendations view** for the **selected item**. | | Shift+M | Open the **recommendations view** for the **currently playing track**. | | Ctrl+V | Open the context menu for a Spotify link in your clipboard (if built with the `share_clipboard` feature). | | Backspace | Close the current view. | When pressing O: - If the _selected item_ is **not** a track, it opens a detail view. - If the _selected item_ **is** a track, it opens a context menu with: - "Artist(s)" (let's you show or (un)follow a track's artist(s)) - "Show Album" - "Share" (if built with the `share_clipboard` feature) - "Add to playlist" - "Similar tracks" ### Sharing (if built with the `share_clipboard` feature) | Key | Command | |-------------------------------|--------------------------------------------------------------------------| | X | Copy the URL to the **currently selected item** to the system clipboard. | | Shift+X | Copy the URL to the **currently playing track** to the system clipboard. | ### Queue | Key | Command | |------------------------------|--------------------------------------| | C | Clear the entire queue. | | D | Delete the currently selected track. | | Ctrl+S | Delete the currently selected track. | ### Library | Key | Command | |--------------|-----------------------------------------| | D | Delete the currently selected playlist. | ### Vim-Like Search Bar | Key | Command | |--------------|-----------------------------| | n | Previous search occurrence. | | N | Next search occurrence. | ## Vim-Like Commands You can open a Vim-style command prompt using :, and close it at any time with Escape. The following is an abridged list of the more useful commands. For the full list, see [source code](/src/command.rs). Note: \ - mandatory arg; [BAR] - optional arg | Command | Action | |------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `help` | Show current key bindings. | | `quit`
Aliases: `q`, `x` | Quit `ncspot`. | | `logout` | Remove any cached credentials from disk and quit `ncspot`. | | `playpause`
Aliases: `pause`, `toggleplay`, `toggleplayback` | Toggle playback. | | `stop` | Stop playback. | | `seek` [`+`\|`-`]\ | Seek to the specified position, or seek relative to current position by prepending `+`/`-`.
\* TIME is anything accepted by [parse_duration](https://docs.rs/parse_duration/latest/parse_duration/)
\* Default unit is `ms` for backward compatibility. | | `repeat` [REPEAT_MODE]
Alias: `loop` | Set repeat mode. Omit argument to step through the available modes.
\* Valid values for REPEAT_MODE: `list` (aliases: `playlist`, `queue`), `track` (aliases: `once`, `single`), `none` (alias: `off`) | | `shuffle` [`on`\|`off`] | Enable or disable shuffle. Omit argument to toggle. | | `previous` | Play the previous track. | | `next` | Play the next track. | | `focus` \ | Switch to a different view.
\* Valid values for SCREEN: `queue`, `search`, `library`, `cover` (if built with the `cover` feature) | | `search` \ | Search for a song/artist/album/etc. | | `clear` | Clear the queue. | | `share` \ | Copy a shareable URL of the item to the system clipboard. Requires the `share_clipboard` feature.
\* Valid values for ITEM: `selected`, `current` | | `newplaylist` \ | Create a new playlist. | | `sort` \ [SORT_DIRECTION] | Sort a playlist.
\* Valid values for SORT_KEY: `title`, `album`, `artist`, `duration`, `added`
\* Valid values for SORT_DIRECTION: `ascending` (default; aliases: `a`, `asc`), `descending` (aliases: `d`, `desc`) | | `exec` \ | Execute a command in the system shell.
\* Command output is printed to the terminal, so redirection (`2> /dev/null`) may be necessary. | | `noop` | Do nothing. Useful for disabling default keybindings. See [custom keybindings](#custom-keybindings). | | `reload` | Reload the configuration from disk. See [Configuration](#configuration). | ## Remote control (IPC) Apart from MPRIS, ncspot will also create a domain socket on UNIX platforms (Linux, macOS, *BSD) at `~/.cache/ncspot/ncspot.sock`. Applications or scripts can connect to this socket to send commands or be notified of the currently playing track, i.e. with `netcat`: ``` % nc -U ~/.cache/ncspot/ncspot.sock play {"mode":{"Playing":{"secs_since_epoch":1672249086,"nanos_since_epoch":547517730}},"playable":{"type":"Track","id":"2wcrQZ7ZJolYEfIaPP9yL4","uri":"spotify:track:2wcrQZ7ZJolYEfIaPP9yL4","title":"Hit Me Where It Hurts","track_number":4,"disc_number":1,"duration":184132,"artists":["Caroline Polachek"],"artist_ids":["4Ge8xMJNwt6EEXOzVXju9a"],"album":"Pang","album_id":"4ClyeVlAKJJViIyfVW0yQD","album_artists":["Caroline Polachek"],"cover_url":"https://i.scdn.co/image/ab67616d0000b2737d983e7bf67c2806218c2759","url":"https://open.spotify.com/track/2wcrQZ7ZJolYEfIaPP9yL4","added_at":"2022-12-19T22:41:05Z","list_index":0}} playpause {"mode":{"Paused":{"secs":25,"nanos":575000000}},"playable":{"type":"Track","id":"2wcrQZ7ZJolYEfIaPP9yL4","uri":"spotify:track:2wcrQZ7ZJolYEfIaPP9yL4","title":"Hit Me Where It Hurts","track_number":4,"disc_number":1,"duration":184132,"artists":["Caroline Polachek"],"artist_ids":["4Ge8xMJNwt6EEXOzVXju9a"],"album":"Pang","album_id":"4ClyeVlAKJJViIyfVW0yQD","album_artists":["Caroline Polachek"],"cover_url":"https://i.scdn.co/image/ab67616d0000b2737d983e7bf67c2806218c2759","url":"https://open.spotify.com/track/2wcrQZ7ZJolYEfIaPP9yL4","added_at":"2022-12-19T22:41:05Z","list_index":0}} ``` Each time the playback status changes (i.e. after sending the `play`/`playpause` command or simply by playing the queue), the current status will be published as a JSON structure. Possible use cases for this could be: - Controlling a detached ncspot session (in `tmux` for example) - Displaying the currently playing track in your favorite application/status bar (see below) - Setting up routines, i.e. to play specific songs/playlists when ncspot starts ### Extracting info on currently playing song Using `netcat` and the domain socket, you can query the currently playing track and other relevant information. Note that not all `netcat` versions are suitable, as they typically tend to keep the connection to the socket open. OpenBSD's `netcat` offers a work-around: by using the `-W` flag, it will close after a specific number of packets have been received. ``` % nc -W 1 -U ~/.cache/ncspot/ncspot.sock {"mode":{"Playing":{"secs_since_epoch":1675188934,"nanos_since_epoch":50913345}},"playable":{"type":"Track","id":"5Cp6a1h2VnuOtsh1Nqxfv6","uri":"spotify:track:5Cp6a1h2VnuOtsh1Nqxfv6","title":"New Track","track_number":1,"disc_number":1,"duration":498358,"artists":["Francis Bebey"],"artist_ids":["0mdmrbu5UZ32uRcRp2z6mr"],"album":"African Electronic Music (1975-1982)","album_id":"7w99Aae1tYSTSb1OiDnxYY","album_artists":["Francis Bebey"],"cover_url":"https://i.scdn.co/image/ab67616d0000b2736ab57cedf27177fae1eaed87","url":"https://open.spotify.com/track/5Cp6a1h2VnuOtsh1Nqxfv6","added_at":"2020-12-22T09:57:17Z","list_index":0}} ``` This results in a single output in `JSON` format, which can e.g. be parsed using [jq](https://stedolan.github.io/jq/). For example, you can get the currently playing artist and title in your terminal as follows: ``` % nc -W 1 -U ~/.cache/ncspot/ncspot.sock | jq '.playable.title' "PUMPIN' JUMPIN'" % nc -W 1 -U ~/.cache/ncspot/ncspot.sock | jq '.playable.artists[0]' "Hideki Naganuma" ``` ## Configuration Configuration is saved to `~/.config/ncspot/config.toml` (or `%AppData%\ncspot\config.toml` on Windows). To reload the configuration during runtime use the `reload` command. Possible configuration values are: | Name | Description | Possible values | Default | |---------------------------------|----------------------------------------------------------------|---------------------------------------------------------------------------|---------------------| | `command_key` | Key to open command line | Single character | `:` | | `initial_screen` | Screen to show after startup | `"library"`, `"search"`, `"queue"`, `"cover"`[1] | `"library"` | | `use_nerdfont` | Turn nerdfont glyphs on/off | `true`, `false` | `false` | | `flip_status_indicators` | Reverse play/pause icon meaning[2] | `true`, `false` | `false` | | `backend` | Audio backend to use | String[3] | | | `backend_device` | Audio device to configure the backend | String | | | `audio_cache` | Enable caching of audio files | `true`, `false` | `true` | | `audio_cache_size` | Maximum size of audio cache in MiB | Number | | | `volnorm` | Enable volume normalization | `true`, `false` | `false` | | `volnorm_pregain` | Normalization pregain to apply in dB (if enabled) | Number | `0.0` | | `default_keybindings` | Enable default keybindings | `true`, `false` | `false` | | `notify`[4] | Enable desktop notifications | `true`, `false` | `false` | | `bitrate` | Audio bitrate to use for streaming | `96`, `160`, `320` | `320` | | `gapless` | Enable gapless playback | `true`, `false` | `true` | | `shuffle` | Set default shuffle state | `true`, `false` | `false` | | `repeat` | Set default repeat mode | `off`, `track`, `playlist` | `off` | | `playback_state` | Set default playback state | `"Stopped"`, `"Paused"`, `"Playing"`, `"Default"` | `"Paused"` | | `library_tabs` | Tabs to show in library screen | Array of `"tracks"`, `"albums"`, `"artists"`, `"playlists"`, `"podcasts"` | All tabs | | `cover_max_scale`[1] | Set maximum scaling ratio for cover art | Number | `1.0` | | `hide_display_names` | Hides spotify usernames in the library header and on playlists | `true`, `false` | `false` | | `statusbar_format` | Formatting for tracks in the statusbar | See [track_formatting](#track-formatting) | `%artists - %track` | | `[track_format]` | Set active fields shown in Library/Queue views | See [track formatting](#track-formatting) | | | `[notification_format]` | Set the text displayed in notifications[4] | See [notification formatting](#notification-formatting) | | | `[theme]` | Custom theme | See [custom theme](#theming) | | | `[keybindings]` | Custom keybindings | See [custom keybindings](#custom-keybindings) | | 1. If built with the `cover` feature. 2. By default the statusbar will show a play icon when a track is playing and a pause icon when playback is stopped. If this setting is enabled, the behavior is reversed. 3. Run `ncspot -h` for a list of devices. 4. If built with the `notify` feature. ### Custom Keybindings Keybindings can be configured in `[keybindings]` section in `config.toml`. Each key-value pair specifies one keybinding, where the key is a string in the format of: ``` [MODIFIER+] where: MODIFIER: Shift|Alt|Ctrl CHAR: Any printable character NAMED_KEY: Enter|Space|Tab|Backspace|Esc|Left|Right|Up|Down |Ins|Del|Home|End|PageUp|PageDown|PauseBreak|NumpadCenter |F0|F1|F2|F3|F4|F5|F6|F7|F8|F9|F10|F11|F12 ``` For implementation see [commands::CommandManager::parse_key](/src/commands.rs). Its value is a string that can be parsed as a command. See [Vim-Like Commands](#vim-like-commands).
Examples: (Click to show/hide) ```toml [keybindings] # Bind "Shift+i" to "Seek forward 10 seconds" "Shift+i" = "seek +10s" ``` To disable a default keybinding, set its command to `noop`: ```toml # Use "Shift+q" to quit instead of the default "q" [keybindings] "Shift+q" = "quit" "q" = "noop" ```
### Proxy `ncspot` will respect system proxy settings defined via the `http_proxy` environment variable. ```sh # In sh-like shells http_proxy="http://foo.bar:4444" ncspot ``` ### Theming [Theme generator](https://ncspot-theme-generator.vaa.red/) by [@vaarad](https://github.com/vaared). The color palette can be modified in the configuration. For instance, to have `ncspot` match Spotify's official client, you can add the following entries to the configuration file: ```toml [theme] background = "black" primary = "light white" secondary = "light black" title = "green" playing = "green" playing_selected = "light green" playing_bg = "black" highlight = "light white" highlight_bg = "#484848" error = "light white" error_bg = "red" statusbar = "black" statusbar_progress = "green" statusbar_bg = "green" cmdline = "light white" cmdline_bg = "black" search_match = "light red" ``` More examples can be found in [this pull request](https://github.com/hrkfdn/ncspot/pull/40). ### Track Formatting It's possible to customize how tracks are shown in Queue/Library views and the statusbar, whereas `statusbar_format` will hold the statusbar formatting and `[track_format]` the formatting for tracks in list views. If you don't define `center` for example, the default value will be used. Available options for tracks: `%artists`, `%title`, `%album`, `%saved`, `%duration` Default configuration: ```toml statusbar_format = "%artists - %title" [track_format] left = "%artists - %title" center = "%album" right = "%saved %duration" ```
Examples: (Click to show/hide) Example 1 - Show only album name and track name after it: ```toml [track_format] left = "%album" center = "%title" right = "" ``` Example 2 - Show track title before artists, and don't show album at all: ```toml [track_format] left = "%title - %artists" center = "" ``` Example 3 - Show everything as default, but hide saved status and track length: ```toml [track_format] right = "" ``` Example 4 - Show everything as default, except show title before artists: ```toml [track_format] left = "%title - %artists" ``` Example 5 - Show saved status and duration first, followed by track title and artists, with the album last: ```toml [track_format] left = "|%saved| %duration | %title - %artists" center = "" right = "%album" ```
### Notification Formatting `ncspot` also supports customizing the way notifications are displayed (which appear when compiled with the `notify` feature and `notify = true`). The title and body of the notification can be set, with `title` and `body`, or the default will be used. The formatting options are the same as those for [track formatting](#track-formatting) (`%artists`, `%title`, etc) Default configuration: ```toml [notification_format] title = "%title" body = "%artists" ``` ## Cover Drawing When compiled with the `cover` feature, `ncspot` can draw the album art of the current track in a dedicated view (`:focus cover` or F8 by default) using [Überzug](https://github.com/seebye/ueberzug). For more information on installation and terminal compatibility, consult that repository. To allow scaling up the album art beyond its native resolution (640x640 for Spotify covers), use the config key `cover_max_scale`. This is especially useful for HiDPI displays: ```toml cover_max_scale = 2 ``` ## Authentication `ncspot` prompts for a Spotify username and password on first launch, uses this to generate an OAuth token, and stores it to disk. The credentials are stored in `~/.cache/ncspot/librespot/credentials.json` (unless the base path has been changed with the `--basepath` option). The `logout` command can be used to remove cached credentials. See [Vim-Like Commands](#vim-like-commands).