Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

### Added

- **Lua plugin scripting**: Added an embedded Lua engine behind the `scripting` feature that loads `~/.config/spotatui/init.lua` and `~/.config/spotatui/plugins/*.lua` at startup. Plugins register callbacks via `spotatui.on(event, fn)` for `start`, `quit`, `track_change`, `playback_state_change`, `seek`, `volume_change`, and `queue_change`, read playback/track/device snapshots, and drive playback through a curated action API (`play`, `pause`, `next`, `previous`, `seek`, `set_volume`, `shuffle`, `search`, `notify`). A broken plugin is disabled with a status message instead of crashing the app. See `docs/scripting.md`.
- **Lua plugin commands and keybindings**: Plugins can now register named commands via `spotatui.register_command(name, fn)` and users can bind those commands to keys in `config.yml` under a new `plugin_commands` map. An erroring command reports a status message but stays bound. See `docs/scripting.md`.
- **Lua plugin UI extension (api_version 2)**: Plugins can now push a persistent segment into the playbar title via `spotatui.set_playbar(text)` (pass `nil` to clear), open a scrollable modal popup via `spotatui.popup(title, lines)` (lines support per-line fg/bold/italic styling; `j`/`k` scroll, `Esc`/`q` close), and apply runtime theme color overrides via `spotatui.set_theme(tbl)` (runtime-only, not persisted to config). See `docs/scripting.md`.
- **Lua plugin HTTP and JSON (api_version 3)**: Plugins can make async HTTP requests with `spotatui.http_get(url, cb)` and `spotatui.http_post(url, body, headers, cb)` (callbacks run on a later UI tick), and convert payloads with `spotatui.json_encode`/`spotatui.json_decode`. See `docs/scripting.md`.
- **Lua plugin installer and ecosystem**: Added a `spotatui plugin` command (`add`/`list`/`remove`/`update`) that installs plugins from git repositories into `~/.config/spotatui/plugins/<name>/` and tracks them in a `plugins.lock` file. The loader now also loads directory plugins (`plugins/<name>/main.lua`, falling back to `init.lua`) with the plugin's own folder on `package.path` so it can `require` sibling modules. Ships runnable example plugins under `examples/plugins/` and a `PLUGINS.md` index. See `docs/scripting.md`.
- **Lua plugin API-version guard and scaffold (api_version 4)**: Plugins can declare their minimum required API version with `spotatui.require_api(n)`; if the build is too old the plugin fails to load with a clear "requires spotatui scripting API vN" message instead of a cryptic nil-call error. Added `spotatui plugin new <name>` to scaffold a working directory plugin (`main.lua` + `README.md`) to start from. See `docs/scripting.md`.
- **SMTC Integration**: System Media Transport Controls is now integrated with the app for Windows users. Users can now control playback state using media keys and check playback state in media flyouts ([#229](https://github.com/LargeModGames/spotatui/issues/229)).
- **Click and drag to seek on the playbar**: The progress bar is now interactive. Click anywhere on the gauge to jump to that position, or click and drag to scrub. Control buttons keep priority, the time label stays non-clickable, and seeks reuse the existing native and throttled-API paths ([#157](https://github.com/LargeModGames/spotatui/issues/157)).

Expand Down
112 changes: 110 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ realfft = { version = "3.4", optional = true }
discord-rich-presence = { version = "1.1", optional = true }
ratatui-image = { version = "11.0.4", optional = true, default-features = false, features = ["crossterm"] }
image = { version = "0.25", optional = true }
mlua = { version = "0.11", features = ["lua54", "vendored", "serde"], optional = true }

# Streaming dependencies (librespot)
# Pin vergen crates to versions compatible with librespot-core 0.8's build.rs
Expand Down Expand Up @@ -105,7 +106,7 @@ objc2 = { version = "0.6", optional = true }
block2 = { version = "0.6", optional = true }

[features]
default = ["telemetry", "streaming", "audio-viz-cpal", "macos-media", "discord-rpc", "mpris", "self-update", "windows-media"]
default = ["telemetry", "streaming", "audio-viz-cpal", "macos-media", "discord-rpc", "mpris", "self-update", "windows-media", "scripting"]
telemetry = []
self-update = ["dep:self_update", "dep:sha2", "dep:hex"]
streaming = ["librespot-core", "librespot-playback", "librespot-connect", "librespot-oauth", "librespot-metadata", "librespot-protocol", "protobuf"]
Expand All @@ -125,6 +126,7 @@ macos-media = ["objc2-media-player", "objc2-foundation", "objc2-app-kit", "objc2
windows-media = ["smtc-tokio", "streaming"] # windows SMTC integration
discord-rpc = ["discord-rich-presence"]
cover-art = ["ratatui-image", "image"]
scripting = ["dep:mlua"]

[target.'cfg(target_env = "musl")'.dependencies]
openssl-sys = { version = "0.9", features = ["vendored"] }
Expand Down
48 changes: 48 additions & 0 deletions PLUGINS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Plugins

spotatui runs user-written Lua plugins. They react to playback events, add commands and key
bindings, draw popups and playbar segments, restyle the theme, and make async HTTP requests.
See [`docs/scripting.md`](docs/scripting.md) for the full API and
[`examples/plugins/`](examples/plugins) for runnable examples.

## Installing a plugin

Plugins published as git repositories install with one command (requires `git`):

```bash
spotatui plugin add owner/repo # clone + record in the lockfile
spotatui plugin list # show installed plugins
spotatui plugin update # update all to their latest commit
spotatui plugin remove <name> # uninstall
spotatui plugin new <name> # scaffold a new plugin to start from
```

Plugins are cloned into `~/.config/spotatui/plugins/<name>/` and loaded at startup. Restart
spotatui after installing, and bind any commands the plugin registers under `plugin_commands` in
`config.yml`.

Plugins are not sandboxed and run with full app privileges and network access, so only install
ones you trust. See [Trust and safety](docs/scripting.md#trust-and-safety).

You can also drop a single `.lua` file into `~/.config/spotatui/plugins/` by hand.

## First-party examples

These ship in this repo under [`examples/plugins/`](examples/plugins):

- **track-notifier** - "Now playing" toast and playbar segment on every track change.
- **track-info-popup** - a command that pops up details of the current track.
- **accent-cycler** - a command that rotates the theme accent color.
- **now-playing-webhook** - POSTs a JSON payload to a webhook on track change.
- **session-stats** - a directory plugin (with a `require`-d helper) that tracks session plays.

## Sharing your own plugin

Run `spotatui plugin new <name>` to scaffold a starting point. A shareable plugin is just a git
repository with a `main.lua` (or `init.lua`) entry point at its root. Helper modules sit alongside
it and load via `require("module")`. Document any command and a suggested key binding in your
README, but ship the binding as a suggestion, not a hard-coded key.

Tag your repository with the GitHub topic `spotatui-plugin` so it's discoverable, and open a pull
request adding it to this list - a short description and the `owner/repo` install line is all it
takes.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
- [Usage](#usage)
- [Native Streaming](#native-streaming)
- [Configuration](#configuration)
- [Plugins](#plugins)
- [Discord Rich Presence](#discord-rich-presence)
- [Limitations](#limitations)
- [Deprecated Spotify API Features](#deprecated-spotify-api-features)
Expand Down Expand Up @@ -227,6 +228,23 @@ behavior:

You can also override via `SPOTATUI_DISCORD_APP_ID` or disable in the setting or by setting `behavior.enable_discord_rpc: false` in ~/.config/spotatui/config.yml.

## Plugins

spotatui runs user-written Lua plugins. They react to playback events, add commands and key
bindings, draw popups and playbar segments, restyle the theme, and make async HTTP requests.

Install a plugin published as a git repository (requires `git`):

```bash
spotatui plugin add owner/repo
spotatui plugin list
spotatui plugin update
spotatui plugin remove <name>
```

See [`PLUGINS.md`](PLUGINS.md) for the ecosystem overview, [`examples/plugins/`](examples/plugins)
for runnable examples, and [`docs/scripting.md`](docs/scripting.md) for the full API reference.

## Limitations

This app uses the [Web API](https://developer.spotify.com/documentation/web-api/) from Spotify, which doesn't handle streaming itself. You have three options for audio playback:
Expand Down
Loading
Loading