navidrome-mcp 2.0.0 → 2.0.1

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 CHANGED
@@ -24,7 +24,7 @@ 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: move a track to the front and it starts playing, shuffle and the new top plays, remove the current track and the next one auto-advances. 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. mpv is lazy-spawned on first use, survives MCP client restarts via a per-user socket, and works on Linux, macOS, and Windows 11.
27
+ The live queue is actively manipulable: move a track to the front and it starts playing, shuffle and the new top plays, remove the current track and the next one auto-advances. 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
 
@@ -380,7 +380,6 @@ Audio plays through the host's speakers. mpv is lazy-spawned on first use and su
380
380
  - **No audio without mpv.** When mpv isn't installed the library and saved-queue tools still work, but audio playback isn't available; use the Navidrome web UI or a Subsonic client.
381
381
  - **Recently-played has no timestamps.** Navidrome exposes play counts and completion status, not last-played times.
382
382
  - **Saved queue ≠ live queue.** The `*_saved_queue` tools operate on Navidrome's server-side advisory queue (web UI sync). The `*_play_queue` tools operate on the local mpv playlist. They are independent.
383
- - **Scrobbling for local playback isn't wired up yet.** Listens through mpv don't currently feed back into Navidrome's play counts. Planned.
384
383
 
385
384
  ## Development
386
385
 
