@rocksky/sdk 0.7.2 → 0.8.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rocksky/sdk",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "TypeScript SDK for Rocksky — built on atcute: AppView reads, AT Protocol PDS writes (scrobble, like, follow, shout), a local dedup index, and Jetstream real-time sync.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "README.md"
30
30
  ],
31
31
  "scripts": {
32
- "build": "bun build ./src/index.ts --outdir ./dist --target node --format esm --external none && bun run build:types",
32
+ "build": "bun build ./src/index.ts --outdir ./dist --target node --format esm --packages external && bun run build:types",
33
33
  "build:types": "tsc -p tsconfig.build.json",
34
34
  "typecheck": "tsc --noEmit",
35
35
  "test": "bun test",
package/src/agent.ts CHANGED
@@ -16,6 +16,7 @@ import type {
16
16
  ArtistRecord,
17
17
  ActorTrackView,
18
18
  ScrobbleRecord,
19
+ ShoutGif,
19
20
  SongRecord,
20
21
  } from "./generated/types.js";
21
22
  import type { IndexStats, RockskyIndex } from "./dedup.js";
@@ -303,23 +304,33 @@ export class Agent {
303
304
  return this.create(C_FOLLOW, { subject: did, createdAt: nowISO() });
304
305
  }
305
306
 
306
- /** Post a shout on a subject. Returns the shout URI. */
307
- shout(subjectUri: string, subjectCid: string, message: string): Promise<string> {
308
- return this.create(C_SHOUT, { subject: { uri: subjectUri, cid: subjectCid }, message, createdAt: nowISO() });
307
+ /** Post a shout on a subject. Returns the shout URI. Pass at least one of
308
+ * `message` / `gif` — `message` may be omitted when a GIF/sticker/clip is
309
+ * attached. */
310
+ shout(subjectUri: string, subjectCid: string, message?: string, gif?: ShoutGif): Promise<string> {
311
+ return this.create(C_SHOUT, {
312
+ subject: { uri: subjectUri, cid: subjectCid },
313
+ ...(message !== undefined ? { message } : {}),
314
+ ...(gif ? { gif } : {}),
315
+ createdAt: nowISO(),
316
+ });
309
317
  }
310
318
 
311
- /** Reply to a shout, with a parent strong-ref. */
319
+ /** Reply to a shout, with a parent strong-ref. Pass at least one of `message`
320
+ * / `gif` — `message` may be omitted when a GIF/sticker/clip is attached. */
312
321
  replyShout(
313
322
  subjectUri: string,
314
323
  subjectCid: string,
315
324
  parentUri: string,
316
325
  parentCid: string,
317
- message: string,
326
+ message?: string,
327
+ gif?: ShoutGif,
318
328
  ): Promise<string> {
319
329
  return this.create(C_SHOUT, {
320
330
  subject: { uri: subjectUri, cid: subjectCid },
321
331
  parent: { uri: parentUri, cid: parentCid },
322
- message,
332
+ ...(message !== undefined ? { message } : {}),
333
+ ...(gif ? { gif } : {}),
323
334
  createdAt: nowISO(),
324
335
  });
325
336
  }
package/src/client.ts CHANGED
@@ -6,14 +6,48 @@ import type {
6
6
  ActorProfileViewBasic,
7
7
  ActorProfileViewDetailed,
8
8
  AlbumViewBasic,
9
+ AlbumViewDetailed,
9
10
  ArtistViewBasic,
11
+ ArtistViewDetailed,
12
+ ChartsView,
13
+ FeedGeneratorsView,
14
+ FeedRecommendationsView,
15
+ FeedRecommendedAlbumsView,
16
+ FeedRecommendedArtistsView,
10
17
  FeedSearchResultsView,
18
+ FeedStoriesView,
19
+ FeedView,
11
20
  GetActorAlbumsOutput,
12
21
  GetActorArtistsOutput,
22
+ GetActorCompatibilityOutput,
23
+ GetActorNeighboursOutput,
24
+ GetActorPlaylistsOutput,
13
25
  GetActorScrobblesOutput,
26
+ GetAlbumShoutsOutput,
27
+ GetApikeysOutput,
28
+ GetArtistListenersOutput,
29
+ GetArtistRecentListenersOutput,
30
+ GetArtistShoutsOutput,
31
+ GetFeedGeneratorOutput,
32
+ GetMirrorSourcesOutput,
33
+ GetProfileShoutsOutput,
34
+ GetShoutRepliesOutput,
35
+ GetSongRecentListenersOutput,
36
+ GetTrackShoutsOutput,
37
+ GetUnreadCountOutput,
38
+ ListNotificationsOutput,
39
+ PlayerCurrentlyPlayingViewDetailed,
40
+ PlayerPlaybackQueueViewDetailed,
41
+ PlaylistGetPlaylistsOutput,
42
+ PlaylistViewDetailed,
43
+ RockboxSettingsView,
14
44
  ScrobbleViewBasic,
15
45
  SongViewBasic,
46
+ SongViewDetailed,
16
47
  StatsGlobalStatsView,
48
+ StatsView,
49
+ StatsWrappedView,
50
+ UpdateSeenOutput,
17
51
  } from "./generated/types.js";
18
52
 
19
53
  /**
@@ -303,51 +337,60 @@ export class RockskyClient {
303
337
  return this.query("app.rocksky.stats.getGlobalStats", {});
304
338
  }
305
339
 
306
- // ---- raw-JSON long tail: bespoke shapes returned as `unknown` ----------
340
+ // ---- detail reads (typed against the generated lexicon views) ----------
307
341
 
308
342
  /** A feed by its at:// URI (paginate via `cursor`). */
309
- feed(feed: string, limit = 50, cursor?: string): Promise<unknown> {
343
+ feed(feed: string, limit = 50, cursor?: string): Promise<FeedView> {
310
344
  return this.query("app.rocksky.feed.getFeed", { feed, limit, cursor });
311
345
  }
312
346
  /** A single album with its tracklist. */
313
- album(uri: string): Promise<unknown> {
347
+ album(uri: string): Promise<AlbumViewDetailed> {
314
348
  return this.query("app.rocksky.album.getAlbum", { uri });
315
349
  }
316
350
  /** A single artist with detail. */
317
- artist(uri: string): Promise<unknown> {
351
+ artist(uri: string): Promise<ArtistViewDetailed> {
318
352
  return this.query("app.rocksky.artist.getArtist", { uri });
319
353
  }
320
354
  /** Resolve full canonical metadata for a bare title + artist
321
355
  * (`app.rocksky.song.matchSong`); optionally anchor with `mbId` / `isrc`. */
322
- matchSong(title: string, artist: string, mbId?: string, isrc?: string): Promise<unknown> {
356
+ matchSong(title: string, artist: string, mbId?: string, isrc?: string): Promise<SongViewDetailed> {
323
357
  return this.query("app.rocksky.song.matchSong", { title, artist, mbId, isrc });
324
358
  }
325
359
  /** A single song by at:// `uri` (or by `mbid` / `isrc` / `spotifyId`). */
326
- song(opts: { uri?: string; mbid?: string; isrc?: string; spotifyId?: string }): Promise<unknown> {
360
+ song(opts: {
361
+ uri?: string;
362
+ mbid?: string;
363
+ isrc?: string;
364
+ spotifyId?: string;
365
+ }): Promise<SongViewDetailed> {
327
366
  return this.query("app.rocksky.song.getSong", opts);
328
367
  }
329
368
  /** An actor's playlists. */
330
- actorPlaylists(actor: string, limit = 50, offset = 0): Promise<unknown> {
369
+ actorPlaylists(actor: string, limit = 50, offset = 0): Promise<GetActorPlaylistsOutput> {
331
370
  return this.query("app.rocksky.actor.getActorPlaylists", { did: actor, limit, offset });
332
371
  }
333
372
  /** Actors with similar taste to `actor`. */
334
- neighbours(actor: string): Promise<unknown> {
373
+ neighbours(actor: string): Promise<GetActorNeighboursOutput> {
335
374
  return this.query("app.rocksky.actor.getActorNeighbours", { did: actor });
336
375
  }
337
376
  /** Music compatibility between the viewer and `actor` (auth). */
338
- compatibility(actor: string): Promise<unknown> {
377
+ compatibility(actor: string): Promise<GetActorCompatibilityOutput> {
339
378
  return this.query("app.rocksky.actor.getActorCompatibility", { did: actor });
340
379
  }
341
380
  /** An artist's all-time listeners. */
342
- artistListeners(uri: string, limit = 50, offset = 0): Promise<unknown> {
381
+ artistListeners(uri: string, limit = 50, offset = 0): Promise<GetArtistListenersOutput> {
343
382
  return this.query("app.rocksky.artist.getArtistListeners", { uri, limit, offset });
344
383
  }
345
384
  /** An artist's recent listeners. */
346
- artistRecentListeners(uri: string, limit = 50, offset = 0): Promise<unknown> {
385
+ artistRecentListeners(
386
+ uri: string,
387
+ limit = 50,
388
+ offset = 0,
389
+ ): Promise<GetArtistRecentListenersOutput> {
347
390
  return this.query("app.rocksky.artist.getArtistRecentListeners", { uri, limit, offset });
348
391
  }
349
392
  /** A song's recent listeners. */
350
- songRecentListeners(uri: string, limit = 50, offset = 0): Promise<unknown> {
393
+ songRecentListeners(uri: string, limit = 50, offset = 0): Promise<GetSongRecentListenersOutput> {
351
394
  return this.query("app.rocksky.song.getSongRecentListeners", { uri, limit, offset });
352
395
  }
353
396
  /** A scrobble time-series chart. Scope with any of `did` / `artisturi` /
@@ -360,91 +403,113 @@ export class RockskyClient {
360
403
  genre?: string;
361
404
  from?: string;
362
405
  to?: string;
363
- }): Promise<unknown> {
406
+ }): Promise<ChartsView> {
364
407
  return this.query("app.rocksky.charts.getScrobblesChart", opts);
365
408
  }
366
409
  /** List the available feed generators. */
367
- feedGenerators(size?: number): Promise<unknown> {
410
+ feedGenerators(size?: number): Promise<FeedGeneratorsView> {
368
411
  return this.query("app.rocksky.feed.getFeedGenerators", { size });
369
412
  }
370
413
  /** A single feed generator's record. */
371
- feedGenerator(feed: string): Promise<unknown> {
414
+ feedGenerator(feed: string): Promise<GetFeedGeneratorOutput> {
372
415
  return this.query("app.rocksky.feed.getFeedGenerator", { feed });
373
416
  }
374
417
  /** The stories row. */
375
- stories(size?: number, feed?: string, following?: boolean): Promise<unknown> {
418
+ stories(size?: number, feed?: string, following?: boolean): Promise<FeedStoriesView> {
376
419
  return this.query("app.rocksky.feed.getStories", { size, feed, following });
377
420
  }
378
421
  /** Track recommendations for `actor`. */
379
- recommendations(actor: string, limit?: number): Promise<unknown> {
422
+ recommendations(actor: string, limit?: number): Promise<FeedRecommendationsView> {
380
423
  return this.query("app.rocksky.feed.getRecommendations", { did: actor, limit });
381
424
  }
382
425
  /** Artist recommendations for `actor`. */
383
- artistRecommendations(actor: string, limit?: number): Promise<unknown> {
426
+ artistRecommendations(actor: string, limit?: number): Promise<FeedRecommendedArtistsView> {
384
427
  return this.query("app.rocksky.feed.getArtistRecommendations", { did: actor, limit });
385
428
  }
386
429
  /** Album recommendations for `actor`. */
387
- albumRecommendations(actor: string, limit?: number): Promise<unknown> {
430
+ albumRecommendations(actor: string, limit?: number): Promise<FeedRecommendedAlbumsView> {
388
431
  return this.query("app.rocksky.feed.getAlbumRecommendations", { did: actor, limit });
389
432
  }
390
433
  /** An actor's aggregate stats. */
391
- stats(actor: string): Promise<unknown> {
434
+ stats(actor: string): Promise<StatsView> {
392
435
  return this.query("app.rocksky.stats.getStats", { did: actor });
393
436
  }
394
437
  /** An actor's year-in-review. */
395
- wrapped(actor: string, year?: number): Promise<unknown> {
438
+ wrapped(actor: string, year?: number): Promise<StatsWrappedView> {
396
439
  return this.query("app.rocksky.stats.getWrapped", { did: actor, year });
397
440
  }
398
441
  /** The viewer's configured scrobble mirror sources (auth). */
399
- mirrorSources(): Promise<unknown> {
442
+ mirrorSources(): Promise<GetMirrorSourcesOutput> {
400
443
  return this.query("app.rocksky.mirror.getMirrorSources", {});
401
444
  }
402
445
  /** What `actor` is playing now. */
403
- currentlyPlaying(playerId?: string, actor?: string): Promise<unknown> {
446
+ currentlyPlaying(playerId?: string, actor?: string): Promise<PlayerCurrentlyPlayingViewDetailed> {
404
447
  return this.query("app.rocksky.player.getCurrentlyPlaying", { playerId, actor });
405
448
  }
406
449
  /** A player's playback queue. */
407
- playbackQueue(playerId: string): Promise<unknown> {
450
+ playbackQueue(playerId: string): Promise<PlayerPlaybackQueueViewDetailed> {
408
451
  return this.query("app.rocksky.player.getPlaybackQueue", { playerId });
409
452
  }
410
453
  /** What `actor` is playing now on Spotify. */
411
- spotifyCurrentlyPlaying(actor: string): Promise<unknown> {
454
+ spotifyCurrentlyPlaying(actor: string): Promise<PlayerCurrentlyPlayingViewDetailed> {
412
455
  return this.query("app.rocksky.spotify.getCurrentlyPlaying", { actor });
413
456
  }
414
457
  /** The playlist catalog. */
415
- playlists(limit = 50, offset = 0): Promise<unknown> {
458
+ playlists(limit = 50, offset = 0): Promise<PlaylistGetPlaylistsOutput> {
416
459
  return this.query("app.rocksky.playlist.getPlaylists", { limit, offset });
417
460
  }
418
461
  /** A single playlist with its items. */
419
- playlist(uri: string): Promise<unknown> {
462
+ playlist(uri: string): Promise<PlaylistViewDetailed> {
420
463
  return this.query("app.rocksky.playlist.getPlaylist", { uri });
421
464
  }
422
465
  /** Shouts on an album. */
423
- albumShouts(uri: string, limit = 50, offset = 0): Promise<unknown> {
466
+ albumShouts(uri: string, limit = 50, offset = 0): Promise<GetAlbumShoutsOutput> {
424
467
  return this.query("app.rocksky.shout.getAlbumShouts", { uri, limit, offset });
425
468
  }
426
469
  /** Shouts on an artist. */
427
- artistShouts(uri: string, limit = 50, offset = 0): Promise<unknown> {
470
+ artistShouts(uri: string, limit = 50, offset = 0): Promise<GetArtistShoutsOutput> {
428
471
  return this.query("app.rocksky.shout.getArtistShouts", { uri, limit, offset });
429
472
  }
430
473
  /** Shouts on a profile. */
431
- profileShouts(actor: string, limit = 50, offset = 0): Promise<unknown> {
474
+ profileShouts(actor: string, limit = 50, offset = 0): Promise<GetProfileShoutsOutput> {
432
475
  return this.query("app.rocksky.shout.getProfileShouts", { did: actor, limit, offset });
433
476
  }
434
477
  /** Shouts on a track. */
435
- trackShouts(uri: string): Promise<unknown> {
478
+ trackShouts(uri: string): Promise<GetTrackShoutsOutput> {
436
479
  return this.query("app.rocksky.shout.getTrackShouts", { uri });
437
480
  }
438
481
  /** Replies to a shout. */
439
- shoutReplies(uri: string, limit = 50, offset = 0): Promise<unknown> {
482
+ shoutReplies(uri: string, limit = 50, offset = 0): Promise<GetShoutRepliesOutput> {
440
483
  return this.query("app.rocksky.shout.getShoutReplies", { uri, limit, offset });
441
484
  }
442
485
  /** An actor's Rockbox EQ / audio settings. */
443
- audioSettings(actor: string): Promise<unknown> {
486
+ audioSettings(actor: string): Promise<RockboxSettingsView> {
444
487
  return this.query("app.rocksky.rockbox.getAudioSettings", { did: actor });
445
488
  }
446
489
  /** The viewer's API keys (auth). */
447
- apikeys(limit = 50, offset = 0): Promise<unknown> {
490
+ apikeys(limit = 50, offset = 0): Promise<GetApikeysOutput> {
448
491
  return this.query("app.rocksky.apikey.getApikeys", { limit, offset });
449
492
  }
493
+
494
+ // ---- notifications (auth-gated — construct the client with a token) -----
495
+
496
+ /** The authenticated viewer's unread-notification count. */
497
+ unreadCount(): Promise<GetUnreadCountOutput> {
498
+ return this.query("app.rocksky.notification.getUnreadCount", {});
499
+ }
500
+ /** The authenticated viewer's notifications, most recent first. `limit`
501
+ * defaults to 30 server-side; paginate via `cursor`. */
502
+ notifications(limit = 30, cursor?: string): Promise<ListNotificationsOutput> {
503
+ return this.query("app.rocksky.notification.listNotifications", { limit, cursor });
504
+ }
505
+ /** Mark notifications as viewed. Pass the notification `ids` to mark, or omit
506
+ * to mark **all** of the viewer's notifications. Returns the number remaining
507
+ * unread. */
508
+ async updateSeen(ids?: string[]): Promise<UpdateSeenOutput> {
509
+ const res = await this.rpc.post("app.rocksky.notification.updateSeen" as never, {
510
+ input: (ids && ids.length ? { ids } : {}) as never,
511
+ } as never);
512
+ if (!res.ok) throw new RockskyError(res.data);
513
+ return res.data as UpdateSeenOutput;
514
+ }
450
515
  }
@@ -103,6 +103,8 @@ export interface ActorTrackView {
103
103
  source?: string;
104
104
  /** MusicBrainz recording ID, if available. */
105
105
  recordingMbId?: string;
106
+ /** The track's position within its album, if known (>= 1). */
107
+ trackNumber?: number;
106
108
  }
107
109
 
108
110
  export interface AddDirectoryToQueueParams {
@@ -492,10 +494,6 @@ export interface DescribeFeedGeneratorOutput {
492
494
  feeds?: FeedUriView[];
493
495
  }
494
496
 
495
- export interface DescribeFeedGeneratorParams {
496
-
497
- }
498
-
499
497
  export interface DislikeShoutInput {
500
498
  /** The unique identifier of the shout to dislike */
501
499
  uri?: AtUri;
@@ -1407,6 +1405,11 @@ export interface GetTrackShoutsParams {
1407
1405
  uri: AtUri;
1408
1406
  }
1409
1407
 
1408
+ export interface GetUnreadCountOutput {
1409
+ /** The number of unread notifications. */
1410
+ count: number;
1411
+ }
1412
+
1410
1413
  export interface GetUserOutput {
1411
1414
 
1412
1415
  }
@@ -1570,6 +1573,19 @@ export interface LikeSongInput {
1570
1573
  uri?: AtUri;
1571
1574
  }
1572
1575
 
1576
+ export interface ListNotificationsOutput {
1577
+ notifications: NotificationView[];
1578
+ /** The number of unread notifications. */
1579
+ unreadCount: number;
1580
+ /** A cursor value to pass to subsequent calls to get the next page of results. */
1581
+ cursor?: string;
1582
+ }
1583
+
1584
+ export interface ListNotificationsParams {
1585
+ limit?: number;
1586
+ cursor?: string;
1587
+ }
1588
+
1573
1589
  export interface MatchSongParams {
1574
1590
  /** The title of the song to retrieve */
1575
1591
  title: string;
@@ -1596,6 +1612,38 @@ export interface MirrorSourceView {
1596
1612
  lastScrobbleSeenAt?: DateTime;
1597
1613
  }
1598
1614
 
1615
+ /** The user who triggered a notification. */
1616
+ export interface NotificationActor {
1617
+ /** The unique identifier of the actor. */
1618
+ id?: string;
1619
+ /** The decentralized identifier of the actor. */
1620
+ did?: Did;
1621
+ /** The handle of the actor. */
1622
+ handle?: AtIdentifier;
1623
+ /** The display name of the actor. */
1624
+ displayName?: string;
1625
+ /** The URL of the actor's avatar image. */
1626
+ avatar?: Uri;
1627
+ }
1628
+
1629
+ export interface NotificationView {
1630
+ /** The unique identifier of the notification. */
1631
+ id: string;
1632
+ /** The notification type: like_scrobble, follow, comment_scrobble, comment_profile, reply, or react_comment. */
1633
+ type: string;
1634
+ /** Whether the notification has been viewed. */
1635
+ read: boolean;
1636
+ /** When the notification was created. */
1637
+ createdAt: DateTime;
1638
+ /** The at-uri of the subject the notification relates to. */
1639
+ subjectUri?: string;
1640
+ /** The id of the related shout, if any. */
1641
+ shoutId?: string;
1642
+ /** The content of the related shout, if any. */
1643
+ shoutContent?: string;
1644
+ actor?: NotificationActor;
1645
+ }
1646
+
1599
1647
  export interface PingOutput {
1600
1648
 
1601
1649
  }
@@ -2135,13 +2183,41 @@ export interface ShoutAuthor {
2135
2183
  avatar?: Uri;
2136
2184
  }
2137
2185
 
2186
+ /** A GIF, sticker, or clip embedded in a shout. `url` may point at an image (GIF/WebP) or a video (MP4); the client decides how to render it from the file extension. */
2187
+ export interface ShoutGif {
2188
+ /** Direct URL of the animated GIF/MP4. */
2189
+ url: Uri;
2190
+ /** Smaller still/preview image URL. */
2191
+ previewUrl?: Uri;
2192
+ /** Alternative text describing the media. */
2193
+ alt?: string;
2194
+ /** The intrinsic width of the media in pixels. */
2195
+ width?: number;
2196
+ /** The intrinsic height of the media in pixels. */
2197
+ height?: number;
2198
+ }
2199
+
2200
+ /** A mention of another actor within the shout message, anchored to a UTF-8 byte range in the message. */
2201
+ export interface ShoutMention {
2202
+ /** The DID of the mentioned actor. */
2203
+ did: Did;
2204
+ /** Inclusive UTF-8 byte offset of the mention start. */
2205
+ byteStart: number;
2206
+ /** Exclusive UTF-8 byte offset of the mention end. */
2207
+ byteEnd: number;
2208
+ }
2209
+
2138
2210
  export interface ShoutRecord {
2139
- /** The message of the shout. */
2140
- message: string;
2211
+ /** The message of the shout. Optional when a gif/sticker/clip is attached. */
2212
+ message?: string;
2141
2213
  /** The date when the shout was created. */
2142
2214
  createdAt: DateTime;
2143
2215
  parent?: StrongRef;
2144
2216
  subject: StrongRef;
2217
+ /** An attached GIF, sticker, or clip (e.g. from KLIPY). */
2218
+ gif?: ShoutGif;
2219
+ /** Mentions of other actors within the message, anchored to UTF-8 byte ranges. */
2220
+ facets?: ShoutMention[];
2145
2221
  }
2146
2222
 
2147
2223
  export interface ShoutView {
@@ -2155,6 +2231,10 @@ export interface ShoutView {
2155
2231
  createdAt?: DateTime;
2156
2232
  /** The author of the shout. */
2157
2233
  author?: ShoutAuthor;
2234
+ /** An attached GIF, sticker, or clip. */
2235
+ gif?: ShoutGif;
2236
+ /** Mentions of other actors within the message, anchored to UTF-8 byte ranges. */
2237
+ facets?: ShoutMention[];
2158
2238
  }
2159
2239
 
2160
2240
  export interface SongFirstScrobbleView {
@@ -2177,6 +2257,34 @@ export interface SongGetSongParams {
2177
2257
  spotifyId?: string;
2178
2258
  }
2179
2259
 
2260
+ /** A ranked candidate match for a song from an external metadata provider. */
2261
+ export interface SongMatchView {
2262
+ /** The provider's numeric identifier for the matched track. */
2263
+ id?: number;
2264
+ /** The title of the matched track. */
2265
+ title?: string;
2266
+ /** The artist of the matched track. */
2267
+ artist?: string;
2268
+ /** The album of the matched track. */
2269
+ album?: string;
2270
+ /** The URL of the matched track's album art image. */
2271
+ albumArt?: Uri;
2272
+ /** The International Standard Recording Code (ISRC) of the matched track. */
2273
+ isrc?: string;
2274
+ /** The duration of the matched track in milliseconds. */
2275
+ durationMs?: number;
2276
+ /** A URL to the matched track on the provider. */
2277
+ link?: Uri;
2278
+ /** A URL to a short audio preview of the matched track. */
2279
+ preview?: Uri;
2280
+ /** The provider's popularity rank for the matched track. */
2281
+ rank?: number;
2282
+ /** Whether the matched track has explicit lyrics. */
2283
+ explicit?: boolean;
2284
+ /** Match confidence score in the range 0-100 (higher is better). */
2285
+ score?: number;
2286
+ }
2287
+
2180
2288
  export interface SongRecentListenerView {
2181
2289
  /** The unique identifier of the listener. */
2182
2290
  id?: string;
@@ -2330,6 +2438,8 @@ export interface SongViewDetailed {
2330
2438
  artists?: ArtistViewBasic[];
2331
2439
  /** The first scrobble of this song on Rocksky. */
2332
2440
  firstScrobble?: SongFirstScrobbleView;
2441
+ /** Ranked list of candidate matches from external metadata providers (e.g. Deezer). Additive field returned by matchSong; may be empty. */
2442
+ matches?: SongMatchView[];
2333
2443
  }
2334
2444
 
2335
2445
  export interface SpotifyGetCurrentlyPlayingParams {
@@ -2596,6 +2706,16 @@ export interface UpdatePlaylistOutput {
2596
2706
 
2597
2707
  }
2598
2708
 
2709
+ export interface UpdateSeenInput {
2710
+ /** The ids of the notifications to mark as viewed. Omit to mark all. */
2711
+ ids?: string[];
2712
+ }
2713
+
2714
+ export interface UpdateSeenOutput {
2715
+ /** The number of unread notifications remaining. */
2716
+ unreadCount: number;
2717
+ }
2718
+
2599
2719
  /**
2600
2720
  * Map of every XRPC method (NSID) to the type of its response body.
2601
2721
  * Endpoints with no output map to `void`. Used by the SDK to type method
@@ -2697,6 +2817,9 @@ export interface Endpoints {
2697
2817
  "app.rocksky.like.likeSong": SongViewDetailed;
2698
2818
  "app.rocksky.mirror.getMirrorSources": GetMirrorSourcesOutput;
2699
2819
  "app.rocksky.mirror.putMirrorSource": MirrorSourceView;
2820
+ "app.rocksky.notification.getUnreadCount": GetUnreadCountOutput;
2821
+ "app.rocksky.notification.listNotifications": ListNotificationsOutput;
2822
+ "app.rocksky.notification.updateSeen": UpdateSeenOutput;
2700
2823
  "app.rocksky.player.addDirectoryToQueue": void;
2701
2824
  "app.rocksky.player.addItemsToQueue": void;
2702
2825
  "app.rocksky.player.getCurrentlyPlaying": PlayerCurrentlyPlayingViewDetailed;