roon-mcp 0.1.0 → 0.2.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +61 -11
  3. package/dist/PlaybackService.d.ts +16 -0
  4. package/dist/PlaybackService.js +59 -2
  5. package/dist/PlaybackService.js.map +1 -1
  6. package/dist/PlaybackService.test.js +63 -0
  7. package/dist/PlaybackService.test.js.map +1 -1
  8. package/dist/RoonClient.d.ts +21 -0
  9. package/dist/RoonClient.js +35 -0
  10. package/dist/RoonClient.js.map +1 -1
  11. package/dist/RoonMcpServer.d.ts +3 -1
  12. package/dist/RoonMcpServer.js +215 -39
  13. package/dist/RoonMcpServer.js.map +1 -1
  14. package/dist/SearchService.d.ts +12 -0
  15. package/dist/SearchService.js +63 -1
  16. package/dist/SearchService.js.map +1 -1
  17. package/dist/SearchService.test.js +79 -4
  18. package/dist/SearchService.test.js.map +1 -1
  19. package/dist/TransportService.d.ts +73 -0
  20. package/dist/TransportService.js +248 -0
  21. package/dist/TransportService.js.map +1 -0
  22. package/dist/TransportService.test.d.ts +1 -0
  23. package/dist/TransportService.test.js +331 -0
  24. package/dist/TransportService.test.js.map +1 -0
  25. package/dist/ZoneService.d.ts +1 -0
  26. package/dist/ZoneService.js +9 -0
  27. package/dist/ZoneService.js.map +1 -1
  28. package/dist/ZoneService.test.js +1 -0
  29. package/dist/ZoneService.test.js.map +1 -1
  30. package/dist/ZoneSubscription.d.ts +67 -0
  31. package/dist/ZoneSubscription.js +187 -0
  32. package/dist/ZoneSubscription.js.map +1 -0
  33. package/dist/ZoneSubscription.test.d.ts +1 -0
  34. package/dist/ZoneSubscription.test.js +185 -0
  35. package/dist/ZoneSubscription.test.js.map +1 -0
  36. package/dist/index.js +3 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/types.d.ts +39 -1
  39. package/dist/types.js.map +1 -1
  40. package/package.json +1 -1
