now-playing-desktops 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- now_playing_desktops-1.0.0/.editorconfig +15 -0
- now_playing_desktops-1.0.0/.env.example +20 -0
- now_playing_desktops-1.0.0/.github/dependabot.yml +11 -0
- now_playing_desktops-1.0.0/.github/workflows/ci.yml +105 -0
- now_playing_desktops-1.0.0/.github/workflows/release.yml +73 -0
- now_playing_desktops-1.0.0/.gitignore +48 -0
- now_playing_desktops-1.0.0/LICENSE +21 -0
- now_playing_desktops-1.0.0/PKG-INFO +362 -0
- now_playing_desktops-1.0.0/README.md +315 -0
- now_playing_desktops-1.0.0/SECURITY.md +15 -0
- now_playing_desktops-1.0.0/docs/demo.gif +0 -0
- now_playing_desktops-1.0.0/docs/screenshot-ariana-grande.png +0 -0
- now_playing_desktops-1.0.0/docs/screenshot-don-toliver.png +0 -0
- now_playing_desktops-1.0.0/docs/social-preview.png +0 -0
- now_playing_desktops-1.0.0/now_playing_desktops/__init__.py +3 -0
- now_playing_desktops-1.0.0/now_playing_desktops/__main__.py +13 -0
- now_playing_desktops-1.0.0/now_playing_desktops/apply_timing.py +68 -0
- now_playing_desktops-1.0.0/now_playing_desktops/art_background_upgrade.py +130 -0
- now_playing_desktops-1.0.0/now_playing_desktops/art_cache.py +83 -0
- now_playing_desktops-1.0.0/now_playing_desktops/art_resolution.py +338 -0
- now_playing_desktops-1.0.0/now_playing_desktops/auth.py +283 -0
- now_playing_desktops-1.0.0/now_playing_desktops/cli.py +420 -0
- now_playing_desktops-1.0.0/now_playing_desktops/compose_verify.py +99 -0
- now_playing_desktops-1.0.0/now_playing_desktops/composer.py +932 -0
- now_playing_desktops-1.0.0/now_playing_desktops/config.py +67 -0
- now_playing_desktops-1.0.0/now_playing_desktops/cover_art.py +55 -0
- now_playing_desktops-1.0.0/now_playing_desktops/env_loader.py +102 -0
- now_playing_desktops-1.0.0/now_playing_desktops/fonts/Inter-Bold.ttf +0 -0
- now_playing_desktops-1.0.0/now_playing_desktops/fonts/Inter-Medium.ttf +0 -0
- now_playing_desktops-1.0.0/now_playing_desktops/fonts/Inter-Regular.ttf +0 -0
- now_playing_desktops-1.0.0/now_playing_desktops/fonts/Inter-SemiBold.ttf +0 -0
- now_playing_desktops-1.0.0/now_playing_desktops/fonts/OFL.txt +92 -0
- now_playing_desktops-1.0.0/now_playing_desktops/fonts/__init__.py +1 -0
- now_playing_desktops-1.0.0/now_playing_desktops/logging_setup.py +101 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/__init__.py +44 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/autostart.py +310 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/base.py +60 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/linux.py +132 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/linux_backends.py +289 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/linux_de.py +85 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/linux_screen.py +142 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/macos.py +214 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/windows.py +300 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/windows_com.py +152 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/windows_com_ctypes.py +134 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/windows_console.py +42 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/windows_diag.py +238 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/windows_dpi.py +363 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/windows_monitors.py +431 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/windows_restore.py +473 -0
- now_playing_desktops-1.0.0/now_playing_desktops/platforms/windows_virtual_compose.py +115 -0
- now_playing_desktops-1.0.0/now_playing_desktops/playback_diag.py +99 -0
- now_playing_desktops-1.0.0/now_playing_desktops/playback_factory.py +46 -0
- now_playing_desktops-1.0.0/now_playing_desktops/playback_source.py +48 -0
- now_playing_desktops-1.0.0/now_playing_desktops/playback_types.py +35 -0
- now_playing_desktops-1.0.0/now_playing_desktops/poll_watchdog.py +65 -0
- now_playing_desktops-1.0.0/now_playing_desktops/runner.py +591 -0
- now_playing_desktops-1.0.0/now_playing_desktops/single_instance.py +172 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/__init__.py +1 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/base.py +21 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/local/__init__.py +25 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/local/base.py +22 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/local/linux_mpris.py +79 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/local/macos_spotify.py +65 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/local/unsupported.py +15 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/local/windows_smtc.py +42 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/local/windows_smtc_async.py +110 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/local/windows_smtc_thumbnail.py +76 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/local/windows_smtc_winrt.py +29 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/local/windows_smtc_worker.py +168 -0
- now_playing_desktops-1.0.0/now_playing_desktops/sources/spotify_provider.py +49 -0
- now_playing_desktops-1.0.0/now_playing_desktops/spotify_art.py +159 -0
- now_playing_desktops-1.0.0/now_playing_desktops/verification/__init__.py +1 -0
- now_playing_desktops-1.0.0/now_playing_desktops/verification/wallpaper_analysis.py +404 -0
- now_playing_desktops-1.0.0/now_playing_desktops/verification/windows_display.py +89 -0
- now_playing_desktops-1.0.0/now_playing_desktops/wallpaper_snapshot.py +155 -0
- now_playing_desktops-1.0.0/now_playing_desktops/wallpaper_state.py +72 -0
- now_playing_desktops-1.0.0/packaging/now-playing-smoke.spec +44 -0
- now_playing_desktops-1.0.0/packaging/now-playing-windows.spec +45 -0
- now_playing_desktops-1.0.0/packaging/pyinstaller_win_entry.py +6 -0
- now_playing_desktops-1.0.0/pyproject.toml +91 -0
- now_playing_desktops-1.0.0/requirements-lock.txt +18 -0
- now_playing_desktops-1.0.0/scripts/profile_compose.py +50 -0
- now_playing_desktops-1.0.0/tests/__init__.py +1 -0
- now_playing_desktops-1.0.0/tests/conftest.py +19 -0
- now_playing_desktops-1.0.0/tests/helpers.py +191 -0
- now_playing_desktops-1.0.0/tests/linux_desktop_capture.py +241 -0
- now_playing_desktops-1.0.0/tests/test_art_cache.py +22 -0
- now_playing_desktops-1.0.0/tests/test_art_resolution.py +228 -0
- now_playing_desktops-1.0.0/tests/test_auth.py +264 -0
- now_playing_desktops-1.0.0/tests/test_autostart.py +66 -0
- now_playing_desktops-1.0.0/tests/test_autostart_compose_regression.py +97 -0
- now_playing_desktops-1.0.0/tests/test_autostart_env.py +159 -0
- now_playing_desktops-1.0.0/tests/test_cli.py +187 -0
- now_playing_desktops-1.0.0/tests/test_cli_autostart.py +95 -0
- now_playing_desktops-1.0.0/tests/test_compose_performance.py +127 -0
- now_playing_desktops-1.0.0/tests/test_composer.py +362 -0
- now_playing_desktops-1.0.0/tests/test_composer_glass_contrast.py +83 -0
- now_playing_desktops-1.0.0/tests/test_composer_layout_fonts.py +163 -0
- now_playing_desktops-1.0.0/tests/test_env_loader.py +100 -0
- now_playing_desktops-1.0.0/tests/test_linux_de_detection.py +33 -0
- now_playing_desktops-1.0.0/tests/test_linux_desktop_real_capture.py +173 -0
- now_playing_desktops-1.0.0/tests/test_linux_desktop_wallpaper.py +103 -0
- now_playing_desktops-1.0.0/tests/test_online_art.py +132 -0
- now_playing_desktops-1.0.0/tests/test_packaging.py +54 -0
- now_playing_desktops-1.0.0/tests/test_platforms_linux.py +244 -0
- now_playing_desktops-1.0.0/tests/test_platforms_macos.py +80 -0
- now_playing_desktops-1.0.0/tests/test_platforms_windows.py +35 -0
- now_playing_desktops-1.0.0/tests/test_platforms_windows_monitors.py +319 -0
- now_playing_desktops-1.0.0/tests/test_playback_sources.py +286 -0
- now_playing_desktops-1.0.0/tests/test_runner.py +378 -0
- now_playing_desktops-1.0.0/tests/test_runner_art_upgrade.py +212 -0
- now_playing_desktops-1.0.0/tests/test_runner_compose_quality.py +122 -0
- now_playing_desktops-1.0.0/tests/test_runner_linux_restore.py +33 -0
- now_playing_desktops-1.0.0/tests/test_runner_upgrade_steady_play.py +167 -0
- now_playing_desktops-1.0.0/tests/test_single_instance.py +75 -0
- now_playing_desktops-1.0.0/tests/test_spotify_art.py +136 -0
- now_playing_desktops-1.0.0/tests/test_spotify_errors.py +43 -0
- now_playing_desktops-1.0.0/tests/test_wallpaper_analysis.py +137 -0
- now_playing_desktops-1.0.0/tests/test_wallpaper_snapshot.py +197 -0
- now_playing_desktops-1.0.0/tests/test_wallpaper_state.py +42 -0
- now_playing_desktops-1.0.0/tests/test_windows_com_probe.py +35 -0
- now_playing_desktops-1.0.0/tests/test_windows_console.py +37 -0
- now_playing_desktops-1.0.0/tests/test_windows_desktop_wallpaper.py +238 -0
- now_playing_desktops-1.0.0/tests/test_windows_diag.py +210 -0
- now_playing_desktops-1.0.0/tests/test_windows_display_simulation.py +136 -0
- now_playing_desktops-1.0.0/tests/test_windows_dm_pels_compose.py +214 -0
- now_playing_desktops-1.0.0/tests/test_windows_dpi.py +69 -0
- now_playing_desktops-1.0.0/tests/test_windows_entry_point_dpi.py +71 -0
- now_playing_desktops-1.0.0/tests/test_windows_multi_monitor.py +222 -0
- now_playing_desktops-1.0.0/tests/test_windows_restore.py +232 -0
- now_playing_desktops-1.0.0/tests/test_windows_smtc_thumbnail_fastpath.py +40 -0
- now_playing_desktops-1.0.0/tests/test_windows_smtc_winrt.py +18 -0
- now_playing_desktops-1.0.0/tests/test_windows_smtc_worker.py +115 -0
- now_playing_desktops-1.0.0/tests/test_windows_wallpaper_geometry.py +154 -0
- now_playing_desktops-1.0.0/tests/test_windows_winrt_deps.py +61 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
charset = utf-8
|
|
5
|
+
end_of_line = lf
|
|
6
|
+
insert_final_newline = true
|
|
7
|
+
trim_trailing_whitespace = true
|
|
8
|
+
indent_style = space
|
|
9
|
+
indent_size = 4
|
|
10
|
+
|
|
11
|
+
[*.{yml,yaml,json,toml}]
|
|
12
|
+
indent_size = 2
|
|
13
|
+
|
|
14
|
+
[*.md]
|
|
15
|
+
trim_trailing_whitespace = false
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Spotify OAuth — create an app at https://developer.spotify.com/dashboard
|
|
2
|
+
# Copy to .env and fill in values (never commit .env).
|
|
3
|
+
#
|
|
4
|
+
# Minimal setup (PKCE): SPOTIPY_CLIENT_ID only — run `now-playing login` once.
|
|
5
|
+
# Legacy setup: also set SPOTIPY_CLIENT_SECRET (browser sign-in can happen on first `run`).
|
|
6
|
+
#
|
|
7
|
+
# Interactive runs: repo .env or shell exports are fine.
|
|
8
|
+
# Login autostart: copy this file to the per-user config directory, e.g.
|
|
9
|
+
# Windows: %APPDATA%\now-playing-desktops\.env
|
|
10
|
+
# macOS: ~/Library/Application Support/now-playing-desktops/.env
|
|
11
|
+
# Linux: ~/.config/now-playing-desktops/.env
|
|
12
|
+
SPOTIPY_CLIENT_ID=your_client_id_here
|
|
13
|
+
# Optional (legacy client-secret OAuth flow):
|
|
14
|
+
# SPOTIPY_CLIENT_SECRET=your_client_secret_here
|
|
15
|
+
# Optional; must match a redirect URI in your Spotify app settings.
|
|
16
|
+
SPOTIPY_REDIRECT_URI=http://127.0.0.1:8897/callback
|
|
17
|
+
# Optional but recommended for autostart (OAuth token cache username).
|
|
18
|
+
SPOTIPY_CLIENT_USERNAME=your_spotify_username
|
|
19
|
+
# Optional: set to 0 to disable internet album-art lookups (iTunes Search / Spotify art URLs).
|
|
20
|
+
# NOW_PLAYING_ONLINE_ART=0
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master, main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test-linux:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.12", "3.13"]
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v7
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
uses: actions/setup-python@v7
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
cache: pip
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade pip
|
|
29
|
+
pip install -e ".[dev]"
|
|
30
|
+
sudo apt-get update -qq
|
|
31
|
+
sudo apt-get install -y --no-install-recommends \
|
|
32
|
+
dbus-x11 xvfb feh openbox imagemagick x11-apps \
|
|
33
|
+
gsettings-desktop-schemas dconf-cli
|
|
34
|
+
|
|
35
|
+
- name: Ruff
|
|
36
|
+
run: ruff check src tests
|
|
37
|
+
|
|
38
|
+
- name: Ruff format
|
|
39
|
+
run: ruff format --check src tests
|
|
40
|
+
|
|
41
|
+
- name: Build package
|
|
42
|
+
run: pip install build && python -m build
|
|
43
|
+
|
|
44
|
+
- name: Verify editable install (clean venv, outside repo)
|
|
45
|
+
run: |
|
|
46
|
+
python -m venv /tmp/npd-packaging-venv
|
|
47
|
+
/tmp/npd-packaging-venv/bin/pip install -e .
|
|
48
|
+
cd /tmp
|
|
49
|
+
/tmp/npd-packaging-venv/bin/python -c "import now_playing_desktops"
|
|
50
|
+
/tmp/npd-packaging-venv/bin/now-playing --help
|
|
51
|
+
/tmp/npd-packaging-venv/bin/python -m now_playing_desktops --help
|
|
52
|
+
|
|
53
|
+
- name: Tests
|
|
54
|
+
run: pytest --cov=now_playing_desktops --cov-report=term-missing -q
|
|
55
|
+
|
|
56
|
+
- name: Linux desktop wallpaper centering (Xvfb + feh + mss)
|
|
57
|
+
run: pytest tests/test_linux_desktop_wallpaper.py -q
|
|
58
|
+
|
|
59
|
+
test-windows:
|
|
60
|
+
runs-on: windows-latest
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v7
|
|
63
|
+
|
|
64
|
+
- name: Set up Python
|
|
65
|
+
uses: actions/setup-python@v7
|
|
66
|
+
with:
|
|
67
|
+
python-version: "3.12"
|
|
68
|
+
cache: pip
|
|
69
|
+
|
|
70
|
+
- name: Install dependencies
|
|
71
|
+
run: |
|
|
72
|
+
python -m pip install --upgrade pip
|
|
73
|
+
pip install -e ".[dev,windows]"
|
|
74
|
+
|
|
75
|
+
- name: Ruff
|
|
76
|
+
run: ruff check src tests
|
|
77
|
+
|
|
78
|
+
- name: Ruff format
|
|
79
|
+
run: ruff format --check src tests
|
|
80
|
+
|
|
81
|
+
- name: Tests
|
|
82
|
+
run: pytest --cov=now_playing_desktops --cov-report=term-missing -q
|
|
83
|
+
|
|
84
|
+
- name: Windows winrt optional dependency imports
|
|
85
|
+
run: pytest tests/test_windows_winrt_deps.py -q
|
|
86
|
+
|
|
87
|
+
- name: Windows display enumeration smoke test
|
|
88
|
+
run: pytest tests/test_platforms_windows_monitors.py::test_real_monitor_enumeration_and_compose -q
|
|
89
|
+
|
|
90
|
+
- name: Windows wallpaper restore smoke test
|
|
91
|
+
run: pytest tests/test_windows_restore.py::test_windows_wallpaper_smoke_snapshot_apply_restore -q
|
|
92
|
+
|
|
93
|
+
- name: Windows desktop wallpaper capture verification
|
|
94
|
+
env:
|
|
95
|
+
NP_ARTIFACTS_DIR: ${{ runner.temp }}/np-artifacts
|
|
96
|
+
run: pytest tests/test_windows_desktop_wallpaper.py -q
|
|
97
|
+
|
|
98
|
+
- name: Upload desktop verification captures
|
|
99
|
+
if: always()
|
|
100
|
+
uses: actions/upload-artifact@v4
|
|
101
|
+
with:
|
|
102
|
+
name: windows-desktop-captures
|
|
103
|
+
path: |
|
|
104
|
+
${{ runner.temp }}/np-artifacts/win-desktop-capture-after-fix.png
|
|
105
|
+
if-no-files-found: warn
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
dry_run:
|
|
10
|
+
description: "Build Windows .exe as artifact only (no GitHub Release or PyPI)"
|
|
11
|
+
type: boolean
|
|
12
|
+
default: true
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: write
|
|
16
|
+
id-token: write
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
windows-exe:
|
|
20
|
+
name: Windows executable
|
|
21
|
+
runs-on: windows-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v7
|
|
24
|
+
|
|
25
|
+
- name: Set up Python
|
|
26
|
+
uses: actions/setup-python@v7
|
|
27
|
+
with:
|
|
28
|
+
python-version: "3.12"
|
|
29
|
+
cache: pip
|
|
30
|
+
|
|
31
|
+
- name: Install package and PyInstaller
|
|
32
|
+
run: |
|
|
33
|
+
python -m pip install --upgrade pip
|
|
34
|
+
pip install -e ".[windows]"
|
|
35
|
+
pip install pyinstaller
|
|
36
|
+
|
|
37
|
+
- name: Build now-playing-windows.exe
|
|
38
|
+
run: pyinstaller --noconfirm packaging/now-playing-windows.spec
|
|
39
|
+
|
|
40
|
+
- name: Upload dry-run artifact
|
|
41
|
+
if: github.event_name == 'workflow_dispatch'
|
|
42
|
+
uses: actions/upload-artifact@v4
|
|
43
|
+
with:
|
|
44
|
+
name: now-playing-windows-exe
|
|
45
|
+
path: dist/now-playing-windows.exe
|
|
46
|
+
|
|
47
|
+
- name: Attach to GitHub Release
|
|
48
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
|
49
|
+
uses: softprops/action-gh-release@v2
|
|
50
|
+
with:
|
|
51
|
+
files: dist/now-playing-windows.exe
|
|
52
|
+
generate_release_notes: true
|
|
53
|
+
|
|
54
|
+
publish-pypi:
|
|
55
|
+
name: Publish to PyPI
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
|
58
|
+
environment: pypi
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v7
|
|
61
|
+
|
|
62
|
+
- name: Set up Python
|
|
63
|
+
uses: actions/setup-python@v7
|
|
64
|
+
with:
|
|
65
|
+
python-version: "3.12"
|
|
66
|
+
|
|
67
|
+
- name: Build sdist and wheel
|
|
68
|
+
run: |
|
|
69
|
+
python -m pip install --upgrade pip build
|
|
70
|
+
python -m build
|
|
71
|
+
|
|
72
|
+
- name: Publish to PyPI (trusted publishing)
|
|
73
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
.venv/
|
|
23
|
+
venv/
|
|
24
|
+
ENV/
|
|
25
|
+
|
|
26
|
+
# Testing / tooling
|
|
27
|
+
.pytest_cache/
|
|
28
|
+
.ruff_cache/
|
|
29
|
+
.coverage
|
|
30
|
+
htmlcov/
|
|
31
|
+
.mypy_cache/
|
|
32
|
+
|
|
33
|
+
# Spotify OAuth cache (spotipy)
|
|
34
|
+
.cache-*
|
|
35
|
+
|
|
36
|
+
# Local artwork
|
|
37
|
+
current_artwork*.jpg
|
|
38
|
+
|
|
39
|
+
# IDE
|
|
40
|
+
.idea/
|
|
41
|
+
.vscode/
|
|
42
|
+
*.swp
|
|
43
|
+
.DS_Store
|
|
44
|
+
|
|
45
|
+
# Env secrets
|
|
46
|
+
.env
|
|
47
|
+
.env.*
|
|
48
|
+
!.env.example
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Joel Shearon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: now-playing-desktops
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Glass-style album-art tile on your desktop wallpaper for the Spotify track (or local player) you are playing now.
|
|
5
|
+
Project-URL: Homepage, https://github.com/hippi345/nowPlayingDesktops
|
|
6
|
+
Project-URL: Repository, https://github.com/hippi345/nowPlayingDesktops
|
|
7
|
+
Project-URL: Issues, https://github.com/hippi345/nowPlayingDesktops/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/hippi345/nowPlayingDesktops#readme
|
|
9
|
+
Author: Joel Shearon
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: album-art,desktop,mpris,now-playing,spotify,wallpaper
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: MacOS X
|
|
15
|
+
Classifier: Environment :: Win32 (MS Windows)
|
|
16
|
+
Classifier: Environment :: X11 Applications
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Desktop Environment
|
|
22
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Requires-Dist: pillow<12,>=11.0.0
|
|
25
|
+
Requires-Dist: python-dotenv<2,>=1.0.0
|
|
26
|
+
Requires-Dist: requests<3,>=2.32.0
|
|
27
|
+
Requires-Dist: spotipy<3,>=2.24.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: build<2,>=1.2.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: mss<11,>=9.0.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pyinstaller<7,>=6.10.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-cov<8,>=7.1.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest<10,>=9.1.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff<1,>=0.9.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: twine<8,>=7.0.0; extra == 'dev'
|
|
36
|
+
Provides-Extra: linux
|
|
37
|
+
Requires-Dist: dbus-next<1,>=0.2.3; extra == 'linux'
|
|
38
|
+
Provides-Extra: macos
|
|
39
|
+
Requires-Dist: appscript<2,>=1.2.5; extra == 'macos'
|
|
40
|
+
Provides-Extra: windows
|
|
41
|
+
Requires-Dist: winrt-runtime~=3.2.1; (sys_platform == 'win32') and extra == 'windows'
|
|
42
|
+
Requires-Dist: winrt-windows-foundation-collections~=3.2.1; (sys_platform == 'win32') and extra == 'windows'
|
|
43
|
+
Requires-Dist: winrt-windows-foundation~=3.2.1; (sys_platform == 'win32') and extra == 'windows'
|
|
44
|
+
Requires-Dist: winrt-windows-media-control~=3.2.1; (sys_platform == 'win32') and extra == 'windows'
|
|
45
|
+
Requires-Dist: winrt-windows-storage-streams~=3.2.1; (sys_platform == 'win32') and extra == 'windows'
|
|
46
|
+
Description-Content-Type: text/markdown
|
|
47
|
+
|
|
48
|
+
# nowPlayingDesktops
|
|
49
|
+
|
|
50
|
+
[](https://github.com/hippi345/nowPlayingDesktops/actions/workflows/ci.yml)
|
|
51
|
+
[](LICENSE)
|
|
52
|
+
|
|
53
|
+
**A glass-style album-art tile on your desktop wallpaper** for whatever you are playing on Spotify (or another supported player in **local** mode).
|
|
54
|
+
|
|
55
|
+
## Screenshots
|
|
56
|
+
|
|
57
|
+
<p>
|
|
58
|
+
<img src="docs/screenshot-don-toliver.png" width="49%" alt="Now-playing wallpaper: Don Toliver, No Pole" />
|
|
59
|
+
<img src="docs/screenshot-ariana-grande.png" width="49%" alt="Now-playing wallpaper: Ariana Grande, Let Me Love You" />
|
|
60
|
+
</p>
|
|
61
|
+
|
|
62
|
+
<p align="center"><img src="docs/demo.gif" alt="Glass album-art tile updating as the track changes, then the original wallpaper restoring on pause" width="720"></p>
|
|
63
|
+
|
|
64
|
+
## Quick start (Windows)
|
|
65
|
+
|
|
66
|
+
1. **Get the app** — download `now-playing-windows.exe` from [Releases](https://github.com/hippi345/nowPlayingDesktops/releases), or install with [pipx](https://pipx.pypa.io/): `pipx install now-playing-desktops`
|
|
67
|
+
2. **Run it** — double-click the `.exe`, or run `now-playing run --source local` (pipx puts `now-playing` on your PATH)
|
|
68
|
+
3. **Done** — play something in the Spotify desktop app; pause or exit to restore your wallpaper
|
|
69
|
+
|
|
70
|
+
For macOS, Linux, Spotify Web API mode, or hacking on the code, see the sections below.
|
|
71
|
+
|
|
72
|
+
## Install from source
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
git clone https://github.com/hippi345/nowPlayingDesktops.git
|
|
76
|
+
cd nowPlayingDesktops
|
|
77
|
+
python3.12 -m venv .venv
|
|
78
|
+
source .venv/bin/activate # Windows: .\.venv\Scripts\Activate.ps1
|
|
79
|
+
pip install -e ".[dev]" # add windows, linux, or macos extras as needed
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Quick Start: Local mode
|
|
85
|
+
|
|
86
|
+
Local mode reads playback from the OS media session on **this** machine. Pick your OS:
|
|
87
|
+
|
|
88
|
+
### Windows (local)
|
|
89
|
+
|
|
90
|
+
**Prerequisites**
|
|
91
|
+
|
|
92
|
+
- **Python 3.12+** (`requires-python` in `pyproject.toml`; CI tests 3.12 and 3.13).
|
|
93
|
+
- **Install:** `pip install -e ".[windows]"` (WinRT optional deps for SMTC).
|
|
94
|
+
- **Player:** Spotify **desktop** app playing on this PC (System Media Transport Controls).
|
|
95
|
+
|
|
96
|
+
**Run**
|
|
97
|
+
|
|
98
|
+
```powershell
|
|
99
|
+
python -m venv .venv
|
|
100
|
+
.\.venv\Scripts\Activate.ps1
|
|
101
|
+
pip install -e ".[windows]"
|
|
102
|
+
now-playing run --source local
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**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).
|
|
106
|
+
|
|
107
|
+
### Linux (local)
|
|
108
|
+
|
|
109
|
+
**Prerequisites**
|
|
110
|
+
|
|
111
|
+
- **Python 3.12+**
|
|
112
|
+
- **Install:** `pip install -e ".[linux]"` (`dbus-next` for MPRIS).
|
|
113
|
+
- **D-Bus session:** `echo $DBUS_SESSION_BUS_ADDRESS` should be set in your desktop session.
|
|
114
|
+
- **Player:** Spotify desktop or another app with **MPRIS** (`org.mpris.MediaPlayer2.*`). `playerctl` is not required by this app.
|
|
115
|
+
- **Wallpaper backend** (one of): GNOME family — `gsettings`; KDE — `plasma-apply-wallpaperimage` and/or `qdbus6` / `qdbus`; other WMs — `feh`, `swaybg`, or `nitrogen` (plus `xrandr` or `wlr-randr` for screen size).
|
|
116
|
+
|
|
117
|
+
**Run**
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
python3.12 -m venv .venv
|
|
121
|
+
source .venv/bin/activate
|
|
122
|
+
pip install -e ".[linux]"
|
|
123
|
+
now-playing run --source local
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### macOS (local)
|
|
127
|
+
|
|
128
|
+
**Prerequisites**
|
|
129
|
+
|
|
130
|
+
- **Python 3.12+**
|
|
131
|
+
- **Install:** `pip install -e ".[macos]"` (`appscript` for Spotify AppleScript control).
|
|
132
|
+
- **Player:** **Spotify.app** playing on this Mac.
|
|
133
|
+
- **Automation:** macOS may prompt for **Automation** permission for your terminal or Python to control Spotify (System Settings → Privacy & Security → Automation).
|
|
134
|
+
|
|
135
|
+
**Run**
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
python3.12 -m venv .venv
|
|
139
|
+
source .venv/bin/activate
|
|
140
|
+
pip install -e ".[macos]"
|
|
141
|
+
now-playing run --source local
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Optional PyObjC (`pyobjc-framework-Cocoa`) improves per-display wallpaper control; without it, `osascript` sets all screens.
|
|
145
|
+
|
|
146
|
+
### Common `run` flags (local)
|
|
147
|
+
|
|
148
|
+
- `-v` / `--verbose` — debug logging
|
|
149
|
+
- `--once` — single poll/apply cycle
|
|
150
|
+
- `--env-file PATH` — load settings from a `.env` file
|
|
151
|
+
- `--no-online-art` — no internet art lookups (see [Album art](#album-art-in-local-mode))
|
|
152
|
+
- `--poll-interval SECONDS` — default `2.5`
|
|
153
|
+
|
|
154
|
+
`YOUR_SPOTIFY_USERNAME` on the command line is **optional** for local mode (only required for `--source spotify`).
|
|
155
|
+
|
|
156
|
+
### Login autostart (local)
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
now-playing autostart enable --source local
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`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`.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Album art in local mode
|
|
167
|
+
|
|
168
|
+
When **online art is enabled** (default), cover resolution is:
|
|
169
|
+
|
|
170
|
+
1. **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).
|
|
171
|
+
2. **[Apple iTunes Search API](https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/iTuneSearchAPI/)** (public HTTPS; no iTunes app or Apple account) when the thumbnail is missing or small.
|
|
172
|
+
3. **Generated placeholder** (gradient + track title/artist) if nothing else is available.
|
|
173
|
+
|
|
174
|
+
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 …`).
|
|
175
|
+
|
|
176
|
+
### Disable online art (offline / privacy)
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
NOW_PLAYING_ONLINE_ART=0
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Or on the CLI:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
now-playing run --source local --no-online-art
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
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.)
|
|
189
|
+
|
|
190
|
+
The app loads `.env` from (first match wins, without overriding variables already set in the shell):
|
|
191
|
+
|
|
192
|
+
1. `--env-file` / `NOW_PLAYING_ENV_FILE`
|
|
193
|
+
2. 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`)
|
|
194
|
+
3. Current working directory `.env`
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Quick Start: Spotify mode
|
|
199
|
+
|
|
200
|
+
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.
|
|
201
|
+
|
|
202
|
+
### Windows (Spotify)
|
|
203
|
+
|
|
204
|
+
**Prerequisites**
|
|
205
|
+
|
|
206
|
+
- **Python 3.12+**
|
|
207
|
+
- **Install:** `pip install -e .` (base package; wallpaper uses `ctypes` / WinRT desktop APIs already in the main install path).
|
|
208
|
+
- **Spotify Developer** app with **Client ID** in `.env` (see below).
|
|
209
|
+
|
|
210
|
+
### Linux (Spotify)
|
|
211
|
+
|
|
212
|
+
**Prerequisites**
|
|
213
|
+
|
|
214
|
+
- **Python 3.12+**
|
|
215
|
+
- **Install:** `pip install -e .`
|
|
216
|
+
- **Wallpaper backend** (same as local): `gsettings` (GNOME), `plasma-apply-wallpaperimage` / `qdbus6` / `qdbus` (KDE), or `feh` / `swaybg` / `nitrogen` on lightweight WMs.
|
|
217
|
+
|
|
218
|
+
### macOS (Spotify)
|
|
219
|
+
|
|
220
|
+
**Prerequisites**
|
|
221
|
+
|
|
222
|
+
- **Python 3.12+**
|
|
223
|
+
- **Install:** `pip install -e .` (wallpaper via AppKit if PyObjC is installed, otherwise `osascript`).
|
|
224
|
+
|
|
225
|
+
### 1. Create a Spotify app
|
|
226
|
+
|
|
227
|
+
1. Open the [Spotify Developer Dashboard](https://developer.spotify.com/dashboard) and create an app.
|
|
228
|
+
2. Copy the **Client ID** (PKCE does **not** need a Client Secret).
|
|
229
|
+
3. Under **Settings** → **Redirect URIs**, add this exact URI (unless you override it):
|
|
230
|
+
|
|
231
|
+
`http://127.0.0.1:8897/callback`
|
|
232
|
+
|
|
233
|
+
This is the default from `SPOTIPY_REDIRECT_URI` / `DEFAULT_REDIRECT_URI` in `auth.py`. If port `8897` is in use, pick another loopback URL (for example `http://127.0.0.1:8898/callback`), set `SPOTIPY_REDIRECT_URI` to match, and register the same URI in the dashboard.
|
|
234
|
+
|
|
235
|
+
### 2. Configure credentials
|
|
236
|
+
|
|
237
|
+
Copy `.env.example` to `.env` or to the per-user config path (recommended for autostart):
|
|
238
|
+
|
|
239
|
+
| OS | Config directory |
|
|
240
|
+
|----|------------------|
|
|
241
|
+
| **Windows** | `%APPDATA%\now-playing-desktops\.env` |
|
|
242
|
+
| **macOS** | `~/Library/Application Support/now-playing-desktops/.env` |
|
|
243
|
+
| **Linux** | `~/.config/now-playing-desktops/.env` |
|
|
244
|
+
|
|
245
|
+
Minimum for PKCE:
|
|
246
|
+
|
|
247
|
+
```env
|
|
248
|
+
SPOTIPY_CLIENT_ID=your_client_id_here
|
|
249
|
+
SPOTIPY_CLIENT_USERNAME=your_spotify_username
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Optional: `NOW_PLAYING_SOURCE=spotify|local|auto` (default `auto`).
|
|
253
|
+
|
|
254
|
+
### 3. Allowlist users (Development mode)
|
|
255
|
+
|
|
256
|
+
Apps in [Development mode](https://developer.spotify.com/documentation/web-api/concepts/quota-modes) 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.
|
|
257
|
+
|
|
258
|
+
### 4. Sign in once
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
now-playing login YOUR_SPOTIFY_USERNAME
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Optional: `--env-file PATH` if your `.env` is not in the default config directory.
|
|
265
|
+
|
|
266
|
+
Token cache file: `spotify-token-<username>.cache` under the same config directory (`config.spotify_token_cache_path`).
|
|
267
|
+
|
|
268
|
+
### 5. Run
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
now-playing run YOUR_SPOTIFY_USERNAME --source spotify
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Autostart:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
now-playing autostart enable YOUR_SPOTIFY_USERNAME --source spotify
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
(`enable` requires Spotify credentials in the resolved `.env` when source is `spotify` or `auto` with Client ID present.)
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Command reference
|
|
285
|
+
|
|
286
|
+
| Command | Purpose |
|
|
287
|
+
|---------|---------|
|
|
288
|
+
| `now-playing run [USER] [--source spotify\|local\|auto] [--once] [--env-file PATH] [--no-online-art] [-v]` | Poll playback and update wallpaper |
|
|
289
|
+
| `now-playing login [USER] [--env-file PATH] [-v]` | Interactive Spotify OAuth (PKCE or legacy secret flow) |
|
|
290
|
+
| `now-playing restore [--state-file PATH] [-v]` | Restore wallpaper saved at session start |
|
|
291
|
+
| `now-playing diag [-v]` | **Windows only:** playback source diagnostics, Spotify auth summary, monitor/DPI/wallpaper report |
|
|
292
|
+
| `now-playing autostart enable [USER] [--source …] [--env-file PATH] [--no-online-art] [-v]` | Register login autostart |
|
|
293
|
+
| `now-playing autostart disable` | Remove autostart |
|
|
294
|
+
| `now-playing autostart status` | Print `enabled` or `disabled` |
|
|
295
|
+
|
|
296
|
+
Stop a foreground `run` with `Ctrl+C`; the original wallpaper is restored automatically.
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Wallpaper behavior
|
|
301
|
+
|
|
302
|
+
- Composes a blurred backdrop, glass panel, cover art, and track labels; restores your original wallpaper on pause or exit.
|
|
303
|
+
- **Windows** — multi-monitor aware; optional per-monitor wallpaper when `IDesktopWallpaper` is available.
|
|
304
|
+
- **macOS** — sets wallpaper on every display (AppKit / `osascript`).
|
|
305
|
+
- **Linux** — GNOME (`gsettings`), KDE (`plasma-apply-wallpaperimage` / `qdbus`), or fallbacks (`feh`, `swaybg`, `nitrogen`).
|
|
306
|
+
|
|
307
|
+
Session state: `wallpaper-state.json` in the config directory. If a previous run exited without restoring (`session_active`), the next startup restores immediately.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Which mode should I pick?
|
|
312
|
+
|
|
313
|
+
| Mode | Command | What it needs | Players / devices | Account / app setup | Pros | Cons |
|
|
314
|
+
|------|---------|---------------|-------------------|----------------------|------|------|
|
|
315
|
+
| **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](#album-art-in-local-mode) is enabled; desktop app must be on this machine |
|
|
316
|
+
| **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](https://developer.spotify.com/dashboard) 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 |
|
|
317
|
+
| **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 |
|
|
318
|
+
|
|
319
|
+
**How `auto` resolves** (see `resolve_effective_playback_source` in `playback_source.py`):
|
|
320
|
+
|
|
321
|
+
1. `--source spotify` or `NOW_PLAYING_SOURCE=spotify` → **Spotify** Web API.
|
|
322
|
+
2. `--source local` or `NOW_PLAYING_SOURCE=local` → **local** OS media session.
|
|
323
|
+
3. Otherwise (`auto`): if `SPOTIPY_CLIENT_ID` is set and non-empty in the environment (after `.env` load), effective mode is **Spotify**; if not, **local**.
|
|
324
|
+
|
|
325
|
+
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](#album-art-in-local-mode).
|
|
326
|
+
|
|
327
|
+
Legacy console scripts `now-playing-macos` and `now-playing-windows` behave like `now-playing run …` when you omit the `run` subcommand.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Troubleshooting
|
|
332
|
+
|
|
333
|
+
| Topic | What to do |
|
|
334
|
+
|-------|------------|
|
|
335
|
+
| **`now-playing diag`** | Windows only. Shows effective playback source, what local/Spotify sources see, auth mode, and display/wallpaper geometry. |
|
|
336
|
+
| **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()`). |
|
|
337
|
+
| **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). |
|
|
338
|
+
| **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. |
|
|
339
|
+
| **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`. |
|
|
340
|
+
| **Verbose** | `-v` / `--verbose` on any subcommand that accepts shared flags. |
|
|
341
|
+
| **Compose quality checks** | Set `NOW_PLAYING_COMPOSE_VERIFY=1` to run optional post-apply checks in a background thread (never blocks wallpaper set). |
|
|
342
|
+
| **Apply timing** | Look for `Apply took X.XXs (fetch=…, compose=…, save=…, set=…, cache=hit\|miss)` in logs; background upgrades log `Upgraded art Apply took …`. |
|
|
343
|
+
| **Linux unsupported platform** | Install `feh`, `swaybg`, or `nitrogen`, or use GNOME/KDE with their wallpaper tools. |
|
|
344
|
+
| **Local mode: no track** | Start the desktop player and press play; on Windows install `pip install -e ".[windows]"`. |
|
|
345
|
+
| **OAuth port in use** | Change `SPOTIPY_REDIRECT_URI` and register the new URI in the Spotify dashboard. |
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Development
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
pip install -e ".[dev]"
|
|
353
|
+
ruff check src tests
|
|
354
|
+
ruff format src tests
|
|
355
|
+
pytest -q
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Profile compose at laptop resolution: `python scripts/profile_compose.py`.
|
|
359
|
+
|
|
360
|
+
## License
|
|
361
|
+
|
|
362
|
+
MIT — see [LICENSE](LICENSE). Copyright (c) 2026 Joel Shearon.
|