Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@nuclearplayer/plugin-sdk

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nuclearplayer/plugin-sdk - npm Package Compare versions

Comparing version
0.0.14
to
1.0.0
+190
-13
dist/index.d.ts

@@ -14,2 +14,4 @@ export declare type Album = {

export declare type AlbumMetadataCapability = 'albumDetails';
export declare type AlbumRef = {

@@ -101,2 +103,25 @@ title: string;

export declare type FetchFunction = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
export declare class HttpAPI {
readonly fetch: FetchFunction;
constructor(host?: HttpHost);
}
export declare type HttpHost = {
fetch: (url: string, init?: HttpRequestInit) => Promise<HttpResponseData>;
};
export declare type HttpRequestInit = {
method?: string;
headers?: Record<string, string>;
body?: string;
};
export declare type HttpResponseData = {
status: number;
headers: Record<string, string>;
body: string;
};
export declare type LoadedPlugin = {

@@ -119,5 +144,26 @@ metadata: PluginMetadata;

declare class MetadataAPI {
#private;
constructor(host?: MetadataHost);
search(params: SearchParams, providerId?: string): Promise<SearchResults>;
fetchArtistDetails(artistId: string, providerId?: string): Promise<Artist>;
fetchArtistAlbums(artistId: string, providerId?: string): Promise<AlbumRef[]>;
fetchArtistTopTracks(artistId: string, providerId?: string): Promise<TrackRef[]>;
fetchArtistRelatedArtists(artistId: string, providerId?: string): Promise<ArtistRef[]>;
fetchAlbumDetails(albumId: string, providerId?: string): Promise<Album>;
}
export declare type MetadataHost = {
search: (params: SearchParams, providerId?: string) => Promise<SearchResults>;
fetchArtistDetails: (artistId: string, providerId?: string) => Promise<Artist>;
fetchArtistAlbums: (artistId: string, providerId?: string) => Promise<AlbumRef[]>;
fetchArtistTopTracks: (artistId: string, providerId?: string) => Promise<TrackRef[]>;
fetchArtistRelatedArtists: (artistId: string, providerId?: string) => Promise<ArtistRef[]>;
fetchAlbumDetails: (albumId: string, providerId?: string) => Promise<Album>;
};
export declare type MetadataProvider = ProviderDescriptor<'metadata'> & {
searchCapabilities?: SearchCapability[];
artistMetadataCapabilities?: ArtistMetadataCapability[];
albumMetadataCapabilities?: AlbumMetadataCapability[];
search?: (params: SearchParams) => Promise<SearchResults>;

@@ -142,5 +188,15 @@ searchArtists?: (params: Omit<SearchParams, 'types'>) => Promise<ArtistRef[]>;

readonly Providers: Providers;
readonly Queue: QueueAPI;
readonly Streaming: StreamingAPI;
readonly Metadata: MetadataAPI;
readonly Http: HttpAPI;
readonly Ytdlp: YtdlpAPI;
constructor(opts?: {
settingsHost?: SettingsHost;
providersHost?: ProvidersHost;
queueHost?: QueueHost;
streamingHost?: StreamingHost;
metadataHost?: MetadataHost;
httpHost?: HttpHost;
ytdlpHost?: YtdlpHost;
});

@@ -151,5 +207,5 @@ }

onLoad?(api: NuclearPluginAPI): void | Promise<void>;
onUnload?(): void | Promise<void>;
onUnload?(api: NuclearPluginAPI): void | Promise<void>;
onEnable?(api: NuclearPluginAPI): void | Promise<void>;
onDisable?(): void | Promise<void>;
onDisable?(api: NuclearPluginAPI): void | Promise<void>;
};

@@ -281,14 +337,72 @@

export declare type Queue = {
items: QueueItem[];
currentIndex: number;
repeatMode: RepeatMode;
shuffleEnabled: boolean;
};
declare class QueueAPI {
#private;
constructor(host?: QueueHost);
getQueue(): Promise<Queue>;
getCurrentItem(): Promise<QueueItem | undefined>;
addToQueue(tracks: Track[]): Promise<void>;
addNext(tracks: Track[]): Promise<void>;
addAt(tracks: Track[], index: number): Promise<void>;
removeByIds(ids: string[]): Promise<void>;
removeByIndices(indices: number[]): Promise<void>;
clearQueue(): Promise<void>;
reorder(fromIndex: number, toIndex: number): Promise<void>;
updateItemState(id: string, updates: QueueItemStateUpdate): Promise<void>;
goToNext(): Promise<void>;
goToPrevious(): Promise<void>;
goToIndex(index: number): Promise<void>;
goToId(id: string): Promise<void>;
setRepeatMode(mode: RepeatMode): Promise<void>;
setShuffleEnabled(enabled: boolean): Promise<void>;
subscribe(listener: (queue: Queue) => void): () => void;
subscribeToCurrentItem(listener: (item: QueueItem | undefined) => void): () => void;
}
export declare type QueueHost = {
getQueue: () => Promise<Queue>;
getCurrentItem: () => Promise<QueueItem | undefined>;
addToQueue: (tracks: Track[]) => Promise<void>;
addNext: (tracks: Track[]) => Promise<void>;
addAt: (tracks: Track[], index: number) => Promise<void>;
removeByIds: (ids: string[]) => Promise<void>;
removeByIndices: (indices: number[]) => Promise<void>;
clearQueue: () => Promise<void>;
reorder: (fromIndex: number, toIndex: number) => Promise<void>;
updateItemState: (id: string, updates: QueueItemStateUpdate) => Promise<void>;
goToNext: () => Promise<void>;
goToPrevious: () => Promise<void>;
goToIndex: (index: number) => Promise<void>;
goToId: (id: string) => Promise<void>;
setRepeatMode: (mode: RepeatMode) => Promise<void>;
setShuffleEnabled: (enabled: boolean) => Promise<void>;
subscribe: (listener: QueueListener) => () => void;
subscribeToCurrentItem: (listener: QueueItemListener) => () => void;
};
export declare type QueueItem = {
id: string;
title: string;
artists: ArtistCredit[];
album?: string;
durationMs?: number;
artwork?: ArtworkSet;
note?: string;
addedAtIso?: string;
source: ProviderRef;
track: Track;
status: 'idle' | 'loading' | 'success' | 'error';
error?: string;
addedAtIso: string;
};
export declare type QueueItemListener = (item: QueueItem | undefined) => void;
export declare type QueueItemStateUpdate = Partial<{
status: QueueItem['status'];
error: QueueItem['error'];
}>;
export declare type QueueListener = (queue: Queue) => void;
export declare type RepeatMode = 'off' | 'all' | 'one';
export declare type SearchCapability = SearchCategory | 'unified';

@@ -318,3 +432,3 @@

constructor(host?: SettingsHost);
register(defs: SettingDefinition[], source: SettingSource): Promise<SettingsRegistrationResult>;
register(defs: SettingDefinition[]): Promise<SettingsRegistrationResult>;
get<T extends SettingValue = SettingValue>(id: string): Promise<T | undefined>;

@@ -326,3 +440,3 @@ set<T extends SettingValue = SettingValue>(id: string, value: T): Promise<void>;

export declare type SettingsHost = {
register(defs: SettingDefinition[], source: SettingSource): Promise<SettingsRegistrationResult>;
register(defs: SettingDefinition[]): Promise<SettingsRegistrationResult>;
get<T extends SettingValue = SettingValue>(id: string): Promise<T | undefined>;

@@ -364,2 +478,39 @@ set<T extends SettingValue = SettingValue>(id: string, value: T): Promise<void>;

export declare type StreamCandidate = {
id: string;
title: string;
durationMs?: number;
thumbnail?: string;
stream?: Stream;
lastResolvedAtIso?: string;
failed: boolean;
source: ProviderRef;
};
declare class StreamingAPI {
#private;
constructor(host?: StreamingHost);
resolveCandidatesForTrack(track: Track): Promise<StreamResolutionResult>;
resolveStreamForCandidate(candidate: StreamCandidate): Promise<StreamCandidate | undefined>;
}
export declare type StreamingHost = {
resolveCandidatesForTrack: (track: Track) => Promise<StreamResolutionResult>;
resolveStreamForCandidate: (candidate: StreamCandidate) => Promise<StreamCandidate | undefined>;
};
export declare type StreamingProvider = ProviderDescriptor<'streaming'> & {
searchForTrack: (artist: string, title: string, album?: string) => Promise<StreamCandidate[]>;
getStreamUrl: (candidateId: string) => Promise<Stream>;
supportsLocalFiles?: boolean;
};
export declare type StreamResolutionResult = {
success: true;
candidates: StreamCandidate[];
} | {
success: false;
error: string;
};
export declare type StringFormat = 'text' | 'url' | 'path' | 'token' | 'language';

@@ -406,3 +557,3 @@

localFile?: LocalFileInfo;
streams?: Stream[];
streamCandidates?: StreamCandidate[];
};

@@ -419,2 +570,28 @@

export declare class YtdlpAPI {
private host?;
constructor(host?: YtdlpHost);
get available(): boolean;
search(query: string, maxResults?: number): Promise<YtdlpSearchResult[]>;
getStream(videoId: string): Promise<YtdlpStreamInfo>;
}
export declare type YtdlpHost = {
search: (query: string, maxResults?: number) => Promise<YtdlpSearchResult[]>;
getStream: (videoId: string) => Promise<YtdlpStreamInfo>;
};
export declare type YtdlpSearchResult = {
id: string;
title: string;
duration: number | null;
thumbnail: string | null;
};
export declare type YtdlpStreamInfo = {
stream_url: string;
duration: number | null;
title: string | null;
};
export { }

@@ -1,3 +0,38 @@

import { useState as g, useEffect as f, useMemo as b } from "react";
import { useState as l, useEffect as f, useMemo as g } from "react";
const b = (s) => {
if (s instanceof Headers) {
const t = {};
return s.forEach((e, r) => {
t[r] = e;
}), t;
}
return Array.isArray(s) ? Object.fromEntries(s) : s;
};
function m(s) {
return async (t, e) => {
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, {
method: e?.method,
headers: a,
body: o
});
return new Response(u.body, {
status: u.status,
headers: new Headers(u.headers)
});
};
}
const A = {
fetch: async () => ({
status: 501,
headers: {},
body: "HTTP host not configured"
})
};
class p {
fetch;
constructor(t) {
this.fetch = m(t ?? A);
}
}
class v {
#e;

@@ -10,2 +45,34 @@ constructor(t) {

if (!e)
throw new Error("Metadata host not available");
return t(e);
}
search(t, e) {
return this.#t((r) => r.search(t, e));
}
fetchArtistDetails(t, e) {
return this.#t((r) => r.fetchArtistDetails(t, e));
}
fetchArtistAlbums(t, e) {
return this.#t((r) => r.fetchArtistAlbums(t, e));
}
fetchArtistTopTracks(t, e) {
return this.#t((r) => r.fetchArtistTopTracks(t, e));
}
fetchArtistRelatedArtists(t, e) {
return this.#t(
(r) => r.fetchArtistRelatedArtists(t, e)
);
}
fetchAlbumDetails(t, e) {
return this.#t((r) => r.fetchAlbumDetails(t, e));
}
}
class w {
#e;
constructor(t) {
this.#e = t;
}
#t(t) {
const e = this.#e;
if (!e)
throw new Error("Providers host not available");

@@ -27,3 +94,3 @@ return t(e);

}
class d {
class I {
#e;

@@ -36,7 +103,73 @@ constructor(t) {

if (!e)
throw new Error("Queue host not available");
return t(e);
}
getQueue() {
return this.#t((t) => t.getQueue());
}
getCurrentItem() {
return this.#t((t) => t.getCurrentItem());
}
addToQueue(t) {
return this.#t((e) => e.addToQueue(t));
}
addNext(t) {
return this.#t((e) => e.addNext(t));
}
addAt(t, e) {
return this.#t((r) => r.addAt(t, e));
}
removeByIds(t) {
return this.#t((e) => e.removeByIds(t));
}
removeByIndices(t) {
return this.#t((e) => e.removeByIndices(t));
}
clearQueue() {
return this.#t((t) => t.clearQueue());
}
reorder(t, e) {
return this.#t((r) => r.reorder(t, e));
}
updateItemState(t, e) {
return this.#t((r) => r.updateItemState(t, e));
}
goToNext() {
return this.#t((t) => t.goToNext());
}
goToPrevious() {
return this.#t((t) => t.goToPrevious());
}
goToIndex(t) {
return this.#t((e) => e.goToIndex(t));
}
goToId(t) {
return this.#t((e) => e.goToId(t));
}
setRepeatMode(t) {
return this.#t((e) => e.setRepeatMode(t));
}
setShuffleEnabled(t) {
return this.#t((e) => e.setShuffleEnabled(t));
}
subscribe(t) {
return this.#t((e) => e.subscribe(t));
}
subscribeToCurrentItem(t) {
return this.#t((e) => e.subscribeToCurrentItem(t));
}
}
class T {
#e;
constructor(t) {
this.#e = t;
}
#t(t) {
const e = this.#e;
if (!e)
throw new Error("Settings host not available");
return t(e);
}
register(t, e) {
return this.#t((i) => i.register(t, e));
register(t) {
return this.#t((e) => e.register(t));
}

@@ -47,16 +180,59 @@ get(t) {

set(t, e) {
return this.#t((i) => i.set(t, e));
return this.#t((r) => r.set(t, e));
}
subscribe(t, e) {
return this.#t((i) => i.subscribe(t, e));
return this.#t((r) => r.subscribe(t, e));
}
}
class v {
class y {
#e;
constructor(t) {
this.#e = t;
}
#t(t) {
const e = this.#e;
if (!e)
throw new Error("Streaming host not available");
return t(e);
}
resolveCandidatesForTrack(t) {
return this.#t((e) => e.resolveCandidatesForTrack(t));
}
resolveStreamForCandidate(t) {
return this.#t((e) => e.resolveStreamForCandidate(t));
}
}
class H {
host;
constructor(t) {
this.host = t;
}
get available() {
return !!this.host;
}
async search(t, e) {
if (!this.host)
throw new Error("YtdlpAPI: No host configured");
return this.host.search(t, e);
}
async getStream(t) {
if (!this.host)
throw new Error("YtdlpAPI: No host configured");
return this.host.getStream(t);
}
}
class S {
Settings;
Providers;
Queue;
Streaming;
Metadata;
Http;
Ytdlp;
// All these are optional so we don't have to provide all of them in tests
constructor(t) {
this.Settings = new d(t?.settingsHost), this.Providers = new p(t?.providersHost);
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);
}
}
class w extends v {
class P extends S {
}

@@ -68,33 +244,33 @@ class M extends Error {

}
const A = (s, t) => {
const [e, i] = g(void 0);
const C = (s, t) => {
const [e, r] = l(void 0);
f(() => {
if (!s)
return;
let u = !0, c = !1;
const r = s.subscribe(t, (n) => {
u && (c = !0, i(n));
let o = !0, u = !1;
const i = s.subscribe(t, (n) => {
o && (u = !0, r(n));
});
return s.get(t).then((n) => {
u && (c || i(n));
o && (u || r(n));
}), () => {
u = !1, r && r();
o = !1, i && i();
};
}, [t, s]);
const o = b(
() => (u) => {
s && s.set(t, u);
const a = g(
() => (o) => {
s && s.set(t, o);
},
[t, s]
);
return [e, o];
return [e, a];
};
function E(s, t, e) {
function R(s, t, e) {
if (!s?.items?.length)
return;
const i = s.items.filter((r) => !(r.purpose && r.purpose !== t || !r.url));
if (!i.length)
const r = s.items.filter((i) => !(i.purpose && i.purpose !== t || !i.url));
if (!r.length)
return s.items[0];
const o = (r) => !r.width || !r.height ? 1 : r.width / r.height, c = ((r) => {
switch (r) {
const a = (i) => !i.width || !i.height ? 1 : i.width / i.height, u = ((i) => {
switch (i) {
case "avatar":

@@ -111,16 +287,18 @@ case "thumbnail":

})(t);
return i.map((r) => {
const n = Math.min(r.width || 0, r.height || 0), a = Math.abs(o(r) - c), h = Math.abs(n - e), l = n < e ? e / n : 1;
return r.map((i) => {
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;
return {
artwork: r,
score: (l > 1.5 ? -1e3 : 0) + -a * 50 + -h * 0.1
artwork: i,
score: (d > 1.5 ? -1e3 : 0) + -h * 50 + -c * 0.1
};
}).sort((r, n) => n.score - r.score)[0]?.artwork;
}).sort((i, n) => n.score - i.score)[0]?.artwork;
}
export {
p as HttpAPI,
M as MissingCapabilityError,
v as NuclearAPI,
w as NuclearPluginAPI,
E as pickArtwork,
A as useSetting
S as NuclearAPI,
P as NuclearPluginAPI,
H as YtdlpAPI,
R as pickArtwork,
C as useSetting
};
+3
-3
{
"name": "@nuclearplayer/plugin-sdk",
"version": "0.0.14",
"version": "1.0.0",
"description": "Plugin SDK for Nuclear music player",

@@ -41,4 +41,4 @@ "type": "module",

"vitest": "^3.2.4",
"@nuclearplayer/eslint-config": "0.0.9",
"@nuclearplayer/tailwind-config": "0.0.9"
"@nuclearplayer/tailwind-config": "0.0.10",
"@nuclearplayer/eslint-config": "0.0.10"
},

@@ -45,0 +45,0 @@ "peerDependencies": {