@nuclearplayer/plugin-sdk 1.0.0 → 1.2.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/dist/index.d.ts CHANGED
@@ -99,6 +99,53 @@ export declare type EnumWidget = {
99
99
  type: 'radio';
100
100
  };
101
101
 
102
+ export declare type FavoriteEntry<T> = {
103
+ ref: T;
104
+ addedAtIso: string;
105
+ };
106
+
107
+ export declare class FavoritesAPI {
108
+ #private;
109
+ constructor(host?: FavoritesHost);
110
+ getTracks(): Promise<FavoriteEntry<Track>[]>;
111
+ getAlbums(): Promise<FavoriteEntry<AlbumRef>[]>;
112
+ getArtists(): Promise<FavoriteEntry<ArtistRef>[]>;
113
+ addTrack(track: Track): Promise<void>;
114
+ removeTrack(source: ProviderRef): Promise<void>;
115
+ isTrackFavorite(source: ProviderRef): Promise<boolean>;
116
+ addAlbum(ref: AlbumRef): Promise<void>;
117
+ removeAlbum(source: ProviderRef): Promise<void>;
118
+ isAlbumFavorite(source: ProviderRef): Promise<boolean>;
119
+ addArtist(ref: ArtistRef): Promise<void>;
120
+ removeArtist(source: ProviderRef): Promise<void>;
121
+ isArtistFavorite(source: ProviderRef): Promise<boolean>;
122
+ subscribe(listener: FavoritesListener): () => void;
123
+ }
124
+
125
+ export declare type FavoritesData = {
126
+ tracks: FavoriteEntry<Track>[];
127
+ albums: FavoriteEntry<AlbumRef>[];
128
+ artists: FavoriteEntry<ArtistRef>[];
129
+ };
130
+
131
+ export declare type FavoritesHost = {
132
+ getTracks: () => Promise<FavoriteEntry<Track>[]>;
133
+ getAlbums: () => Promise<FavoriteEntry<AlbumRef>[]>;
134
+ getArtists: () => Promise<FavoriteEntry<ArtistRef>[]>;
135
+ addTrack: (track: Track) => Promise<void>;
136
+ removeTrack: (source: ProviderRef) => Promise<void>;
137
+ isTrackFavorite: (source: ProviderRef) => Promise<boolean>;
138
+ addAlbum: (ref: AlbumRef) => Promise<void>;
139
+ removeAlbum: (source: ProviderRef) => Promise<void>;
140
+ isAlbumFavorite: (source: ProviderRef) => Promise<boolean>;
141
+ addArtist: (ref: ArtistRef) => Promise<void>;
142
+ removeArtist: (source: ProviderRef) => Promise<void>;
143
+ isArtistFavorite: (source: ProviderRef) => Promise<boolean>;
144
+ subscribe: (listener: FavoritesListener) => () => void;
145
+ };
146
+
147
+ export declare type FavoritesListener = (favorites: FavoritesData) => void;
148
+
102
149
  export declare type FetchFunction = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
103
150
 
