@groovenet/player 0.0.0-stage → 0.1.2

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.
@@ -0,0 +1,133 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { createConnection } from "node:net";
3
+ export const PLAYER_PROTOCOL_VERSION = 1;
4
+ export class PlayerProtocolError extends Error {
5
+ code;
6
+ constructor(code, message) {
7
+ super(message);
8
+ this.code = code;
9
+ this.name = "PlayerProtocolError";
10
+ }
11
+ }
12
+ export function encodeRpcRequest(id, method, params) {
13
+ return `${JSON.stringify({
14
+ jsonrpc: "2.0",
15
+ version: PLAYER_PROTOCOL_VERSION,
16
+ id,
17
+ method,
18
+ ...(params ? { params } : {}),
19
+ })}\n`;
20
+ }
21
+ export function encodeRpcResponse(response) {
22
+ return `${JSON.stringify({ ...response, jsonrpc: "2.0" })}\n`;
23
+ }
24
+ export function encodeRpcEvent(event, data) {
25
+ return `${JSON.stringify({ jsonrpc: "2.0", version: PLAYER_PROTOCOL_VERSION, event, data })}\n`;
26
+ }
27
+ export class PlayerControlClient extends EventEmitter {
28
+ socketPath;
29
+ timeoutMs;
30
+ connectSocket;
31
+ socket = null;
32
+ buffer = "";
33
+ nextId = 1;
34
+ pending = new Map();
35
+ constructor(socketPath, timeoutMs = 5000, connectSocket = createConnection) {
36
+ super();
37
+ this.socketPath = socketPath;
38
+ this.timeoutMs = timeoutMs;
39
+ this.connectSocket = connectSocket;
40
+ }
41
+ async connect() {
42
+ if (this.socket?.writable)
43
+ return;
44
+ const socket = this.connectSocket(this.socketPath);
45
+ await new Promise((resolve, reject) => {
46
+ const failed = (error) => {
47
+ socket.destroy();
48
+ reject(new PlayerProtocolError("DAEMON_UNAVAILABLE", `Player daemon is unavailable. Start it with “groovenet player start --detach”. (${error.message})`));
49
+ };
50
+ socket.once("error", failed);
51
+ socket.once("connect", () => {
52
+ socket.off("error", failed);
53
+ this.socket = socket;
54
+ socket.on("data", (chunk) => this.onData(chunk));
55
+ socket.on("error", (error) => this.rejectPending(new PlayerProtocolError("IPC_ERROR", error.message)));
56
+ socket.on("close", () => {
57
+ this.socket = null;
58
+ this.rejectPending(new PlayerProtocolError("DAEMON_DISCONNECTED", "Player daemon disconnected."));
59
+ });
60
+ resolve();
61
+ });
62
+ });
63
+ }
64
+ async request(method, params) {
65
+ await this.connect();
66
+ const socket = this.socket;
67
+ if (!socket?.writable)
68
+ throw new PlayerProtocolError("DAEMON_UNAVAILABLE", "Player daemon is not connected.");
69
+ const id = this.nextId++;
70
+ return new Promise((resolve, reject) => {
71
+ const timer = setTimeout(() => {
72
+ this.pending.delete(id);
73
+ reject(new PlayerProtocolError("REQUEST_TIMEOUT", `Player daemon did not respond to “${method}”.`));
74
+ }, this.timeoutMs);
75
+ this.pending.set(id, { resolve: (result) => resolve(result), reject, timer });
76
+ socket.write(encodeRpcRequest(id, method, params), (error) => {
77
+ if (!error)
78
+ return;
79
+ const pending = this.pending.get(id);
80
+ this.pending.delete(id);
81
+ if (pending)
82
+ clearTimeout(pending.timer);
83
+ reject(new PlayerProtocolError("IPC_ERROR", `Could not send “${method}” to the player daemon: ${error.message}`));
84
+ });
85
+ });
86
+ }
87
+ async close() {
88
+ this.socket?.end();
89
+ this.socket = null;
90
+ this.rejectPending(new PlayerProtocolError("CLIENT_CLOSED", "Player control connection closed."));
91
+ }
92
+ onData(chunk) {
93
+ this.buffer += chunk.toString("utf8");
94
+ let newline;
95
+ while ((newline = this.buffer.indexOf("\n")) >= 0) {
96
+ const line = this.buffer.slice(0, newline);
97
+ this.buffer = this.buffer.slice(newline + 1);
98
+ let message;
99
+ try {
100
+ message = JSON.parse(line);
101
+ }
102
+ catch {
103
+ continue;
104
+ }
105
+ if (message.version !== PLAYER_PROTOCOL_VERSION) {
106
+ this.rejectPending(new PlayerProtocolError("VERSION_MISMATCH", `Player protocol mismatch (daemon version ${message.version}, client version ${PLAYER_PROTOCOL_VERSION}). Upgrade the Groovenet packages together.`));
107
+ continue;
108
+ }
109
+ if ("id" in message) {
110
+ const pending = this.pending.get(message.id);
111
+ if (!pending)
112
+ continue;
113
+ this.pending.delete(message.id);
114
+ clearTimeout(pending.timer);
115
+ if (message.error)
116
+ pending.reject(new PlayerProtocolError(message.error.code, message.error.message));
117
+ else
118
+ pending.resolve(message.result);
119
+ }
120
+ else {
121
+ this.emit("event", message);
122
+ }
123
+ }
124
+ }
125
+ rejectPending(error) {
126
+ for (const pending of this.pending.values()) {
127
+ clearTimeout(pending.timer);
128
+ pending.reject(error);
129
+ }
130
+ this.pending.clear();
131
+ }
132
+ }
133
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Track } from "@groovenet/client";
2
+ import type { QueueEntry } from "./types.js";
3
+ export declare class TrackQueue {
4
+ private entries;
5
+ get items(): QueueEntry[];
6
+ get length(): number;
7
+ add(tracks: Track[], at?: number): QueueEntry[];
8
+ remove(idOrIndex: string | number): QueueEntry | undefined;
9
+ move(from: number, to: number): void;
10
+ take(index: number): QueueEntry | undefined;
11
+ }
12
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAoB;IAEnC,IAAI,KAAK,IAAI,UAAU,EAAE,CAExB;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,SAAsB,GAAG,UAAU,EAAE,CAO3D;IAED,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAMzD;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAMnC;IAED,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAG1C;CACF"}
package/build/queue.js ADDED
@@ -0,0 +1,39 @@
1
+ import { randomUUID } from "node:crypto";
2
+ export class TrackQueue {
3
+ entries = [];
4
+ get items() {
5
+ return this.entries.slice();
6
+ }
7
+ get length() {
8
+ return this.entries.length;
9
+ }
10
+ add(tracks, at = this.entries.length) {
11
+ if (!Number.isInteger(at) || at < 0 || at > this.entries.length) {
12
+ throw new RangeError("Queue insertion index is out of range");
13
+ }
14
+ const added = tracks.map((track) => ({ id: randomUUID(), track }));
15
+ this.entries.splice(at, 0, ...added);
16
+ return added;
17
+ }
18
+ remove(idOrIndex) {
19
+ const index = typeof idOrIndex === "number"
20
+ ? idOrIndex
21
+ : this.entries.findIndex((entry) => entry.id === idOrIndex);
22
+ if (!Number.isInteger(index) || index < 0 || index >= this.entries.length)
23
+ return undefined;
24
+ return this.entries.splice(index, 1)[0];
25
+ }
26
+ move(from, to) {
27
+ if (![from, to].every(Number.isInteger) || from < 0 || from >= this.entries.length || to < 0 || to >= this.entries.length) {
28
+ throw new RangeError("Queue move index is out of range");
29
+ }
30
+ const [entry] = this.entries.splice(from, 1);
31
+ this.entries.splice(to, 0, entry);
32
+ }
33
+ take(index) {
34
+ if (!Number.isInteger(index) || index < 0 || index >= this.entries.length)
35
+ return undefined;
36
+ return this.entries.splice(index, 1)[0];
37
+ }
38
+ }
39
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,OAAO,UAAU;IACb,OAAO,GAAiB,EAAE,CAAC;IAEnC,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IAC7B,CAAC;IAED,GAAG,CAAC,MAAe,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM;QAC3C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAChE,MAAM,IAAI,UAAU,CAAC,uCAAuC,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC;QACrC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,CAAC,SAA0B;QAC/B,MAAM,KAAK,GAAG,OAAO,SAAS,KAAK,QAAQ;YACzC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC;QAC9D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC5F,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,CAAC,IAAY,EAAE,EAAU;QAC3B,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YAC1H,MAAM,IAAI,UAAU,CAAC,kCAAkC,CAAC,CAAC;QAC3D,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,EAAE,KAAM,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,CAAC,KAAa;QAChB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC5F,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;CACF"}
@@ -0,0 +1,17 @@
1
+ import type { GroovenetClient, Track } from "@groovenet/client";
2
+ import type { PlaybackSource } from "./types.js";
3
+ export interface ResolverConfig {
4
+ apiBase: string;
5
+ apiKey?: string;
6
+ }
7
+ /** Build the authenticated, range-capable app URL for a downloaded track. */
8
+ export declare function audioSource(track: Track, config: ResolverConfig): PlaybackSource | null;
9
+ export declare class TrackResolver {
10
+ private readonly client;
11
+ private readonly config;
12
+ constructor(client: GroovenetClient, config: ResolverConfig);
13
+ search(query: string, limit?: number): Promise<Track[]>;
14
+ playlist(playlistId: number | string): Promise<Track[]>;
15
+ source(track: Track): PlaybackSource | null;
16
+ }
17
+ //# sourceMappingURL=resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,GAAG,cAAc,GAAG,IAAI,CAUvF;AAED,qBAAa,aAAa;IAEtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFzB,YACmB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,cAAc,EACrC;IAEE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAGxD;IAEK,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAU5D;IAED,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,cAAc,GAAG,IAAI,CAE1C;CACF"}
@@ -0,0 +1,41 @@
1
+ /** Build the authenticated, range-capable app URL for a downloaded track. */
2
+ export function audioSource(track, config) {
3
+ const filename = track.local_audio_url?.trim();
4
+ if (!filename)
5
+ return null;
6
+ const base = config.apiBase.endsWith("/") ? config.apiBase : `${config.apiBase}/`;
7
+ const url = new URL("audio", base);
8
+ url.searchParams.set("filename", filename);
9
+ return {
10
+ url: url.toString(),
11
+ ...(config.apiKey ? { headers: { Authorization: `Bearer ${config.apiKey}` } } : {}),
12
+ };
13
+ }
14
+ export class TrackResolver {
15
+ client;
16
+ config;
17
+ constructor(client, config) {
18
+ this.client = client;
19
+ this.config = config;
20
+ }
21
+ async search(query, limit = 50) {
22
+ const result = await this.client.searchTracks({ query, limit });
23
+ return result.tracks;
24
+ }
25
+ async playlist(playlistId) {
26
+ const { track_refs } = await this.client.getPlaylistTracks(playlistId);
27
+ const refs = track_refs.slice().sort((a, b) => (a.position ?? 0) - (b.position ?? 0));
28
+ if (!refs.length)
29
+ return [];
30
+ const tracks = await this.client.batchGetTracks(refs);
31
+ const byRef = new Map(tracks.map((track) => [`${track.friend_id}:${track.track_id}`, track]));
32
+ return refs.flatMap((ref) => {
33
+ const track = byRef.get(`${ref.friend_id}:${ref.track_id}`);
34
+ return track ? [track] : [];
35
+ });
36
+ }
37
+ source(track) {
38
+ return audioSource(track, this.config);
39
+ }
40
+ }
41
+ //# sourceMappingURL=resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAQA,6EAA6E;AAC7E,MAAM,UAAU,WAAW,CAAC,KAAY,EAAE,MAAsB;IAC9D,MAAM,QAAQ,GAAG,KAAK,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,GAAG,CAAC;IAClF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC3C,OAAO;QACL,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE;QACnB,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpF,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,aAAa;IAEL,MAAM;IACN,MAAM;IAFzB,YACmB,MAAuB,EACvB,MAAsB;sBADtB,MAAM;sBACN,MAAM;IACtB,CAAC;IAEJ,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,KAAK,GAAG,EAAE;QACpC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAChE,OAAO,MAAM,CAAC,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,UAA2B;QACxC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACvE,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC;QACtF,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9F,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC5D,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,KAAY;QACjB,OAAO,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;CACF"}
@@ -0,0 +1,28 @@
1
+ import { EventEmitter } from "node:events";
2
+ import type { AudioDevice, PlaybackBackend, PlaybackBackendEvents, PlaybackSource } from "./types.js";
3
+ import { PlayerError } from "./types.js";
4
+ /** In-memory backend for apps and tests that exercise PlayerCore without audio. */
5
+ export declare class TestPlaybackBackend extends EventEmitter implements PlaybackBackend {
6
+ source: PlaybackSource | null;
7
+ paused: boolean;
8
+ position: number;
9
+ volume: number;
10
+ outputDevice: string | null;
11
+ closed: boolean;
12
+ devices: AudioDevice[];
13
+ on(event: string | symbol, listener: (...args: any[]) => void): this;
14
+ on<E extends keyof PlaybackBackendEvents>(event: E, listener: PlaybackBackendEvents[E]): this;
15
+ off(event: string | symbol, listener: (...args: any[]) => void): this;
16
+ off<E extends keyof PlaybackBackendEvents>(event: E, listener: PlaybackBackendEvents[E]): this;
17
+ load(source: PlaybackSource): Promise<void>;
18
+ pause(paused: boolean): Promise<void>;
19
+ seek(seconds: number): Promise<void>;
20
+ setVolume(volume: number): Promise<void>;
21
+ listDevices(): Promise<AudioDevice[]>;
22
+ setOutputDevice(deviceId: string): Promise<void>;
23
+ close(): Promise<void>;
24
+ advance(seconds: number, duration?: number | null): void;
25
+ finish(): void;
26
+ fail(error: PlayerError): void;
27
+ }
28
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACtG,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,mFAAmF;AACnF,qBAAa,mBAAoB,SAAQ,YAAa,YAAW,eAAe;IAC9E,MAAM,EAAE,cAAc,GAAG,IAAI,CAAQ;IACrC,MAAM,UAAS;IACf,QAAQ,SAAK;IACb,MAAM,SAAO;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IACnC,MAAM,UAAS;IACf,OAAO,EAAE,WAAW,EAAE,CAAM;IAI5B,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACrE,EAAE,CAAC,CAAC,SAAS,MAAM,qBAAqB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAI9F,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACtE,GAAG,CAAC,CAAC,SAAS,MAAM,qBAAqB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAGzF,IAAI,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAA+C;IAC1F,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAA0B;IAC/D,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAmE;IACvG,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAA0B;IAClE,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAiC;IACtE,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrD;IACK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAwB;IACpD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,GAAG,IAAW,GAAG,IAAI,CAAuE;IACrI,MAAM,IAAI,IAAI,CAAwB;IACtC,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAA+B;CAC9D"}
@@ -0,0 +1,31 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { PlayerError } from "./types.js";
3
+ /** In-memory backend for apps and tests that exercise PlayerCore without audio. */
4
+ export class TestPlaybackBackend extends EventEmitter {
5
+ source = null;
6
+ paused = false;
7
+ position = 0;
8
+ volume = 100;
9
+ outputDevice = null;
10
+ closed = false;
11
+ devices = [];
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+ on(event, listener) { return super.on(event, listener); }
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ off(event, listener) { return super.off(event, listener); }
16
+ async load(source) { this.source = source; this.paused = false; }
17
+ async pause(paused) { this.paused = paused; }
18
+ async seek(seconds) { this.position = seconds; this.emit("position", seconds, null); }
19
+ async setVolume(volume) { this.volume = volume; }
20
+ async listDevices() { return this.devices.slice(); }
21
+ async setOutputDevice(deviceId) {
22
+ if (!this.devices.some((device) => device.id === deviceId))
23
+ throw new PlayerError("DEVICE_UNAVAILABLE", `No audio device named “${deviceId}” is available.`);
24
+ this.outputDevice = deviceId;
25
+ }
26
+ async close() { this.closed = true; }
27
+ advance(seconds, duration = null) { this.position = seconds; this.emit("position", seconds, duration); }
28
+ finish() { this.emit("ended"); }
29
+ fail(error) { this.emit("error", error); }
30
+ }
31
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,mFAAmF;AACnF,MAAM,OAAO,mBAAoB,SAAQ,YAAY;IACnD,MAAM,GAA0B,IAAI,CAAC;IACrC,MAAM,GAAG,KAAK,CAAC;IACf,QAAQ,GAAG,CAAC,CAAC;IACb,MAAM,GAAG,GAAG,CAAC;IACb,YAAY,GAAkB,IAAI,CAAC;IACnC,MAAM,GAAG,KAAK,CAAC;IACf,OAAO,GAAkB,EAAE,CAAC;IAM5B,8DAA8D;IAC9D,EAAE,CAAC,KAAsB,EAAE,QAAkC,IAAU,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAI1G,8DAA8D;IAC9D,GAAG,CAAC,KAAsB,EAAE,QAAkC,IAAU,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC5G,KAAK,CAAC,IAAI,CAAC,MAAsB,IAAmB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC;IAChG,KAAK,CAAC,KAAK,CAAC,MAAe,IAAmB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IACrE,KAAK,CAAC,IAAI,CAAC,OAAe,IAAmB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7G,KAAK,CAAC,SAAS,CAAC,MAAc,IAAmB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IACxE,KAAK,CAAC,WAAW,KAA6B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC5E,KAAK,CAAC,eAAe,CAAC,QAAgB;QACpC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,QAAQ,CAAC;YAAE,MAAM,IAAI,WAAW,CAAC,oBAAoB,EAAE,0BAA0B,QAAQ,iBAAiB,CAAC,CAAC;QAC7J,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC;IAC/B,CAAC;IACD,KAAK,CAAC,KAAK,KAAoB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,OAAe,EAAE,QAAQ,GAAkB,IAAI,IAAU,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IACrI,MAAM,KAAW,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACtC,IAAI,CAAC,KAAkB,IAAU,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;CAC9D"}
@@ -0,0 +1,46 @@
1
+ import type { Track } from "@groovenet/client";
2
+ export interface QueueEntry {
3
+ id: string;
4
+ track: Track;
5
+ }
6
+ export type PlaybackStatus = "stopped" | "loading" | "playing" | "paused" | "ended" | "error";
7
+ export interface PlaybackSnapshot {
8
+ status: PlaybackStatus;
9
+ current: QueueEntry | null;
10
+ queue: QueueEntry[];
11
+ position: number;
12
+ duration: number | null;
13
+ volume: number;
14
+ error: PlayerError | null;
15
+ }
16
+ export interface AudioDevice {
17
+ id: string;
18
+ name: string;
19
+ description?: string;
20
+ }
21
+ export interface PlaybackSource {
22
+ url: string;
23
+ headers?: Record<string, string>;
24
+ }
25
+ export interface PlaybackBackendEvents {
26
+ position: (position: number, duration: number | null) => void;
27
+ ended: () => void;
28
+ error: (error: PlayerError) => void;
29
+ }
30
+ export interface PlaybackBackend {
31
+ on<E extends keyof PlaybackBackendEvents>(event: E, listener: PlaybackBackendEvents[E]): this;
32
+ off<E extends keyof PlaybackBackendEvents>(event: E, listener: PlaybackBackendEvents[E]): this;
33
+ load(source: PlaybackSource): Promise<void>;
34
+ pause(paused: boolean): Promise<void>;
35
+ seek(seconds: number): Promise<void>;
36
+ setVolume(volume: number): Promise<void>;
37
+ listDevices(): Promise<AudioDevice[]>;
38
+ setOutputDevice(deviceId: string): Promise<void>;
39
+ close(): Promise<void>;
40
+ }
41
+ export type PlayerErrorCode = "NO_AUDIO" | "MPV_NOT_FOUND" | "MPV_EXITED" | "MPV_IPC" | "STREAM_FAILED" | "DEVICE_UNAVAILABLE" | "INVALID_ARGUMENT";
42
+ export declare class PlayerError extends Error {
43
+ readonly code: PlayerErrorCode;
44
+ constructor(code: PlayerErrorCode, message: string, options?: ErrorOptions);
45
+ }
46
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;AAE9F,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC9D,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,CAAC,CAAC,SAAS,MAAM,qBAAqB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC9F,GAAG,CAAC,CAAC,SAAS,MAAM,qBAAqB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC/F,IAAI,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IACtC,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,MAAM,eAAe,GACvB,UAAU,GACV,eAAe,GACf,YAAY,GACZ,SAAS,GACT,eAAe,GACf,oBAAoB,GACpB,kBAAkB,CAAC;AAEvB,qBAAa,WAAY,SAAQ,KAAK;IAElC,QAAQ,CAAC,IAAI,EAAE,eAAe;IADhC,YACW,IAAI,EAAE,eAAe,EAC9B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,YAAY,EAIvB;CACF"}
package/build/types.js ADDED
@@ -0,0 +1,9 @@
1
+ export class PlayerError extends Error {
2
+ code;
3
+ constructor(code, message, options) {
4
+ super(message, options);
5
+ this.code = code;
6
+ this.name = "PlayerError";
7
+ }
8
+ }
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAyDA,MAAM,OAAO,WAAY,SAAQ,KAAK;IAEzB,IAAI;IADf,YACW,IAAqB,EAC9B,OAAe,EACf,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;oBAJf,IAAI;QAKb,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,52 @@
1
1
  {
2
2
  "name": "@groovenet/player",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.2",
4
+ "description": "Headless playback core and mpv backend for Groovenet",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=22.12.0"
8
+ },
9
+ "main": "build/index.js",
10
+ "types": "build/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./build/index.js",
14
+ "types": "./build/index.d.ts"
15
+ },
16
+ "./protocol": {
17
+ "import": "./build/protocol.js",
18
+ "types": "./build/protocol.d.ts"
19
+ },
20
+ "./testing": {
21
+ "import": "./build/testing.js",
22
+ "types": "./build/testing.d.ts"
23
+ }
24
+ },
25
+ "scripts": {
26
+ "build": "tsc",
27
+ "lint": "eslint .",
28
+ "test": "vitest run",
29
+ "test:coverage": "vitest run --coverage"
30
+ },
31
+ "files": [
32
+ "build/"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "dependencies": {
38
+ "@groovenet/client": "^3.3.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^26.6.4",
42
+ "@vitest/coverage-v8": "^5.0.3",
43
+ "typescript": "^7.0",
44
+ "vitest": "^5.0.0"
45
+ },
46
+ "license": "MIT",
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "git+https://github.com/Public-Vinyl-Radio/groovenet.git",
50
+ "directory": "packages/groovenet-player"
51
+ }
52
+ }