@groovenet/player 0.1.2 → 0.1.4

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.
Files changed (86) hide show
  1. package/MPRIS.md +97 -0
  2. package/OWNTONE.md +145 -0
  3. package/README.md +29 -0
  4. package/SERVER_PLAYERS.md +215 -0
  5. package/build/daemon.d.ts.map +1 -1
  6. package/build/daemon.js +42 -16
  7. package/build/daemon.js.map +1 -1
  8. package/build/index.d.ts +4 -0
  9. package/build/index.d.ts.map +1 -1
  10. package/build/index.js +4 -0
  11. package/build/index.js.map +1 -1
  12. package/build/mpris.d.ts +11 -0
  13. package/build/mpris.d.ts.map +1 -0
  14. package/build/mpris.js +145 -0
  15. package/build/mpris.js.map +1 -0
  16. package/build/mprisBin.d.ts +3 -0
  17. package/build/mprisBin.d.ts.map +1 -0
  18. package/build/mprisBin.js +26 -0
  19. package/build/mprisBin.js.map +1 -0
  20. package/build/mprisMapping.d.ts +12 -0
  21. package/build/mprisMapping.d.ts.map +1 -0
  22. package/build/mprisMapping.js +35 -0
  23. package/build/mprisMapping.js.map +1 -0
  24. package/build/mpvBackend.d.ts +3 -0
  25. package/build/mpvBackend.d.ts.map +1 -1
  26. package/build/mpvBackend.js +6 -0
  27. package/build/mpvBackend.js.map +1 -1
  28. package/build/ownToneBackend.d.ts +62 -0
  29. package/build/ownToneBackend.d.ts.map +1 -0
  30. package/build/ownToneBackend.js +349 -0
  31. package/build/ownToneBackend.js.map +1 -0
  32. package/build/paths.d.ts +4 -0
  33. package/build/paths.d.ts.map +1 -0
  34. package/build/paths.js +8 -0
  35. package/build/paths.js.map +1 -0
  36. package/build/playLogger.d.ts +39 -0
  37. package/build/playLogger.d.ts.map +1 -0
  38. package/build/playLogger.js +59 -0
  39. package/build/playLogger.js.map +1 -0
  40. package/build/playThreshold.d.ts +31 -0
  41. package/build/playThreshold.d.ts.map +1 -0
  42. package/build/playThreshold.js +40 -0
  43. package/build/playThreshold.js.map +1 -0
  44. package/build/player.d.ts +1 -0
  45. package/build/player.d.ts.map +1 -1
  46. package/build/player.js +11 -0
  47. package/build/player.js.map +1 -1
  48. package/build/protocol.d.ts.map +1 -1
  49. package/build/protocol.js +1 -0
  50. package/build/protocol.js.map +1 -1
  51. package/build/resolver.d.ts +3 -1
  52. package/build/resolver.d.ts.map +1 -1
  53. package/build/resolver.js +30 -1
  54. package/build/resolver.js.map +1 -1
  55. package/build/server/bin.d.ts +3 -0
  56. package/build/server/bin.d.ts.map +1 -0
  57. package/build/server/bin.js +7 -0
  58. package/build/server/bin.js.map +1 -0
  59. package/build/server/http.d.ts +5 -0
  60. package/build/server/http.d.ts.map +1 -0
  61. package/build/server/http.js +129 -0
  62. package/build/server/http.js.map +1 -0
  63. package/build/server/manager.d.ts +45 -0
  64. package/build/server/manager.d.ts.map +1 -0
  65. package/build/server/manager.js +423 -0
  66. package/build/server/manager.js.map +1 -0
  67. package/build/server/observer.d.ts +9 -0
  68. package/build/server/observer.d.ts.map +1 -0
  69. package/build/server/observer.js +29 -0
  70. package/build/server/observer.js.map +1 -0
  71. package/build/server/runtime.d.ts +16 -0
  72. package/build/server/runtime.d.ts.map +1 -0
  73. package/build/server/runtime.js +78 -0
  74. package/build/server/runtime.js.map +1 -0
  75. package/build/server/store.d.ts +33 -0
  76. package/build/server/store.d.ts.map +1 -0
  77. package/build/server/store.js +108 -0
  78. package/build/server/store.js.map +1 -0
  79. package/build/types.d.ts +3 -1
  80. package/build/types.d.ts.map +1 -1
  81. package/build/types.js.map +1 -1
  82. package/owntone/Dockerfile +27 -0
  83. package/owntone/smoke.mjs +135 -0
  84. package/owntone/terminal-v1.patch +96 -0
  85. package/package.json +24 -4
  86. package/scripts/mpris-smoke.mjs +54 -0
