spotatui Documentation

repository·main·Indexed 20 days ago

https://github.com/largemodgames/spotatui

A terminal-based music player written in Rust and powered by Ratatui. It serves as a lightweight alternative to Electron-based clients, supporting Spotify (via native streaming), YouTube, Subsonic/Navidrome, Internet Radio, and Local Files. Features include a Lua plugin API for extensibility, custom themes, an audio visualizer, and Discord Rich Presence integration.

Tokens
46.4K
Snippets
137
Records
219
Agent score
73%

What's inside spotatui

  1. Configure Spotify playback modes

    main

    To play Spotify tracks, you must have a Spotify Premium account. You can use one of the following three methods:

    1. Native Streaming (Recommended): spotatui plays audio directly using its built-in streaming engine.
    2. Official Spotify Client: Keep the official Spotify desktop application open on your computer.
    3. spotifyd: Use the spotifyd lightweight background daemon.

    Note: With a free Spotify account, you can authenticate and browse/search, but playback actions (play/pause/seek/transfer) will not function.

  2. Create custom full-screen views

    main

    Plugins can register retained-mode full-screen views using the following lifecycle:

    1. Register: spotatui.register_screen(name, spec)
      • name: Unique, non-empty string.
      • spec: A table containing title (optional), on_key(key) (required), and on_open()/on_close() (optional).
    2. Publish Content: spotatui.set_screen(name, widgets)
      • widgets: An array of layout/UI tables.
    3. Navigate: spotatui.show_screen(name) to open, or spotatui.close_screen(name) to leave.

    Widget Types:

    • paragraph: Styled text. Supports lines (same format as popups) and scroll (boolean).
    • list: A bordered list. Fields: items (array), title?, selected? (1-based index).
    • gauge: A progress bar. Fields: ratio (0..1), label?.
    • cover_art: Renders current track artwork. Fields: source (only "current" allowed), fit ("contain" or "scale"). Only one cover_art widget is allowed per screen.
    • row / column: Layout containers that stack children horizontally or vertically.
    -- Example: A minimal interactive screen
    spotatui.require_api(5)
    
    local selected = 1
    local names = {}
    
    local function render()
      spotatui.set_screen("my_playlists", {
        { type = "paragraph", lines = { { text = "j/k to move, Esc to leave", italic = true } }, height = 2 },
        { type = "list", title = "Playlists", items = names, selected = selected },
      })
    end
    
    spotatui.register_screen("my_playlists", {
      title = "My Playlists",
      on_key = function(key)
        if key == "j" and selected < #names then selected = selected + 1 end
        if key == "k" and selected > 1 then selected = selected - 1 end
        render()
      end,
      on_open = function()
        spotatui.get_playlists(function(playlists, err)
          names = {}
          for _, p in ipairs(playlists or {}) do
            names[#names + 1] = p.name
          end
          render()
        end)
      end,
    })
    
    spotatui.register_command("my_playlists", function()
      spotatui.show_screen("my_playlists")
    end)
  3. Platform-specific requirements for audio backends

    main

    Depending on your installation method and OS, you may need specific libraries for audio visualization and stability:

    • Linux (Pre-built/AUR): Uses the PipeWire backend. Ensure pipewire is installed (e.g., sudo apt-get install libpipewire-0.3-0 on Debian/Ubuntu).
    • Linux (Cargo/Source): Uses the cpal-based backend and does not require PipeWire.
    • macOS: Uses the portaudio backend for better stability and Bluetooth support (e.g., AirPods). Install via brew install portaudio.
  4. Handle plugin errors and safety

    main

    Plugin code is isolated so it cannot crash the main application.

    Error Behavior:

    • If a callback (e.g., on_key, on_open, or a command) raises an error or panics, the error is logged and a highlighted status message is shown in the playbar for 6 seconds.
    • One-Strike Rule: An erroring callback is disabled after its first failure. Other callbacks for the same event or other plugin callbacks continue to run.
    • Priority: Plugin errors take precedence over normal notifications (like spotatui.notify). A notification will only appear after the error message expires.
  5. System integration for macOS and Windows native streaming

    main

    When using native streaming, spotatui integrates with system-level media controls:

    macOS (Now Playing Integration)

    Uses Apple's MPRemoteCommandCenter API to enable:

    • Keyboard media keys (Play/Pause, Next, Previous).
    • macOS Control Center integration.
    • MacBook Pro Touch Bar support.
    • Bluetooth headphone/AirPods button controls.

    Windows (System Media Transport Controls)

    Uses the smtc-tokio crate to enable:

    • Keyboard media keys.
    • Windows volume flyout/media overlay (showing track title, artist, album, and cover art).
    • OS-level transport controls (Play, Pause, Next, Previous, Stop, and Seek).
  6. How Homebrew packaging is automated

    main

    Homebrew publishing is handled automatically by the Continuous Delivery (CD) workflow when a new tag is pushed. The publish-homebrew job performs the following:

    1. Downloads the release artifacts.
    2. Calculates SHA256 checksums for each platform binary.
    3. Updates the formula in the homebrew-spotatui repository.
  7. Configure widget sizing and layout

    main

    Widgets in a screen can use size hints to control how they occupy space.

    Sizing Fields:

    • height / width: Absolute number of cells. 0 hides the widget.
    • height_percent / width_percent: Value from 1 to 100 representing percentage of parent.

    Layout Rules:

    • The top level is an implicit vertical stack.
    • A column stacks children vertically; a row stacks them horizontally.
    • Only the hint matching the parent's axis is used (width is ignored at top level; height is ignored inside a row).
    • Unsized widgets share remaining space evenly.
    • Setting both absolute and percentage values for the same axis is an error.
    • Trees are capped at 8 levels of nesting and 256 widgets per screen.
    spotatui.set_screen("now_playing", {
      { type = "paragraph", lines = { "q to close" }, height = 1, scroll = false },
      {
        type = "row",
        children = {
          { type = "cover_art", width_percent = 40 },
          { type = "paragraph", lines = lyrics, width_percent = 60 },
        },
      },
    })
  8. How Lua scripting and plugins work in spotatui

    main

    spotatui supports Lua plugins that react to playback events and can control playback via a curated API. Scripting is enabled by default via the scripting feature.

    Plugin Loading Order

    Plugins are loaded from the config directory (~/.config/spotatui/) at startup in this specific order:

    1. init.lua (if present).
    2. Single-file plugins: Every plugins/*.lua file, sorted by filename.
    3. Directory plugins: Every plugins/<name>/ folder, sorted by name. The entry point is main.lua, falling back to init.lua.

    Module Loading and Namespacing

    For directory plugins, the plugin's own folder is added to Lua's package.path. This allows you to split a plugin across multiple files and use require("module") (which resolves to plugins/<name>/module.lua).

    Warning: The package.path and module cache are shared across all plugins. If two plugins both require("util"), the first-loaded plugin's util.lua is cached and served to the second plugin. To avoid conflicts, always use distinctive, plugin-prefixed names for helper modules (e.g., require("my_plugin_util")).

  9. Security and Trust for Lua Plugins

    main

    Lua plugins in spotatui are not sandboxed. A plugin runs with the same privileges as the spotatui process itself. This means:

    • It has access to the full Lua standard library, including filesystem access via io and os.
    • It can make arbitrary network requests using spotatui.http_get and spotatui.http_post.
    • spotatui plugin add clones a git repository and executes its main.lua on the next startup.

    Recommendation: Only install plugins from authors you trust or from source code you have personally reviewed. There is no permission prompt or isolation between a plugin and your account.

  10. How native Spotify streaming works

    main

    spotatui can act as a Spotify Connect device, playing audio directly without needing the official Spotify app or spotifyd.

    Requirements & Behavior:

    • Spotify Premium is required.
    • It uses a maintained fork of librespot to handle audio delivery.
    • It supports media keys, MPRIS (on Linux), and macOS Now Playing.
    • Note: While you can add Spotify to an existing session via the d menu without restarting, Native (librespot) streaming requires a restart to initialize the audio engine.
  11. Quickstart with spotatui

    main

    Run spotatui to start the application. On the first launch, you will be prompted to choose a music source:

    1. Spotify (requires login/Premium for native streaming)
    2. YouTube (free, requires yt-dlp)
    3. Subsonic (free, requires a Subsonic/Navidrome server)
    4. Internet Radio (free)
    5. Local Files (free)

    Essential In-App Shortcuts

    • ?: Open the in-app help menu.
    • d: Open the Source & Device picker to switch sources.
    • z: Queue the selected track.
    • Shift+Q: Open the queue.
    • v: Toggle the real-time audio visualizer.
    • F: Save an Internet Radio station to the sidebar.
    spotatui