@rocksky/sdk 0.5.0 → 0.7.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/src/agent.ts CHANGED
@@ -52,6 +52,19 @@ function nowISO(): string {
52
52
  // Write inputs: `createdAt` is optional (the SDK defaults it to now).
53
53
  /** Input for {@link Agent.scrobble} (createdAt defaults to now). */
54
54
  export type ScrobbleInput = Omit<ScrobbleRecord, "createdAt"> & { createdAt?: string };
55
+ /** Input for {@link Agent.scrobbleMatch} — `title`/`artist` required, rest optional. */
56
+ export interface ScrobbleMatchInput {
57
+ title: string;
58
+ artist: string;
59
+ /** Overrides the matched album. */
60
+ album?: string;
61
+ /** MusicBrainz recording id — anchors the match. */
62
+ mbId?: string;
63
+ /** ISRC — anchors the match. */
64
+ isrc?: string;
65
+ /** Scrobbled-at Unix seconds; omitted = now. */
66
+ timestamp?: number;
67
+ }
55
68
  /** Input for {@link Agent.createSong}. */
56
69
  export type SongInput = Omit<SongRecord, "createdAt"> & { createdAt?: string };
57
70
  /** Input for {@link Agent.createAlbum} (`artist` is the album artist). */
@@ -157,14 +170,8 @@ export class Agent {
157
170
  * `mbId`/`isrc` anchors): resolve full metadata via `matchSong`, then write.
158
171
  * Matching uses the public AppView unless `appview` is given; an empty match
159
172
  * falls back to a minimal record. */
160
- async scrobbleMatch(
161
- title: string,
162
- artist: string,
163
- album?: string,
164
- mbId?: string,
165
- isrc?: string,
166
- appview?: string,
167
- ): Promise<string> {
173
+ async scrobbleMatch(input: ScrobbleMatchInput, appview?: string): Promise<string> {
174
+ const { title, artist, album, mbId, isrc, timestamp } = input;
168
175
  const { RockskyClient } = await import("./client.js");
169
176
  const m = (await new RockskyClient(appview).matchSong(title, artist, mbId, isrc)) as Record<
170
177
  string,
@@ -196,6 +203,8 @@ export class Agent {
196
203
  appleMusicLink: s("appleMusicLink"),
197
204
  }
198
205
  : { title, artist, album: album ?? "", albumArtist: artist, duration: 0 };
206
+ // "Scrobbled at" — Unix seconds; omitted -> scrobble() defaults to now.
207
+ if (timestamp !== undefined) rec.createdAt = new Date(timestamp * 1000).toISOString();
199
208
  return this.scrobble(rec);
200
209
  }
201
210
 
package/src/client.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Client, simpleFetchHandler } from "@atcute/client";
2
2
 
3
3
  import { RockskyError } from "./errors.js";
4
+ import { RockskyLibrary } from "./library.js";
4
5
  import type {
5
6
  ActorProfileViewBasic,
6
7
  ActorProfileViewDetailed,
@@ -58,14 +59,16 @@ export const DEFAULT_APPVIEW = "https://api.rocksky.app";
58
59
  /** Unauthenticated read client over the public Rocksky AppView XRPC. */
59
60
  export class RockskyClient {
60
61
  private rpc: Client;
62
+ private token?: string;
61
63
 
62
64
  /**
63
65
  * Build a read client against an AppView base URL (defaults to
64
66
  * {@link DEFAULT_APPVIEW}). Pass `token` to send it as
65
67
  * `Authorization: Bearer <token>` on every read — needed only for auth-gated
66
- * queries.
68
+ * queries and the whole {@link RockskyClient.library} surface.
67
69
  */
68
70
  constructor(appview: string = DEFAULT_APPVIEW, token?: string) {
71
+ this.token = token;
69
72
  let handler = simpleFetchHandler({ service: appview });
70
73
  if (token) {
71
74
  const inner = handler;
@@ -78,6 +81,20 @@ export class RockskyClient {
78
81
  this.rpc = new Client({ handler });
79
82
  }
80
83
 
84
+ /**
85
+ * The authenticated `app.rocksky.library.*` (uploaded-music) API. Every
86
+ * library method requires auth, so this throws unless the client was built
87
+ * with a token.
88
+ */
89
+ library(): RockskyLibrary {
90
+ if (!this.token) {
91
+ throw new Error(
92
+ "app.rocksky.library.* requires an access token; construct RockskyClient(appview, token) first",
93
+ );
94
+ }
95
+ return new RockskyLibrary(this.rpc);
96
+ }
97
+
81
98
  private async query<T>(nsid: string, params: Record<string, unknown>): Promise<T> {
82
99
  const clean: Record<string, unknown> = {};
83
100
  for (const [k, v] of Object.entries(params)) {