nowPlayingDesktops
A glass-style album-art tile on your desktop wallpaper for whatever you are playing on Spotify (or another supported player in local mode).
Screenshots
Quick start (Windows)
- Get the app — download
now-playing-windows.exefrom Releases, or install with pipx:pipx install now-playing-desktops - Run it — double-click the
.exe, or runnow-playing run --source local(pipx putsnow-playingon your PATH) - Done — play something in the Spotify desktop app; pause or exit to restore your wallpaper
For macOS, Linux, Spotify Web API mode, or hacking on the code, see the sections below.
Install from source
git clone https://github.com/hippi345/nowPlayingDesktops.git
cd nowPlayingDesktops
python3.12 -m venv .venv
source .venv/bin/activate # Windows: .\.venv\Scripts\Activate.ps1
pip install -e ".[dev]" # add windows, linux, or macos extras as needed
Quick Start: Local mode
Local mode reads playback from the OS media session on this machine. Pick your OS:
Windows (local)
Prerequisites
- Python 3.12+ (
requires-pythoninpyproject.toml; CI tests 3.12 and 3.13). - Install:
pip install -e ".[windows]"(WinRT optional deps for SMTC). - Player: Spotify desktop app playing on this PC (System Media Transport Controls).
Run
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -e ".[windows]"
now-playing run --source local
Nice extra: When the lock screen is set to use the same picture as the desktop wallpaper, the composed tile can appear on the lock screen while a track is playing; it is restored when you pause (same restore path as the desktop).
Linux (local)
Prerequisites
- Python 3.12+
- Install:
pip install -e ".[linux]"(dbus-nextfor MPRIS). - D-Bus session:
echo $DBUS_SESSION_BUS_ADDRESSshould be set in your desktop session. - Player: Spotify desktop or another app with MPRIS (
org.mpris.MediaPlayer2.*).playerctlis not required by this app. - Wallpaper backend (one of): GNOME family —
gsettings; KDE —plasma-apply-wallpaperimageand/orqdbus6/qdbus; other WMs —feh,swaybg, ornitrogen(plusxrandrorwlr-randrfor screen size).
Run
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e ".[linux]"
now-playing run --source local
macOS (local)
Prerequisites
- Python 3.12+
- Install:
pip install -e ".[macos]"(appscriptfor Spotify AppleScript control). - Player: Spotify.app playing on this Mac.
- Automation: macOS may prompt for Automation permission for your terminal or Python to control Spotify (System Settings → Privacy & Security → Automation).
Run
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e ".[macos]"
now-playing run --source local
Optional PyObjC (pyobjc-framework-Cocoa) improves per-display wallpaper control; without it, osascript sets all screens.
Common run flags (local)
-v/--verbose— debug logging--once— single poll/apply cycle--env-file PATH— load settings from a.envfile--no-online-art— no internet art lookups (see Album art)--poll-interval SECONDS— default2.5
YOUR_SPOTIFY_USERNAME on the command line is optional for local mode (only required for --source spotify).
Login autostart (local)
now-playing autostart enable --source local
enable forwards --source, --env-file, --no-online-art, and an optional username into the registered autostart command (Windows Run key, XDG autostart .desktop, or macOS LaunchAgent). Check status with now-playing autostart status; remove with now-playing autostart disable.
Album art in local mode
When online art is enabled (default), cover resolution is:
- System media thumbnail when available (SMTC on Windows, MPRIS art on Linux, AppleScript artwork URL on macOS — URL download only if online art is enabled).
- Apple iTunes Search API (public HTTPS; no iTunes app or Apple account) when the thumbnail is missing or small.
- Generated placeholder (gradient + track title/artist) if nothing else is available.
If the iTunes lookup fails (network error, no match, timeout), the app logs a warning and continues with the system thumbnail or placeholder; the wallpaper still applies for that poll. A background thread may upgrade wallpaper art after the first apply when iTunes returns a larger image for the same playing track (see logs: Upgraded art Apply took …).
Disable online art (offline / privacy)
NOW_PLAYING_ONLINE_ART=0
Or on the CLI:
now-playing run --source local --no-online-art
When disabled: local thumbnails only, then placeholder; no iTunes requests, no Spotify art URL downloads, no background iTunes upgrader. (Spotify Web API mode still uses the network for playback and API album art unless you also pass --no-online-art, which skips art URL downloads and uses placeholders.)
The app loads .env from (first match wins, without overriding variables already set in the shell):
--env-file/NOW_PLAYING_ENV_FILE- Per-user config:
%APPDATA%\now-playing-desktops\.env(Windows),~/Library/Application Support/now-playing-desktops/.env(macOS),$XDG_CONFIG_HOME/now-playing-desktops/.env(Linux, default~/.config/now-playing-desktops/.env) - Current working directory
.env
Quick Start: Spotify mode
Spotify mode uses the Web API (any device). Wallpaper setup is the same on every OS; playback detection does not need the [windows] / [linux] / [macos] extras.
Windows (Spotify)
Prerequisites
- Python 3.12+
- Install:
pip install -e .(base package; wallpaper usesctypes/ WinRT desktop APIs already in the main install path). - Spotify Developer app with Client ID in
.env(see below).
Linux (Spotify)
Prerequisites
- Python 3.12+
- Install:
pip install -e . - Wallpaper backend (same as local):
gsettings(GNOME),plasma-apply-wallpaperimage/qdbus6/qdbus(KDE), orfeh/swaybg/nitrogenon lightweight WMs.
macOS (Spotify)
Prerequisites
- Python 3.12+
- Install:
pip install -e .(wallpaper via AppKit if PyObjC is installed, otherwiseosascript).
1. Create a Spotify app
-
Open the Spotify Developer Dashboard and create an app.
-
Copy the Client ID (PKCE does not need a Client Secret).
-
Under Settings → Redirect URIs, add this exact URI (unless you override it):
http://127.0.0.1:8897/callbackThis is the default from
SPOTIPY_REDIRECT_URI/DEFAULT_REDIRECT_URIinauth.py. If port8897is in use, pick another loopback URL (for examplehttp://127.0.0.1:8898/callback), setSPOTIPY_REDIRECT_URIto match, and register the same URI in the dashboard.
2. Configure credentials
Copy .env.example to .env or to the per-user config path (recommended for autostart):
| OS | Config directory |
|---|---|
| Windows | %APPDATA%\now-playing-desktops\.env |
| macOS | ~/Library/Application Support/now-playing-desktops/.env |
| Linux | ~/.config/now-playing-desktops/.env |
Minimum for PKCE:
SPOTIPY_CLIENT_ID=your_client_id_here
SPOTIPY_CLIENT_USERNAME=your_spotify_username
Optional: NOW_PLAYING_SOURCE=spotify|local|auto (default auto).
3. Allowlist users (Development mode)
Apps in Development mode only work for users listed under the app’s Users Management (currently up to five users plus the owner). Add every Spotify account that will use the app.
4. Sign in once
now-playing login YOUR_SPOTIFY_USERNAME
Optional: --env-file PATH if your .env is not in the default config directory.
Token cache file: spotify-token-<username>.cache under the same config directory (config.spotify_token_cache_path).
5. Run
now-playing run YOUR_SPOTIFY_USERNAME --source spotify
Autostart:
now-playing autostart enable YOUR_SPOTIFY_USERNAME --source spotify
(enable requires Spotify credentials in the resolved .env when source is spotify or auto with Client ID present.)
Command reference
| Command | Purpose |
|---|---|
now-playing run [USER] [--source spotify|local|auto] [--once] [--env-file PATH] [--no-online-art] [-v] |
Poll playback and update wallpaper |
now-playing login [USER] [--env-file PATH] [-v] |
Interactive Spotify OAuth (PKCE or legacy secret flow) |
now-playing restore [--state-file PATH] [-v] |
Restore wallpaper saved at session start |
now-playing diag [-v] |
Windows only: playback source diagnostics, Spotify auth summary, monitor/DPI/wallpaper report |
now-playing autostart enable [USER] [--source …] [--env-file PATH] [--no-online-art] [-v] |
Register login autostart |
now-playing autostart disable |
Remove autostart |
now-playing autostart status |
Print enabled or disabled |
Stop a foreground run with Ctrl+C; the original wallpaper is restored automatically.
Wallpaper behavior
- Composes a blurred backdrop, glass panel, cover art, and track labels; restores your original wallpaper on pause or exit.
- Windows — multi-monitor aware; optional per-monitor wallpaper when
IDesktopWallpaperis available. - macOS — sets wallpaper on every display (AppKit /
osascript). - Linux — GNOME (
gsettings), KDE (plasma-apply-wallpaperimage/qdbus), or fallbacks (feh,swaybg,nitrogen).
Session state: wallpaper-state.json in the config directory. If a previous run exited without restoring (session_active), the next startup restores immediately.
Which mode should I pick?
| Mode | Command | What it needs | Players / devices | Account / app setup | Pros | Cons |
|---|---|---|---|---|---|---|
| Local | --source local |
Python 3.12+, OS extras below, media app playing on this computer | Spotify desktop (Windows SMTC, Linux MPRIS, macOS AppleScript); other MPRIS players on Linux may work if they expose metadata | None | No Spotify Developer app; no OAuth; playback detection works offline | Art is often a small system thumbnail unless online art is enabled; desktop app must be on this machine |
| Spotify | --source spotify |
Same Python install + SPOTIPY_CLIENT_ID in .env |
Any device where your Spotify account is playing (phone, web, desktop, etc.) | Spotify Developer app; PKCE login once; Development mode user allowlist (up to 5 users) |
High-resolution album art from the Web API | Requires Spotify credentials and allowlisted users in dev mode |
| Auto | omit --source, or --source auto, or NOW_PLAYING_SOURCE=auto (default) |
Resolves to Spotify or local using the rules below | Same as the effective mode | Same as the effective mode | One command whether or not you use the Web API | Surprising if you expected local but have SPOTIPY_CLIENT_ID set |
How auto resolves (see resolve_effective_playback_source in playback_source.py):
--source spotifyorNOW_PLAYING_SOURCE=spotify→ Spotify Web API.--source localorNOW_PLAYING_SOURCE=local→ local OS media session.- Otherwise (
auto): ifSPOTIPY_CLIENT_IDis set and non-empty in the environment (after.envload), effective mode is Spotify; if not, local.
A resolved Spotify run still needs a username (SPOTIPY_CLIENT_USERNAME or a run/login argument) and a usable token cache from now-playing login (or legacy client-secret flow). Online art (iTunes Search lookups and Spotify art URL downloads) applies to local mode by default; see Album art in local mode.
Legacy console scripts now-playing-macos and now-playing-windows behave like now-playing run … when you omit the run subcommand.
Troubleshooting
| Topic | What to do |
|---|---|
now-playing diag |
Windows only. Shows effective playback source, what local/Spotify sources see, auth mode, and display/wallpaper geometry. |
| Log file | During run, logs go to a rotating file under the config directory: Windows %APPDATA%\now-playing-desktops\logs\now-playing.log; macOS ~/Library/Application Support/now-playing-desktops/logs/now-playing.log; Linux ~/.config/now-playing-desktops/logs/now-playing.log (logging_setup.log_file_path()). |
| Pause / resume does not restore or re-apply | Run with -v and inspect the log above. On pause or no session, the runner calls restore_original_wallpaper(); on resume it should log Updated wallpaper for …. If nothing changes, check whether a second now-playing run is holding the single-instance lock (below) or whether restore failed (Failed to apply wallpaper restore snapshot in the log). |
| Single instance | Only one now-playing run at a time. A second instance logs Another now-playing run instance is already active (...); exiting and exits 0 without changing the wallpaper. Stop only the other instance: Windows — mutex Local\now-playing-desktops-run (see now-playing diag run-lock lines) or find the process whose command line contains now_playing_desktops / now-playing run; Linux/macOS — read PID from run.lock in the config dir (~/.config/now-playing-desktops/run.lock on Linux) and kill that PID, or end the terminal session running run. Do not delete run.lock while the holder is still running. |
| Online art lookup failed | Warning in logs (iTunes artwork lookup failed / Failed to download iTunes artwork); wallpaper still uses SMTC/MPRIS thumbnail or placeholder. Opt out entirely: NOW_PLAYING_ONLINE_ART=0 or --no-online-art. |
| Verbose | -v / --verbose on any subcommand that accepts shared flags. |
| Compose quality checks | Set NOW_PLAYING_COMPOSE_VERIFY=1 to run optional post-apply checks in a background thread (never blocks wallpaper set). |
| Apply timing | Look for Apply took X.XXs (fetch=…, compose=…, save=…, set=…, cache=hit|miss) in logs; background upgrades log Upgraded art Apply took …. |
| Linux unsupported platform | Install feh, swaybg, or nitrogen, or use GNOME/KDE with their wallpaper tools. |
| Local mode: no track | Start the desktop player and press play; on Windows install pip install -e ".[windows]". |
| OAuth port in use | Change SPOTIPY_REDIRECT_URI and register the new URI in the Spotify dashboard. |
Development
pip install -e ".[dev]"
ruff check src tests
ruff format src tests
pytest -q
Profile compose at laptop resolution: python scripts/profile_compose.py.
License
MIT — see LICENSE. Copyright (c) 2026 Joel Shearon.
Metadata
Release files for now-playing-desktops 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| now_playing_desktops-1.0.0.tar.gz | 2.3 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| now_playing_desktops-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.2 MB
Release files / now_playing_desktops-1.0.0.tar.gz
| Download URL | now_playing_desktops-1.0.0.tar.gz |
|---|---|
| Size | 2.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f8ef08c2fcafabb55823056b339baa20ad9b5053f87da8f02d8437eb8e984e59
|
|
BLAKE2b-256 checksum How to use checksums |
685f505fba6d7f55d91d6b089c1d287111f045373d0d096b15d21037b44ec53f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.
Transparency logRelease files / now_playing_desktops-1.0.0-py3-none-any.whl
| Download URL | now_playing_desktops-1.0.0-py3-none-any.whl |
|---|---|
| Size | 908.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e6838585587e92a59c0d09aa43d8e55479fc55869c396087de2fcd3aeb4a2653
|
|
BLAKE2b-256 checksum How to use checksums |
c61606f4063ed108f7ad4c186147641a36ca91a4fe6aa483a8689c3e921e1670
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.
Transparency log