navidrome-mcp 2.0.1 → 2.1.0
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.
- package/README.md +161 -67
- package/assets/navidrome-player.icns +0 -0
- package/assets/navidrome-player.ico +0 -0
- package/assets/navidrome-player.png +0 -0
- package/dist/bootstrap.d.ts +43 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +52 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/client/auth-manager.d.ts.map +1 -1
- package/dist/client/auth-manager.js +14 -3
- package/dist/client/auth-manager.js.map +1 -1
- package/dist/client/navidrome-client.d.ts.map +1 -1
- package/dist/client/navidrome-client.js +34 -5
- package/dist/client/navidrome-client.js.map +1 -1
- package/dist/config/map-config.d.ts +29 -0
- package/dist/config/map-config.d.ts.map +1 -0
- package/dist/config/map-config.js +97 -0
- package/dist/config/map-config.js.map +1 -0
- package/dist/config/schema.d.ts +65 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +95 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/seed.d.ts +58 -0
- package/dist/config/seed.d.ts.map +1 -0
- package/dist/config/seed.js +190 -0
- package/dist/config/seed.js.map +1 -0
- package/dist/config/store-path.d.ts +32 -0
- package/dist/config/store-path.d.ts.map +1 -0
- package/dist/config/store-path.js +57 -0
- package/dist/config/store-path.js.map +1 -0
- package/dist/config/store.d.ts +84 -0
- package/dist/config/store.d.ts.map +1 -0
- package/dist/config/store.js +141 -0
- package/dist/config/store.js.map +1 -0
- package/dist/config-app/degraded-tools.d.ts +27 -0
- package/dist/config-app/degraded-tools.d.ts.map +1 -0
- package/dist/config-app/degraded-tools.js +61 -0
- package/dist/config-app/degraded-tools.js.map +1 -0
- package/dist/config-app/main.d.ts +20 -0
- package/dist/config-app/main.d.ts.map +1 -0
- package/dist/config-app/main.js +38 -0
- package/dist/config-app/main.js.map +1 -0
- package/dist/config-app/public/app.js +224 -0
- package/dist/config-app/public/index.html +137 -0
- package/dist/config-app/public/styles.css +120 -0
- package/dist/config-app/routes.d.ts +28 -0
- package/dist/config-app/routes.d.ts.map +1 -0
- package/dist/config-app/routes.js +183 -0
- package/dist/config-app/routes.js.map +1 -0
- package/dist/config-app/server.d.ts +48 -0
- package/dist/config-app/server.d.ts.map +1 -0
- package/dist/config-app/server.js +155 -0
- package/dist/config-app/server.js.map +1 -0
- package/dist/config.d.ts +36 -35
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +47 -204
- package/dist/config.js.map +1 -1
- package/dist/constants/defaults.d.ts +20 -1
- package/dist/constants/defaults.d.ts.map +1 -1
- package/dist/constants/defaults.js +20 -1
- package/dist/constants/defaults.js.map +1 -1
- package/dist/constants/timeouts.d.ts +19 -4
- package/dist/constants/timeouts.d.ts.map +1 -1
- package/dist/constants/timeouts.js +19 -4
- package/dist/constants/timeouts.js.map +1 -1
- package/dist/index.js +103 -33
- package/dist/index.js.map +1 -1
- package/dist/resources/index.js +3 -3
- package/dist/resources/index.js.map +1 -1
- package/dist/schemas/common.d.ts +10 -7
- package/dist/schemas/common.d.ts.map +1 -1
- package/dist/schemas/common.js +35 -20
- package/dist/schemas/common.js.map +1 -1
- package/dist/schemas/pagination.d.ts +5 -0
- package/dist/schemas/pagination.d.ts.map +1 -1
- package/dist/schemas/pagination.js +22 -10
- package/dist/schemas/pagination.js.map +1 -1
- package/dist/schemas/validation.d.ts +15 -3
- package/dist/schemas/validation.d.ts.map +1 -1
- package/dist/schemas/validation.js +43 -12
- package/dist/schemas/validation.js.map +1 -1
- package/dist/services/filter-cache-manager.d.ts +2 -1
- package/dist/services/filter-cache-manager.d.ts.map +1 -1
- package/dist/services/filter-cache-manager.js +35 -25
- package/dist/services/filter-cache-manager.js.map +1 -1
- package/dist/services/library-manager.d.ts +1 -0
- package/dist/services/library-manager.d.ts.map +1 -1
- package/dist/services/library-manager.js +50 -29
- package/dist/services/library-manager.js.map +1 -1
- package/dist/services/playback/mpv-process.d.ts +11 -0
- package/dist/services/playback/mpv-process.d.ts.map +1 -1
- package/dist/services/playback/mpv-process.js +28 -2
- package/dist/services/playback/mpv-process.js.map +1 -1
- package/dist/services/playback/playback-engine.d.ts +59 -19
- package/dist/services/playback/playback-engine.d.ts.map +1 -1
- package/dist/services/playback/playback-engine.js +180 -36
- package/dist/services/playback/playback-engine.js.map +1 -1
- package/dist/services/playback/scrobble-tracker.d.ts +20 -1
- package/dist/services/playback/scrobble-tracker.d.ts.map +1 -1
- package/dist/services/playback/scrobble-tracker.js +80 -4
- package/dist/services/playback/scrobble-tracker.js.map +1 -1
- package/dist/tools/handlers/playback-handlers.d.ts.map +1 -1
- package/dist/tools/handlers/playback-handlers.js +54 -16
- package/dist/tools/handlers/playback-handlers.js.map +1 -1
- package/dist/tools/handlers/playlist-handlers.d.ts.map +1 -1
- package/dist/tools/handlers/playlist-handlers.js +20 -10
- package/dist/tools/handlers/playlist-handlers.js.map +1 -1
- package/dist/tools/handlers/queue-handlers.d.ts.map +1 -1
- package/dist/tools/handlers/queue-handlers.js +10 -0
- package/dist/tools/handlers/queue-handlers.js.map +1 -1
- package/dist/tools/handlers/radio-handlers.d.ts.map +1 -1
- package/dist/tools/handlers/radio-handlers.js +26 -9
- package/dist/tools/handlers/radio-handlers.js.map +1 -1
- package/dist/tools/handlers/registry.d.ts.map +1 -1
- package/dist/tools/handlers/registry.js +4 -15
- package/dist/tools/handlers/registry.js.map +1 -1
- package/dist/tools/handlers/search-handlers.d.ts.map +1 -1
- package/dist/tools/handlers/search-handlers.js +20 -0
- package/dist/tools/handlers/search-handlers.js.map +1 -1
- package/dist/tools/handlers/tag-handlers.js +1 -1
- package/dist/tools/handlers/tag-handlers.js.map +1 -1
- package/dist/tools/handlers/user-preferences-handlers.d.ts.map +1 -1
- package/dist/tools/handlers/user-preferences-handlers.js +14 -9
- package/dist/tools/handlers/user-preferences-handlers.js.map +1 -1
- package/dist/tools/lastfm-discovery.d.ts.map +1 -1
- package/dist/tools/lastfm-discovery.js +223 -158
- package/dist/tools/lastfm-discovery.js.map +1 -1
- package/dist/tools/library.d.ts.map +1 -1
- package/dist/tools/library.js +18 -21
- package/dist/tools/library.js.map +1 -1
- package/dist/tools/listening-history.d.ts.map +1 -1
- package/dist/tools/listening-history.js +112 -80
- package/dist/tools/listening-history.js.map +1 -1
- package/dist/tools/lyrics.d.ts.map +1 -1
- package/dist/tools/lyrics.js +35 -39
- package/dist/tools/lyrics.js.map +1 -1
- package/dist/tools/media-library.d.ts.map +1 -1
- package/dist/tools/media-library.js +14 -10
- package/dist/tools/media-library.js.map +1 -1
- package/dist/tools/playback.d.ts +38 -14
- package/dist/tools/playback.d.ts.map +1 -1
- package/dist/tools/playback.js +162 -21
- package/dist/tools/playback.js.map +1 -1
- package/dist/tools/playlist-management/playlist-crud.d.ts.map +1 -1
- package/dist/tools/playlist-management/playlist-crud.js +118 -24
- package/dist/tools/playlist-management/playlist-crud.js.map +1 -1
- package/dist/tools/playlist-management/playlist-export.d.ts.map +1 -1
- package/dist/tools/playlist-management/playlist-export.js +23 -10
- package/dist/tools/playlist-management/playlist-export.js.map +1 -1
- package/dist/tools/playlist-management/track-management.d.ts.map +1 -1
- package/dist/tools/playlist-management/track-management.js +24 -11
- package/dist/tools/playlist-management/track-management.js.map +1 -1
- package/dist/tools/queue-management.d.ts.map +1 -1
- package/dist/tools/queue-management.js +80 -64
- package/dist/tools/queue-management.js.map +1 -1
- package/dist/tools/radio-discovery.d.ts.map +1 -1
- package/dist/tools/radio-discovery.js +137 -68
- package/dist/tools/radio-discovery.js.map +1 -1
- package/dist/tools/radio-validation/index.d.ts +0 -6
- package/dist/tools/radio-validation/index.d.ts.map +1 -1
- package/dist/tools/radio-validation/index.js +6 -5
- package/dist/tools/radio-validation/index.js.map +1 -1
- package/dist/tools/radio-validation/network-validator.d.ts +2 -1
- package/dist/tools/radio-validation/network-validator.d.ts.map +1 -1
- package/dist/tools/radio-validation/network-validator.js +101 -75
- package/dist/tools/radio-validation/network-validator.js.map +1 -1
- package/dist/tools/radio-validation/recommendation-engine.d.ts.map +1 -1
- package/dist/tools/radio-validation/recommendation-engine.js +9 -6
- package/dist/tools/radio-validation/recommendation-engine.js.map +1 -1
- package/dist/tools/radio-validation/stream-detector.d.ts +2 -3
- package/dist/tools/radio-validation/stream-detector.d.ts.map +1 -1
- package/dist/tools/radio-validation/stream-detector.js +13 -7
- package/dist/tools/radio-validation/stream-detector.js.map +1 -1
- package/dist/tools/radio-validation/validation-core.d.ts.map +1 -1
- package/dist/tools/radio-validation/validation-core.js +46 -20
- package/dist/tools/radio-validation/validation-core.js.map +1 -1
- package/dist/tools/radio.d.ts.map +1 -1
- package/dist/tools/radio.js +199 -169
- package/dist/tools/radio.js.map +1 -1
- package/dist/tools/search/filter-resolver.d.ts +13 -0
- package/dist/tools/search/filter-resolver.d.ts.map +1 -1
- package/dist/tools/search/filter-resolver.js +65 -3
- package/dist/tools/search/filter-resolver.js.map +1 -1
- package/dist/tools/search/parallel-searcher.js +3 -3
- package/dist/tools/search/parallel-searcher.js.map +1 -1
- package/dist/tools/search/result-aggregator.d.ts +28 -3
- package/dist/tools/search/result-aggregator.d.ts.map +1 -1
- package/dist/tools/search/result-aggregator.js +44 -17
- package/dist/tools/search/result-aggregator.js.map +1 -1
- package/dist/tools/search/search-orchestrator.d.ts +2 -1
- package/dist/tools/search/search-orchestrator.d.ts.map +1 -1
- package/dist/tools/search/search-orchestrator.js +5 -5
- package/dist/tools/search/search-orchestrator.js.map +1 -1
- package/dist/tools/tags.d.ts.map +1 -1
- package/dist/tools/tags.js +38 -13
- package/dist/tools/tags.js.map +1 -1
- package/dist/tools/test.js +3 -3
- package/dist/tools/test.js.map +1 -1
- package/dist/tools/user-preferences.d.ts +24 -0
- package/dist/tools/user-preferences.d.ts.map +1 -1
- package/dist/tools/user-preferences.js +200 -126
- package/dist/tools/user-preferences.js.map +1 -1
- package/dist/transformers/album-transformer.d.ts +6 -2
- package/dist/transformers/album-transformer.d.ts.map +1 -1
- package/dist/transformers/album-transformer.js +44 -25
- package/dist/transformers/album-transformer.js.map +1 -1
- package/dist/transformers/artist-transformer.d.ts +6 -2
- package/dist/transformers/artist-transformer.d.ts.map +1 -1
- package/dist/transformers/artist-transformer.js +39 -20
- package/dist/transformers/artist-transformer.js.map +1 -1
- package/dist/transformers/playlist-transformer.d.ts.map +1 -1
- package/dist/transformers/playlist-transformer.js +12 -6
- package/dist/transformers/playlist-transformer.js.map +1 -1
- package/dist/transformers/shared-transformers.d.ts +27 -0
- package/dist/transformers/shared-transformers.d.ts.map +1 -1
- package/dist/transformers/shared-transformers.js +31 -7
- package/dist/transformers/shared-transformers.js.map +1 -1
- package/dist/transformers/song-transformer.d.ts +7 -2
- package/dist/transformers/song-transformer.d.ts.map +1 -1
- package/dist/transformers/song-transformer.js +55 -25
- package/dist/transformers/song-transformer.js.map +1 -1
- package/dist/types/core.d.ts +7 -2
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/playlists.d.ts +7 -5
- package/dist/types/playlists.d.ts.map +1 -1
- package/dist/types/tags.d.ts +4 -4
- package/dist/types/tags.d.ts.map +1 -1
- package/dist/utils/cache.d.ts.map +1 -1
- package/dist/utils/cache.js +1 -3
- package/dist/utils/cache.js.map +1 -1
- package/dist/utils/error-formatter.js +3 -3
- package/dist/utils/error-formatter.js.map +1 -1
- package/dist/utils/fetch-with-timeout.js +1 -1
- package/dist/utils/fetch-with-timeout.js.map +1 -1
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +24 -9
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/message-manager.js +2 -2
- package/dist/utils/message-manager.js.map +1 -1
- package/dist/utils/network-safety.d.ts.map +1 -1
- package/dist/utils/network-safety.js +12 -1
- package/dist/utils/network-safety.js.map +1 -1
- package/dist/utils/open-browser.d.ts +27 -0
- package/dist/utils/open-browser.d.ts.map +1 -0
- package/dist/utils/open-browser.js +54 -0
- package/dist/utils/open-browser.js.map +1 -0
- package/dist/utils/strip-html.js +1 -1
- package/dist/utils/strip-html.js.map +1 -1
- package/dist/utils/version.d.ts.map +1 -1
- package/dist/utils/version.js +10 -6
- package/dist/utils/version.js.map +1 -1
- package/dist/web/acquire.d.ts +69 -0
- package/dist/web/acquire.d.ts.map +1 -0
- package/dist/web/acquire.js +134 -0
- package/dist/web/acquire.js.map +1 -0
- package/dist/web/main.d.ts +20 -0
- package/dist/web/main.d.ts.map +1 -0
- package/dist/web/main.js +292 -0
- package/dist/web/main.js.map +1 -0
- package/dist/web/player-runtime.d.ts +48 -0
- package/dist/web/player-runtime.d.ts.map +1 -0
- package/dist/web/player-runtime.js +62 -0
- package/dist/web/player-runtime.js.map +1 -0
- package/dist/web/spawn.d.ts +82 -0
- package/dist/web/spawn.d.ts.map +1 -0
- package/dist/web/spawn.js +184 -0
- package/dist/web/spawn.js.map +1 -0
- package/dist/webui/broadcaster.d.ts.map +1 -1
- package/dist/webui/broadcaster.js +17 -3
- package/dist/webui/broadcaster.js.map +1 -1
- package/dist/webui/http-helpers.d.ts +7 -0
- package/dist/webui/http-helpers.d.ts.map +1 -1
- package/dist/webui/http-helpers.js +15 -0
- package/dist/webui/http-helpers.js.map +1 -1
- package/dist/webui/loopback.d.ts +27 -0
- package/dist/webui/loopback.d.ts.map +1 -0
- package/dist/webui/loopback.js +34 -0
- package/dist/webui/loopback.js.map +1 -0
- package/dist/webui/public/app.js +193 -2
- package/dist/webui/public/index.html +58 -0
- package/dist/webui/public/styles.css +92 -0
- package/dist/webui/routes/controls.d.ts +7 -0
- package/dist/webui/routes/controls.d.ts.map +1 -1
- package/dist/webui/routes/controls.js +11 -17
- package/dist/webui/routes/controls.js.map +1 -1
- package/dist/webui/routes/cover.d.ts.map +1 -1
- package/dist/webui/routes/cover.js +28 -1
- package/dist/webui/routes/cover.js.map +1 -1
- package/dist/webui/routes/health.d.ts +35 -0
- package/dist/webui/routes/health.d.ts.map +1 -0
- package/dist/webui/routes/health.js +42 -0
- package/dist/webui/routes/health.js.map +1 -0
- package/dist/webui/routes/player.d.ts +47 -0
- package/dist/webui/routes/player.d.ts.map +1 -0
- package/dist/webui/routes/player.js +127 -0
- package/dist/webui/routes/player.js.map +1 -0
- package/dist/webui/routes/playlists.d.ts +37 -0
- package/dist/webui/routes/playlists.d.ts.map +1 -0
- package/dist/webui/routes/playlists.js +56 -0
- package/dist/webui/routes/playlists.js.map +1 -0
- package/dist/webui/routes/snapshot.d.ts +1 -1
- package/dist/webui/routes/snapshot.d.ts.map +1 -1
- package/dist/webui/routes/snapshot.js +4 -2
- package/dist/webui/routes/snapshot.js.map +1 -1
- package/dist/webui/routes/static-files.d.ts.map +1 -1
- package/dist/webui/routes/static-files.js +6 -1
- package/dist/webui/routes/static-files.js.map +1 -1
- package/dist/webui/server.d.ts +5 -2
- package/dist/webui/server.d.ts.map +1 -1
- package/dist/webui/server.js +45 -5
- package/dist/webui/server.js.map +1 -1
- package/package.json +15 -6
- package/scripts/make-launcher.mjs +388 -0
- package/dist/webui/index.d.ts +0 -74
- package/dist/webui/index.d.ts.map +0 -1
- package/dist/webui/index.js +0 -165
- package/dist/webui/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -24,15 +24,15 @@ Browse and search songs, albums, artists, genres, and tags with rich filtering:
|
|
|
24
24
|
|
|
25
25
|
Audio plays through your machine's speakers, no browser or Navidrome web UI needed. Search and play in a single step: *"play 5 random starred albums"*, *"queue everything I've starred from the 90s sorted by year"*, *"add 10 random rock songs to whatever's already playing, shuffled"*. Three shuffle modes for albums (keep order, randomize album order, fully interleave tracks).
|
|
26
26
|
|
|
27
|
-
The live queue is actively manipulable:
|
|
27
|
+
The live queue is actively manipulable: reorder and shuffle tracks without interrupting what's playing — shuffle keeps the current song going and reshuffles the rest around it — and removing the current track auto-advances to the next. Saved Navidrome radio stations (Icecast, SHOUTcast, etc.) stream through mpv with ICY metadata flowing through so you can see what the station is currently playing. Plays scrobble back to Navidrome so your recently-played and play counts stay in sync with what you actually listen to through mpv. mpv is lazy-spawned on first use, survives MCP client restarts via a per-user socket, and works on Linux, macOS, and Windows 11.
|
|
28
28
|
|
|
29
29
|
This design is built for conversational control and pairs cleanly with voice transports (Whisper STT + TTS) to build a hands-free music device on a Raspberry Pi or always-on machine.
|
|
30
30
|
|
|
31
|
-
### 🎛️ MPV Remote (Web
|
|
31
|
+
### 🎛️ MPV Remote (Standalone Web Player)
|
|
32
32
|
|
|
33
|
-
> Requires `mpv` (same as Local Audio Playback). On by default;
|
|
33
|
+
> Requires `mpv` (same as Local Audio Playback). On by default; starts with the server.
|
|
34
34
|
|
|
35
|
-
A companion web UI at `http://localhost:8808` for controlling local mpv playback from any browser
|
|
35
|
+
A companion web UI at `http://localhost:8808` for controlling local mpv playback from any browser: a now-playing card with cover art, transport controls, a seek bar, volume, and a live queue you can click to jump around. A built-in playlist picker starts any Navidrome playlist straight from the page, so it works as a real remote, not just a now-playing mirror. Expose it on your LAN to use a phone or tablet to control playback. See [MPV Remote (Web UI)](#mpv-remote-web-ui) for setup, lifetime, and the security note.
|
|
36
36
|
|
|
37
37
|
### 🎶 Playlists
|
|
38
38
|
|
|
@@ -40,13 +40,13 @@ Create, update, reorder, and delete playlists conversationally. Add content flex
|
|
|
40
40
|
|
|
41
41
|
### 🎼 Music Discovery (Last.fm)
|
|
42
42
|
|
|
43
|
-
> Requires
|
|
43
|
+
> Requires a Last.fm API key (free at [last.fm/api](https://www.last.fm/api/account/create)), set in the settings page.
|
|
44
44
|
|
|
45
45
|
Find similar artists and tracks, fetch biographies and top tracks, and browse global music charts. Combine with your library to do gap analysis (*"albums missing from my top 5 artists, ranked by popularity"*), rediscover overlooked music (*"tracks similar to my favorites that I own but never play"*), or build curated "Best Of" playlists scoped to what you actually own.
|
|
46
46
|
|
|
47
47
|
### 🎤 Synchronized Lyrics
|
|
48
48
|
|
|
49
|
-
>
|
|
49
|
+
> Enabled in the settings page (LRCLIB provider + a user agent). No API key needed.
|
|
50
50
|
|
|
51
51
|
Fetch time-synced lyrics with millisecond-precision timestamps (LRC format) and plain-text fallbacks from LRCLIB's community database. Matched automatically by title, artist, album, and duration.
|
|
52
52
|
|
|
@@ -54,7 +54,7 @@ Fetch time-synced lyrics with millisecond-precision timestamps (LRC format) and
|
|
|
54
54
|
|
|
55
55
|
Manage Navidrome radio stations and discover new ones globally. Stream URLs are validated before adding (MP3, AAC, OGG, FLAC detection) and SHOUTcast/Icecast metadata is extracted automatically. Bulk maintenance is supported: *"validate all my stations and remove the broken ones"* or *"test these 10 URLs and add the working ones"*.
|
|
56
56
|
|
|
57
|
-
Global station discovery via Radio Browser (requires
|
|
57
|
+
Global station discovery via Radio Browser (requires a Radio Browser user agent, set in the settings page) covers thousands of stations filterable by genre, country, language, codec, bitrate, and popularity, with vote and click registration so your usage feeds the community ranking.
|
|
58
58
|
|
|
59
59
|
### 📊 Listening Analytics
|
|
60
60
|
|
|
@@ -66,7 +66,7 @@ Star/unstar songs, albums, and artists, set 0-5 star ratings, and list everythin
|
|
|
66
66
|
|
|
67
67
|
### 📚 Multi-Library Support
|
|
68
68
|
|
|
69
|
-
Filter all operations to a subset of your Navidrome libraries, either by setting a default in
|
|
69
|
+
Filter all operations to a subset of your Navidrome libraries, either by setting a default in the settings page (**Default libraries**, `library.defaultLibraryIds`) or by switching active libraries at runtime.
|
|
70
70
|
|
|
71
71
|
## Installation
|
|
72
72
|
|
|
@@ -98,6 +98,11 @@ pnpm build
|
|
|
98
98
|
|
|
99
99
|
### Configure Your MCP Client
|
|
100
100
|
|
|
101
|
+
The MCP client config does just one thing: tell it how to *launch* the server. Your
|
|
102
|
+
Navidrome credentials and all options live in a local `settings.json`, edited through a
|
|
103
|
+
browser-based settings page (no secrets in the client JSON or environment). The settings
|
|
104
|
+
page opens automatically on first run (see [First-run setup](#first-run-setup)).
|
|
105
|
+
|
|
101
106
|
For Claude Desktop, edit `claude_desktop_config.json` (locations: `%APPDATA%/Claude/` on Windows, `~/Library/Application Support/Claude/` on macOS, `~/.config/Claude/` on Linux). Other MCP clients use the same JSON shape.
|
|
102
107
|
|
|
103
108
|
```json
|
|
@@ -105,17 +110,7 @@ For Claude Desktop, edit `claude_desktop_config.json` (locations: `%APPDATA%/Cla
|
|
|
105
110
|
"mcpServers": {
|
|
106
111
|
"navidrome": {
|
|
107
112
|
"command": "npx",
|
|
108
|
-
"args": ["navidrome-mcp"]
|
|
109
|
-
"env": {
|
|
110
|
-
"NAVIDROME_URL": "http://your-server:4533",
|
|
111
|
-
"NAVIDROME_USERNAME": "your_username",
|
|
112
|
-
"NAVIDROME_PASSWORD": "your_password",
|
|
113
|
-
"NAVIDROME_DEFAULT_LIBRARIES": "1,2",
|
|
114
|
-
"LASTFM_API_KEY": "your_api_key",
|
|
115
|
-
"RADIO_BROWSER_USER_AGENT": "Navidrome-MCP/2.0 (+https://github.com/Blakeem/Navidrome-MCP)",
|
|
116
|
-
"LYRICS_PROVIDER": "lrclib",
|
|
117
|
-
"LRCLIB_USER_AGENT": "Navidrome-MCP/2.0 (+https://github.com/Blakeem/Navidrome-MCP)"
|
|
118
|
-
}
|
|
113
|
+
"args": ["navidrome-mcp"]
|
|
119
114
|
}
|
|
120
115
|
}
|
|
121
116
|
}
|
|
@@ -128,17 +123,41 @@ For a manual build, replace `command`/`args` with:
|
|
|
128
123
|
"args": ["/absolute/path/to/Navidrome-MCP/dist/index.js"]
|
|
129
124
|
```
|
|
130
125
|
|
|
131
|
-
|
|
126
|
+
### First-run setup
|
|
132
127
|
|
|
133
|
-
**
|
|
134
|
-
|
|
135
|
-
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
- `WEBUI_PORT` / `WEBUI_HOST` / `WEBUI_EXPOSE` / `WEBUI_ENABLED`: configure the [MPV Remote web UI](#mpv-remote-web-ui) — defaults to `localhost:8808` and lazy-binds once mpv is playing.
|
|
128
|
+
On first start without configuration, the **settings page** opens automatically in your
|
|
129
|
+
browser. This happens whether you launched the MCP server or the standalone web player
|
|
130
|
+
(`navidrome-web`) first; either one, run unconfigured, brings it up. If a browser can't
|
|
131
|
+
open (e.g. over SSH), the URL is printed to the console, and the unconfigured MCP server
|
|
132
|
+
also exposes an `open_settings` tool that returns it. You can open the settings page any
|
|
133
|
+
time with:
|
|
140
134
|
|
|
141
|
-
|
|
135
|
+
```bash
|
|
136
|
+
npx navidrome-config
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Enter your Navidrome URL, username, and password (plus any optional features), click
|
|
140
|
+
**Test connection**, then **Save**. This writes a local `settings.json` (shape:
|
|
141
|
+
[`settings.example.json`](settings.example.json)). Settings load at startup and don't
|
|
142
|
+
hot-reload, so restart whatever you launched to apply them: the MCP client (quit and
|
|
143
|
+
reopen, e.g. Claude Desktop) or the web player (re-run `navidrome-web`). If the web
|
|
144
|
+
player brought up the settings page itself, it stays open for further edits and
|
|
145
|
+
self-closes when idle; re-launch `navidrome-web` to start playing. Upgrading from the old
|
|
146
|
+
env-based setup? The form pre-fills from your previous `env`/`.env` values; verify and
|
|
147
|
+
save.
|
|
148
|
+
|
|
149
|
+
**Required:** Navidrome URL, username, password.
|
|
150
|
+
|
|
151
|
+
**Optional (set in the settings page):**
|
|
152
|
+
- **Default libraries:** comma-separated library IDs to activate by default; blank = all.
|
|
153
|
+
- **Last.fm API key:** enables Last.fm discovery features.
|
|
154
|
+
- **Radio Browser user agent:** enables global station discovery.
|
|
155
|
+
- **Lyrics provider (LRCLIB)** + user agent: enables lyrics fetching.
|
|
156
|
+
- **mpv path:** point at the mpv binary if it's not on `PATH`; blank auto-detects.
|
|
157
|
+
- **Transcode format:** defaults to `raw` (streams the original file untouched for best quality and reliable seeking). Set a codec (e.g. `mp3`, `opus`) to transcode for slow/metered links; the bitrate applies then.
|
|
158
|
+
- **Web UI** (port / host / expose / enabled / auto-open browser): configures the [MPV Remote web UI](#mpv-remote-web-ui) (defaults to `localhost:8808`).
|
|
159
|
+
|
|
160
|
+
Features turn on automatically when their settings are present. Restart your MCP client after saving.
|
|
142
161
|
|
|
143
162
|
### Installing mpv (optional)
|
|
144
163
|
|
|
@@ -164,66 +183,109 @@ scoop install mpv
|
|
|
164
183
|
choco install mpv
|
|
165
184
|
```
|
|
166
185
|
|
|
167
|
-
> Use the full
|
|
186
|
+
> Use the full ID `shinchiro.mpv`; plain `winget install mpv` prompts you to disambiguate from an unofficial Store package. The shinchiro build is the one [mpv.io](https://mpv.io/installation/) points to for Windows.
|
|
168
187
|
>
|
|
169
|
-
> **Windows `PATH` note.** The `shinchiro.mpv`
|
|
170
|
-
> - Add
|
|
171
|
-
> -
|
|
188
|
+
> **Windows `PATH` note.** The `shinchiro.mpv` package installs to `C:\Program Files\MPV Player\` and does **not** add itself to `PATH`. Either:
|
|
189
|
+
> - Add that folder to your `PATH` (System Properties → Environment Variables → Path → New), then open a new terminal, or
|
|
190
|
+
> - Set **mpv path** in the settings page (`playback.mpvPath`) to the full `mpv.exe` path, e.g. `C:\Program Files\MPV Player\mpv.exe`.
|
|
172
191
|
>
|
|
173
|
-
> Other install methods (scoop,
|
|
192
|
+
> Other install methods (scoop, choco, manual zip) use different folders. If `mpv --version` fails in a fresh terminal, locate `mpv.exe` and apply one of the fixes above.
|
|
174
193
|
|
|
175
194
|
Or a pre-built binary from [mpv.io](https://mpv.io/installation/). Verify with `mpv --version`, then restart your MCP client so the server re-detects mpv.
|
|
176
195
|
|
|
177
196
|
### A Note on ChatGPT Desktop
|
|
178
197
|
|
|
179
|
-
ChatGPT's MCP support (web and desktop) requires a hosted HTTPS endpoint and
|
|
198
|
+
ChatGPT's MCP support (web and desktop) requires a hosted HTTPS endpoint and isn't compatible with local stdio servers like this one. You can bridge a stdio server to HTTPS with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote), but for a self-hosted music server it's simpler to use Claude Desktop, Claude Code, Cursor, or another client with native stdio support.
|
|
180
199
|
|
|
181
200
|
## MPV Remote (Web UI)
|
|
182
201
|
|
|
183
|
-
When local audio playback is active, the
|
|
202
|
+
When local audio playback is active, the server runs a companion web interface: a now-playing display and transport-control remote. Open it in any browser on the host (or anywhere on your LAN once exposed).
|
|
184
203
|
|
|
185
204
|
[](navidome-mcp-mpv-remote-large.png)
|
|
186
205
|
|
|
187
206
|
### What it does
|
|
188
207
|
|
|
189
|
-
- **Now-playing card
|
|
190
|
-
- **Transport controls
|
|
191
|
-
- **Volume slider
|
|
192
|
-
- **Queue list
|
|
193
|
-
- **
|
|
208
|
+
- **Now-playing card:** cover art, title, artist, album, and queue position. A `Live` indicator confirms the SSE stream is healthy.
|
|
209
|
+
- **Transport controls:** previous / pause-resume / next, with a seek bar showing position and remaining time.
|
|
210
|
+
- **Volume slider:** drives mpv's internal volume (independent of your OS volume).
|
|
211
|
+
- **Queue list:** every track with title, artist · album, and duration. Click a row to jump to it; the **clear** icon empties the queue and stops playback.
|
|
212
|
+
- **Playlist picker:** the playlist icon opens your Navidrome playlists; pick one to start it (Replace or Add to queue, with optional Shuffle).
|
|
213
|
+
- **Gear + power buttons (host only):** the top bar shows a **gear** (player settings, including "keep playing after the MCP server closes") and a **power** button that stops mpv and the player. Both are hidden for remote (LAN) browsers.
|
|
214
|
+
- **Live updates:** Server-Sent Events push state changes as they happen (throttled to ~1 Hz) and auto-reconnect on disconnect.
|
|
215
|
+
|
|
216
|
+
### Enabling & lifetime
|
|
217
|
+
|
|
218
|
+
On by default, it starts with the server as a separate `navidrome-web` process; the port binds immediately, so the page and its playlist picker are reachable before anything plays. Hosts without mpv don't start it.
|
|
219
|
+
|
|
220
|
+
**Does it keep playing after you close the AI?**
|
|
221
|
+
|
|
222
|
+
- **Default (off):** the MCP-launched player and mpv stop when you close or restart the MCP server.
|
|
223
|
+
- **Keep playing after the MCP server closes** (`webui.persistAfterMcpExit`, in the settings page or the in-player gear modal): the player keeps running after you close the AI; stop it later with the **power** button.
|
|
224
|
+
- **Launched it yourself** (`navidrome-web`, below): always runs independently; the MCP server attaches to it and never shuts it down.
|
|
225
|
+
|
|
226
|
+
mpv stops exactly when the player stops (no background idle timeout).
|
|
227
|
+
|
|
228
|
+
To disable the panel entirely, uncheck **Enable the companion control panel** in the settings page (`webui.enabled`).
|
|
194
229
|
|
|
195
|
-
###
|
|
230
|
+
### Running it standalone (without an MCP client)
|
|
196
231
|
|
|
197
|
-
|
|
232
|
+
Launch the player directly to run it independently of any MCP client. It reads the `settings.json` and opens your browser automatically. A standalone launch always persists: it runs in the background until you stop it with the **power** button in the UI. It **coexists** with an MCP-launched instance: whoever binds the configured port first owns it and the other connects to it (an MCP server attaches rather than replacing or stopping it). Logs go to `navidrome-web.log` in your config directory.
|
|
198
233
|
|
|
199
|
-
|
|
234
|
+
> **Configure first.** The player needs your Navidrome details in `settings.json`. If it isn't configured yet, launching it brings up the **settings page** instead of the player (see [First-run setup](#first-run-setup)). Fill it in and Save, then re-launch `navidrome-web` to start playing. The setup page self-closes when idle, so it never lingers. You can also configure ahead of time with `npx navidrome-config`.
|
|
235
|
+
|
|
236
|
+
#### Desktop shortcut (recommended)
|
|
237
|
+
|
|
238
|
+
Generate a double-clickable icon for your platform. It starts the player in the background with **no terminal window** and opens your browser; if a player is already running, it just opens the browser. Stop it with the **power** button in the UI.
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
navidrome-web-shortcut # after: npm install -g navidrome-mcp
|
|
242
|
+
# or, from a dev clone (see Development):
|
|
243
|
+
pnpm make:launcher
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
This bakes the absolute paths to your `node` and the built player into the shortcut, so it works without anything on your `PATH`. It writes:
|
|
247
|
+
|
|
248
|
+
- **Linux:** `Navidrome Player.desktop` on your Desktop **and** in your app menu (`~/.local/share/applications`). On GNOME, right-click → *Allow Launching* the first time.
|
|
249
|
+
- **macOS:** `Navidrome Player.app` on your Desktop (drag to `/Applications` if you like).
|
|
250
|
+
- **Windows:** `Navidrome Player.vbs` on your Desktop **and** Start Menu. (If your Desktop is redirected into OneDrive, it lands there.)
|
|
251
|
+
|
|
252
|
+
Re-run the generator any time you move or rebuild the project to refresh the baked-in paths.
|
|
253
|
+
|
|
254
|
+
#### From the command line
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
navidrome-web # after: npm install -g navidrome-mcp
|
|
258
|
+
# or, from a dev clone / manual build:
|
|
259
|
+
node dist/web/main.js
|
|
260
|
+
```
|
|
200
261
|
|
|
201
262
|
### Configuration
|
|
202
263
|
|
|
203
|
-
All
|
|
264
|
+
All of these are optional and live in the **Web UI** section of the settings page (`navidrome-config`); the keys below are their `settings.json` paths. Restart the client after saving (except `persistAfterMcpExit`, which the in-player gear modal applies live).
|
|
204
265
|
|
|
205
|
-
|
|
|
266
|
+
| Setting (`settings.json`) | Default | Effect |
|
|
206
267
|
|---|---|---|
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
209
|
-
| `
|
|
210
|
-
| `
|
|
268
|
+
| `webui.enabled` | `true` | Disable the panel entirely. |
|
|
269
|
+
| `webui.port` | `8808` | Port the HTTP server listens on. Pick a free port if 8808 is taken on your host. |
|
|
270
|
+
| `webui.host` | `127.0.0.1` | Bind address. Override only if you know which interface you want; usually **Expose on LAN** is the right knob. |
|
|
271
|
+
| `webui.expose` | `false` | Bind on `0.0.0.0` so other devices on your LAN can reach the panel. |
|
|
272
|
+
| `webui.autoOpenBrowser` | `false` | Open the player in your browser automatically when the MCP server starts. (Running `navidrome-web` directly always opens a browser regardless.) |
|
|
273
|
+
| `webui.persistAfterMcpExit` | `false` | Keep an MCP-launched player (and mpv) running after the MCP server closes/restarts. Toggle it live in the in-player gear modal too. |
|
|
211
274
|
|
|
212
|
-
When
|
|
275
|
+
When **Expose on LAN** is enabled, the player logs the LAN URLs it's reachable on at bind time (e.g. `http://192.168.1.42:8808`). Open one of those on your phone or tablet.
|
|
213
276
|
|
|
214
277
|
### Using it as a phone/tablet remote
|
|
215
278
|
|
|
216
|
-
1.
|
|
217
|
-
2. Restart the MCP client.
|
|
218
|
-
3.
|
|
219
|
-
4. Open the LAN URL from the startup log on your phone's browser. Bookmark it for one-tap access — the page is a single static HTML/CSS/JS bundle, no install required.
|
|
279
|
+
1. Enable **Expose on LAN** in the settings page and Save.
|
|
280
|
+
2. Restart the MCP client (or restart `navidrome-web`).
|
|
281
|
+
3. Open the LAN URL from the startup log in your phone's browser. The player is reachable immediately; start a playlist from the picker without touching the assistant. Bookmark it for one-tap access (the page is a single static bundle, no install required).
|
|
220
282
|
|
|
221
283
|
### Security note
|
|
222
284
|
|
|
223
|
-
The web UI has **no authentication
|
|
285
|
+
The web UI has **no authentication**: anyone who can reach the port can pause, skip, seek, change volume, and jump around the queue.
|
|
224
286
|
|
|
225
|
-
-
|
|
226
|
-
-
|
|
287
|
+
- With `webui.host=127.0.0.1` (the default) it's only reachable from the host machine, which is safe.
|
|
288
|
+
- With **Expose on LAN** (`webui.expose=true`) it's reachable from anything on the LAN. That's usually fine on a trusted home network, but **do not expose it directly to the public internet**. There's no rate-limiting, no auth, and the control API allows queue manipulation and starting playlists. The **player settings and the power button are loopback-only** (and hidden in the UI for remote browsers), so a phone on your LAN can control playback but can't change settings or shut the server down. The browser-based main settings page is likewise never exposed.
|
|
227
289
|
|
|
228
290
|
## Available Tools
|
|
229
291
|
|
|
@@ -297,7 +359,7 @@ Tools marked **conditional** are only registered when the corresponding configur
|
|
|
297
359
|
| `get_tag_distribution` | Tag usage counts across the library |
|
|
298
360
|
| `get_filter_options` | Discover available filter values for search operations |
|
|
299
361
|
|
|
300
|
-
### Last.fm Discovery (requires
|
|
362
|
+
### Last.fm Discovery (requires a Last.fm API key)
|
|
301
363
|
|
|
302
364
|
| Tool | Description |
|
|
303
365
|
|------|-------------|
|
|
@@ -307,7 +369,7 @@ Tools marked **conditional** are only registered when the corresponding configur
|
|
|
307
369
|
| `get_top_tracks_by_artist` | Top tracks for an artist |
|
|
308
370
|
| `get_trending_music` | Trending artists, tracks, and tags from Last.fm charts |
|
|
309
371
|
|
|
310
|
-
### Lyrics (requires
|
|
372
|
+
### Lyrics (requires the LRCLIB provider, set in the settings page)
|
|
311
373
|
|
|
312
374
|
| Tool | Description |
|
|
313
375
|
|------|-------------|
|
|
@@ -323,7 +385,7 @@ Tools marked **conditional** are only registered when the corresponding configur
|
|
|
323
385
|
| `delete_radio_station` | Delete a station |
|
|
324
386
|
| `validate_radio_stream` | Test an http(s) stream URL for accessibility and audio content |
|
|
325
387
|
|
|
326
|
-
### Global Radio Discovery (requires
|
|
388
|
+
### Global Radio Discovery (requires a Radio Browser user agent)
|
|
327
389
|
|
|
328
390
|
| Tool | Description |
|
|
329
391
|
|------|-------------|
|
|
@@ -335,7 +397,7 @@ Tools marked **conditional** are only registered when the corresponding configur
|
|
|
335
397
|
|
|
336
398
|
### Local Playback (requires [`mpv`](https://mpv.io/))
|
|
337
399
|
|
|
338
|
-
Audio plays through the host's speakers. mpv is lazy-spawned on first use and survives MCP client restarts via a per-user IPC socket.
|
|
400
|
+
Audio plays through the host's speakers. mpv is lazy-spawned on first use and survives MCP client restarts via a per-user IPC socket. Playback streams the original file by default; set **Transcode format** to a codec for constrained bandwidth (see [First-run setup](#first-run-setup)).
|
|
339
401
|
|
|
340
402
|
| Tool | Description |
|
|
341
403
|
|------|-------------|
|
|
@@ -355,8 +417,8 @@ Audio plays through the host's speakers. mpv is lazy-spawned on first use and su
|
|
|
355
417
|
| `playback_status` | Engine health probe (running, mpv version, idle) without spawning mpv |
|
|
356
418
|
| `get_play_queue` | Snapshot of the live queue with metadata and current-track index |
|
|
357
419
|
| `clear_play_queue` | Clear the queue and stop playback |
|
|
358
|
-
| `shuffle_play_queue` | Randomize queue order (membership unchanged) |
|
|
359
|
-
| `move_in_play_queue` | Move a queue entry between indices |
|
|
420
|
+
| `shuffle_play_queue` | Randomize queue order (membership unchanged); the current track keeps playing and is lifted to the top |
|
|
421
|
+
| `move_in_play_queue` | Move a queue entry between indices; never changes what's currently playing |
|
|
360
422
|
| `remove_from_play_queue` | Remove an entry; mpv auto-advances if the current track is removed |
|
|
361
423
|
| `play_queue_index` | Jump directly to the queue entry at the given index; does not reorder |
|
|
362
424
|
|
|
@@ -364,8 +426,8 @@ Audio plays through the host's speakers. mpv is lazy-spawned on first use and su
|
|
|
364
426
|
|
|
365
427
|
**Connection problems**
|
|
366
428
|
- Verify Navidrome is running and reachable
|
|
367
|
-
- Ensure
|
|
368
|
-
- Test credentials with `curl`
|
|
429
|
+
- Ensure the **Navidrome URL** in the settings page includes the protocol (`http://` or `https://`)
|
|
430
|
+
- Use the settings page's **Test connection** button (or test credentials with `curl` / a browser) before saving
|
|
369
431
|
|
|
370
432
|
**macOS-specific**
|
|
371
433
|
- See the [macOS Troubleshooting Guide](docs/MACOS_TROUBLESHOOTING.md) (commonly: Node.js path not found; fix with symlinks or full paths)
|
|
@@ -386,8 +448,10 @@ Audio plays through the host's speakers. mpv is lazy-spawned on first use and su
|
|
|
386
448
|
```bash
|
|
387
449
|
git clone https://github.com/Blakeem/Navidrome-MCP.git
|
|
388
450
|
cd Navidrome-MCP
|
|
389
|
-
|
|
390
|
-
|
|
451
|
+
pnpm install
|
|
452
|
+
pnpm build
|
|
453
|
+
node dist/config-app/main.js # opens the settings page; fill in + Save
|
|
454
|
+
# (writes settings.json to your OS config dir; see settings.example.json)
|
|
391
455
|
|
|
392
456
|
pnpm dev # hot reload
|
|
393
457
|
pnpm test # watch-mode tests
|
|
@@ -396,6 +460,36 @@ pnpm check:all # lint + typecheck + dead-code
|
|
|
396
460
|
pnpm build # production bundle
|
|
397
461
|
```
|
|
398
462
|
|
|
463
|
+
### Testing the standalone web player from a dev build
|
|
464
|
+
|
|
465
|
+
This is the from-source path for trying the player **before it's published to npm** (the published package may lag behind `dev`). It applies to the MCP server too; both run from the same `dist/`.
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
# 1. Build (also bundles the web UI's static assets into dist/)
|
|
469
|
+
pnpm build
|
|
470
|
+
|
|
471
|
+
# 2. Configure if needed; writes settings.json to your OS config dir
|
|
472
|
+
node dist/config-app/main.js # opens the settings page; fill in + Save
|
|
473
|
+
|
|
474
|
+
# 3. Run the standalone player directly
|
|
475
|
+
node dist/web/main.js # serves http://127.0.0.1:8808 and opens your browser
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
To make a double-clickable icon out of that build (no global install needed):
|
|
479
|
+
|
|
480
|
+
```bash
|
|
481
|
+
pnpm make:launcher # writes a shortcut to your Desktop + app menu
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
**Windows notes** (PowerShell):
|
|
485
|
+
|
|
486
|
+
- Use `pnpm build` then `node dist\web\main.js`, same as above with backslashes.
|
|
487
|
+
- `pnpm make:launcher` writes `Navidrome Player.vbs` to your Desktop **and** Start Menu; it launches `node dist\web\main.js` with **no console window** and bakes in the absolute path to *this* checkout, so don't move the folder afterward (re-run it if you do).
|
|
488
|
+
- If a redirected/OneDrive Desktop hides the file, the Start Menu copy still works (Start → type "Navidrome").
|
|
489
|
+
- mpv must be installed and discoverable for playback to start; set `playback.mpvPath` in the settings page if it isn't on `PATH`.
|
|
490
|
+
|
|
491
|
+
When you publish, `npm install -g navidrome-mcp` puts `navidrome-web`, `navidrome-config`, and `navidrome-web-shortcut` on the user's `PATH`, so the same flows become `navidrome-web` / `navidrome-config` / `navidrome-web-shortcut` with no clone or build.
|
|
492
|
+
|
|
399
493
|
Testing with [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
|
|
400
494
|
|
|
401
495
|
```bash
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navidrome MCP Server - Shared runtime bootstrap
|
|
3
|
+
* Copyright (C) 2025
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
7
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import type { Config } from './config.js';
|
|
19
|
+
import { NavidromeClient } from './client/navidrome-client.js';
|
|
20
|
+
/**
|
|
21
|
+
* The fully-initialized core every entry point needs to operate: the resolved
|
|
22
|
+
* config plus an authenticated, ready-to-use client. The library and filter
|
|
23
|
+
* caches and the playback engine are module singletons configured as a side
|
|
24
|
+
* effect of this call, so they don't need to be returned.
|
|
25
|
+
*/
|
|
26
|
+
interface Runtime {
|
|
27
|
+
config: Config;
|
|
28
|
+
client: NavidromeClient;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Shared startup sequence used by both the MCP server (`src/index.ts`) and,
|
|
32
|
+
* later, the standalone web server. Resolves config, authenticates the client,
|
|
33
|
+
* primes the library/filter caches, and configures the playback engine so any
|
|
34
|
+
* transport-agnostic tool impl can run identically regardless of who launched
|
|
35
|
+
* the process.
|
|
36
|
+
*
|
|
37
|
+
* Deliberately does NOT attach the scrobbler — scrobbling ownership is
|
|
38
|
+
* process-conditional (the playback survivor scrobbles; see the standalone-web
|
|
39
|
+
* spec §6.4), so each entry point wires it itself after calling this.
|
|
40
|
+
*/
|
|
41
|
+
export declare function createRuntime(): Promise<Runtime>;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=bootstrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAM/D;;;;;GAKG;AACH,UAAU,OAAO;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,eAAe,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,CAqBtD"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navidrome MCP Server - Shared runtime bootstrap
|
|
3
|
+
* Copyright (C) 2025
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
7
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { loadConfig } from './config.js';
|
|
19
|
+
import { NavidromeClient } from './client/navidrome-client.js';
|
|
20
|
+
import { libraryManager } from './services/library-manager.js';
|
|
21
|
+
import { filterCacheManager } from './services/filter-cache-manager.js';
|
|
22
|
+
import { playbackEngine } from './services/playback/playback-engine.js';
|
|
23
|
+
import { logger } from './utils/logger.js';
|
|
24
|
+
/**
|
|
25
|
+
* Shared startup sequence used by both the MCP server (`src/index.ts`) and,
|
|
26
|
+
* later, the standalone web server. Resolves config, authenticates the client,
|
|
27
|
+
* primes the library/filter caches, and configures the playback engine so any
|
|
28
|
+
* transport-agnostic tool impl can run identically regardless of who launched
|
|
29
|
+
* the process.
|
|
30
|
+
*
|
|
31
|
+
* Deliberately does NOT attach the scrobbler — scrobbling ownership is
|
|
32
|
+
* process-conditional (the playback survivor scrobbles; see the standalone-web
|
|
33
|
+
* spec §6.4), so each entry point wires it itself after calling this.
|
|
34
|
+
*/
|
|
35
|
+
export async function createRuntime() {
|
|
36
|
+
const config = await loadConfig();
|
|
37
|
+
logger.setDebug(config.debug);
|
|
38
|
+
const client = new NavidromeClient(config);
|
|
39
|
+
await client.initialize();
|
|
40
|
+
// Initialize library manager with user data and configuration.
|
|
41
|
+
await libraryManager.initialize(client, config);
|
|
42
|
+
// Initialize filter cache manager for enhanced search functionality.
|
|
43
|
+
await filterCacheManager.initialize(client, config);
|
|
44
|
+
// Configure the singleton engine with the loaded config so tools can
|
|
45
|
+
// lazy-spawn mpv on first invocation. Gated on the playback feature (mpv
|
|
46
|
+
// detected) — `buildStreamUrl()` and every play_* tool depend on it.
|
|
47
|
+
if (config.features.playback) {
|
|
48
|
+
playbackEngine.configure(config);
|
|
49
|
+
}
|
|
50
|
+
return { config, client };
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAa3C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAClC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE9B,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;IAE1B,+DAA+D;IAC/D,MAAM,cAAc,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhD,qEAAqE;IACrE,MAAM,kBAAkB,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpD,qEAAqE;IACrE,yEAAyE;IACzE,qEAAqE;IACrE,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;QAC7B,cAAc,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-manager.d.ts","sourceRoot":"","sources":["../../src/client/auth-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAQ3C,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAIhC,OAAO,CAAC,cAAc,CAA8B;gBAExC,MAAM,EAAE,MAAM;IAIpB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IASnC;;;OAGG;IACH,UAAU,IAAI,IAAI;IAKZ,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;YAYnB,mBAAmB;
|
|
1
|
+
{"version":3,"file":"auth-manager.d.ts","sourceRoot":"","sources":["../../src/client/auth-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAQ3C,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAIhC,OAAO,CAAC,cAAc,CAA8B;gBAExC,MAAM,EAAE,MAAM;IAIpB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IASnC;;;OAGG;IACH,UAAU,IAAI,IAAI;IAKZ,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;YAYnB,mBAAmB;CA2ClC"}
|
|
@@ -47,10 +47,10 @@ export class AuthManager {
|
|
|
47
47
|
this.tokenExpiry = null;
|
|
48
48
|
}
|
|
49
49
|
async getToken() {
|
|
50
|
-
if (this.token === null || this.token ===
|
|
50
|
+
if (this.token === null || this.token === '' || this.tokenExpiry === null || this.tokenExpiry <= new Date()) {
|
|
51
51
|
await this.authenticate();
|
|
52
52
|
}
|
|
53
|
-
if (this.token === null || this.token ===
|
|
53
|
+
if (this.token === null || this.token === '') {
|
|
54
54
|
throw new Error(ErrorFormatter.authentication('token not available after authentication'));
|
|
55
55
|
}
|
|
56
56
|
return this.token;
|
|
@@ -77,7 +77,18 @@ export class AuthManager {
|
|
|
77
77
|
if (!response.ok) {
|
|
78
78
|
throw new Error(ErrorFormatter.authentication(`${response.status} ${response.statusText}`));
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
let data;
|
|
81
|
+
try {
|
|
82
|
+
data = (await response.json());
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// 200 OK but body wasn't valid JSON (empty body, HTML error page from a
|
|
86
|
+
// proxy, etc.) — surface with auth context instead of a bare SyntaxError.
|
|
87
|
+
throw new Error(ErrorFormatter.authentication('invalid JSON in /auth/login response'));
|
|
88
|
+
}
|
|
89
|
+
if (typeof data.token !== 'string' || data.token === '') {
|
|
90
|
+
throw new Error(ErrorFormatter.authentication('server returned no token'));
|
|
91
|
+
}
|
|
81
92
|
this.token = data.token;
|
|
82
93
|
this.tokenExpiry = new Date(Date.now() + this.config.tokenExpiry * 1000); // Convert seconds to milliseconds
|
|
83
94
|
logger.debug('Authentication successful');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-manager.js","sourceRoot":"","sources":["../../src/client/auth-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EACL,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,gCAAgC,CAAC;AAExC,MAAM,OAAO,WAAW;IACd,KAAK,GAAkB,IAAI,CAAC;IAC5B,WAAW,GAAgB,IAAI,CAAC;IACvB,MAAM,CAAS;IAChC,yEAAyE;IACzE,yEAAyE;IACzE,4EAA4E;IACpE,cAAc,GAAyB,IAAI,CAAC;IAEpD,YAAY,MAAc;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC,cAAc,KAAK,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,cAAc,CAAC;QAC5B,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,UAAU;QACR,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,
|
|
1
|
+
{"version":3,"file":"auth-manager.js","sourceRoot":"","sources":["../../src/client/auth-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EACL,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,gCAAgC,CAAC;AAExC,MAAM,OAAO,WAAW;IACd,KAAK,GAAkB,IAAI,CAAC;IAC5B,WAAW,GAAgB,IAAI,CAAC;IACvB,MAAM,CAAS;IAChC,yEAAyE;IACzE,yEAAyE;IACzE,4EAA4E;IACpE,cAAc,GAAyB,IAAI,CAAC;IAEpD,YAAY,MAAc;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC,cAAc,KAAK,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,cAAc,CAAC;QAC5B,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,UAAU;QACR,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,EAAE,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;YAC5G,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,KAAK,EAAE,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,0CAA0C,CAAC,CAAC,CAAC;QAC7F,CAAC;QAED,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAEO,KAAK,CAAC,mBAAmB;QAC/B,oEAAoE;QACpE,sEAAsE;QACtE,sEAAsE;QACtE,sEAAsE;QACtE,uEAAuE;QACvE,gEAAgE;QAChE,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CACrC,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,aAAa,EACxC;YACE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB;gBACvC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,iBAAiB;aACxC,CAAC;SACH,EACD;YACE,SAAS,EAAE,yBAAyB,EAAE;YACtC,WAAW,EAAE,OAAO;YACpB,cAAc,EAAE,uBAAuB;SACxC,CACF,CAAC;QAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;QAC9F,CAAC;QAED,IAAI,IAAuB,CAAC;QAC5B,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAsB,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,wEAAwE;YACxE,0EAA0E;YAC1E,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,sCAAsC,CAAC,CAAC,CAAC;QACzF,CAAC;QACD,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,EAAE,EAAE,CAAC;YACxD,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,0BAA0B,CAAC,CAAC,CAAC;QAC7E,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,kCAAkC;QAC5G,MAAM,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC5C,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navidrome-client.d.ts","sourceRoot":"","sources":["../../src/client/navidrome-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAW3C,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,EAAE,MAAM;IAMpB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAKjC;;;;;;;;OAQG;IACG,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxC;;;;OAIG;IACG,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;IAKzE;;;;;;;;OAQG;IACG,eAAe,CAAC,CAAC,EACrB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAuB7C;;;;OAIG;IACG,wBAAwB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;IAK1F;;;;OAIG;IACG,+BAA+B,CAAC,CAAC,EACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAM7C;;;;OAIG;IACH,OAAO,CAAC,4BAA4B;IAgBpC;;;;;;OAMG;IACG,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACnC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAAO,GACxC,OAAO,CAAC,OAAO,CAAC;IAsDnB;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;
|
|
1
|
+
{"version":3,"file":"navidrome-client.d.ts","sourceRoot":"","sources":["../../src/client/navidrome-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAW3C,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,EAAE,MAAM;IAMpB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAKjC;;;;;;;;OAQG;IACG,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxC;;;;OAIG;IACG,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;IAKzE;;;;;;;;OAQG;IACG,eAAe,CAAC,CAAC,EACrB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAuB7C;;;;OAIG;IACG,wBAAwB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;IAK1F;;;;OAIG;IACG,+BAA+B,CAAC,CAAC,EACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAM7C;;;;OAIG;IACH,OAAO,CAAC,4BAA4B;IAgBpC;;;;;;OAMG;IACG,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACnC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAAO,GACxC,OAAO,CAAC,OAAO,CAAC;IAsDnB;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;YAsBZ,OAAO;YA+CP,aAAa;CAiC5B"}
|