caezium816 downloadsControl Spotify from your notes. Now-playing sidebar with hover controls, time-synced lyrics via LRCLIB, queue, search, transport hotkeys, and insert-track-into-note.
Control Spotify from inside Obsidian. A now-playing sidebar with hover-revealed transport, time-synced lyrics, upcoming-tracks queue, search palette, hotkey-bindable transport commands, and track/lyrics capture for your vault.
Spotify Development Mode now requires the developer-app owner to have an active Spotify Premium subscription. Playback control also requires Premium. A Free account can use display and capture features only when it has been allowlisted on somebody else's Premium-owned development app; Spotify limits each development app to five authorized users.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Now-playing sidebar. Album art (with optional hover overlay for prev/play/next/shuffle/repeat), large track title, artist, album, seek bar, volume, custom device picker. Polls Spotify every 3s (configurable). Pauses polling when the Obsidian window is hidden.
Time-synced lyrics. Toggle a lyrics panel from a corner button on the album art. Lyrics come from LRCLIB — free, no account, community-maintained LRC database. The active line is highlighted and auto-scrolls; click any line to seek to that timestamp. Falls back to plain-text lyrics when no synced version exists. Prefetched on every track change so the panel opens instantly.
Upcoming-tracks queue. Toggle button next to lyrics. Each row shows thumbnail + title + artist + duration. Click any track to jump to it — playback stays inside the current playlist/album context so the queue keeps rolling. Pre-loaded album art means scrolling is instant.
Search palette. Command-palette-style modal for searching tracks, albums, and playlists. Track results prompt "Play now" or "Add to queue."
Capture tracks as notes. Insert the current track into an active editor, including editors opened by QuickAdd or Commander macros, or create/reopen a dedicated song note in a configurable folder. The filename and note body are templated; variables are {{name}} {{artist}} {{album}} {{url}} {{uri}} {{lyrics}} {{lrc}}, plus {{show}} and {{publisher}} for podcasts.
Capture lyrics. Insert lyrics into the active note with a configurable template, copy them to the clipboard, or save them in the vault. The original synchronized LRCLIB payload is saved as .lrc; tracks with plain lyrics only fall back to .txt.
Play any Spotify URI under cursor. Bindable command — works on spotify:track:abc URIs and https://open.spotify.com/... URLs (including ?si= share tokens and intl-XX/ locale prefixes).
Open Spotify Web Player. Command/setting to open open.spotify.com either in your external browser (audio works) or in an Obsidian tab (UI only — Obsidian's Electron build doesn't bundle Widevine DRM).
When a Spotify podcast episode is playing the sidebar adapts:
Oct 24, 2025 • 50 min 44 sec left).{{show}} and {{publisher}} template variables.Not available (Spotify Web API limitations, not a plugin design choice): transcripts, auto-generated chapters, variable playback speed (1×/1.5×/2×), and sleep timer. These are exposed only by Spotify's own clients via private endpoints we don't have access to. If you find yourself wanting them, use the Spotify app for the listening session itself; the sidebar still shows what's playing.
The plugin runs on Obsidian mobile (iOS + Android) with isDesktopOnly: false. It's tagged "preview" rather than full support because the development cycle is primarily desktop and not every interaction has been validated on a phone. Known differences from desktop:
data.json. Desktop uses Electron's safeStorage (OS keychain on macOS, libsecret on Linux, DPAPI on Windows), which doesn't exist on mobile. The plugin warns about this on first load and on the settings screen.obsidian://spotify-control/auth protocol handler, same as desktop — your phone's default browser opens Spotify's auth page, you approve, the redirect bounces back into Obsidian.If anything misbehaves on mobile (layout, auth callback, missing UI elements), please file an issue with your phone model + Obsidian version.
Sign in to the Spotify Developer Dashboard with the Premium account that will own the app, then create an app. Select Web API if Spotify asks which APIs you plan to use.
In the app settings, add this exact Redirect URI:
obsidian://spotify-control/auth
Copy the Client ID from your app's main page. No Client Secret needed — this plugin uses PKCE.
If you will connect a Spotify account other than the app owner, open the app's Settings → Users Management page and add that account's name and Spotify email. Development Mode allows up to five authorized users.
In Obsidian → Settings → Community plugins, enable Spotify Control.
Open the Spotify Control settings tab, paste the Client ID, and click Log in. Your browser will open Spotify's authorization page; after approving, it redirects to obsidian://spotify-control/auth and back into Obsidian.
The first time you control playback after starting cold, the plugin auto-transfers playback to the first available device (so you don't need to manually pick one).
The plugin does not generate, receive, or validate an OTP. That page belongs to Spotify account authentication, so first verify that the address is on accounts.spotify.com, then use the delivery method Spotify names on the page. If Spotify offers another verification or account-recovery method, use that; if it rate-limits more attempts, wait for the cooldown it shows. Never post the code in an issue or screenshot.
If the code is accepted but Spotify Control still does not connect:
403 responses usually means the exact account is not listed under the app's Settings → Users Management page.obsidian://spotify-control/auth, allow the browser to open Obsidian, then click Log in again. The callback must finish while that login attempt is still pending.429 with QUOTA_EXCEEDED is Spotify's shared Development Mode quota, not a bad OTP. Close duplicate Spotify sidebars or increase the poll interval, then try again later.If these steps do not match the page you see, open an issue with the page URL, Obsidian version, platform, and exact error text — with all codes, tokens, emails, and Client IDs redacted.
invalid_grant as a clean re-login instead of a permanent refresh loop.obsidian:// callback, so no Client Secret or localhost callback is needed.Spotify documents these changes in its February 2026 migration guide, June refresh-token update, and July quota update.
| Setting | What |
|---|---|
| Spotify Client ID | From your Spotify Developer Dashboard |
| Account | Log in / Re-login / Log out |
| Reveal controls on album art hover | When on, prev/play/next/shuffle/repeat live as a hover overlay on the art. When off, they sit in a permanent transport row below the art. |
| Show lyrics button | Enables the lyrics toggle. Default on. |
| Show queue button | Enables the queue toggle. Default on. |
| Lyrics + queue panel position | "Below art" (default — panel slots between art and controls) or "Replace album art" (panel covers the art square) |
| Progress bar on album art | Thin progress line along the bottom edge of the art, clickable to seek. Hides the separate seek row. Default off. |
| Volume button on album art | Speaker button at the bottom-right of the art with a popover slider. Hides the separate volume row. Default off. |
| Sidebar poll interval (ms) | How often to refresh playback state. Default 3000. |
| Insert-now-playing template | Template for inserting track metadata into the active note |
| Now-playing note folder | Destination for song notes; missing folders are created automatically |
| Now-playing note filename | Filename template shared by song notes and exported lyrics |
| Now-playing note template | Full Markdown body used when creating a song note |
| Lyrics insert template | Template for inserting lyrics into the active note |
| Lyrics export folder | Destination for .lrc and plain-text lyrics files |
| Spotify Web Player → Open in | External browser (recommended) or Obsidian tab (UI only) |
src/
├── main.ts Plugin entry, settings tab, lifecycle
├── auth.ts PKCE OAuth + token refresh (deduped + backoff retry)
├── api.ts Direct requestUrl wrapper for Spotify Web API
├── view.ts Now-playing sidebar (ItemView)
├── search.ts Spotify search modal (SuggestModal)
├── commands.ts Transport and navigation commands
├── capture-commands.ts Note, clipboard, and lyrics-file commands
├── capture.ts Pure note paths, metadata templates, and lyrics export
├── lyrics.ts LRC parsing + LyricsService (pure, no Obsidian deps)
├── lyrics-fetcher.ts Obsidian requestUrl bridge for LyricsService
├── queue.ts Queue snapshot + cache (pure)
├── queue-fetcher.ts Obsidian requestUrl bridge for QueueService
├── secure-storage.ts OAuth tokens via Electron safeStorage, plaintext fallback
├── util.ts Pure helpers (formatTime, parseSpotifyResource, PKCE primitives, template)
└── types.ts Settings shape + scopes + redirect URI
tests/
├── capture.test.ts Now-playing metadata, paths, and lyrics export
├── lyrics.test.ts LRC parsing + LyricsService
├── queue.test.ts Queue snapshot normalization and cache
├── spotify-compat.test.ts OAuth and Spotify platform compatibility
└── util.test.ts Tests for the pure helpers
OAuth tokens are encrypted at rest when possible:
data.json encrypted via Electron's safeStorage (uses Keychain / DPAPI / kwallet under the hood). Settings tab shows 🔒 Tokens encrypted via OS keychain.⚠️ indicator in settings.Either way, data.json is in the plugin's local folder. Don't commit it if you sync your vault to a public git repo — the included .gitignore excludes data.json from version control. If you use Obsidian Sync or iCloud, encrypted tokens travel with your vault but are bound to your OS keychain (won't decrypt on a different machine — you'd just re-login).
Steady-state cost:
Optimizations:
setTimeout (not setInterval) so slow networks don't queue up overlapping requeststogglePlay reads cached lastState instead of an extra fetch per clickrequestUrl for both reads and writes)Smart Shuffle. Spotify's "Smart Shuffle" mode is exposed only via their private API — the public Web API has no endpoint to read or toggle it. If you've enabled Smart Shuffle in the desktop app, it affects the queue order you receive, but this plugin can't read or change the state.
Playback inside Obsidian itself. The Spotify Web Playback SDK requires Widevine DRM, which Obsidian's Electron build doesn't bundle. The "Open Spotify Web Player → Obsidian tab" mode loads the UI but audio won't play in-tab. Use the external-browser mode (or the desktop app, or your phone) — the plugin controls whichever device is active.
npm install
node esbuild.config.mjs production # build main.js
npm test # run unit tests
node esbuild.config.mjs # dev (watch mode)
After source changes, Cmd+R (or Ctrl+R) inside Obsidian reloads the window and re-imports every plugin — more reliable than toggling the plugin off and on.
MIT.