A macOS app that uses a local LLM to generate themed playlists from your Doppler music library, then writes them back into Doppler so they sit next to your hand-curated ones.
Everything runs on your machine. Your library never leaves it — the only network calls are to your own LM Studio server and to MusicBrainz for artist genre tags.
- Reads your Doppler library directly and shows you what's in it — counts, recently added, most played, existing playlists.
- Looks up every artist on MusicBrainz once to learn their genre tags, and caches the result locally.
- Asks your local model for themed playlist concepts, then fills each one with tracks you actually own — the model only ever picks from a list of your real song titles, so it can't invent music you don't have.
- Writes a finished playlist into Doppler as a native playlist, or exports it as a
.m3ufor any other player.
A generated playlist, after Add to Doppler, sitting in Doppler's own sidebar:
- macOS 15.7 or later (Apple Silicon)
- Doppler for macOS installed, with at least one song in its library
- LM Studio running locally with a chat model loaded. Qwen 3 was used during development.
- Set the model's context length to ≥ 8 K (16 K recommended). The per-theme prompts include your real song titles and won't fit in a 4 K window.
- Xcode 26 or later to build — there's no pre-built download yet, see Installing below. The project uses an Icon Composer app icon and Xcode 26 build settings, so earlier versions won't open it. Xcode 26 itself runs on macOS 15.6+, so you don't need to upgrade past Sequoia to build.
Clone the repo and build it:
git clone https://github.com/jbartolozzi/Doppler-Smart-Playlists.git
cd Doppler-Smart-Playlists
./build.shThat produces (and opens) build/Build/Products/Release/DopplerSmartPlaylists.app. Drag it to /Applications if you want to keep it.
Or open DopplerSmartPlaylists.xcodeproj in Xcode and hit ⌘R.
Signing: no configuration needed. The project ships without a development team, so macOS signs the app ad-hoc ("Sign to Run Locally") and the sandbox entitlements still apply. If you want a real signature — to notarize it, say — open the project in Xcode, go to Signing & Capabilities, and pick your own team.
The Settings window opens automatically the first time. Set up two things:
- LLM tab — confirm the LM Studio base URL (default
http://localhost:1234), pick your loaded model from the list, and hit Test Connection. - Library tab — Choose Library… and select your
Library.dopplerdbbundle. It normally lives in~/Library/Application Support/Doppler/. The picker opens there for you, with hidden files shown so you can navigate into~/Library.
Then switch to My Doppler. The app immediately starts caching artist metadata from MusicBrainz. This takes roughly 1 second per artist — MusicBrainz rate-limits anonymous users, and the app respects that. A 140-artist library takes a couple of minutes. It only happens once; the results are cached on disk.
The Playlists tab unlocks when that finishes.
- Go to Playlists, choose how many you want with the stepper (1–20, default 9), and hit Generate.
- Themes appear first, then each playlist fills in independently as the model works through them — you don't have to wait for all of them.
- Click any tile to open its tracklist. From there:
- Add to Doppler — writes it into your library as a real Doppler playlist. Quit Doppler first (see below). Relaunch Doppler and it's there.
- Export .m3u — saves an Extended M3U with paths relative to your music folder, so it works in any player.
- Retry — if one playlist failed (the model returned something unparseable, say), this regenerates just that one, without touching the others.
Tracks the model named but the app couldn't find in your library are shown struck through, and are skipped on both write and export.
- Quit Doppler before Add to Doppler. The app refuses to write while Doppler is running, and tells you why: Doppler holds the database open and would overwrite the change when it quits. Export .m3u is read-only and works either way.
- There's no automatic backup. The write is wrapped in a single transaction, so you'll never get half a playlist — but if you want to be careful, copy
~/Library/Application Support/Doppler/Library.dopplerdbsomewhere safe first. - Generated playlists don't survive quitting. They live in memory until you add or export them. Anything already written into Doppler is a normal Doppler playlist and persists normally.
- The app only ever reads your library until the moment you press Add to Doppler. Browsing, generating and exporting are all read-only.
- Nothing is uploaded. Prompts go to your local LM Studio server. The only outbound requests are artist-name lookups to MusicBrainz during the initial sync.
| Symptom | Cause |
|---|---|
| Playlists tab is locked | The MusicBrainz sync hasn't finished. Check the banner on My Doppler. |
| Generation fails immediately | LM Studio isn't running, or no model is loaded. Re-run Test Connection in Settings. |
| Every playlist fails, or output is empty | Model context is too small. Raise it to 8 K+ in LM Studio and regenerate. |
| Add to Doppler is refused | Doppler is still running. Quit it fully and try again. |
| Lots of struck-through tracks | The model drifted off the provided song list. Hit Retry on that playlist. |
| Something else | Open the Debug Log (⌘⌥L) — every library, LLM and write operation is logged there. |
- My Doppler — library stats, recently added, most played, your existing playlists, sync banner.
- Playlists — the generated grid.
- Settings — ⌘, — LLM config, library picker, cache stats and reset.
- Debug Log — ⌘⌥L — filterable in-app log, also mirrored to OSLog.
# Debug build — the normal inner loop
xcodebuild -project DopplerSmartPlaylists.xcodeproj -scheme DopplerSmartPlaylists \
-configuration Debug -destination 'platform=macOS' build
# Release build into ./build, then launch it
./build.shThere are no tests, lint config, or package manifests. There are no third-party dependencies — the app uses only the system SQLite3 C API, SwiftUI, AppKit, Foundation and OSLog.
The Xcode target uses PBXFileSystemSynchronizedRootGroup, so any file added to DopplerSmartPlaylists/ is picked up automatically. Never hand-edit project.pbxproj to add sources.
Build settings worth knowing: MACOSX_DEPLOYMENT_TARGET = 15.7, SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor, SWIFT_APPROACHABLE_CONCURRENCY = YES, app sandbox on.
Doppler access is split in two on purpose. DopplerLibrary.swift is a read-only actor (SQLITE_OPEN_READONLY) that's safe to use while Doppler is running. DopplerLibraryWriter.swift is a separate read-write actor that refuses to open while Doppler is running and re-checks again immediately before writing. Keeping them separate is what makes the dangerous surface obvious — don't merge them.
Generation is two-pass (ShowcaseGenerator.swift): one small call for N themes, then one parallel call per theme that ships only that theme's artists along with their real song titles. This exists because model context is usually tight, and because giving the model actual titles took the library-match rate from ~19 % to ~100 %.
The sandbox (DopplerSmartPlaylists.entitlements) means library access runs through an NSOpenPanel grant persisted as a security-scoped bookmark — see DopplerLibraryLocation.swift.
Everything else:
- MusicBrainz cache — MetadataCache.swift (sync orchestration, progress, cancel), MetadataCacheStore.swift (local SQLite in the app container), MusicBrainzClient.swift (rate-limited REST client with artist disambiguation).
- LLM — LMStudioClient.swift, an OpenAI-compatible
/v1/chat/completionsclient usingjson_schemaresponse format. - Settings — AppSettings.swift, an
@Observablesingleton backed by UserDefaults. - Logging — Log.swift + DebugLogView.swift.
- Theme — Theme.swift, the single source of truth for the Doppler-derived purple palette.
- Views — ContentView.swift (tab shell), MyDopplerView.swift, ShowcaseView.swift, SettingsView.swift, SyncBanner.swift.
The "Playlists" tab is called
Showcasethroughout the code — it's the original name, kept to avoid churn.
CLAUDE.md has the deep detail: Doppler's Core Data schema and the exact write recipe, the EBMK bookmark format used for .m3u export, the artist-name canonicalization contract, and the LM Studio quirks that are easy to rediscover the hard way. Read it before changing the write path or the prompts.
MIT.
This is an unofficial third-party tool. It isn't affiliated with or endorsed by Brushed Type, the makers of Doppler. It writes directly to Doppler's database, which is not a documented or supported interface — see the warning above about backing up first.