104
151
  export declare class HttpAPI {
@@ -139,6 +186,23 @@ export declare type LocalFileInfo = {
139
186
  scannedAtIso?: string;
140
187
  };
141
188
 
189
+ export declare class LoggerAPI {
190
+ private host;
191
+ constructor(host?: LoggerHost);
192
+ trace(message: string): void;
193
+ debug(message: string): void;
194
+ info(message: string): void;
195
+ warn(message: string): void;
196
+ error(message: string): void;
197
+ log(level: LogLevel, message: string): void;
198
+ }
199
+
200
+ export declare type LoggerHost = {
201
+ log: (level: LogLevel, message: string) => void;
202
+ };
203
+
204
+ export declare type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';
205
+
142
206
  declare class MetadataAPI {
143
207
  #private;
144
208
  constructor(host?: MetadataHost);
@@ -187,6 +251,8 @@ export declare class NuclearAPI {
187
251
  readonly Metadata: MetadataAPI;
188
252
  readonly Http: HttpAPI;
189
253
  readonly Ytdlp: YtdlpAPI;
254
+ readonly Favorites: FavoritesAPI;
255
+ readonly Logger: LoggerAPI;
190
256
  constructor(opts?: {
191
257
  settingsHost?: SettingsHost;
192
258
  providersHost?: ProvidersHost;
@@ -195,6 +261,8 @@ export declare class NuclearAPI {
195
261
  metadataHost?: MetadataHost;
196
262
  httpHost?: HttpHost;
197
263
  ytdlpHost?: YtdlpHost;
264
+ favoritesHost?: FavoritesHost;
265
+ loggerHost?: LoggerHost;
198
266
  });
199
267
  }
200
268
 
package/dist/index.js CHANGED
@@ -1,40 +1,119 @@
1
- import { useState as l, useEffect as f, useMemo as g } from "react";
2
- const b = (s) => {
3
- if (s instanceof Headers) {
1
+ import { useState as l, useEffect as g, useMemo as f } from "react";
2
+ class b {
3
+ #e;
4
+ constructor(t) {
5
+ this.#e = t;
6
+ }
7
+ #t(t) {
8
+ const e = this.#e;
9
+ if (!e)
10
+ throw new Error("Favorites host not available");
11
+ return t(e);
12
+ }
13
+ getTracks() {
14
+ return this.#t((t) => t.getTracks());
15
+ }
16
+ getAlbums() {
17
+ return this.#t((t) => t.getAlbums());
18
+ }
19
+ getArtists() {
20
+ return this.#t((t) => t.getArtists());
21
+ }
22
+ addTrack(t) {
23
+ return this.#t((e) => e.addTrack(t));
24
+ }
25
+ removeTrack(t) {
26
+ return this.#t((e) => e.removeTrack(t));
27
+ }
28
+ isTrackFavorite(t) {
29
+ return this.#t((e) => e.isTrackFavorite(t));
30
+ }
31
+ addAlbum(t) {
32
+ return this.#t((e) => e.addAlbum(t));
33
+ }
34
+ removeAlbum(t) {
35
+ return this.#t((e) => e.removeAlbum(t));
36
+ }
37
+ isAlbumFavorite(t) {
38
+ return this.#t((e) => e.isAlbumFavorite(t));
39
+ }
40
+ addArtist(t) {
41
+ return this.#t((e) => e.addArtist(t));
42
+ }
43
+ removeArtist(t) {
44
+ return this.#t((e) => e.removeArtist(t));
45
+ }
46
+ isArtistFavorite(t) {
47
+ return this.#t((e) => e.isArtistFavorite(t));
48
+ }
49
+ subscribe(t) {
50
+ return this.#t((e) => e.subscribe(t));
51
+ }
52
+ }
53
+ const m = (r) => {
54
+ if (r instanceof Headers) {
4
55
  const t = {};
5
- return s.forEach((e, r) => {
6
- t[r] = e;
56
+ return r.forEach((e, s) => {
57
+ t[s] = e;
7
58
  }), t;
8
59
  }
9
- return Array.isArray(s) ? Object.fromEntries(s) : s;
60
+ return Array.isArray(r) ? Object.fromEntries(r) : r;
10
61
  };
11
- function m(s) {
62
+ function A(r) {
12
63
  return async (t, e) => {
13
- const r = String(t instanceof Request ? t.url : t), a = e?.headers ? b(e.headers) : void 0, o = typeof e?.body == "string" ? e.body : void 0, u = await s.fetch(r, {
64
+ const s = String(t instanceof Request ? t.url : t), u = e?.headers ? m(e.headers) : void 0, n = typeof e?.body == "string" ? e.body : void 0, a = await r.fetch(s, {
14
65
  method: e?.method,
15
- headers: a,
16
- body: o
66
+ headers: u,
67
+ body: n
17
68
  });
18
- return new Response(u.body, {
19
- status: u.status,
20
- headers: new Headers(u.headers)
69
+ return new Response(a.body, {
70
+ status: a.status,
71
+ headers: new Headers(a.headers)
21
72
  });
22
73
  };
23
74
  }
24
- const A = {
75
+ const v = {
25
76
  fetch: async () => ({
26
77
  status: 501,
27
78
  headers: {},
28
79
  body: "HTTP host not configured"
29
80
  })
30
81
  };
31
- class p {
82
+ class w {
32
83
  fetch;
33
84
  constructor(t) {
34
- this.fetch = m(t ?? A);
85
+ this.fetch = A(t ?? v);
86
+ }
87
+ }
88
+ const T = {
89
+ log: () => {
90
+ }
91
+ };
92
+ class p {
93
+ host;
94
+ constructor(t) {
95
+ this.host = t ?? T;
96
+ }
97
+ trace(t) {
98
+ this.host.log("trace", t);
99
+ }
100
+ debug(t) {
101
+ this.host.log("debug", t);
102
+ }
103
+ info(t) {
104
+ this.host.log("info", t);
105
+ }
106
+ warn(t) {
107
+ this.host.log("warn", t);
108
+ }
109
+ error(t) {
110
+ this.host.log("error", t);
111
+ }
112
+ log(t, e) {
113
+ this.host.log(t, e);
35
114
  }
36
115
  }
37
- class v {
116
+ class I {
38
117
  #e;
39
118
  constructor(t) {
40
119
  this.#e = t;
@@ -46,27 +125,27 @@ class v {
46
125
  return t(e);
47
126
  }
48
127
  search(t, e) {
49
- return this.#t((r) => r.search(t, e));
128
+ return this.#t((s) => s.search(t, e));
50
129
  }
51
130
  fetchArtistDetails(t, e) {
52
- return this.#t((r) => r.fetchArtistDetails(t, e));
131
+ return this.#t((s) => s.fetchArtistDetails(t, e));
53
132
  }
54
133
  fetchArtistAlbums(t, e) {
55
- return this.#t((r) => r.fetchArtistAlbums(t, e));
134
+ return this.#t((s) => s.fetchArtistAlbums(t, e));
56
135
  }
57
136
  fetchArtistTopTracks(t, e) {
58
- return this.#t((r) => r.fetchArtistTopTracks(t, e));
137
+ return this.#t((s) => s.fetchArtistTopTracks(t, e));
59
138
  }
60
139
  fetchArtistRelatedArtists(t, e) {
61
140
  return this.#t(
62
- (r) => r.fetchArtistRelatedArtists(t, e)
141
+ (s) => s.fetchArtistRelatedArtists(t, e)
63
142
  );
64
143
  }
65
144
  fetchAlbumDetails(t, e) {
66
- return this.#t((r) => r.fetchAlbumDetails(t, e));
145
+ return this.#t((s) => s.fetchAlbumDetails(t, e));
67
146
  }
68
147
  }
69
- class w {
148
+ class H {
70
149
  #e;
71
150
  constructor(t) {
72
151
  this.#e = t;
@@ -90,7 +169,7 @@ class w {
90
169
  return this.#t((e) => e.get(t));
91
170
  }
92
171
  }
93
- class I {
172
+ class y {
94
173
  #e;
95
174
  constructor(t) {
96
175
  this.#e = t;
@@ -114,7 +193,7 @@ class I {
114
193
  return this.#t((e) => e.addNext(t));
115
194
  }
116
195
  addAt(t, e) {
117
- return this.#t((r) => r.addAt(t, e));
196
+ return this.#t((s) => s.addAt(t, e));
118
197
  }
119
198
  removeByIds(t) {
120
199
  return this.#t((e) => e.removeByIds(t));
@@ -126,10 +205,10 @@ class I {
126
205
  return this.#t((t) => t.clearQueue());
127
206
  }
128
207
  reorder(t, e) {
129
- return this.#t((r) => r.reorder(t, e));
208
+ return this.#t((s) => s.reorder(t, e));
130
209
  }
131
210
  updateItemState(t, e) {
132
- return this.#t((r) => r.updateItemState(t, e));
211
+ return this.#t((s) => s.updateItemState(t, e));
133
212
  }
134
213
  goToNext() {
135
214
  return this.#t((t) => t.goToNext());
@@ -156,7 +235,7 @@ class I {
156
235
  return this.#t((e) => e.subscribeToCurrentItem(t));
157
236
  }
158
237
  }
159
- class T {
238
+ class S {
160
239
  #e;
161
240
  constructor(t) {
162
241
  this.#e = t;
@@ -174,13 +253,13 @@ class T {
174
253
  return this.#t((e) => e.get(t));
175
254
  }
176
255
  set(t, e) {
177
- return this.#t((r) => r.set(t, e));
256
+ return this.#t((s) => s.set(t, e));
178
257
  }
179
258
  subscribe(t, e) {
180
- return this.#t((r) => r.subscribe(t, e));
259
+ return this.#t((s) => s.subscribe(t, e));
181
260
  }
182
261
  }
183
- class y {
262
+ class F {
184
263
  #e;
185
264
  constructor(t) {
186
265
  this.#e = t;
@@ -198,7 +277,7 @@ class y {
198
277
  return this.#t((e) => e.resolveStreamForCandidate(t));
199
278
  }
200
279
  }
201
- class H {
280
+ class P {
202
281
  host;
203
282
  constructor(t) {
204
283
  this.host = t;
@@ -217,7 +296,7 @@ class H {
217
296
  return this.host.getStream(t);
218
297
  }
219
298
  }
220
- class S {
299
+ class E {
221
300
  Settings;
222
301
  Providers;
223
302
  Queue;
@@ -225,48 +304,49 @@ class S {
225
304
  Metadata;
226
305
  Http;
227
306
  Ytdlp;
228
- // All these are optional so we don't have to provide all of them in tests
307
+ Favorites;
308
+ Logger;
229
309
  constructor(t) {
230
- this.Settings = new T(t?.settingsHost), this.Providers = new w(t?.providersHost), this.Queue = new I(t?.queueHost), this.Streaming = new y(t?.streamingHost), this.Metadata = new v(t?.metadataHost), this.Http = new p(t?.httpHost), this.Ytdlp = new H(t?.ytdlpHost);
310
+ this.Settings = new S(t?.settingsHost), this.Providers = new H(t?.providersHost), this.Queue = new y(t?.queueHost), this.Streaming = new F(t?.streamingHost), this.Metadata = new I(t?.metadataHost), this.Http = new w(t?.httpHost), this.Ytdlp = new P(t?.ytdlpHost), this.Favorites = new b(t?.favoritesHost), this.Logger = new p(t?.loggerHost);
231
311
  }
232
312
  }
233
- class P extends S {
313
+ class C extends E {
234
314
  }
235
- class M extends Error {
315
+ class R extends Error {
236
316
  constructor(t) {
237
317
  super(`Missing capability: ${t}`), this.name = "MissingCapabilityError";
238
318
  }
239
319
  }
240
- const C = (s, t) => {
241
- const [e, r] = l(void 0);
242
- f(() => {
243
- if (!s)
320
+ const k = (r, t) => {
321
+ const [e, s] = l(void 0);
322
+ g(() => {
323
+ if (!r)
244
324
  return;
245
- let o = !0, u = !1;
246
- const i = s.subscribe(t, (n) => {
247
- o && (u = !0, r(n));
325
+ let n = !0, a = !1;
326
+ const i = r.subscribe(t, (o) => {
327
+ n && (a = !0, s(o));
248
328
  });
249
- return s.get(t).then((n) => {
250
- o && (u || r(n));
329
+ return r.get(t).then((o) => {
330
+ n && (a || s(o));
251
331
  }), () => {
252
- o = !1, i && i();
332
+ n = !1, i && i();
253
333
  };
254
- }, [t, s]);
255
- const a = g(
256
- () => (o) => {
257
- s && s.set(t, o);
334
+ }, [t, r]);
335
+ const u = f(
336
+ () => (n) => {
337
+ r && r.set(t, n);
258
338
  },
259
- [t, s]
339
+ [t, r]
260
340
  );
261
- return [e, a];
341
+ return [e, u];
262
342
  };
263
- function R(s, t, e) {
264
- if (!s?.items?.length)
343
+ function Q(r, t, e) {
344
+ if (!r?.items?.length)
265
345
  return;
266
- const r = s.items.filter((i) => !(i.purpose && i.purpose !== t || !i.url));
267
- if (!r.length)
268
- return s.items[0];
269
- const a = (i) => !i.width || !i.height ? 1 : i.width / i.height, u = ((i) => {
346
+ const s = r.items.filter((i) => !(i.purpose && i.purpose !== t || !i.url));
347
+ if (!s.length)
348
+ return r.items[0];
349
+ const u = (i) => !i.width || !i.height ? 1 : i.width / i.height, a = ((i) => {
270
350
  switch (i) {
271
351
  case "avatar":
272
352
  case "thumbnail":
@@ -279,20 +359,22 @@ function R(s, t, e) {
279
359
  return 1;
280
360
  }
281
361
  })(t);
282
- return r.map((i) => {
283
- const n = Math.min(i.width || 0, i.height || 0), h = Math.abs(a(i) - u), c = Math.abs(n - e), d = n < e ? e / n : 1;
362
+ return s.map((i) => {
363
+ const o = Math.min(i.width || 0, i.height || 0), h = Math.abs(u(i) - a), c = Math.abs(o - e), d = o < e ? e / o : 1;
284
364
  return {
285
365
  artwork: i,
286
366
  score: (d > 1.5 ? -1e3 : 0) + -h * 50 + -c * 0.1
287
367
  };
288
- }).sort((i, n) => n.score - i.score)[0]?.artwork;
368
+ }).sort((i, o) => o.score - i.score)[0]?.artwork;
289
369
  }
290
370
  export {
291
- p as HttpAPI,
292
- M as MissingCapabilityError,
293
- S as NuclearAPI,
294
- P as NuclearPluginAPI,
295
- H as YtdlpAPI,
296
- R as pickArtwork,
297
- C as useSetting
371
+ b as FavoritesAPI,
372
+ w as HttpAPI,
373
+ p as LoggerAPI,
374
+ R as MissingCapabilityError,
375
+ E as NuclearAPI,
376
+ C as NuclearPluginAPI,
377
+ P as YtdlpAPI,
378
+ Q as pickArtwork,
379
+ k as useSetting
298
380
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuclearplayer/plugin-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Plugin SDK for Nuclear music player",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -38,8 +38,8 @@
38
38
  "vite": "^7.1.3",
39
39
  "vite-plugin-dts": "^4.5.4",
40
40
  "vitest": "^3.2.4",
41
- "@nuclearplayer/tailwind-config": "0.0.10",
42
- "@nuclearplayer/eslint-config": "0.0.10"
41
+ "@nuclearplayer/eslint-config": "0.0.10",
42
+ "@nuclearplayer/tailwind-config": "0.0.10"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "react": "^18.3.1"