@@ -21,13 +21,15 @@ export class RoonMcpServer {
21
21
  search;
22
22
  tracks;
23
23
  playback;
24
+ transport;
24
25
  server;
25
- constructor(roon, zones, search, tracks, playback) {
26
+ constructor(roon, zones, search, tracks, playback, transport) {
26
27
  this.roon = roon;
27
28
  this.zones = zones;
28
29
  this.search = search;
29
30
  this.tracks = tracks;
30
31
  this.playback = playback;
32
+ this.transport = transport;
31
33
  this.server = new McpServer({
32
34
  name: "roon-mcp",
33
35
  version: "0.1.0",
@@ -37,8 +39,12 @@ export class RoonMcpServer {
37
39
  registerTools() {
38
40
  this.server.registerTool("list_zones", {
39
41
  title: "List Roon zones",
40
- description: "List the playable zones/outputs exposed by the paired Roon Core. " +
41
- "Returns each zone's id, display name, playback state, and output ids.",
42
+ description: "Use this when the user asks which rooms, speakers, or outputs Roon can " +
43
+ "play to, or before starting playback when the target zone is unclear " +
44
+ "(e.g. \"what zones are on?\", \"play in the kitchen\", \"which speaker is " +
45
+ "in the office?\"). Lists every zone/output the paired Core exposes with " +
46
+ "its id, display name, current playback state, and output ids. Call this " +
47
+ "first if no zone is obvious and ROON_DEFAULT_ZONE is not set.",
42
48
  inputSchema: {},
43
49
  }, async () => {
44
50
  try {
@@ -52,24 +58,38 @@ export class RoonMcpServer {
52
58
  });
53
59
  this.server.registerTool("search_music", {
54
60
  title: "Search Roon music",
55
- description: "Resolve a text query into ranked Roon browse candidates. Optionally " +
56
- "filter by item type (artist, album, track, genre, playlist, radio); " +
57
- "for non-genre types, an empty typed search broadens to all categories. " +
58
- "type:\"genre\" is special: genres don't appear in Roon's flat search, so " +
59
- "the server walks the dedicated Genres tree and returns the nearest-match " +
60
- "genre nodes (with their parent path in the subtitle) without broadening — " +
61
- "e.g. \"Psychedelic Trance\" yields \"Psytrance\"/\"Trance\". Set " +
62
- "includeStreaming:true (only meaningful for type:\"genre\") to also pull a " +
63
- "track mix from streaming services (e.g. TIDAL): the server takes the " +
64
- "genre-relevant albums and samples tracks across them, so library genre " +
65
- "nodes are listed first and ready-to-play streaming tracks appended after. " +
66
- "Returns opaque, session-scoped item keys for use by playback tools.",
61
+ description: "Use this when the user names music to find an artist, album, track, " +
62
+ "playlist, radio station, or genre (e.g. \"find Tycho\", \"look up the " +
63
+ "album In Rainbows\", \"play some Psytrance\", \"anything by Ryuichi " +
64
+ "Sakamoto?\"). Resolves a free-text query into ranked Roon browse " +
65
+ "candidates. Optionally restrict to one item type (artist, album, track, " +
66
+ "genre, playlist, radio); for non-genre types, an empty typed search " +
67
+ "broadens to all categories. type:\"genre\" is special — genres don't " +
68
+ "appear in Roon's flat search, so the server walks the dedicated Genres " +
69
+ "tree and returns the nearest-match genre nodes (with parent path in the " +
70
+ "subtitle) without broadening; e.g. \"Psychedelic Trance\" yields " +
71
+ "\"Psytrance\"/\"Trance\". Set includeStreaming:true (meaningful for " +
72
+ "type:\"genre\" and type:\"artist\") to also pull a track mix from " +
73
+ "streaming services (e.g. TIDAL): for a genre the server takes the " +
74
+ "genre-relevant albums and samples tracks across them; for an artist " +
75
+ "the server runs a track search and filters to entries by that artist. " +
76
+ "Library candidates come first, then ready-to-play streaming tracks " +
77
+ "(each a `track` candidate, source group `Streaming`) appended after. " +
78
+ "An artist with no library content (e.g. subtitle \"0 Albums\") is " +
79
+ "reported via the result's `message` so the agent can opt in. Returns " +
80
+ "opaque, session-scoped item keys for use by the playback tools — pair " +
81
+ "with get_tracks_for to expand, then play_now or enqueue_and_play.",
67
82
  inputSchema: {
68
- query: z.string().min(1).describe("Free-text search, e.g. 'Dark Ambient' or 'Tycho'."),
83
+ query: z
84
+ .string()
85
+ .min(1)
86
+ .describe("What to search for — an artist, album, track, playlist, radio, or " +
87
+ "genre name (e.g. 'Tycho', 'In Rainbows', 'Dark Ambient')."),
69
88
  type: z
70
89
  .enum(MUSIC_ITEM_TYPES)
71
90
  .optional()
72
- .describe("Restrict results to this item type."),
91
+ .describe("Restrict the search to one item type. Omit to broaden across all " +
92
+ "non-genre categories. Use 'genre' for music-genre lookups."),
73
93
  limit: z
74
94
  .number()
75
95
  .int()
@@ -80,9 +100,11 @@ export class RoonMcpServer {
80
100
  includeStreaming: z
81
101
  .boolean()
82
102
  .optional()
83
- .describe("Also pull a track mix from streaming services. " +
84
- "Only applies when type is \"genre\": library genre nodes come first, " +
85
- "then tracks sampled across genre-relevant streaming albums. Default false."),
103
+ .describe("Meaningful for type 'genre' and type 'artist': also pull a track " +
104
+ "mix from streaming services (e.g. TIDAL). For a genre, library " +
105
+ "genre nodes come first, then sampled streaming tracks. For an " +
106
+ "artist, library candidates come first, then streaming tracks by " +
107
+ "that artist. Default false (library only)."),
86
108
  },
87
109
  }, async (args) => {
88
110
  try {
@@ -95,16 +117,21 @@ export class RoonMcpServer {
95
117
  });
96
118
  this.server.registerTool("get_tracks_for", {
97
119
  title: "Expand a Roon item into tracks",
98
- description: "Expand an artist, album, genre, or playlist candidate into concrete " +
99
- "playable tracks. Pass an itemKey from a recent search_music result. " +
100
- "Returns track candidates with session-scoped item keys (use them " +
101
- "promptly with enqueue_and_play). Non-expandable items return empty " +
102
- "tracks with a skipped reason rather than an error.",
120
+ description: "Use this after search_music when the user wants a concrete list of " +
121
+ "songs to preview tracks, build a queue, or pick one to start with " +
122
+ "(e.g. \"what tracks are on this album?\", \"give me 5 tracks of " +
123
+ "Dark Ambient\", \"what's on this playlist?\"). Expands an artist, " +
124
+ "album, genre, or playlist candidate into concrete playable tracks. " +
125
+ "Pass an itemKey from a recent search_music result. Returns track " +
126
+ "candidates with session-scoped item keys (use them promptly with " +
127
+ "enqueue_and_play). Non-expandable items return empty tracks with a " +
128
+ "skipped reason rather than an error.",
103
129
  inputSchema: {
104
130
  itemKey: z
105
131
  .string()
106
132
  .min(1)
107
- .describe("Opaque item key from a recent search_music result."),
133
+ .describe("Item key to expand — from a recent search_music candidate (artist, " +
134
+ "album, genre, or playlist)."),
108
135
  limit: z
109
136
  .number()
110
137
  .int()
@@ -124,22 +151,30 @@ export class RoonMcpServer {
124
151
  });
125
152
  this.server.registerTool("play_now", {
126
153
  title: "Play an item now in a Roon zone",
127
- description: "Immediately play a single search candidate in the target zone. Pass an " +
128
- "itemKey from a recent search_music result (item keys are session-scoped, " +
129
- "so use a fresh one). zoneId is optional: omit it to use the configured " +
130
- "default zone (ROON_DEFAULT_ZONE) or the single/Office/playing fallback; " +
131
- "an ambiguous result returns ZONE_AMBIGUOUS so you can ask. Optionally " +
132
- "shuffle. Returns a PlaybackResult.",
154
+ description: "Use this when the user wants one specific thing playing right now " +
155
+ "an album, artist, playlist, genre mix, or single track (e.g. \"play " +
156
+ "Tycho\", \"put on In Rainbows\", \"start some Psytrance in the office\", " +
157
+ "\"play that track\"). Immediately plays a single search candidate in " +
158
+ "the target zone and replaces whatever was queued. Pass an itemKey from " +
159
+ "a recent search_music (or get_tracks_for) result — item keys are " +
160
+ "session-scoped, so use a fresh one. zoneId is optional: omit it to use " +
161
+ "ROON_DEFAULT_ZONE, or fall back to the only zone / an \"Office\" zone / " +
162
+ "the currently-playing zone; if it still can't decide it returns " +
163
+ "ZONE_AMBIGUOUS so the agent can ask the user or call list_zones. " +
164
+ "Optionally shuffle. Returns a PlaybackResult.",
133
165
  inputSchema: {
134
166
  zoneId: z
135
167
  .string()
136
168
  .min(1)
137
169
  .optional()
138
- .describe("Target zone id or output id (from list_zones). Omit to use the default zone."),
170
+ .describe("Target zone id or output id from list_zones (e.g. an id, or a name " +
171
+ "substring like 'Office'). Omit to use ROON_DEFAULT_ZONE or fall " +
172
+ "back automatically."),
139
173
  itemKey: z
140
174
  .string()
141
175
  .min(1)
142
- .describe("Opaque item key from a recent search_music result."),
176
+ .describe("What to play — item key from a recent search_music or get_tracks_for " +
177
+ "result (album, artist, playlist, genre, track, etc.)."),
143
178
  shuffle: z
144
179
  .boolean()
145
180
  .optional()
@@ -156,10 +191,15 @@ export class RoonMcpServer {
156
191
  });
157
192
  this.server.registerTool("enqueue_and_play", {
158
193
  title: "Build and start a curated Roon queue",
159
- description: "Build an ad-hoc queue from an ordered list of curated item keys and " +
160
- "start playback in the target zone. This replaces the zone's current " +
194
+ description: "Use this when the user wants a custom lineup a mix of artists, a " +
195
+ "shuffled selection across albums, a hand-picked set of tracks, or any " +
196
+ "time \"queue\", \"setlist\", \"mix of\", or \"play these in order\" comes " +
197
+ "up (e.g. \"queue up five Tycho tracks then some Boards of Canada\", " +
198
+ "\"shuffle 10 ambient tracks\", \"build a set: artist A, then B, then C\"). " +
199
+ "Builds an ad-hoc queue from an ordered list of curated item keys and " +
200
+ "starts playback in the target zone. This replaces the zone's current " +
161
201
  "queue: the first playable item starts immediately (Play Now), the rest " +
162
- "are appended in order. Pass itemKeys from recent get_tracks_for/" +
202
+ "are appended in order. Pass itemKeys from recent get_tracks_for / " +
163
203
  "search_music results (use them promptly — they are session-scoped). " +
164
204
  "zoneId is optional (omit to use the default zone; see play_now). " +
165
205
  "Optionally shuffle. Returns a PlaybackResult with queued/skipped counts " +
@@ -169,11 +209,13 @@ export class RoonMcpServer {
169
209
  .string()
170
210
  .min(1)
171
211
  .optional()
172
- .describe("Target zone id or output id (from list_zones). Omit to use the default zone."),
212
+ .describe("Target zone id or output id from list_zones. Omit to use the default " +
213
+ "zone (see play_now)."),
173
214
  itemKeys: z
174
215
  .array(z.string().min(1))
175
216
  .min(1)
176
- .describe("Ordered item keys to queue (from get_tracks_for/search_music)."),
217
+ .describe("Ordered item keys to queue, from recent get_tracks_for or " +
218
+ "search_music results (tracks, albums, artists, etc.)."),
177
219
  shuffle: z
178
220
  .boolean()
179
221
  .optional()
@@ -188,6 +230,140 @@ export class RoonMcpServer {
188
230
  return toToolError(err);
189
231
  }
190
232
  });
233
+ this.server.registerTool("now_playing", {
234
+ title: "Get the currently-playing track in a Roon zone",
235
+ description: "Use this when the user asks what is currently playing, what song is " +
236
+ "on, who's singing, what just started, or wants a snapshot before " +
237
+ "skipping or pausing (e.g. \"what's playing?\", \"what's playing in " +
238
+ "the kitchen?\", \"who's this?\", \"what's the current track?\", " +
239
+ "\"what song is this?\"). Returns a structured snapshot: zone id and " +
240
+ "name, playback state (playing/paused/loading/stopped), title, " +
241
+ "artist, album, and the current seek position when available. " +
242
+ "`title`/`artist`/`album` are undefined when nothing is playing. " +
243
+ "Call this before pause/skip/volume changes when the user hasn't " +
244
+ "named a zone — it confirms where to act and what the state is. " +
245
+ "zoneId is optional: omit to use ROON_DEFAULT_ZONE, or fall back " +
246
+ "to the only zone / an \"Office\" zone / the currently-playing zone; " +
247
+ "if it still can't decide it returns ZONE_AMBIGUOUS so the agent " +
248
+ "can ask the user or call list_zones.",
249
+ inputSchema: {
250
+ zoneId: z
251
+ .string()
252
+ .min(1)
253
+ .optional()
254
+ .describe("Target zone id or output id from list_zones (e.g. an id, or a " +
255
+ "name substring like 'Office'). Omit to use ROON_DEFAULT_ZONE " +
256
+ "or fall back automatically."),
257
+ },
258
+ }, async (args) => {
259
+ try {
260
+ const info = await this.transport.getNowPlaying(args.zoneId);
261
+ return structured(info);
262
+ }
263
+ catch (err) {
264
+ return toToolError(err);
265
+ }
266
+ });
267
+ this.server.registerTool("control_playback", {
268
+ title: "Run a Roon transport verb (pause/resume/next/previous/stop)",
269
+ description: "Use this for the common transport verbs — pause, resume, skip, " +
270
+ "next track, previous track, stop (e.g. \"pause\", \"pause the " +
271
+ "kitchen\", \"skip\", \"next track\", \"next song\", \"play the " +
272
+ "next song\", \"resume\", \"resume in the office\", \"stop\", " +
273
+ "\"stop the music\"). Runs one transport verb against the resolved " +
274
+ "zone. For \"louder\"/\"softer\" without a number, call now_playing " +
275
+ "first to read the current state, then set_volume with a target " +
276
+ "percent — this tool is verbs only. zoneId is optional and resolves " +
277
+ "like now_playing. Returns the resolved zoneId, the action taken, " +
278
+ "and the resulting playback state.",
279
+ inputSchema: {
280
+ zoneId: z
281
+ .string()
282
+ .min(1)
283
+ .optional()
284
+ .describe("Target zone id or output id from list_zones. Omit to use " +
285
+ "ROON_DEFAULT_ZONE or fall back automatically (see now_playing)."),
286
+ action: z
287
+ .enum(["pause", "resume", "next", "previous", "stop"])
288
+ .describe("Transport verb to run: 'pause' to stop playback, 'resume' to " +
289
+ "restart it, 'next' to skip to the next track, 'previous' to " +
290
+ "go back, 'stop' to release the audio device."),
291
+ },
292
+ }, async (args) => {
293
+ try {
294
+ const result = await this.transport.control(args.zoneId, args.action);
295
+ return structured(result);
296
+ }
297
+ catch (err) {
298
+ return toToolError(err);
299
+ }
300
+ });
301
+ this.server.registerTool("set_volume", {
302
+ title: "Set a Roon zone's volume to a target percent",
303
+ description: "Use this when the user wants to change how loud a zone is — turn " +
304
+ "it up, turn it down, set the volume, or pick a level for a " +
305
+ "specific room (e.g. \"turn it up to 60\", \"volume to 30 in the " +
306
+ "office\", \"set the kitchen to 50\", \"louder\" — see note). Sets " +
307
+ "the zone's volume to `level` percent (0 = silent, 100 = max). The " +
308
+ "server rescales to each output's native range, so a single value " +
309
+ "works across mixed devices in a grouped zone. Incremental " +
310
+ "outputs (IR blasters with no numeric range) are reported as " +
311
+ "skipped rather than guessed at. zoneId is optional and resolves " +
312
+ "like now_playing. NOTE on relative changes (\"louder\" / \"softer\" " +
313
+ "without a number): this tool is absolute — read the current " +
314
+ "state with now_playing isn't enough on its own (volume isn't " +
315
+ "exposed there), so for relative changes, ask the user for a " +
316
+ "target percent or apply a reasonable default delta.",
317
+ inputSchema: {
318
+ zoneId: z
319
+ .string()
320
+ .min(1)
321
+ .optional()
322
+ .describe("Target zone id or output id from list_zones. Omit to use " +
323
+ "ROON_DEFAULT_ZONE or fall back automatically."),
324
+ level: z
325
+ .number()
326
+ .min(0)
327
+ .max(100)
328
+ .describe("Target volume in percent (0 = silent, 100 = max). Mapped to " +
329
+ "each output's native range."),
330
+ },
331
+ }, async (args) => {
332
+ try {
333
+ const result = await this.transport.setVolume(args.zoneId, args.level);
334
+ return structured(result);
335
+ }
336
+ catch (err) {
337
+ return toToolError(err);
338
+ }
339
+ });
340
+ this.server.registerTool("mute", {
341
+ title: "Mute or unmute a Roon zone",
342
+ description: "Use this when the user wants to mute or unmute a zone (e.g. " +
343
+ "\"mute\", \"mute the kitchen\", \"unmute\", \"silence the " +
344
+ "office\", \"stop the noise\"). Mutes (or unmutes) every output " +
345
+ "in the resolved zone. zoneId is optional and resolves like " +
346
+ "now_playing. `muted: true` mutes, `muted: false` unmutes.",
347
+ inputSchema: {
348
+ zoneId: z
349
+ .string()
350
+ .min(1)
351
+ .optional()
352
+ .describe("Target zone id or output id from list_zones. Omit to use " +
353
+ "ROON_DEFAULT_ZONE or fall back automatically."),
354
+ muted: z
355
+ .boolean()
356
+ .describe("`true` to mute, `false` to unmute."),
357
+ },
358
+ }, async (args) => {
359
+ try {
360
+ const result = await this.transport.mute(args.zoneId, args.muted);
361
+ return structured(result);
362
+ }
363
+ catch (err) {
364
+ return toToolError(err);
365
+ }
366
+ });
191
367
  }
192
368
  async start() {
193
369
  this.roon.start();
@@ -1 +1 @@
1
- {"version":3,"file":"RoonMcpServer.js","sourceRoot":"","sources":["../src/RoonMcpServer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RoonMcpServer.js","sourceRoot":"","sources":["../src/RoonMcpServer.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,18 @@ export declare class SearchService {
21
21
  * opt-in cost we accept for the streaming path.
22
22
  */
23
23
  private collectStreamingGenreTracks;
24
+ /**
25
+ * Artist discovery beyond the library. The library album search that
26
+ * collectStreamingGenreTracks leans on returns nothing for an artist that
27
+ * has been fully removed from the library, so we go one level shallower: a
28
+ * track search, filtered to entries whose subtitle (the track's artist in
29
+ * Roon's flat search) matches the queried artist. On a TIDAL Core this
30
+ * surfaces the artist's streaming catalog as ready-to-play tracks. The
31
+ * result is best-effort: a small fraction may be features or compilations
32
+ * — the agent's per-artist cap / dedupe (see README "Curation is
33
+ * agent-side") handles that.
34
+ */
35
+ private collectStreamingArtistTracks;
24
36
  private performSearch;
25
37
  /** Indices (into the top-level group list) to scan for the requested type. */
26
38
  private selectGroupIndices;
@@ -46,7 +46,24 @@ export class SearchService {
46
46
  }
47
47
  // Search builds its own item keys inside performSearch, so a stale-session
48
48
  // failure is recoverable with one reset-and-replay (see runExclusiveWithRetry).
49
- return this.browse.runExclusiveWithRetry(() => this.performSearch(input, limit));
49
+ const library = await this.browse.runExclusiveWithRetry(() => this.performSearch(input, limit));
50
+ // Artist search is library-only: when the artist has no library content
51
+ // (subtitle "0 Albums") or isn't in the library at all, optionally append a
52
+ // streaming track mix so the user request "play/queue <artist>" doesn't
53
+ // dead-end at get_tracks_for. Mirrors the genre streaming path's shape:
54
+ // library results first, streaming tracks appended with sourceGroup
55
+ // "Streaming". See collectStreamingArtistTracks.
56
+ if (input.type === "artist" && input.includeStreaming) {
57
+ const streaming = await this.collectStreamingArtistTracks(input.query, limit);
58
+ if (streaming.length > 0) {
59
+ const candidates = [...library.candidates, ...streaming];
60
+ const message = library.message
61
+ ? `${library.message} Also showing ${streaming.length} streaming track(s).`
62
+ : `No library artist matched "${input.query}"; showing streaming tracks.`;
63
+ return { query: input.query, candidates, broadened: library.broadened, message };
64
+ }
65
+ }
66
+ return library;
50
67
  }
51
68
  async searchGenres(query, limit, includeStreaming) {
52
69
  const libraryGenres = await this.genres.searchGenres(query, limit);
@@ -107,6 +124,42 @@ export class SearchService {
107
124
  }
108
125
  return out;
109
126
  }
127
+ /**
128
+ * Artist discovery beyond the library. The library album search that
129
+ * collectStreamingGenreTracks leans on returns nothing for an artist that
130
+ * has been fully removed from the library, so we go one level shallower: a
131
+ * track search, filtered to entries whose subtitle (the track's artist in
132
+ * Roon's flat search) matches the queried artist. On a TIDAL Core this
133
+ * surfaces the artist's streaming catalog as ready-to-play tracks. The
134
+ * result is best-effort: a small fraction may be features or compilations
135
+ * — the agent's per-artist cap / dedupe (see README "Curation is
136
+ * agent-side") handles that.
137
+ */
138
+ async collectStreamingArtistTracks(query, limit) {
139
+ const trackSearch = await this.browse.runExclusiveWithRetry(() => this.performSearch({ query, type: "track" }, limit));
140
+ const tracks = trackSearch.candidates.filter((c) => c.type === "track");
141
+ if (tracks.length === 0)
142
+ return [];
143
+ const q = normalize(query);
144
+ const matched = tracks.filter((t) => {
145
+ const artist = normalize(t.subtitle ?? "");
146
+ // Substring is enough: Roon track subtitles are the artist (sometimes
147
+ // "Artist / Album"); a fuller match doesn't help here. We fall back to
148
+ // the track's own title as a last resort so a query that exactly
149
+ // matches a single-song single (e.g. a self-titled track) still
150
+ // surfaces something.
151
+ return artist === q || artist.includes(q) || normalize(t.title) === q;
152
+ });
153
+ return matched.slice(0, limit).map((t) => ({
154
+ itemKey: t.itemKey,
155
+ title: t.title,
156
+ subtitle: t.subtitle,
157
+ type: "track",
158
+ score: t.score,
159
+ available: t.available,
160
+ sourceGroup: "Streaming",
161
+ }));
162
+ }
110
163
  async performSearch(input, limit) {
111
164
  // Roon only registers the search when `input` and `pop_all` ride in the
112
165
  // same browse call; submitting `input` after a separate reset yields a
@@ -148,6 +201,15 @@ export class SearchService {
148
201
  else if (ranked.length === 0) {
149
202
  message = "No results.";
150
203
  }
204
+ // When an artist search surfaces a candidate with no library content,
205
+ // surface that up front so the agent doesn't have to discover the dead
206
+ // end at get_tracks_for time. The streaming-artist fallback in
207
+ // searchMusic picks this up.
208
+ if (input.type === "artist" && /^\s*0\s+albums?\s*$/i.test(ranked[0]?.subtitle ?? "")) {
209
+ const name = ranked[0].title;
210
+ const hint = `Artist "${name}" has no library albums; pass includeStreaming:true to sample streaming tracks.`;
211
+ message = message ? `${message} ${hint}` : hint;
212
+ }
151
213
  return { query: input.query, candidates: ranked, broadened, message };
152
214
  }
153
215
  /** Indices (into the top-level group list) to scan for the requested type. */
@@ -1 +1 @@
1
- {"version":3,"file":"SearchService.js","sourceRoot":"","sources":["../src/SearchService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SearchService.js","sourceRoot":"","sources":["../src/SearchService.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAyB,MAAM,4BAA4B,CAAC;AACnF,OAAO,EACL,YAAY,GAKb,MAAM,YAAY,CAAC;AAEpB,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,4EAA4E;AAC5E,uDAAuD;AACvD,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB,uDAAuD;AACvD,MAAM,mBAAmB,GAAkC;IACzD,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,OAAO;IACf,MAAM,EAAE,OAAO;IACf,MAAM,EAAE,OAAO;IACf,SAAS,EAAE,UAAU;IACrB,QAAQ,EAAE,OAAO;IACjB,gBAAgB,EAAE,OAAO;CAC1B,CAAC;AAEF,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACnC,CAAC;AAED,6DAA6D;AAC7D,SAAS,gBAAgB,CAAC,KAAa;IACrC,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;IAC/D,OAAO,mBAAmB,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC;AACnD,CAAC;AAED,iFAAiF;AACjF,MAAM,OAAO,aAAa;IAEL;IACA;IACA;IAHnB,YACmB,MAA4B,EAC5B,MAAoB,EACpB,MAA6B;QAF7B,WAAM,GAAN,MAAM,CAAsB;QAC5B,WAAM,GAAN,MAAM,CAAc;QACpB,WAAM,GAAN,MAAM,CAAuB;IAC7C,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,KAAuB;QACvC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,aAAa,EAAE,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QAE7E,4EAA4E;QAC5E,2EAA2E;QAC3E,gDAAgD;QAChD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;QACvE,CAAC;QAED,2EAA2E;QAC3E,gFAAgF;QAChF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QAEhG,wEAAwE;QACxE,4EAA4E;QAC5E,wEAAwE;QACxE,wEAAwE;QACxE,oEAAoE;QACpE,iDAAiD;QACjD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,gBAAgB,EAAE,CAAC;YACtD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,4BAA4B,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAC9E,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzB,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,CAAC,UAAU,EAAE,GAAG,SAAS,CAAC,CAAC;gBACzD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;oBAC7B,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,iBAAiB,SAAS,CAAC,MAAM,sBAAsB;oBAC3E,CAAC,CAAC,8BAA8B,KAAK,CAAC,KAAK,8BAA8B,CAAC;gBAC5E,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC;YACnF,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,KAAK,CAAC,YAAY,CACxB,KAAa,EACb,KAAa,EACb,gBAA0B;QAE1B,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAEnE,IAAI,eAAe,GAAqB,EAAE,CAAC;QAC3C,IAAI,gBAAgB,EAAE,CAAC;YACrB,eAAe,GAAG,MAAM,IAAI,CAAC,2BAA2B,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,UAAU,GAAG,CAAC,GAAG,aAAa,EAAE,GAAG,eAAe,CAAC,CAAC;QAE1D,IAAI,OAA2B,CAAC;QAChC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,GAAG,mBAAmB,KAAK,IAAI,CAAC;QACzC,CAAC;aAAM,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO,GAAG,8BAA8B,KAAK,8BAA8B,CAAC;QAC9E,CAAC;aAAM,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9C,MAAM,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,OAAO,GAAG,2BAA2B,KAAK,4BAA4B,MAAM,GAAG,CAAC;QAClF,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAC1D,CAAC;IAED;;;;;;;;;OASG;IACK,KAAK,CAAC,2BAA2B,CACvC,KAAa,EACb,KAAa;QAEb,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAC/D,IAAI,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,CAAC,CACpD,CAAC;QACF,MAAM,MAAM,GAAG,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QACxE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEnC,8EAA8E;QAC9E,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QAEtD,MAAM,GAAG,GAAqB,EAAE,CAAC;QACjC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,GAAG,CAAC,MAAM,IAAI,KAAK;gBAAE,MAAM;YAC/B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC7F,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;gBACpC,IAAI,GAAG,CAAC,MAAM,IAAI,KAAK;oBAAE,MAAM;gBAC/B,GAAG,CAAC,IAAI,CAAC;oBACP,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK;oBACrC,IAAI,EAAE,OAAO;oBACb,yEAAyE;oBACzE,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,SAAS,EAAE,KAAK,CAAC,SAAS;oBAC1B,WAAW,EAAE,WAAW;iBACzB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;;;;;;;;;OAUG;IACK,KAAK,CAAC,4BAA4B,CACxC,KAAa,EACb,KAAa;QAEb,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAC/D,IAAI,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,CAAC,CACpD,CAAC;QACF,MAAM,MAAM,GAAG,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QACxE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAEnC,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QAC3B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YAClC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;YAC3C,sEAAsE;YACtE,uEAAuE;YACvE,iEAAiE;YACjE,gEAAgE;YAChE,sBAAsB;YACtB,OAAO,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;QAEH,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACzC,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,IAAI,EAAE,OAAgB;YACtB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,WAAW,EAAE,WAAW;SACzB,CAAC,CAAC,CAAC;IACN,CAAC;IAEO,KAAK,CAAC,aAAa,CACzB,KAAuB,EACvB,KAAa;QAEb,wEAAwE;QACxE,uEAAuE;QACvE,gDAAgD;QAChD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;YACzC,SAAS,EAAE,gBAAgB;YAC3B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACnC,OAAO;gBACL,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,UAAU,EAAE,EAAE;gBACd,SAAS,EAAE,KAAK;gBAChB,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,6BAA6B;aAC5D,CAAC;QACJ,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YACxC,SAAS,EAAE,gBAAgB;YAC3B,MAAM,EAAE,CAAC;YACT,KAAK,EAAE,gBAAgB;SACxB,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACrD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;QAC1F,CAAC;QAED,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,UAAU,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAC3C,KAAK,CAAC,KAAK,EACX,MAAM,EACN,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,EAC3C,KAAK,CACN,CAAC;QAEF,gEAAgE;QAChE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAC1C,SAAS,GAAG,IAAI,CAAC;YACjB,UAAU,GAAG,MAAM,IAAI,CAAC,iBAAiB,CACvC,KAAK,CAAC,KAAK,EACX,MAAM,EACN,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAC3B,KAAK,CACN,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAExF,IAAI,OAA2B,CAAC;QAChC,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,GAAG,OAAO,KAAK,CAAC,IAAI,yCAAyC,CAAC;QACvE,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,GAAG,aAAa,CAAC;QAC1B,CAAC;QAED,sEAAsE;QACtE,uEAAuE;QACvE,+DAA+D;QAC/D,6BAA6B;QAC7B,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,sBAAsB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;YACtF,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC;YAC9B,MAAM,IAAI,GAAG,WAAW,IAAI,iFAAiF,CAAC;YAC9G,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAClD,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;IACxE,CAAC;IAED,8EAA8E;IACtE,kBAAkB,CAAC,MAAoB,EAAE,IAAoB;QACnE,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI;YAAE,OAAO,GAAG,CAAC;QACtB,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAE,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;IAC5E,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAC7B,KAAa,EACb,MAAoB,EACpB,OAAiB,EACjB,KAAa;QAEb,MAAM,GAAG,GAAqB,EAAE,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;YACzB,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAChG,wEAAwE;gBACxE,0DAA0D;gBAC1D,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;oBAAE,SAAS;gBACpC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;oBACpC,SAAS,EAAE,gBAAgB;oBAC3B,MAAM,EAAE,CAAC;oBACT,KAAK,EAAE,KAAK;iBACb,CAAC,CAAC;gBACH,wEAAwE;gBACxE,yEAAyE;gBACzE,uEAAuE;gBACvE,kEAAkE;gBAClE,8BAA8B;gBAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;gBACnD,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;oBAC3B,GAAG,CAAC,IAAI,CAAC;wBACP,OAAO,EAAE,aAAa,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;wBAC1C,KAAK,EAAE,IAAI,CAAC,KAAK;wBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;wBACvB,IAAI;wBACJ,KAAK,EAAE,CAAC;wBACR,SAAS,EAAE,IAAI;wBACf,WAAW,EAAE,KAAK,CAAC,KAAK;qBACzB,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;gBACH,oEAAoE;gBACpE,qCAAqC;gBACrC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;YAC3E,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,sDAAsD;gBACtD,IAAI,GAAG,YAAY,YAAY,IAAI,GAAG,CAAC,IAAI,KAAK,kBAAkB;oBAAE,SAAS;gBAC7E,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAEO,cAAc,CACpB,UAA4B,EAC5B,KAAa,EACb,IAAoB;QAEpB,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QAC3B,KAAK,MAAM,CAAC,IAAI,UAAU;YAAE,CAAC,CAAC,KAAK,GAAG,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;QACjE,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAC3D,CAAC;CACF;AAED,SAAS,cAAc,CAAC,CAAiB,EAAE,CAAS,EAAE,IAAoB;IACxE,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,KAAa,CAAC;IAClB,IAAI,KAAK,KAAK,CAAC;QAAE,KAAK,GAAG,GAAG,CAAC;SACxB,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,KAAK,GAAG,GAAG,CAAC;SACrC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,KAAK,GAAG,GAAG,CAAC;;QACnC,KAAK,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC;IAEjD,IAAI,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI;QAAE,KAAK,IAAI,UAAU,CAAC;IACjD,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,YAAY,CAAC,KAAa,EAAE,KAAa;IAChD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAChE,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAClE,OAAO,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC;AACnC,CAAC"}
@@ -162,10 +162,85 @@ test("includeStreaming samples a track mix across genre-relevant albums", async
162
162
  }));
163
163
  assert.ok(albumIndices.size >= 2);
164
164
  });
165
- test("includeStreaming is ignored for non-genre searches", async () => {
166
- const svc = buildService([ARTISTS]);
167
- const out = await svc.searchMusic({ query: "Tycho", type: "artist", includeStreaming: true });
168
- assert.ok(out.candidates.every((c) => c.type === "artist"));
165
+ test("includeStreaming for type:artist appends streaming tracks by that artist", async () => {
166
+ // Mirrors the live "Helene Fischer" scenario from issue #2: the artist
167
+ // exists in the library with subtitle "0 Albums", and streaming-side tracks
168
+ // for the same artist surface from a track search. The artist search
169
+ // returns the (empty) library node; the streaming path runs a track search
170
+ // filtered to that artist's subtitle.
171
+ const artists = {
172
+ title: "Artists",
173
+ key: "g:artists",
174
+ items: [{ ...item("Helene Fischer", "a:hf"), subtitle: "0 Albums" }],
175
+ };
176
+ const tracks = {
177
+ title: "Tracks",
178
+ key: "g:tracks",
179
+ items: [
180
+ { title: "Atemlos durch die Nacht", item_key: "tk:1", subtitle: "Helene Fischer" },
181
+ { title: "Herzbeben", item_key: "tk:2", subtitle: "Helene Fischer" },
182
+ { title: "Other Artist Song", item_key: "tk:3", subtitle: "Other Artist" },
183
+ ],
184
+ };
185
+ const svc = buildService([artists, tracks]);
186
+ const out = await svc.searchMusic({
187
+ query: "Helene Fischer",
188
+ type: "artist",
189
+ includeStreaming: true,
190
+ });
191
+ // Library artist comes first, then streaming tracks by the same artist.
192
+ // Other-artist entries are filtered out by the subtitle match in
193
+ // collectStreamingArtistTracks.
194
+ assert.equal(out.candidates[0]?.title, "Helene Fischer");
195
+ assert.equal(out.candidates[0]?.sourceGroup, "Artists");
196
+ const streaming = out.candidates.filter((c) => c.sourceGroup === "Streaming");
197
+ assert.equal(streaming.length, 2);
198
+ assert.ok(streaming.every((c) => c.type === "track"));
199
+ assert.ok(streaming.every((c) => c.subtitle === "Helene Fischer"));
200
+ assert.ok(streaming.every((c) => c.title !== "Other Artist Song"));
201
+ assert.match(out.message ?? "", /no library albums/i);
202
+ assert.match(out.message ?? "", /includeStreaming/i);
203
+ });
204
+ test("an artist with 0 Albums surfaces a hint to pass includeStreaming", async () => {
205
+ const empty = {
206
+ title: "Artists",
207
+ key: "g:artists",
208
+ items: [item("Helene Fischer", "a:hf")],
209
+ };
210
+ // The fake's `item()` doesn't set a subtitle; override to "0 Albums" so the
211
+ // diagnostic in performSearch fires.
212
+ empty.items[0].subtitle = "0 Albums";
213
+ const svc = buildService([empty]);
214
+ const out = await svc.searchMusic({ query: "Helene Fischer", type: "artist" });
215
+ assert.equal(out.candidates[0]?.title, "Helene Fischer");
216
+ assert.match(out.message ?? "", /no library albums/i);
217
+ assert.match(out.message ?? "", /includeStreaming/i);
218
+ });
219
+ test("streaming-artist results are capped by limit", async () => {
220
+ const artists = {
221
+ title: "Artists",
222
+ key: "g:artists",
223
+ items: [{ ...item("Tycho", "a:tycho"), subtitle: "0 Albums" }],
224
+ };
225
+ const tracks = {
226
+ title: "Tracks",
227
+ key: "g:tracks",
228
+ items: Array.from({ length: 10 }, (_, i) => ({
229
+ title: `Song ${i}`,
230
+ item_key: `tk:${i}`,
231
+ subtitle: "Tycho",
232
+ })),
233
+ };
234
+ const svc = buildService([artists, tracks]);
235
+ const out = await svc.searchMusic({ query: "Tycho", type: "artist", includeStreaming: true, limit: 3 });
236
+ // 1 library artist + up to 3 streaming tracks. The streaming slice is
237
+ // capped independently of the library slice (the limit is a per-section
238
+ // cap on the streaming-track result, mirroring the genre path's per-album
239
+ // budget).
240
+ const streaming = out.candidates.filter((c) => c.sourceGroup === "Streaming");
241
+ assert.ok(streaming.length <= 3, `expected ≤3 streaming candidates, got ${streaming.length}`);
242
+ assert.equal(out.candidates[0]?.sourceGroup, "Artists");
243
+ assert.equal(out.candidates[0]?.title, "Tycho");
169
244
  });
170
245
  test("limit caps the number of returned candidates", async () => {
171
246
  const many = {