@@ -0,0 +1,423 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { randomUUID } from "node:crypto";
3
+ import { createServerPlayerSchema, serverPlayerCommandSchema } from "@groovenet/contracts";
4
+ import { audioSource } from "../resolver.js";
5
+ import { PlayerError } from "../types.js";
6
+ import { ServerPlayerError } from "./store.js";
7
+ export const unknownSpeaker = () => ({ availability: "unknown", observed_at: null, source: "unconfigured", sender_identity: "unknown" });
8
+ const errorValue = (error) => error instanceof PlayerError || error instanceof ServerPlayerError ? { code: error.code, message: error.message } : { code: "PLAYER_OPERATION_FAILED", message: "Player operation failed" };
9
+ const clone = (value) => structuredClone(value);
10
+ /** Owns all player lifecycles; no HTTP connection owns a backend. */
11
+ export class ServerPlayerManager extends EventEmitter {
12
+ store;
13
+ engines;
14
+ observe;
15
+ players = new Map();
16
+ runtimes = new Map();
17
+ generation = 0;
18
+ tail = Promise.resolve();
19
+ stopping = false;
20
+ checkpointAt = new Map();
21
+ constructor(store, engines, observe = async () => unknownSpeaker()) {
22
+ super();
23
+ this.store = store;
24
+ this.engines = engines;
25
+ this.observe = observe;
26
+ }
27
+ serial(action) {
28
+ const result = this.tail.then(action);
29
+ this.tail = result.catch(() => { });
30
+ return result;
31
+ }
32
+ async initialize() {
33
+ // No engine object or network connection is created during restoration.
34
+ for (const saved of await this.store.list()) {
35
+ const player = clone(saved);
36
+ player.assigned_output = null;
37
+ player.state.status = "stopped";
38
+ player.state.checkpoint = true;
39
+ this.players.set(player.id, await this.store.save(player, saved.revision));
40
+ }
41
+ }
42
+ engineIds() { return Object.keys(this.engines).sort(); }
43
+ list() { return [...this.players.values()].map(clone); }
44
+ get(id) {
45
+ const player = this.players.get(id);
46
+ if (!player)
47
+ throw new ServerPlayerError("PLAYER_NOT_FOUND", "Player does not exist", 404);
48
+ return clone(player);
49
+ }
50
+ create(input) {
51
+ return this.serial(async () => {
52
+ this.available();
53
+ const parsed = createServerPlayerSchema.parse(input);
54
+ if (!this.engines[parsed.engine_id])
55
+ throw new ServerPlayerError("ENGINE_NOT_CONFIGURED", "Engine is not configured on this server", 400);
56
+ const player = await this.store.create(parsed, { queue: [], current_entry_id: null, status: "stopped", position: 0, duration: null, volume: 50, desired_output: null, checkpoint: false, error: null });
57
+ this.players.set(player.id, player);
58
+ this.publish(player);
59
+ return clone(player);
60
+ });
61
+ }
62
+ async devices(id) {
63
+ const player = this.get(id);
64
+ const runtime = this.runtimes.get(id);
65
+ const backend = runtime?.backend ?? this.engine(player).create();
66
+ try {
67
+ const outputs = await backend.listDevices();
68
+ return await Promise.all(outputs.map(async (output) => ({ ...output,
69
+ assigned_player_id: this.list().find((p) => p.assigned_output === output.id)?.id ?? null,
70
+ desired_by: this.list().filter((p) => p.state.desired_output === output.id).map((p) => p.id),
71
+ observed: await this.observe(output.id),
72
+ })));
73
+ }
74
+ finally {
75
+ if (!runtime)
76
+ await backend.close();
77
+ }
78
+ }
79
+ command(id, input) {
80
+ return this.serial(async () => {
81
+ this.available();
82
+ const command = serverPlayerCommandSchema.parse(input);
83
+ let player = this.get(id);
84
+ this.revision(player, command.expected_revision);
85
+ if (command.type === "output.transfer")
86
+ return this.transfer(player, command);
87
+ try {
88
+ switch (command.type) {
89
+ case "rename":
90
+ player.name = command.name;
91
+ break;
92
+ case "queue.add": {
93
+ const at = command.at ?? player.state.queue.length;
94
+ if (at > player.state.queue.length || player.state.queue.length + command.tracks.length > 10000)
95
+ throw new ServerPlayerError("INVALID_QUEUE", "Queue insertion is out of range", 400);
96
+ const added = [];
97
+ for (const ref of command.tracks) {
98
+ const track = await this.store.resolve(ref);
99
+ if (!track)
100
+ throw new ServerPlayerError("TRACK_NOT_FOUND", "Queued track does not exist or is ambiguous", 404);
101
+ added.push({ ...ref, id: randomUUID(), title: track.title, artist: track.artist });
102
+ }
103
+ player.state.queue.splice(at, 0, ...added);
104
+ break;
105
+ }
106
+ case "queue.remove": {
107
+ const index = player.state.queue.findIndex((entry) => entry.id === command.entry_id);
108
+ if (index < 0)
109
+ throw new ServerPlayerError("ENTRY_NOT_FOUND", "Queue entry does not exist", 404);
110
+ if (command.entry_id === player.state.current_entry_id)
111
+ await this.stop(player);
112
+ player.state.queue.splice(index, 1);
113
+ break;
114
+ }
115
+ case "queue.move": {
116
+ const index = player.state.queue.findIndex((entry) => entry.id === command.entry_id);
117
+ if (index < 0 || command.to >= player.state.queue.length)
118
+ throw new ServerPlayerError("INVALID_QUEUE", "Queue move is out of range", 400);
119
+ player.state.queue.splice(command.to, 0, player.state.queue.splice(index, 1)[0]);
120
+ break;
121
+ }
122
+ case "queue.clear":
123
+ await this.stop(player);
124
+ player.state.queue = [];
125
+ break;
126
+ case "output.select":
127
+ if (this.runtimes.has(id))
128
+ await this.stop(player, true);
129
+ player.state.desired_output = command.output_id;
130
+ // Remembering a desired output does not reserve or connect it.
131
+ break;
132
+ case "play": {
133
+ const entryId = command.entry_id ?? player.state.current_entry_id ?? player.state.queue[0]?.id;
134
+ await this.play(player, entryId, command);
135
+ return { player: this.get(id) };
136
+ }
137
+ case "resume":
138
+ if (this.runtimes.has(id)) {
139
+ const backend = this.runtimes.get(id).backend;
140
+ if (await backend.outputConnection?.() !== "connected")
141
+ await this.requireAvailable(player.state.desired_output, command);
142
+ await backend.pause(false);
143
+ player.state.status = "playing";
144
+ player.state.checkpoint = false;
145
+ }
146
+ else {
147
+ await this.play(player, player.state.current_entry_id ?? undefined, command);
148
+ return { player: this.get(id) };
149
+ }
150
+ break;
151
+ case "pause":
152
+ await this.active(id).backend.pause(true);
153
+ player.state.status = "paused";
154
+ break;
155
+ case "stop":
156
+ await this.stop(player);
157
+ break;
158
+ case "next":
159
+ case "previous": {
160
+ const index = player.state.queue.findIndex((entry) => entry.id === player.state.current_entry_id);
161
+ const next = command.type === "next" ? index + 1 : Math.max(0, index - 1);
162
+ if (next >= player.state.queue.length) {
163
+ await this.stop(player);
164
+ player.state.status = "ended";
165
+ }
166
+ else {
167
+ await this.play(player, player.state.queue[next]?.id, command, false);
168
+ return { player: this.get(id) };
169
+ }
170
+ break;
171
+ }
172
+ case "seek":
173
+ if (!player.state.current_entry_id)
174
+ throw new ServerPlayerError("NO_CURRENT_TRACK", "No current track", 400);
175
+ if (this.runtimes.has(id))
176
+ await this.active(id).backend.seek(command.seconds);
177
+ player.state.position = command.seconds;
178
+ player.state.checkpoint = !this.runtimes.has(id);
179
+ break;
180
+ case "volume":
181
+ if (this.runtimes.has(id))
182
+ await this.active(id).backend.setVolume(command.volume);
183
+ player.state.volume = command.volume;
184
+ break;
185
+ }
186
+ // Queue/settings edits do not recover failed playback. Keep its cause
187
+ // visible until a transport command successfully leaves the error state.
188
+ if (player.state.status !== "error")
189
+ player.state.error = null;
190
+ player = await this.save(player);
191
+ return { player: clone(player) };
192
+ }
193
+ catch (error) {
194
+ // Validation failures do not mutate authoritative state. Engine operations
195
+ // persist an actionable error; they never advance or silently retry.
196
+ if (!(error instanceof ServerPlayerError)) {
197
+ player = this.get(id);
198
+ player.state.status = "error";
199
+ player.state.error = errorValue(error);
200
+ await this.save(player);
201
+ }
202
+ throw error;
203
+ }
204
+ });
205
+ }
206
+ async play(player, entryId, policy, resumeCheckpoint = true) {
207
+ const entry = player.state.queue.find((entry) => entry.id === entryId);
208
+ if (!entry)
209
+ throw new ServerPlayerError("ENTRY_NOT_FOUND", "Queue is empty or the entry does not exist", 400);
210
+ const output = player.state.desired_output;
211
+ if (!output)
212
+ throw new ServerPlayerError("OUTPUT_REQUIRED", "Select a desired output before playing", 400);
213
+ const owner = this.list().find((p) => p.assigned_output === output && p.id !== player.id);
214
+ if (owner)
215
+ throw new ServerPlayerError("OUTPUT_ASSIGNED", "Speaker belongs to another player; use explicit transfer");
216
+ const needsReconnect = (player.state.status === "paused" || player.state.status === "error") && await this.runtimes.get(player.id)?.backend.outputConnection?.() !== "connected";
217
+ if (player.assigned_output !== output || needsReconnect)
218
+ await this.requireAvailable(output, policy);
219
+ const track = await this.store.resolve(entry);
220
+ const engine = this.engine(player);
221
+ const source = track ? audioSource(track, { apiBase: "http://unused/api", audioLibraryRoot: engine.libraryRoot }) : null;
222
+ if (!source)
223
+ throw new ServerPlayerError("NO_AUDIO", "Current track has no indexed audio source", 422);
224
+ const position = resumeCheckpoint && player.state.current_entry_id === entry.id ? player.state.position : 0;
225
+ await this.detach(player);
226
+ player.assigned_output = output;
227
+ player.state.current_entry_id = entry.id;
228
+ player.state.position = position;
229
+ player.state.duration = null;
230
+ player.state.status = "loading";
231
+ player.state.checkpoint = false;
232
+ player.state.error = null;
233
+ // Persist the selected entry before engine effects. A crash never replays a
234
+ // completed entry or automatically starts this pending load on restoration.
235
+ player = await this.save(player);
236
+ this.available();
237
+ const backend = engine.create(output);
238
+ const runtime = { backend, generation: ++this.generation };
239
+ this.runtimes.set(player.id, runtime);
240
+ backend.on("position", (position, duration) => this.enqueueEvent(player.id, runtime, async (current) => {
241
+ if (!Number.isFinite(position) || position < 0)
242
+ return;
243
+ current.state.position = position;
244
+ current.state.duration = duration !== null && Number.isFinite(duration) && duration >= 0 ? duration : null;
245
+ current.state.checkpoint = false;
246
+ this.players.set(current.id, current);
247
+ this.emit("position", { player_id: current.id, revision: current.revision, current_entry_id: current.state.current_entry_id, position: current.state.position, duration: current.state.duration, observed_at: new Date().toISOString() });
248
+ if (Date.now() - (this.checkpointAt.get(current.id) ?? 0) >= 5000) {
249
+ await this.store.checkpoint(current);
250
+ this.checkpointAt.set(current.id, Date.now());
251
+ }
252
+ }));
253
+ backend.on("ended", () => this.enqueueEvent(player.id, runtime, async (current) => {
254
+ const index = current.state.queue.findIndex((entry) => entry.id === current.state.current_entry_id);
255
+ const next = current.state.queue[index + 1];
256
+ if (next) {
257
+ current.state.current_entry_id = next.id;
258
+ current.state.position = 0;
259
+ current.state.duration = null;
260
+ current.state.status = "loading";
261
+ const advanced = await this.save(current);
262
+ await this.play(advanced, next.id, { allow_unknown: true, takeover: false }, false);
263
+ }
264
+ else {
265
+ await this.stop(current);
266
+ current.state.status = "ended";
267
+ await this.save(current);
268
+ }
269
+ }));
270
+ backend.on("error", (error) => this.enqueueEvent(player.id, runtime, async (current) => {
271
+ current.state.status = "error";
272
+ current.state.error = errorValue(error);
273
+ try {
274
+ await this.detach(current);
275
+ }
276
+ catch { /* retain the uncertain claim */ }
277
+ await this.save(current);
278
+ }));
279
+ try {
280
+ await backend.load(source);
281
+ if (position > 0)
282
+ await backend.seek(position);
283
+ await backend.setVolume(player.state.volume);
284
+ player.state.status = "playing";
285
+ await this.save(player);
286
+ }
287
+ catch (error) {
288
+ player = this.get(player.id);
289
+ player.state.status = "error";
290
+ player.state.error = errorValue(error);
291
+ try {
292
+ await this.detach(player);
293
+ }
294
+ catch { /* don't transfer an uncertain claim */ }
295
+ await this.save(player);
296
+ throw error;
297
+ }
298
+ }
299
+ enqueueEvent(id, runtime, action) {
300
+ void this.serial(async () => {
301
+ if (this.stopping || this.runtimes.get(id)?.generation !== runtime.generation)
302
+ return;
303
+ try {
304
+ await action(this.get(id));
305
+ }
306
+ catch (error) {
307
+ const current = this.get(id);
308
+ current.state.status = "error";
309
+ current.state.error = errorValue(error);
310
+ try {
311
+ await this.detach(current);
312
+ }
313
+ catch { /* preserve uncertain claim */ }
314
+ await this.save(current);
315
+ }
316
+ }).catch((error) => { this.emit("fault", error); });
317
+ }
318
+ async detach(player) {
319
+ const runtime = this.runtimes.get(player.id);
320
+ if (runtime) {
321
+ // Invalidate callbacks before awaiting close, including duplicate EOFs.
322
+ this.runtimes.delete(player.id);
323
+ await runtime.backend.close();
324
+ }
325
+ else if (player.assigned_output !== null) {
326
+ throw new ServerPlayerError("RELEASE_UNCERTAIN", "Previous engine release is uncertain; stop that engine and restart the worker before reassigning", 503);
327
+ }
328
+ player.assigned_output = null;
329
+ }
330
+ async stop(player, preserveCurrent = false) {
331
+ await this.detach(player);
332
+ player.state.status = preserveCurrent && player.state.current_entry_id ? "paused" : "stopped";
333
+ player.state.checkpoint = preserveCurrent && player.state.current_entry_id !== null;
334
+ if (!preserveCurrent) {
335
+ player.state.current_entry_id = null;
336
+ player.state.position = 0;
337
+ player.state.duration = null;
338
+ }
339
+ }
340
+ async transfer(target, command) {
341
+ if (target.id === command.from_player_id)
342
+ throw new ServerPlayerError("INVALID_TRANSFER", "Transfer requires two different players", 400);
343
+ let source = this.get(command.from_player_id);
344
+ this.revision(source, command.from_revision);
345
+ if (source.assigned_output !== command.output_id)
346
+ throw new ServerPlayerError("OUTPUT_NOT_ASSIGNED", "Source player does not own this speaker");
347
+ let detached = false;
348
+ try {
349
+ await this.stop(source, true);
350
+ source = await this.save(source);
351
+ detached = true;
352
+ if (this.runtimes.has(target.id)) {
353
+ await this.stop(target, true);
354
+ target = await this.save(target);
355
+ }
356
+ target.state.desired_output = command.output_id;
357
+ target = await this.save(target);
358
+ if (command.play)
359
+ await this.play(target, target.state.current_entry_id ?? target.state.queue[0]?.id, command);
360
+ return { player: this.get(target.id), transfer: { from_player: this.get(source.id), detached, attached: command.play, error: null } };
361
+ }
362
+ catch (error) {
363
+ if (!detached) {
364
+ source.state.status = "error";
365
+ source.state.error = errorValue(error);
366
+ try {
367
+ await this.save(source);
368
+ }
369
+ catch { /* keep the transfer result truthful */ }
370
+ }
371
+ // The source may already be detached. Report each phase; do not reconnect
372
+ // it to pretend the operation was atomic or compete with an external app.
373
+ return { player: this.get(target.id), transfer: { from_player: this.get(source.id), detached, attached: false, error: errorValue(error) } };
374
+ }
375
+ }
376
+ async shutdown() {
377
+ this.stopping = true;
378
+ await this.serial(async () => {
379
+ for (const player of this.list()) {
380
+ try {
381
+ await this.stop(player, true);
382
+ await this.save(player);
383
+ }
384
+ catch (error) {
385
+ this.emit("fault", error);
386
+ }
387
+ }
388
+ });
389
+ }
390
+ async requireAvailable(output, policy) {
391
+ const observed = await this.observe(output);
392
+ if (observed.availability === "occupied" && !policy.takeover)
393
+ throw new ServerPlayerError("SPEAKER_OCCUPIED", "Observed speaker activity requires explicit takeover");
394
+ if (observed.availability === "unknown" && !policy.allow_unknown)
395
+ throw new ServerPlayerError("AVAILABILITY_UNKNOWN", "Speaker availability is unknown; explicitly acknowledge it before connecting");
396
+ }
397
+ engine(player) {
398
+ const engine = this.engines[player.engine_id];
399
+ if (!engine)
400
+ throw new ServerPlayerError("ENGINE_NOT_CONFIGURED", "Player engine is not configured", 503);
401
+ return engine;
402
+ }
403
+ active(id) {
404
+ const runtime = this.runtimes.get(id);
405
+ if (!runtime)
406
+ throw new ServerPlayerError("PLAYER_INACTIVE", "Player has no active engine session; explicitly play to start one");
407
+ return runtime;
408
+ }
409
+ revision(player, expected) {
410
+ if (player.revision !== expected)
411
+ throw new ServerPlayerError("REVISION_CONFLICT", "Player changed; refresh its state before retrying");
412
+ }
413
+ available() { if (this.stopping)
414
+ throw new ServerPlayerError("WORKER_STOPPING", "Player worker is stopping", 503); }
415
+ async save(player) {
416
+ const saved = await this.store.save(player, player.revision);
417
+ this.players.set(player.id, saved);
418
+ this.publish(saved);
419
+ return clone(saved);
420
+ }
421
+ publish(player) { this.emit("snapshot", clone(player)); }
422
+ }
423
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/server/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type SpeakerObserver } from "./manager.js";
2
+ /** Read-only observation. Neither a room state nor sender IP proves app identity. */
3
+ export declare function homeAssistantObserver(options: {
4
+ url?: string;
5
+ token?: string;
6
+ entities?: Record<string, string>;
7
+ fetch?: typeof fetch;
8
+ }): SpeakerObserver;
9
+ //# sourceMappingURL=observer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.d.ts","sourceRoot":"","sources":["../../src/server/observer.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpE,qFAAqF;AACrF,wBAAgB,qBAAqB,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;CAAE,GAAG,eAAe,CAsBzJ"}
@@ -0,0 +1,29 @@
1
+ import { unknownSpeaker } from "./manager.js";
2
+ /** Read-only observation. Neither a room state nor sender IP proves app identity. */
3
+ export function homeAssistantObserver(options) {
4
+ const transport = options.fetch ?? globalThis.fetch;
5
+ const cache = new Map();
6
+ return async (outputId) => {
7
+ const entity = options.entities?.[outputId];
8
+ if (!options.url || !options.token || !entity)
9
+ return unknownSpeaker();
10
+ const cached = cache.get(outputId);
11
+ if (cached && cached.until > Date.now())
12
+ return structuredClone(cached.value);
13
+ let value = { availability: "unknown", observed_at: null, source: "home_assistant", sender_identity: "unknown" };
14
+ try {
15
+ const response = await transport(new URL(`api/states/${encodeURIComponent(entity)}`, options.url.endsWith("/") ? options.url : `${options.url}/`), { headers: { Authorization: `Bearer ${options.token}` }, signal: AbortSignal.timeout(2000), redirect: "error" });
16
+ if (response.ok) {
17
+ const body = await response.json();
18
+ if (body && typeof body === "object" && "state" in body && typeof body.state === "string") {
19
+ const state = body.state.toLowerCase();
20
+ value = { ...value, availability: ["unknown", "unavailable"].includes(state) ? "unknown" : ["idle", "off", "standby"].includes(state) ? "idle" : "occupied", observed_at: new Date().toISOString() };
21
+ }
22
+ }
23
+ }
24
+ catch { /* Missing/stale observation is unknown, never free. */ }
25
+ cache.set(outputId, { until: Date.now() + 5000, value });
26
+ return structuredClone(value);
27
+ };
28
+ }
29
+ //# sourceMappingURL=observer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observer.js","sourceRoot":"","sources":["../../src/server/observer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAwB,MAAM,cAAc,CAAC;AAEpE,qFAAqF;AACrF,MAAM,UAAU,qBAAqB,CAAC,OAAkG;IACtI,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC;IACpD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAwD,CAAC;IAC9E,OAAO,KAAK,EAAE,QAAQ,EAAE,EAAE;QACxB,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO,cAAc,EAAE,CAAC;QACvE,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,MAAM,IAAI,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE;YAAE,OAAO,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9E,IAAI,KAAK,GAAuB,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC;QACrI,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,IAAI,GAAG,CAAC,cAAc,kBAAkB,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;YACpQ,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAChB,MAAM,IAAI,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAC5C,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;oBAC1F,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;oBACvC,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,EAAE,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;gBACvM,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAC,uDAAuD,CAAC,CAAC;QACnE,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACzD,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { ServerPlayerManager } from "./manager.js";
2
+ import { PostgresPlayerStore } from "./store.js";
3
+ import type { Server } from "node:http";
4
+ import type { EventEmitter } from "node:events";
5
+ export declare function runPlayerWorker(env?: NodeJS.ProcessEnv, dependencies?: {
6
+ store?: PostgresPlayerStore;
7
+ manager?: ServerPlayerManager;
8
+ server?: Server;
9
+ signals?: EventEmitter;
10
+ }): Promise<{
11
+ manager: ServerPlayerManager;
12
+ server: Server<typeof import("node:http").IncomingMessage, typeof import("node:http").ServerResponse>;
13
+ store: PostgresPlayerStore;
14
+ shutdown: () => Promise<void>;
15
+ }>;
16
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/server/runtime.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,mBAAmB,EAAqB,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAIjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAsB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,EAAE,YAAY,GAAE;IAAE,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAAC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,YAAY,CAAA;CAAO;;;;oBAkB/K,OAAO,CAAC,IAAI,CAAC;GAqCnC"}
@@ -0,0 +1,78 @@
1
+ import { Pool } from "pg";
2
+ import { z } from "zod";
3
+ import { OwnToneBackend } from "../ownToneBackend.js";
4
+ import { playerIdSchema } from "@groovenet/contracts";
5
+ import { ServerPlayerManager } from "./manager.js";
6
+ import { PostgresPlayerStore } from "./store.js";
7
+ import { homeAssistantObserver } from "./observer.js";
8
+ import { playerHttpServer } from "./http.js";
9
+ export async function runPlayerWorker(env = process.env, dependencies = {}) {
10
+ const connectionSchema = z.object({ apiUrl: z.url().refine((value) => { const url = new URL(value); return ["http:", "https:"].includes(url.protocol) && !url.username && !url.password && !url.search && !url.hash; }), libraryRoot: z.string().startsWith("/"), password: z.string().optional() }).strict();
11
+ const configured = z.record(playerIdSchema, connectionSchema).parse(JSON.parse(env.PLAYER_ENGINES_JSON ?? "{}"));
12
+ if (Object.keys(configured).length === 0)
13
+ throw new Error("PLAYER_ENGINES_JSON requires at least one dedicated engine");
14
+ const urls = Object.values(configured).map((engine) => new URL(engine.apiUrl).href.replace(/\/$/, ""));
15
+ if (new Set(urls).size !== urls.length)
16
+ throw new Error("Each player engine must have a different API URL");
17
+ const key = env.PLAYER_API_KEY ?? "";
18
+ if (key.length < 32)
19
+ throw new Error("PLAYER_API_KEY must contain at least 32 characters");
20
+ if (!env.DATABASE_URL)
21
+ throw new Error("DATABASE_URL is required");
22
+ const engines = Object.fromEntries(Object.entries(configured).map(([id, options]) => [id, { libraryRoot: options.libraryRoot, create: (outputId) => new OwnToneBackend({ apiUrl: options.apiUrl, password: options.password, outputId }) }]));
23
+ const entities = z.record(z.string(), z.string().regex(/^[a-z_]+\.[a-z0-9_]+$/)).parse(JSON.parse(env.PLAYER_HA_ENTITIES_JSON ?? "{}"));
24
+ const store = dependencies.store ?? new PostgresPlayerStore(new Pool({ connectionString: env.DATABASE_URL, connectionTimeoutMillis: 5000, query_timeout: 5000, statement_timeout: 5000 }));
25
+ const manager = dependencies.manager ?? new ServerPlayerManager(store, engines, homeAssistantObserver({ url: env.PLAYER_HA_URL, token: env.PLAYER_HA_TOKEN, entities }));
26
+ const server = dependencies.server ?? playerHttpServer(manager, key);
27
+ const signals = dependencies.signals ?? process;
28
+ let stopping = false;
29
+ let shutdownPromise;
30
+ let heartbeat;
31
+ function shutdown() {
32
+ if (shutdownPromise)
33
+ return shutdownPromise;
34
+ stopping = true;
35
+ clearInterval(heartbeat);
36
+ signals.off("SIGINT", stop);
37
+ signals.off("SIGTERM", stop);
38
+ server.close();
39
+ server.closeAllConnections();
40
+ shutdownPromise = (async () => {
41
+ try {
42
+ await manager.shutdown();
43
+ }
44
+ finally {
45
+ manager.off("fault", failStop);
46
+ await store.release();
47
+ }
48
+ })();
49
+ return shutdownPromise;
50
+ }
51
+ const stop = () => { void shutdown().catch(() => { process.exitCode = 1; }); };
52
+ const failStop = () => { process.exitCode = 1; stop(); };
53
+ try {
54
+ await store.acquire(failStop);
55
+ manager.on("fault", failStop);
56
+ await manager.initialize();
57
+ if (stopping)
58
+ throw new Error("Player worker lost its lease during initialization");
59
+ let checking = false;
60
+ heartbeat = setInterval(() => {
61
+ if (checking)
62
+ return;
63
+ checking = true;
64
+ void store.heartbeat().catch(failStop).finally(() => { checking = false; });
65
+ }, 1000);
66
+ signals.once("SIGINT", stop);
67
+ signals.once("SIGTERM", stop);
68
+ const port = z.coerce.number().int().min(1).max(65535).parse(env.PLAYER_WORKER_PORT ?? "13700");
69
+ server.once("error", failStop);
70
+ server.listen(port, "0.0.0.0", () => console.log(`Player worker listening on ${port}`));
71
+ return { manager, server, store, shutdown };
72
+ }
73
+ catch (error) {
74
+ await shutdown().catch(() => { });
75
+ throw error;
76
+ }
77
+ }
78
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/server/runtime.ts"],"names":[],"mappings":"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"}