@vestra-player/sdk 1.5.6 → 1.5.7
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/CHANGELOG.md +9 -0
- package/dist/plugin/stream.js +62 -4
- package/dist/plugin/testing.d.ts +23 -0
- package/dist/plugin/testing.js +44 -1
- package/dist/plugin/types.d.ts +19 -4
- package/llms-full.txt +32 -3
- package/llms.txt +27 -27
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.7
|
|
4
|
+
|
|
5
|
+
**Replaced or closed streams stop with `null`.** A fix; no API change, `apiVersion` stays `vestra/1`, any player.
|
|
6
|
+
|
|
7
|
+
- **`Stream.onStop(null)` when another source replaced the stream or it was closed**, as documented. The player retires a stream's producer at once in both cases, and the SDK then passed the player's answer (`unavailable`, "producer absent") to `onStop` as an error. The SDK now reads the player's state to tell the two apart: replaced or closed is `null`; a failure is a `stream` error with the player's text (also when the player retired the producer right after failing it, which used to give "producer absent" too).
|
|
8
|
+
- The same in the start-up paths: `openStream` replaced or closed before it ran rejects with `replaced` (was `unavailable`); a file resolver's stream replaced after the player selected it is a cancellation and no longer reaches `onError`.
|
|
9
|
+
- **A file resolver's stream that cannot start after `resolve` returned** fails the open with Chinese text instead of the SDK's English (`"audio device is not ready: deviceLost"`): `device` "音频输出设备不可用", `timeout` "启动超时", `busy` "播放器正忙,请稍后重试", other start-up failures "无法启动播放"; the English detail goes to the plugin's log. The plugin never saw these errors, so the user read the SDK's words. An error thrown by `frame()` keeps its message; text for a file the plugin can judge (a bad header) belongs in `resolve`.
|
|
10
|
+
- **`TestHost` retires producers like the player**: on `open`, `close`, another `openProvider` or resolved stream, a retired producer's `producer.state` answers `unavailable` and `producer.ack` `stale`. New `FakeProducer.retired`, and **`host.failStream(producerId, message)`** to fail the playing stream the way the player does (`phase: "error"` with `message`, producer retired). Tests that replaced a stream used to pass where the player failed.
|
|
11
|
+
|
|
3
12
|
## 1.5.6
|
|
4
13
|
|
|
5
14
|
**A reference for AI assistants.** Documentation and packaging only; no API change, `apiVersion` stays `vestra/1`. (1.5.5 was not published to npm; this release includes its changes.)
|
package/dist/plugin/stream.js
CHANGED
|
@@ -79,7 +79,8 @@ class StreamHandle {
|
|
|
79
79
|
}, 5);
|
|
80
80
|
}
|
|
81
81
|
catch (error) {
|
|
82
|
-
|
|
82
|
+
const retired = await retirement(this.#service, this.#generation, error);
|
|
83
|
+
void this.#end(retired === undefined ? toError(error) : retired);
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
async #render() {
|
|
@@ -131,6 +132,28 @@ class StreamHandle {
|
|
|
131
132
|
await this.stop().catch(() => { });
|
|
132
133
|
}
|
|
133
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The player retires a producer the moment another source replaces it or
|
|
137
|
+
* the source closes, and after it failed, so its `producer.state` then
|
|
138
|
+
* answers `unavailable` (and `producer.ack` `stale`). The player's state
|
|
139
|
+
* says which: `null` (replaced or closed), the failure with the player's
|
|
140
|
+
* words, or `undefined` when the error is not a retirement.
|
|
141
|
+
*/
|
|
142
|
+
async function retirement(service, generation, error) {
|
|
143
|
+
const code = error?.code;
|
|
144
|
+
if (code !== "unavailable" && code !== "stale")
|
|
145
|
+
return undefined;
|
|
146
|
+
const readable = service.runtime.can("playback.read");
|
|
147
|
+
if (readable)
|
|
148
|
+
await service.refresh().catch(() => { });
|
|
149
|
+
const raw = service.raw;
|
|
150
|
+
if (raw.stamp.generation !== generation || raw.phase === "empty")
|
|
151
|
+
return null;
|
|
152
|
+
if (raw.phase === "error")
|
|
153
|
+
return failure("stream", service.state.error ?? "无法打开此媒体");
|
|
154
|
+
// Without `playback.read` the state may be behind: no failure was seen.
|
|
155
|
+
return readable ? undefined : null;
|
|
156
|
+
}
|
|
134
157
|
/** Start-up timeouts say which step ran out of time. */
|
|
135
158
|
function timeout(stage, message) {
|
|
136
159
|
return Object.assign(failure("timeout", message), { stage });
|
|
@@ -303,10 +326,40 @@ export async function openStream(scope, service, options) {
|
|
|
303
326
|
// An abort can arrive before the host answered the selecting request: wait
|
|
304
327
|
// for that answer so a source selected afterwards is still closed.
|
|
305
328
|
await opened?.catch(() => { });
|
|
329
|
+
// Read before closing: replaced during activation is not a failure.
|
|
330
|
+
const retired = generation === undefined
|
|
331
|
+
? undefined
|
|
332
|
+
: await retirement(service, generation, error);
|
|
306
333
|
await abandon(service, p, generation);
|
|
307
|
-
|
|
334
|
+
if (retired === null)
|
|
335
|
+
throw failure("replaced", "另一个来源已替换此媒体");
|
|
336
|
+
throw retired ?? error;
|
|
308
337
|
}
|
|
309
338
|
}
|
|
339
|
+
/**
|
|
340
|
+
* What the user reads when a file resolver's stream cannot start. The
|
|
341
|
+
* player shows the answer as the open's failure, and the plugin never sees
|
|
342
|
+
* these errors: they happen after `resolve` returned. The SDK's and the
|
|
343
|
+
* player's own codes get the player's words; the detail goes to the log.
|
|
344
|
+
* An error the plugin threw from `frame()` keeps its own words.
|
|
345
|
+
*/
|
|
346
|
+
const START_TEXT = {
|
|
347
|
+
device: "音频输出设备不可用",
|
|
348
|
+
timeout: "启动超时",
|
|
349
|
+
busy: "播放器正忙,请稍后重试",
|
|
350
|
+
stream: "无法启动播放",
|
|
351
|
+
producer: "无法启动播放",
|
|
352
|
+
unavailable: "无法启动播放",
|
|
353
|
+
invalid: "无法启动播放",
|
|
354
|
+
};
|
|
355
|
+
function startFailure(scope, error) {
|
|
356
|
+
const code = error?.code;
|
|
357
|
+
const text = code === undefined ? undefined : START_TEXT[code];
|
|
358
|
+
if (text === undefined)
|
|
359
|
+
return error;
|
|
360
|
+
console.error(`[${scope.id}] file resolver stream did not start:`, error.message);
|
|
361
|
+
return Object.assign(failure(code, text), { cause: error });
|
|
362
|
+
}
|
|
310
363
|
/**
|
|
311
364
|
* A file resolver's stream (`ResolvedStream`): prepared and prerolled while
|
|
312
365
|
* the open waits, then handed to the host as the answer. Once the host
|
|
@@ -320,6 +373,8 @@ export async function resolvedStream(scope, service, stream, signal) {
|
|
|
320
373
|
const p = await prepare(host, stream, () => {
|
|
321
374
|
if (signal.aborted || scope.disposed)
|
|
322
375
|
throw abortError();
|
|
376
|
+
}).catch((error) => {
|
|
377
|
+
throw startFailure(scope, error);
|
|
323
378
|
});
|
|
324
379
|
return {
|
|
325
380
|
kind: "stream",
|
|
@@ -336,9 +391,12 @@ export async function resolvedStream(scope, service, stream, signal) {
|
|
|
336
391
|
throw abortError();
|
|
337
392
|
}
|
|
338
393
|
catch (error) {
|
|
394
|
+
// Replaced or closed before it ran is a cancellation, not a failure.
|
|
395
|
+
const retired = await retirement(service, stamp.generation, error);
|
|
339
396
|
await abandon(service, p, stamp.generation);
|
|
340
|
-
|
|
341
|
-
|
|
397
|
+
const reported = retired === undefined ? error : retired;
|
|
398
|
+
if (reported && reported.code !== "aborted")
|
|
399
|
+
scope.report(reported);
|
|
342
400
|
return;
|
|
343
401
|
}
|
|
344
402
|
const handle = handleOf(scope, service, p, stamp.generation);
|
package/dist/plugin/testing.d.ts
CHANGED
|
@@ -117,6 +117,13 @@ export interface FakeProducer {
|
|
|
117
117
|
videoFrames: number;
|
|
118
118
|
/** `true` once the plugin released the producer (stream stopped or disposed). */
|
|
119
119
|
released: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* `true` once the player retired the producer, as the real player does at
|
|
122
|
+
* once: another source replaced it (`open`, another stream, a resolved
|
|
123
|
+
* file), the source was closed, or {@link TestHost.failStream} failed it.
|
|
124
|
+
* A retired producer's `producer.state` answers `unavailable`.
|
|
125
|
+
*/
|
|
126
|
+
retired: boolean;
|
|
120
127
|
/**
|
|
121
128
|
* `true` if the frame connection closed while this stream was still the
|
|
122
129
|
* player's source: the player reports that as a playback failure
|
|
@@ -1025,6 +1032,22 @@ export declare class TestHost implements Transport {
|
|
|
1025
1032
|
* ```
|
|
1026
1033
|
*/
|
|
1027
1034
|
openElsewhere(source: string, phase?: SourceState["phase"]): void;
|
|
1035
|
+
/**
|
|
1036
|
+
* Simulate a generated stream failing in the player (its audio device
|
|
1037
|
+
* lost, its frame connection broken), like the player: the source fails
|
|
1038
|
+
* with `message` (`phase: "error"`) and the producer is retired at once.
|
|
1039
|
+
* The stream's `onStop` listeners get a `stream` error with `message`.
|
|
1040
|
+
*
|
|
1041
|
+
* @param producerId - The producer, from {@link TestHost.producers}.
|
|
1042
|
+
* @param message - The player's failure text.
|
|
1043
|
+
*
|
|
1044
|
+
* @example
|
|
1045
|
+
* ```ts
|
|
1046
|
+
* const [producer] = host.producers.values();
|
|
1047
|
+
* host.failStream(producer.id, "producer transport closed");
|
|
1048
|
+
* ```
|
|
1049
|
+
*/
|
|
1050
|
+
failStream(producerId: number, message: string): void;
|
|
1028
1051
|
/**
|
|
1029
1052
|
* Press a binding like the host's table would: panel-focus bindings first
|
|
1030
1053
|
* when `panel` is true, then global ones; the earliest registration wins a
|
package/dist/plugin/testing.js
CHANGED
|
@@ -1385,6 +1385,31 @@ export class TestHost {
|
|
|
1385
1385
|
if (this.#resolve(source, false) === null)
|
|
1386
1386
|
this.setSource({ phase });
|
|
1387
1387
|
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Simulate a generated stream failing in the player (its audio device
|
|
1390
|
+
* lost, its frame connection broken), like the player: the source fails
|
|
1391
|
+
* with `message` (`phase: "error"`) and the producer is retired at once.
|
|
1392
|
+
* The stream's `onStop` listeners get a `stream` error with `message`.
|
|
1393
|
+
*
|
|
1394
|
+
* @param producerId - The producer, from {@link TestHost.producers}.
|
|
1395
|
+
* @param message - The player's failure text.
|
|
1396
|
+
*
|
|
1397
|
+
* @example
|
|
1398
|
+
* ```ts
|
|
1399
|
+
* const [producer] = host.producers.values();
|
|
1400
|
+
* host.failStream(producer.id, "producer transport closed");
|
|
1401
|
+
* ```
|
|
1402
|
+
*/
|
|
1403
|
+
failStream(producerId, message) {
|
|
1404
|
+
const producer = this.producers.get(producerId);
|
|
1405
|
+
if (!producer || !this.#current(producerId))
|
|
1406
|
+
throw new Error(`producer ${producerId} is not the player's source`);
|
|
1407
|
+
this.setSource({
|
|
1408
|
+
phase: "error",
|
|
1409
|
+
terminalReason: { failed: message },
|
|
1410
|
+
});
|
|
1411
|
+
producer.retired = true;
|
|
1412
|
+
}
|
|
1388
1413
|
/**
|
|
1389
1414
|
* Press a binding like the host's table would: panel-focus bindings first
|
|
1390
1415
|
* when `panel` is true, then global ones; the earliest registration wins a
|
|
@@ -1721,6 +1746,10 @@ export class TestHost {
|
|
|
1721
1746
|
if (control.kind === "rate")
|
|
1722
1747
|
next.rate = Number(control.rate);
|
|
1723
1748
|
}
|
|
1749
|
+
// Like the player: the selected producer is retired as soon as another
|
|
1750
|
+
// source replaces it or the source closes.
|
|
1751
|
+
if (["open", "openProvider", "close"].includes(String(intent.kind)))
|
|
1752
|
+
this.#retireSelected(intent.kind === "openProvider" ? Number(intent.producerId) : null);
|
|
1724
1753
|
if (intent.kind === "openProvider")
|
|
1725
1754
|
this.#selected = Number(intent.producerId);
|
|
1726
1755
|
if (intent.kind === "open" || intent.kind === "close")
|
|
@@ -1748,6 +1777,14 @@ export class TestHost {
|
|
|
1748
1777
|
}
|
|
1749
1778
|
return null;
|
|
1750
1779
|
}
|
|
1780
|
+
/** Retire the producer that is the player's source, unless it is `keep`. */
|
|
1781
|
+
#retireSelected(keep) {
|
|
1782
|
+
for (const producer of this.producers.values())
|
|
1783
|
+
if (producer.id !== keep &&
|
|
1784
|
+
!producer.retired &&
|
|
1785
|
+
this.#current(producer.id))
|
|
1786
|
+
producer.retired = true;
|
|
1787
|
+
}
|
|
1751
1788
|
/** Whether producer `id` is the player's source. */
|
|
1752
1789
|
#current(id) {
|
|
1753
1790
|
return (this.source.kind === "producer" &&
|
|
@@ -1853,9 +1890,10 @@ export class TestHost {
|
|
|
1853
1890
|
const producer = this.producers.get(Number(result.producerId));
|
|
1854
1891
|
if (result.kind !== "stream")
|
|
1855
1892
|
fail("invalid resolved stream descriptor");
|
|
1856
|
-
if (!producer || producer.released)
|
|
1893
|
+
if (!producer || producer.released || producer.retired)
|
|
1857
1894
|
fail("unavailable: producer absent");
|
|
1858
1895
|
// Like openProvider, but the source keeps the path and `startPaused`.
|
|
1896
|
+
this.#retireSelected(producer.id);
|
|
1859
1897
|
this.#selected = producer.id;
|
|
1860
1898
|
producer.active = true;
|
|
1861
1899
|
producer.acknowledged = false;
|
|
@@ -2922,6 +2960,7 @@ export class TestHost {
|
|
|
2922
2960
|
receivedFrames: 0,
|
|
2923
2961
|
videoFrames: 0,
|
|
2924
2962
|
released: false,
|
|
2963
|
+
retired: false,
|
|
2925
2964
|
droppedWhileSelected: false,
|
|
2926
2965
|
path,
|
|
2927
2966
|
admitted: !tcp,
|
|
@@ -2988,6 +3027,8 @@ export class TestHost {
|
|
|
2988
3027
|
const producer = this.producers.get(Number(p.producerId));
|
|
2989
3028
|
if (!producer)
|
|
2990
3029
|
throw new HostError("missing", "producer");
|
|
3030
|
+
if (producer.retired || producer.released)
|
|
3031
|
+
throw new HostError("unavailable", "producer absent");
|
|
2991
3032
|
const current = this.#current(producer.id);
|
|
2992
3033
|
return {
|
|
2993
3034
|
input: null,
|
|
@@ -3017,6 +3058,8 @@ export class TestHost {
|
|
|
3017
3058
|
const producer = this.producers.get(Number(control.producerId));
|
|
3018
3059
|
if (!producer)
|
|
3019
3060
|
throw new HostError("missing", "producer");
|
|
3061
|
+
if (producer.retired || producer.released)
|
|
3062
|
+
throw new HostError("stale", "producer control changed");
|
|
3020
3063
|
producer.pending = null;
|
|
3021
3064
|
producer.acknowledged = true;
|
|
3022
3065
|
producer.epoch = Number(control.epoch);
|
package/dist/plugin/types.d.ts
CHANGED
|
@@ -3002,9 +3002,15 @@ export interface Stream extends Disposable {
|
|
|
3002
3002
|
/**
|
|
3003
3003
|
* The stream stopped for a reason outside this plugin: `error` is null when
|
|
3004
3004
|
* another source replaced it or the user closed it, otherwise the failure
|
|
3005
|
-
* (`stream
|
|
3006
|
-
* itself stops it (`stop()`, `dispose()`,
|
|
3007
|
-
* `stop()` is that notification.
|
|
3005
|
+
* (`stream` with the player's text, `invalid` for a bad frame, …). Not
|
|
3006
|
+
* called when the plugin itself stops it (`stop()`, `dispose()`,
|
|
3007
|
+
* deactivation): the promise of `stop()` is that notification.
|
|
3008
|
+
*
|
|
3009
|
+
* The player retires a stream's producer as soon as it is replaced or
|
|
3010
|
+
* closed; the SDK reads the player's state to tell that from a failure
|
|
3011
|
+
* (before 1.5.7 a replaced or closed stream could get `unavailable`
|
|
3012
|
+
* "producer absent"). Without `playback.read` a failure the SDK has not
|
|
3013
|
+
* seen yet is reported as null.
|
|
3008
3014
|
*/
|
|
3009
3015
|
onStop(listener: (error: PluginError | null) => void): Disposable;
|
|
3010
3016
|
/**
|
|
@@ -3147,7 +3153,8 @@ export interface Player {
|
|
|
3147
3153
|
* activation, pausing and acknowledgement. Frames travel over a local
|
|
3148
3154
|
* connection the SDK sets up: a Unix socket on macOS and Linux, loopback
|
|
3149
3155
|
* TCP with a token on Windows. Rejects with a {@link StreamStartError}
|
|
3150
|
-
* (`timeout`), `stream` or `
|
|
3156
|
+
* (`timeout`), `stream`, `device`, or `replaced` when another source
|
|
3157
|
+
* replaced it (or it was closed) before it ran.
|
|
3151
3158
|
*
|
|
3152
3159
|
* @example
|
|
3153
3160
|
* ```ts
|
|
@@ -3937,6 +3944,14 @@ export interface FileResolverOptions {
|
|
|
3937
3944
|
/**
|
|
3938
3945
|
* What a file resolver returns: a stream, with the same frame contract as
|
|
3939
3946
|
* {@link StreamOptions} (size limits, `frame()` on the audio clock).
|
|
3947
|
+
*
|
|
3948
|
+
* After `resolve` returns, the SDK starts the stream while the open waits.
|
|
3949
|
+
* If that fails, the open fails with the player's words instead of the
|
|
3950
|
+
* SDK's (the detail goes to the log): `device` "音频输出设备不可用",
|
|
3951
|
+
* `timeout` "启动超时", `busy` "播放器正忙,请稍后重试", other start-up
|
|
3952
|
+
* failures "无法启动播放". An error thrown by `frame()` keeps its message.
|
|
3953
|
+
* To give your own text for a file you can tell is unusable (a bad header),
|
|
3954
|
+
* check it in `resolve` and throw there.
|
|
3940
3955
|
*/
|
|
3941
3956
|
export interface ResolvedStream {
|
|
3942
3957
|
/** Always `"stream"`. */
|
package/llms-full.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @vestra-player/sdk 1.5.
|
|
1
|
+
# @vestra-player/sdk 1.5.7: full reference
|
|
2
2
|
|
|
3
3
|
> Everything needed to write a Vestra Player plugin (plugin API `vestra/1`): the guides, every public declaration of `@vestra-player/sdk` and `@vestra-player/sdk/testing` with its documentation, and the complete source of the example plugins. Generated from the SDK's sources by scripts/llms.mjs; the short index is llms.txt.
|
|
4
4
|
|
|
@@ -2616,7 +2616,17 @@ interface Stream extends Disposable {
|
|
|
2616
2616
|
/** The stream is producing frames. */
|
|
2617
2617
|
readonly running: boolean;
|
|
2618
2618
|
/**
|
|
2619
|
-
* The stream stopped for a reason outside this plugin: `error` is null when
|
|
2619
|
+
* The stream stopped for a reason outside this plugin: `error` is null when
|
|
2620
|
+
* another source replaced it or the user closed it, otherwise the failure
|
|
2621
|
+
* (`stream` with the player's text, `invalid` for a bad frame, …). Not
|
|
2622
|
+
* called when the plugin itself stops it (`stop()`, `dispose()`,
|
|
2623
|
+
* deactivation): the promise of `stop()` is that notification.
|
|
2624
|
+
*
|
|
2625
|
+
* The player retires a stream's producer as soon as it is replaced or
|
|
2626
|
+
* closed; the SDK reads the player's state to tell that from a failure
|
|
2627
|
+
* (before 1.5.7 a replaced or closed stream could get `unavailable`
|
|
2628
|
+
* "producer absent"). Without `playback.read` a failure the SDK has not
|
|
2629
|
+
* seen yet is reported as null.
|
|
2620
2630
|
*/
|
|
2621
2631
|
onStop(listener: (error: PluginError | null) => void): Disposable;
|
|
2622
2632
|
/**
|
|
@@ -2758,7 +2768,8 @@ interface Player {
|
|
|
2758
2768
|
* activation, pausing and acknowledgement. Frames travel over a local
|
|
2759
2769
|
* connection the SDK sets up: a Unix socket on macOS and Linux, loopback
|
|
2760
2770
|
* TCP with a token on Windows. Rejects with a StreamStartError
|
|
2761
|
-
* (`timeout`), `stream` or `
|
|
2771
|
+
* (`timeout`), `stream`, `device`, or `replaced` when another source
|
|
2772
|
+
* replaced it (or it was closed) before it ran.
|
|
2762
2773
|
*/
|
|
2763
2774
|
openStream(options: StreamOptions): Promise<Stream>;
|
|
2764
2775
|
/** Pause or resume. */
|
|
@@ -3389,6 +3400,14 @@ interface FileResolverOptions {
|
|
|
3389
3400
|
What a file resolver returns: a stream, with the same frame contract as
|
|
3390
3401
|
StreamOptions (size limits, `frame()` on the audio clock).
|
|
3391
3402
|
|
|
3403
|
+
After `resolve` returns, the SDK starts the stream while the open waits.
|
|
3404
|
+
If that fails, the open fails with the player's words instead of the
|
|
3405
|
+
SDK's (the detail goes to the log): `device` "音频输出设备不可用",
|
|
3406
|
+
`timeout` "启动超时", `busy` "播放器正忙,请稍后重试", other start-up
|
|
3407
|
+
failures "无法启动播放". An error thrown by `frame()` keeps its message.
|
|
3408
|
+
To give your own text for a file you can tell is unusable (a bad header),
|
|
3409
|
+
check it in `resolve` and throw there.
|
|
3410
|
+
|
|
3392
3411
|
```ts
|
|
3393
3412
|
interface ResolvedStream {
|
|
3394
3413
|
/** Always `"stream"`. */
|
|
@@ -4260,6 +4279,10 @@ interface FakeProducer {
|
|
|
4260
4279
|
videoFrames: number;
|
|
4261
4280
|
/** `true` once the plugin released the producer (stream stopped or disposed). */
|
|
4262
4281
|
released: boolean;
|
|
4282
|
+
/**
|
|
4283
|
+
* `true` once the player retired the producer, as the real player does at once: another source replaced it (`open`, another stream, a resolved file), the source was closed, or TestHost.failStream failed it. A retired producer's `producer.state` answers `unavailable`.
|
|
4284
|
+
*/
|
|
4285
|
+
retired: boolean;
|
|
4263
4286
|
/**
|
|
4264
4287
|
* `true` if the frame connection closed while this stream was still the player's source: the player reports that as a playback failure ("producer transport closed"). `stop()` closes the source first.
|
|
4265
4288
|
*/
|
|
@@ -4854,6 +4877,12 @@ class TestHost implements Transport {
|
|
|
4854
4877
|
* @param phase - The phase to end in (default `"ready"`).
|
|
4855
4878
|
*/
|
|
4856
4879
|
openElsewhere(source: string, phase?: SourceState["phase"]): void;
|
|
4880
|
+
/**
|
|
4881
|
+
* Simulate a generated stream failing in the player (its audio device lost, its frame connection broken), like the player: the source fails with `message` (`phase: "error"`) and the producer is retired at once. The stream's `onStop` listeners get a `stream` error with `message`.
|
|
4882
|
+
* @param producerId - The producer, from TestHost.producers.
|
|
4883
|
+
* @param message - The player's failure text.
|
|
4884
|
+
*/
|
|
4885
|
+
failStream(producerId: number, message: string): void;
|
|
4857
4886
|
/**
|
|
4858
4887
|
* Press a binding like the host's table would: panel-focus bindings first when `panel` is true, then global ones; the earliest registration wins a conflict. Resolves after the command ran; rejects when nothing is bound or the command is disabled or busy. Bindings use the reference syntax (`"Space"`, `"Ctrl+O"`, `"Mouse:WheelUp"`); the player's core actions match first, and user overrides in `bindings.overlay` apply.
|
|
4859
4888
|
*/
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vestra Player SDK
|
|
2
2
|
|
|
3
|
-
> @vestra-player/sdk 1.5.
|
|
3
|
+
> @vestra-player/sdk 1.5.7: write plugins for Vestra Player in TypeScript (plugin API `vestra/1`). A plugin is a folder with `manifest.json` and an entry module whose default export `activate(plugin)` the player calls; it runs in its own Node.js process. llms-full.txt, next to this file, has every declaration, the guides and the examples; L<n> below are its line numbers.
|
|
4
4
|
|
|
5
5
|
**How plugins run**
|
|
6
6
|
|
|
@@ -150,8 +150,8 @@ test("shows the position", async () => {
|
|
|
150
150
|
- [Concept: Windows, panels, settings pages, dialogs and the OSD](https://vestrasys.github.io/vestra-player-sdk/documents/Windows__panels__settings_pages__dialogs_and_the_OSD.html): L281–626
|
|
151
151
|
- [How-to: Keep a slider responsive while dragging](https://vestrasys.github.io/vestra-player-sdk/documents/Keep_a_slider_responsive_while_dragging.html): L627–801
|
|
152
152
|
- [How-to: Remember a window's state between runs](https://vestrasys.github.io/vestra-player-sdk/documents/Remember_a_window_s_state_between_runs.html): L802–931
|
|
153
|
-
- [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L932–
|
|
154
|
-
- [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html) (TestHost, startPlugin):
|
|
153
|
+
- [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L932–4172
|
|
154
|
+
- [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html) (TestHost, startPlugin): L4173–5113
|
|
155
155
|
|
|
156
156
|
## API
|
|
157
157
|
|
|
@@ -162,24 +162,24 @@ Where each module of `plugin` is declared:
|
|
|
162
162
|
- plugin.windows: Windows L1554
|
|
163
163
|
- plugin.panels: Panels L2005
|
|
164
164
|
- plugin.dialogs: Dialogs L2287
|
|
165
|
-
- plugin.player: Player
|
|
166
|
-
- plugin.video: VideoFilter
|
|
167
|
-
- plugin.audio: AudioFilter
|
|
168
|
-
- plugin.subtitles: Subtitles
|
|
169
|
-
- plugin.media: MediaCapture
|
|
170
|
-
- plugin.resolvers: Resolvers
|
|
171
|
-
- plugin.packages: Packages
|
|
172
|
-
- plugin.osd: Osd
|
|
173
|
-
- plugin.host: Host
|
|
174
|
-
- plugin.clipboard: Clipboard
|
|
175
|
-
- plugin.power: Power
|
|
176
|
-
- plugin.tools: Tools
|
|
177
|
-
- plugin.storage: Storage
|
|
178
|
-
- plugin.system: System
|
|
179
|
-
- plugin.secrets: Secrets
|
|
165
|
+
- plugin.player: Player L2726
|
|
166
|
+
- plugin.video: VideoFilter L2975, VideoView L3038, RenderOptions L3080
|
|
167
|
+
- plugin.audio: AudioFilter L2999
|
|
168
|
+
- plugin.subtitles: Subtitles L3178
|
|
169
|
+
- plugin.media: MediaCapture L3336
|
|
170
|
+
- plugin.resolvers: Resolvers L3441
|
|
171
|
+
- plugin.packages: Packages L3662
|
|
172
|
+
- plugin.osd: Osd L2912
|
|
173
|
+
- plugin.host: Host L2875
|
|
174
|
+
- plugin.clipboard: Clipboard L3769
|
|
175
|
+
- plugin.power: Power L3789
|
|
176
|
+
- plugin.tools: Tools L3822
|
|
177
|
+
- plugin.storage: Storage L3865
|
|
178
|
+
- plugin.system: System L3888
|
|
179
|
+
- plugin.secrets: Secrets L3906
|
|
180
180
|
- plugin.window: MainWindow L2128
|
|
181
181
|
- plugin.app: App L2209
|
|
182
|
-
- plugin.bindings: Bindings
|
|
182
|
+
- plugin.bindings: Bindings L3739
|
|
183
183
|
- plugin.ui: Ui L1441
|
|
184
184
|
- plugin.timeline: Timeline L2085
|
|
185
185
|
- plugin.drops: Drops L1851
|
|
@@ -190,14 +190,14 @@ Where each module of `plugin` is declared:
|
|
|
190
190
|
|
|
191
191
|
Complete plugins with tests, in [examples/](https://github.com/vestrasys/vestra-player-sdk/tree/main/examples) and in llms-full.txt:
|
|
192
192
|
|
|
193
|
-
- hello: The README Quick start: one menu command and an on-screen message.
|
|
194
|
-
- blur: A tool window with one slider that blurs the playing video.
|
|
195
|
-
- signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone.
|
|
196
|
-
- bookmark: A tool window bound to the playback state: remember the position and jump back to it.
|
|
197
|
-
- panel-menu: A sidebar panel with its own right-click menu.
|
|
198
|
-
- menus: Radio menu items, separator groups and a menu item whose title changes.
|
|
199
|
-
- theme-widgets: A tool window drawn with the player's theme and widgets.
|
|
200
|
-
- markdown: A window that shows a Markdown document.
|
|
193
|
+
- hello: The README Quick start: one menu command and an on-screen message. L5527–5616
|
|
194
|
+
- blur: A tool window with one slider that blurs the playing video. L5617–5878
|
|
195
|
+
- signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L5879–6211
|
|
196
|
+
- bookmark: A tool window bound to the playback state: remember the position and jump back to it. L6212–6396
|
|
197
|
+
- panel-menu: A sidebar panel with its own right-click menu. L6397–6602
|
|
198
|
+
- menus: Radio menu items, separator groups and a menu item whose title changes. L6603–6762
|
|
199
|
+
- theme-widgets: A tool window drawn with the player's theme and widgets. L6763–6926
|
|
200
|
+
- markdown: A window that shows a Markdown document. L6927–7120
|
|
201
201
|
|
|
202
202
|
## Optional
|
|
203
203
|
|