@@ -1 +1 @@
1
- {"version":3,"file":"mpv-process.d.ts","sourceRoot":"","sources":["../../../src/services/playback/mpv-process.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIxE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAkB1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAyB/C;AAsCD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CA6B1E"}
1
+ {"version":3,"file":"mpv-process.d.ts","sourceRoot":"","sources":["../../../src/services/playback/mpv-process.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIxE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAkB1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAyB/C;AA8CD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CA6B1E"}
@@ -116,6 +116,14 @@ function buildMpvArgs(ipcPath) {
116
116
  '--load-scripts=no',
117
117
  '--gapless-audio=weak',
118
118
  '--prefetch-playlist=yes',
119
+ // Network cache. mpv defaults (--cache-secs=1.0, --demuxer-readahead-secs=1.0)
120
+ // are tuned for local files; on a streamed HTTP source they leave a ~1s budget
121
+ // that any network jitter at track-change time blows through, causing the
122
+ // decoder to underrun and stop after a second of playback. Audio bitrates
123
+ // are tiny (30s of FLAC ≈ 6MB, MP3 ≈ 1.2MB), so a generous prebuffer is free.
124
+ '--cache=yes',
125
+ '--cache-secs=30',
126
+ '--demuxer-readahead-secs=20',
119
127
  `--input-ipc-server=${ipcPath}`,
120
128
  '--volume=80',
121
129
  '--audio-display=no',
@@ -1 +1 @@
1
- {"version":3,"file":"mpv-process.js","sourceRoot":"","sources":["../../../src/services/playback/mpv-process.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mpv-process.js","sourceRoot":"","sources":["../../../src/services/playback/mpv-process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Navidrome MCP Server - Scrobble Tracker
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 { StateChangeEvent } from './playback-engine.js';
19
+ /**
20
+ * Subset of the playback engine the tracker depends on. Defined here so
21
+ * tests can pass a minimal fake without constructing the full engine.
22
+ */
23
+ export interface ScrobbleEngine {
24
+ onStateChange(handler: (event: StateChangeEvent) => void): () => void;
25
+ getPlaylist(): Promise<Array<{
26
+ index: number;
27
+ songId: string | null;
28
+ duration?: number;
29
+ }>>;
30
+ getCachedProperty(name: string): unknown;
31
+ }
32
+ /**
33
+ * Subset of the Navidrome client the tracker depends on.
34
+ */
35
+ export interface ScrobbleClient {
36
+ subsonicRequest(endpoint: string, params?: Record<string, string>, options?: {
37
+ method?: 'GET' | 'POST';
38
+ }): Promise<unknown>;
39
+ }
40
+ /**
41
+ * Watches the playback engine and submits Subsonic `/scrobble` calls to
42
+ * Navidrome, mirroring the web UI / Last.fm rules:
43
+ *
44
+ * 1. On track start → `submission=false` (now-playing notification).
45
+ * 2. After listening past half the duration OR 4 minutes (whichever first),
46
+ * once per play → `submission=true&time=<startedAt-ms>`.
47
+ *
48
+ * Tracks shorter than 30s, and radio streams (no `songId`), never scrobble.
49
+ *
50
+ * The tracker is fire-and-forget: every Subsonic call is dispatched async
51
+ * with errors logged at warn level. It never blocks playback or surfaces
52
+ * errors to tool callers.
53
+ *
54
+ * Attach semantics: mpv is intentionally configured to outlive the MCP
55
+ * process, so when MCP starts the engine often attaches to an mpv that's
56
+ * already mid-track. The engine's `installObservers` triggers mpv to emit
57
+ * immediate "current value" change events for every observed property,
58
+ * which look identical to real transitions. The first `playlist-pos`
59
+ * event after attach is therefore treated as initial state — it hydrates
60
+ * a sentinel but does NOT trigger now-playing or scrobble tracking. Only
61
+ * subsequent events that actually change the value are real transitions.
62
+ */
63
+ export declare class ScrobbleTracker {
64
+ private readonly client;
65
+ private readonly engine;
66
+ private unsubscribe;
67
+ private currentSongId;
68
+ private currentDuration;
69
+ private startedAtMs;
70
+ private submitted;
71
+ private lastTimePos;
72
+ private lastPlaylistPos;
73
+ private generation;
74
+ constructor(client: ScrobbleClient, engine: ScrobbleEngine);
75
+ /**
76
+ * Subscribe to engine state changes. Idempotent — a second call while
77
+ * already attached is a no-op.
78
+ */
79
+ attach(): void;
80
+ /**
81
+ * Unsubscribe and reset state. Used by tests; in production the tracker's
82
+ * lifetime is the process lifetime.
83
+ */
84
+ detach(): void;
85
+ private handleEvent;
86
+ private onPlaylistPos;
87
+ private onQueueMutation;
88
+ private maybeRehydrateAfterQueue;
89
+ private hydrateAndStart;
90
+ private startTrackingTrack;
91
+ private onDuration;
92
+ private onTimePos;
93
+ private maybeSubmit;
94
+ private reset;
95
+ private sendNowPlaying;
96
+ private sendSubmission;
97
+ }
98
+ //# sourceMappingURL=scrobble-tracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrobble-tracker.d.ts","sourceRoot":"","sources":["../../../src/services/playback/scrobble-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAQ7D;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACtE,WAAW,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAC3F,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1C;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,eAAe,CACb,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,GACpC,OAAO,CAAC,OAAO,CAAC,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,WAAW,CAA6B;IAEhD,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,SAAS,CAAS;IAM1B,OAAO,CAAC,WAAW,CAAuB;IAO1C,OAAO,CAAC,eAAe,CAAwC;IAM/D,OAAO,CAAC,UAAU,CAAK;gBAEX,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc;IAK1D;;;OAGG;IACH,MAAM,IAAI,IAAI;IAOd;;;OAGG;IACH,MAAM,IAAI,IAAI;IAWd,OAAO,CAAC,WAAW;IAqBnB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,eAAe;YAaT,wBAAwB;YA2BxB,eAAe;IAgB7B,OAAO,CAAC,kBAAkB;IAoB1B,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,cAAc;CAcvB"}
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Navidrome MCP Server - Scrobble Tracker
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 { logger } from '../../utils/logger.js';
19
+ // Last.fm scrobble rules: track must be at least 30s long, and counts as
20
+ // played after the user has listened to half the duration OR 4 minutes,
21
+ // whichever comes first.
22
+ const MIN_DURATION_SECONDS = 30;
23
+ const MAX_THRESHOLD_SECONDS = 240;
24
+ /**
25
+ * Watches the playback engine and submits Subsonic `/scrobble` calls to
26
+ * Navidrome, mirroring the web UI / Last.fm rules:
27
+ *
28
+ * 1. On track start → `submission=false` (now-playing notification).
29
+ * 2. After listening past half the duration OR 4 minutes (whichever first),
30
+ * once per play → `submission=true&time=<startedAt-ms>`.
31
+ *
32
+ * Tracks shorter than 30s, and radio streams (no `songId`), never scrobble.
33
+ *
34
+ * The tracker is fire-and-forget: every Subsonic call is dispatched async
35
+ * with errors logged at warn level. It never blocks playback or surfaces
36
+ * errors to tool callers.
37
+ *
38
+ * Attach semantics: mpv is intentionally configured to outlive the MCP
39
+ * process, so when MCP starts the engine often attaches to an mpv that's
40
+ * already mid-track. The engine's `installObservers` triggers mpv to emit
41
+ * immediate "current value" change events for every observed property,
42
+ * which look identical to real transitions. The first `playlist-pos`
43
+ * event after attach is therefore treated as initial state — it hydrates
44
+ * a sentinel but does NOT trigger now-playing or scrobble tracking. Only
45
+ * subsequent events that actually change the value are real transitions.
46
+ */
47
+ export class ScrobbleTracker {
48
+ client;
49
+ engine;
50
+ unsubscribe = null;
51
+ currentSongId = null;
52
+ currentDuration = null;
53
+ startedAtMs = null;
54
+ submitted = false;
55
+ // Latest mpv time-pos value observed for the current play, in seconds.
56
+ // Tracked here (rather than read from the engine cache) so a stale
57
+ // time-pos belonging to the previous track can't leak into the new
58
+ // play's threshold check during the brief window between a playlist-pos
59
+ // change and the first time-pos event for the new file.
60
+ lastTimePos = null;
61
+ // Sentinel 'unknown' until the first playlist-pos event after attach.
62
+ // That first event is mpv's observe-emitted snapshot of current state —
63
+ // it must NOT trigger hydration, because the engine may have just
64
+ // attached to an mpv already mid-track from a previous MCP session.
65
+ // Subsequent events that change this value are real transitions.
66
+ lastPlaylistPos = 'unknown';
67
+ // Bumped on every real transition (onPlaylistPos / onQueueMutation).
68
+ // hydrateAndStart captures gen at call time and bails after each await
69
+ // if it has advanced — guards against out-of-order playlist reads
70
+ // under fast skips. Persists across reset() (attach-lifetime state).
71
+ generation = 0;
72
+ constructor(client, engine) {
73
+ this.client = client;
74
+ this.engine = engine;
75
+ }
76
+ /**
77
+ * Subscribe to engine state changes. Idempotent — a second call while
78
+ * already attached is a no-op.
79
+ */
80
+ attach() {
81
+ if (this.unsubscribe !== null)
82
+ return;
83
+ this.unsubscribe = this.engine.onStateChange((event) => {
84
+ this.handleEvent(event);
85
+ });
86
+ }
87
+ /**
88
+ * Unsubscribe and reset state. Used by tests; in production the tracker's
89
+ * lifetime is the process lifetime.
90
+ */
91
+ detach() {
92
+ if (this.unsubscribe !== null) {
93
+ this.unsubscribe();
94
+ this.unsubscribe = null;
95
+ }
96
+ this.reset();
97
+ // Reset attach-lifetime state too so detach-then-reattach starts clean.
98
+ this.lastPlaylistPos = 'unknown';
99
+ this.generation = 0;
100
+ }
101
+ handleEvent(event) {
102
+ if (event.kind === 'queue') {
103
+ this.onQueueMutation();
104
+ return;
105
+ }
106
+ if (event.kind !== 'property')
107
+ return;
108
+ switch (event.name) {
109
+ case 'playlist-pos':
110
+ this.onPlaylistPos(event.data);
111
+ return;
112
+ case 'duration':
113
+ this.onDuration(event.data);
114
+ return;
115
+ case 'time-pos':
116
+ this.onTimePos(event.data);
117
+ return;
118
+ default:
119
+ return;
120
+ }
121
+ }
122
+ onPlaylistPos(data) {
123
+ const next = typeof data === 'number' ? data : null;
124
+ const prev = this.lastPlaylistPos;
125
+ this.lastPlaylistPos = next;
126
+ // First event since attach is mpv's observe-emitted current state —
127
+ // hydrate the sentinel but do nothing else.
128
+ if (prev === 'unknown')
129
+ return;
130
+ // mpv re-emit at the same value (or jumpToPlaylistEntry to current
131
+ // index): not a real track change. Last.fm wouldn't accept a
132
+ // re-scrobble within minutes anyway, so silently ignore.
133
+ if (prev === next)
134
+ return;
135
+ this.reset();
136
+ if (next === null || next < 0)
137
+ return;
138
+ void this.hydrateAndStart(next, ++this.generation);
139
+ }
140
+ onQueueMutation() {
141
+ // A queue-mutating engine operation just completed (enqueue / clear /
142
+ // shuffle / move / remove / enqueueRadio). mpv does not emit a
143
+ // playlist-pos change event when the index stays the same (e.g.
144
+ // enqueue('replace') while at index 0 — the most common case for
145
+ // play_songs called on an attached mpv that's already playing).
146
+ // Force a re-hydration; the songId comparison inside the async path
147
+ // makes this a no-op when the current track wasn't actually displaced
148
+ // (shuffle that left index 0 alone), and the generation token makes
149
+ // concurrent transitions safe.
150
+ void this.maybeRehydrateAfterQueue();
151
+ }
152
+ async maybeRehydrateAfterQueue() {
153
+ const cachedPos = this.engine.getCachedProperty('playlist-pos');
154
+ if (typeof cachedPos !== 'number' || cachedPos < 0) {
155
+ this.reset();
156
+ this.lastPlaylistPos = typeof cachedPos === 'number' ? cachedPos : null;
157
+ return;
158
+ }
159
+ let entry;
160
+ try {
161
+ const playlist = await this.engine.getPlaylist();
162
+ entry = playlist.find((e) => e.index === cachedPos);
163
+ }
164
+ catch (err) {
165
+ logger.warn(`scrobble: failed to read playlist after queue mutation: ${String(err)}`);
166
+ return;
167
+ }
168
+ if (entry === undefined)
169
+ return;
170
+ // Already tracking this exact song — either a concurrent property-change
171
+ // handler hydrated, or the queue mutation didn't displace the current
172
+ // track. No-op in either case.
173
+ if (entry.songId !== null && entry.songId === this.currentSongId)
174
+ return;
175
+ this.reset();
176
+ this.lastPlaylistPos = cachedPos;
177
+ this.generation++;
178
+ if (entry.songId === null)
179
+ return; // radio
180
+ this.startTrackingTrack(entry.songId, entry.duration);
181
+ }
182
+ async hydrateAndStart(pos, gen) {
183
+ let entry;
184
+ try {
185
+ const playlist = await this.engine.getPlaylist();
186
+ if (gen !== this.generation)
187
+ return; // superseded by a newer transition
188
+ entry = playlist.find((e) => e.index === pos);
189
+ }
190
+ catch (err) {
191
+ logger.warn(`scrobble: failed to read playlist for pos=${pos}: ${String(err)}`);
192
+ return;
193
+ }
194
+ if (entry === undefined)
195
+ return;
196
+ if (entry.songId === null)
197
+ return; // radio stream
198
+ if (gen !== this.generation)
199
+ return; // defensive after the find
200
+ this.startTrackingTrack(entry.songId, entry.duration);
201
+ }
202
+ startTrackingTrack(songId, duration) {
203
+ this.currentSongId = songId;
204
+ this.startedAtMs = Date.now();
205
+ this.submitted = false;
206
+ this.lastTimePos = null;
207
+ this.currentDuration = null;
208
+ if (typeof duration === 'number' && duration > 0) {
209
+ this.currentDuration = duration;
210
+ }
211
+ else {
212
+ // Fall back to mpv's cached duration if the playlist entry didn't
213
+ // carry one (e.g. post-MCP-restart attach where the metadata cache
214
+ // is empty).
215
+ const cachedDuration = this.engine.getCachedProperty('duration');
216
+ if (typeof cachedDuration === 'number' && cachedDuration > 0) {
217
+ this.currentDuration = cachedDuration;
218
+ }
219
+ }
220
+ this.sendNowPlaying(songId);
221
+ }
222
+ onDuration(data) {
223
+ if (typeof data !== 'number' || data <= 0)
224
+ return;
225
+ this.currentDuration = data;
226
+ // Re-evaluate threshold against the last time-pos we observed for
227
+ // this play. Handles the (rare) case where duration arrives after a
228
+ // qualifying time-pos tick.
229
+ this.maybeSubmit(this.lastTimePos);
230
+ }
231
+ onTimePos(data) {
232
+ if (typeof data === 'number')
233
+ this.lastTimePos = data;
234
+ this.maybeSubmit(data);
235
+ }
236
+ maybeSubmit(timePos) {
237
+ if (this.submitted)
238
+ return;
239
+ if (this.currentSongId === null)
240
+ return;
241
+ if (this.startedAtMs === null)
242
+ return;
243
+ if (this.currentDuration === null || this.currentDuration < MIN_DURATION_SECONDS)
244
+ return;
245
+ if (typeof timePos !== 'number')
246
+ return;
247
+ if (timePos < this.currentDuration / 2 && timePos < MAX_THRESHOLD_SECONDS)
248
+ return;
249
+ // Set the flag BEFORE dispatching to prevent re-entry from a subsequent
250
+ // time-pos tick before the async call resolves.
251
+ this.submitted = true;
252
+ this.sendSubmission(this.currentSongId, this.startedAtMs);
253
+ }
254
+ reset() {
255
+ this.currentSongId = null;
256
+ this.currentDuration = null;
257
+ this.startedAtMs = null;
258
+ this.submitted = false;
259
+ this.lastTimePos = null;
260
+ // lastPlaylistPos and generation are intentionally preserved across
261
+ // reset() — they track attach-lifetime state, not per-play state.
262
+ }
263
+ sendNowPlaying(songId) {
264
+ this.client
265
+ .subsonicRequest('/scrobble', { id: songId, submission: 'false' }, { method: 'POST' })
266
+ .then(() => {
267
+ logger.debug(`scrobble: now-playing sent for ${songId}`);
268
+ })
269
+ .catch((err) => {
270
+ logger.warn(`scrobble: now-playing failed for ${songId}: ${String(err)}`);
271
+ });
272
+ }
273
+ sendSubmission(songId, startedAtMs) {
274
+ this.client
275
+ .subsonicRequest('/scrobble', { id: songId, submission: 'true', time: String(startedAtMs) }, { method: 'POST' })
276
+ .then(() => {
277
+ logger.debug(`scrobble: submission sent for ${songId} (started ${startedAtMs})`);
278
+ })
279
+ .catch((err) => {
280
+ logger.warn(`scrobble: submission failed for ${songId}: ${String(err)}`);
281
+ });
282
+ }
283
+ }
284
+ //# sourceMappingURL=scrobble-tracker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrobble-tracker.js","sourceRoot":"","sources":["../../../src/services/playback/scrobble-tracker.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/tools/handlers/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAG/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAI9C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/D;AAID,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwC;IACnE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IAEvC,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI;IAK5D,WAAW,IAAI,IAAI,EAAE;IAIf,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;CASpE;AA6BD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA4C3F"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/tools/handlers/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAG/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAI9C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/D;AAID,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwC;IACnE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IAEvC,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI;IAK5D,WAAW,IAAI,IAAI,EAAE;IAIf,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;CASpE;AA8BD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAqD3F"}
@@ -62,6 +62,7 @@ import { createLyricsToolCategory } from './lyrics-handlers.js';
62
62
  import { createTagsToolCategory } from './tag-handlers.js';
63
63
  import { createPlaybackToolCategory } from './playback-handlers.js';
64
64
  import { playbackEngine } from '../../services/playback/playback-engine.js';
65
+ import { ScrobbleTracker } from '../../services/playback/scrobble-tracker.js';
65
66
  // Main registration function
66
67
  export function registerTools(server, client, config) {
67
68
  const registry = new ToolRegistry();
@@ -89,6 +90,15 @@ export function registerTools(server, client, config) {
89
90
  // Configure the singleton engine with the loaded config so tools can
90
91
  // lazy-spawn mpv on first invocation.
91
92
  playbackEngine.configure(config);
93
+ // Auto-scrobble plays to Navidrome (Last.fm rules: now-playing on start,
94
+ // submission past 50% of duration or 4 min, whichever first; ≥30s
95
+ // tracks only). The tracker is constructed once and attached for the
96
+ // rest of the process lifetime — there is no explicit shutdown. On
97
+ // SIGINT/SIGTERM the engine closes its IPC socket (mpv keeps running,
98
+ // detached) and the tracker is torn down with the process; any
99
+ // in-flight /scrobble request is abandoned, which is acceptable per
100
+ // Last.fm best-effort semantics.
101
+ new ScrobbleTracker(client, playbackEngine).attach();
92
102
  registry.register('playback', createPlaybackToolCategory(client, config));
93
103
  }
94
104
  // Register MCP handlers
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/tools/handlers/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/tools/handlers/registry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "navidrome-mcp",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "MCP server for interacting with Navidrome music server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",