A Spotify speaker
Install and manage Soloist from preferences. Pair once through Spotify’s phone or desktop app.
GNOME Shell extension
A Spotify speaker, controlled from your top bar.
Spotify Soloist playback, Liked Songs, and named playlist shortcuts. Set up your speaker in preferences, then control it from the GNOME panel.
01
QuickSpot turns this computer into a Spotify speaker and puts playback controls in the GNOME top bar. It uses Spotify Soloist for audio playback, downloaded separately from Spotify.
The extension, the player service, and the optional playlist library login have separate lifetimes. Disabling the extension leaves the player and its MPRIS media controls running. Pairing the speaker does not connect the playlist library.
Install and manage Soloist from preferences. Pair once through Spotify’s phone or desktop app.
Previous, play/pause, next, device activation, shuffle, Liked Songs, and named playlist shortcuts.
02
Requires GNOME Shell 50; GJS, libsecret, libsoup 3, GTK 4, and libadwaita; Python 3.12 or newer; a graphical systemd user session; and PipeWire or PulseAudio. Spotify Premium and your own Soloist API key are required. Official player downloads support x86_64, aarch64, and armv7l Linux.
With mise activated in your shell and just available, install from a checkout with mise. GNOME libraries come from the host; mise supplies development tools.
just setup just install
The build creates
quickspot@napalm255.github.io.shell-extension.zip. Installation unpacks it and compiles its settings
schema. Log out and back in so GNOME discovers the
extension, then enable it and open preferences:
just enable just prefs
After updating a loaded extension, log out and back in to load its new panel code. Opening preferences does not reload GNOME Shell.
03
The web player cannot discover an unpaired speaker. The device name can be changed in preferences. QuickSpot remembers pairing through Soloist’s stored device identity.
| State | Meaning |
|---|---|
| Player not installed | Install Soloist before starting. |
| Player needs setup | Choose Repair to restore the service. |
| API key needed | Save a key in GNOME Keyring. |
| Player stopped | The service is available but stopped. |
| Player running · connection pending | The service is running; its local API is not connected yet. |
| Ready to pair | The local API is connected; pair through Spotify. |
| Paired · ready to play | Pairing is complete; this speaker is not the active device. |
| Connected · this device is active | Spotify has selected this speaker. Check audio output separately. |
A running service alone does not prove pairing or audible playback. Stop remains available while the player runs, including when GNOME Keyring is locked.
04
Open the top-bar item to use the menu. While a track is available, its label shows artist and title. Playback actions become available when the local speaker is paired.
| Control | Behavior |
|---|---|
| Previous / Play or Pause / Next | Control playback on the Soloist speaker. |
| Use this device | Activate this speaker in Spotify. |
| Shuffle → On / Off | Activate the speaker and change ordinary shuffle. |
| Playlist shortcuts | Play a saved playlist by its chosen name. |
| Liked Songs / Your playlists | Use the optional connected library account. |
| Refresh playlists | Reload the saved playlist list. |
| Start / Stop | Control the independent player service when setup permits it. |
| Open Spotify in browser | Open the Spotify web player. |
| QuickSpot settings | Open GTK preferences. |
Smart Shuffle is displayed when Soloist reports it. Enable that mode in Spotify’s phone app with this speaker selected; About Smart Shuffle… explains the limitation.
05
On Library → Playlist shortcuts, enter a name and a playlist ID, Spotify link, or URI. These three forms identify the same example playlist:
37i9dQZF1DXcBWIGoYBM5 https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5 spotify:playlist:37i9dQZF1DXcBWIGoYBM5
Choose Add, then select the name in the panel. Shortcuts work without the optional library login, including Discover Weekly and other playlists Spotify may omit from the library API. Add the same playlist under a new name to rename it; Remove deletes its shortcut. Up to 100 shortcuts can be saved.
http://127.0.0.1:43821/callback as
its redirect URI.
No client secret is needed. Login uses PKCE with
playlist-read-private and
playlist-read-collaborative. Tokens are
bound to the client ID that issued them. Playlist
loading follows pagination, refreshes expired tokens,
and observes rate limits.
Cancel stops the current login; closing preferences cancels it too. Reconnect authorizes again, and Disconnect removes the saved playlist login without unpairing the speaker.
Development-mode apps require their owner to retain Premium and allow the connecting account. See Spotify’s development-mode requirements for current app and user limits.
06
Open preferences with
just prefs or
QuickSpot settings in the panel.
| Setting or action | Effect |
|---|---|
| Install / Repair / Update | Download Soloist or restore its user service, according to installation state. |
| Start at login | Enable or disable the systemd user service for login. |
| Name shown in Spotify | Default: QuickSpot. Saving restarts a running player. |
| Soloist API key | Store the key in GNOME Keyring. Saving restarts a running player. |
| Import credentials | Read a selected .env file with a Soloist key and/or library client ID. |
| Spotify client ID / Connect | Authorize the optional playlist library in your browser. |
| Playlist shortcuts | Add, rename, and remove saved playlist choices. |
An update attempts to restore a previously running service even if downloading fails or preferences closes. Soloist builds expire after 90 days; use Update to install a newer build. See Spotify’s downloads and updates documentation.
Use literal values in a private .env file.
Replace these placeholders with your own values; do not
commit the file:
SPOTIFY_SOLOIST_KEY=your-soloist-api-key SPOTIFY_CLIENT_ID=your-developer-app-client-id
Select it with Import credentials, or
import this checkout’s .env:
just import-credentials
Importing the client ID does not authorize the library. Choose Connect separately. The importer parses values literally rather than executing shell code.
07
Choose quality in Spotify with this speaker selected. Soloist’s command line and local API do not expose quality controls or the active bitrate to QuickSpot. QuickSpot cannot confirm that a stream is lossless.
08
Start with the credential-safe diagnostic command:
just doctor
It checks installation, service state, saved-key presence, local API connectivity, and pairing without printing credentials or account information.
| Symptom | Next step |
|---|---|
| Extension not found after installation | Log out and back in, then enable QuickSpot. |
| Player needs setup | Choose Repair in preferences. |
| Player failed to start | Check the saved API key, network, and audio output. An expired build needs Update. |
| Local connection stays pending | Restart the player and run doctor again. |
| Speaker missing in Spotify | Use the phone or desktop app on the same network. Check guest Wi-Fi, client isolation, VPN routing, and local-network permission on iOS. |
| Library login fails | Check the exact loopback redirect URI, client ID, developer-app access, and Premium requirements. Keep preferences open. |
| Playlist missing from library | Refresh playlists or add a manual playlist shortcut. |
| Paired but silent | Select this speaker and check the host audio output; pairing alone does not verify audio. |
Discovery uses multicast DNS (UDP 5353) and a dynamic TCP pairing port, separate from the loopback API. Review applicable network and firewall rules. See Spotify Connect troubleshooting.
just logs just logs-player systemctl --user stop quickspot-soloist.service systemctl --user start quickspot-soloist.service
Extension logs and fixed launcher diagnostics omit credentials, account data, and track metadata. Raw Soloist output is silenced because it can contain sensitive information.
09
API keys and OAuth tokens live in GNOME Keyring, outside GSettings, service files, and the extension bundle. Device names and playlist shortcut names and URIs live in GSettings. XDG paths fall back to their standard defaults when unset.
| Data | Location |
|---|---|
| Soloist binary and upstream notices |
$XDG_DATA_HOME/quickspot/
|
| Device identity and pairing |
$XDG_DATA_HOME/quickspot/player/
|
| Audio cache (limited to 1 GiB) |
$XDG_CACHE_HOME/quickspot/
|
| User service |
$XDG_CONFIG_HOME/systemd/user/quickspot-soloist.service
|
Soloist requires the API key in process arguments;
processes with sufficient inspection access can see it.
Its WebSocket API binds to 127.0.0.1 on a
dynamic port.
Upstream provides no authentication or Origin
validation, so loopback binding does not isolate it from local
processes or browser code able to reach the port.
The installer bounds downloads and extraction, rejects unsafe archive members, restricts redirects to Spotify’s HTTPS download origin, and checks the binary’s version response before replacement. Downloads use mutable upstream URLs without independent signature or checksum verification. Archive validation does not prove authenticity beyond HTTPS.
Soloist is downloaded separately and never included in QuickSpot’s bundle. Preserve its upstream terms and third-party notices.
10
just uninstall
This stops and disables Soloist, then removes the extension. It preserves keyring credentials, player data, the downloaded binary, and the disabled service definition.
Disabling the extension alone leaves the independent player service running. Stop it from preferences or with:
systemctl --user stop quickspot-soloist.service
11
QuickSpot uses native GJS ES modules. The GNOME Shell panel, GTK preferences, and service runner execute in separate processes. Shell UI imports stay in the extension; GTK and Adwaita imports stay in preferences.
| Component | Responsibility |
|---|---|
| extension.js | Top-bar item, menus, playback actions, and lifecycle teardown. |
| prefs.js | GTK/Adwaita setup, credential entry, library login, and shortcuts. |
| modules/model.js | Pure input validation and presentation decisions, shared with Node tests. |
| modules/player.js | Installation, service, credentials, and playback as distinct states. |
| modules/soloist.js | Loopback WebSocket connection and playback commands. |
| modules/spotify.js | Web API requests, PKCE authorization, and client-bound tokens. |
| modules/secrets.js / credentials.js | GNOME Keyring and bounded literal credential import. |
| modules/mpris.js | Desktop media control bridge. |
| scripts/soloist-runner.js | Player process and MPRIS bridge independent of the extension. |
| scripts/install_soloist.py | Bounded download, safe extraction, service setup, and updates. |
The panel and service bridge control Soloist through its local API. Library authorization supplies playlist choices and Liked Songs separately. Teardown releases signals, timers, sockets, subprocesses, and UI references; asynchronous completions are guarded against closed windows and replaced clients.
12
just test runs Node validation tests,
Python installer and bundle tests, and native GJS
integration tests. Fixtures use temporary XDG paths and
private session buses, without the real account,
keyring, player service, or desktop settings.
just test-live adds headless GNOME Shell
and GTK checks for startup rollback, populated menus
across disable/re-enable, preferences, and command-line
uninstall. It leaves the desktop’s enabled extensions
alone.
just test-docs checks this site in Chromium
and Firefox: accessibility in both color schemes,
keyboard navigation, mobile layout, reduced motion,
links, assets, and agreement with project metadata. The
site ships no JavaScript and loads assets only from its
own origin.
Actual Spotify authorization, audible playback, discovery from another device, and stream quality require a real account and manual verification. Automated accessibility checks also need a human review of reading and focus order.
13
just build
The output is
quickspot@napalm255.github.io.shell-extension.zip
at the repository root, like the other Quick projects.
scripts/build.py validates the schema and
packages an explicit runtime allowlist using Python’s
standard library. just install builds that
ZIP and installs it for the current user through
gnome-extensions install --force.
just pack-check compares every runtime file
and its contents with GNOME’s official extension packer.
It also runs before just test-live. Bundle
tests verify excluded files stay outside the archive.
Docs, tests, npm dependencies, credentials, downloaded player binaries, and planning artifacts stay outside the ZIP. Update the allowlist when adding runtime files. QuickSpot is GPL-3.0-or-later; Soloist has separate terms and notices.
The repository currently provides local build and verification commands, with no automated release or documentation deployment workflow.
14
mise.toml pins tool versions,
justfile owns commands, and npm manages
development dependencies and the lockfile. There is no
transpilation step or npm runtime dependency.
just # list recipes just setup # tools, dependencies, docs browsers just fmt # format changes just lint # source, formatting, schemas just test # offline behavior suites just test-docs # Chromium and Firefox docs checks just security # credential-safe secret scans just build # extension ZIP at the repository root just pack-check # compare with GNOME’s official packer just ci # all checks and build just test-live # isolated Shell and GTK checks just docs # local docs at localhost:8000
Run ci before submitting changes and
test-live when changing panel or
preferences behavior. Keep documentation aligned with
actual commands and code. See
the engineering instructions
for contribution conventions.