@vestra-player/sdk 1.5.53 → 1.5.55
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 +20 -0
- package/dist/docs-tree.json.gz +0 -0
- package/dist/plugin/index.d.ts +11 -1
- package/dist/plugin/index.js +3 -2
- package/dist/plugin/packages.js +4 -1
- package/dist/plugin/runtime.d.ts +10 -1
- package/dist/plugin/runtime.js +22 -0
- package/dist/plugin/scope.d.ts +3 -2
- package/dist/plugin/scope.js +9 -1
- package/dist/plugin/testing.d.ts +108 -8
- package/dist/plugin/testing.js +176 -10
- package/dist/plugin/types.d.ts +177 -6
- package/dist/wire.d.ts +6 -1
- package/dist/wire.js +8 -1
- package/llms.txt +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.55
|
|
4
|
+
|
|
5
|
+
**Who must change: nobody, but check `TestHost` tests that update a disabled plugin.** Additive: `InstallOptions` gains `enabled` and `TestHost` gains `installKeepsDisabled`, `apiVersion` stays `vestra/1`, no new capability, and existing code still compiles. Older players ignore the option, and an install there always enables the plugin. One `TestHost` behaviour change at run time: like player 0.6.4, it now keeps a `disabled` entry disabled when the plugin under test installs an update or reinstall over it without `enabled`, where it used to make it `running`. A test that expects the update to run (the player's Store test did) sets `host.installKeepsDisabled = false` to keep playing players up to 0.6.3, or asserts the new rule; a test that covers both runs once with each.
|
|
6
|
+
|
|
7
|
+
- **Choose whether an installed plugin runs** (`candidate.install({ enabled })`). Planned for player 0.6.4 (not released yet), where an update or reinstall (from any source, `replace` included) keeps the installed plugin's state when `enabled` is left out: a plugin the user or a store disabled stays disabled, and only its files are installed. A first install runs as before. `true` enables and starts it, for a store that disabled a revoked version and installs the update that fixes it (if the new version fails to start, the old one is restored, still disabled); `false` installs it disabled, stopping a running version and dropping its session values as `setEnabled(id, false)` does. Built-in plugins always run. The SDK sends `enabled` only when given and refuses anything but a boolean with `invalid` before sending. On players up to 0.6.3 the option is ignored and every install runs; call `packages.setEnabled` after the install is committed when the state matters there.
|
|
8
|
+
- **`TestHost`** models player 0.6.4 by default: a first install is `running`, an update of a `disabled` entry stays `disabled` unless `enabled: true`, `enabled: false` installs it `disabled`, and a non-boolean `enabled` on the wire is `invalid` (`null` reads as absent, as in the player). `installKeepsDisabled = false` plays players up to 0.6.3 instead: every install is `running` and `enabled` is ignored. `packageCalls` records `install`'s `enabled` when it was given.
|
|
9
|
+
- **Stop reasons, corrected** (SDK 1.5.54 docs, from the player's implementation for 0.6.4, still planned). `upgrade` also covers a new version stopped because its install was cancelled (the previous version comes back). `idle` comes only with lazy activation, which is off by default (`VESTRA_LAZY=1`), and also parks a plugin whose first run it judges lazy right after it is ready, not only after a while without use. `failed` is not sent for a full input queue: an own process is killed, and a member's full queue restarts the shared runner as a whole, as does an unreadable line from the runner itself. In those cases, and when an own process fails to activate or the process has already exited, the plugin gets no stop at all and its `onDispose` cleanups do not run. `host-lost` is not proof of a crash: player 0.6.4 sends `quit` on a normal exit, while an older player could close its pipes first when its window closed. [How a plugin runs › Restarts](docs/explanation/plugins.md#restarts-activate-can-run-more-than-once) now states as fact that plugins sharing one process are restarted together (player 0.5.6 and later). `StopReason`, [The plugin model](docs/explanation/plugins.md#stopping) and [Host and Node](docs/explanation/host-and-node.md#stopping-a-plugin) say so.
|
|
10
|
+
- _Docs_: `InstallOptions.enabled`, `PackageCandidate.install` (with an example; the `replace` example no longer promises a running plugin), `TestHost.candidates` and `TestHost.installKeepsDisabled` describe it.
|
|
11
|
+
|
|
12
|
+
## 1.5.54
|
|
13
|
+
|
|
14
|
+
**Who must change: nobody, but check cleanup functions passed directly to `onDispose`.** Additive: `Plugin.stopReason`, the types `StopReason` and `DisposeInfo`, an optional argument for `onDispose` cleanups, `TestHost.previousCrash`, an optional `code` for `TestHost.failVideoShaders` and `stop({ reason })` / `disconnect()` from `startPlugin`; `apiVersion` stays `vestra/1`, no new capability, and existing code still compiles. One runtime difference: with this SDK in the player (or in `TestHost`) an `onDispose` cleanup is called with one argument, the `DisposeInfo`. A function that takes optional parameters of its own and is passed directly now receives it as its first argument: `plugin.onDispose(controller.abort.bind(controller))` aborts with the `DisposeInfo` as the abort reason, `onDispose(save)` with `save(force?: boolean)` gets an object as `force`. Wrap such a function (`() => controller.abort()`). The stop reasons are planned for player 0.6.4; `previous-crash` comes from player 0.6.3.
|
|
15
|
+
|
|
16
|
+
- **Why the plugin stops** (API request of the Shader Upscaling author). A plugin can tell an orderly stop from a lost player: `plugin.stopReason` is set when the stop begins, and every `onDispose` cleanup receives a `DisposeInfo` (`{ stopping, reason }`); a cleanup without parameters works as before. `stopping` is `false` for a child scope the plugin's own code disposed. Planned for player 0.6.4 (not released yet): the player sends `quit`, `disable`, `uninstall`, `reload`, `upgrade`, `idle` or `failed` with `runtime.stop`, and its runner passes `host-lost` when its input closes (or its output breaks) without a `runtime.stop` (the player crashed or was killed; requests are refused, clean up locally only). The runner hands the reason to the SDK (`PluginInstance.dispose({ reason })` in `@vestra-player/sdk/runtime`; a reason the SDK does not know reads as `undefined`).
|
|
17
|
+
- **Older players.** Players up to 0.6.3 give no reason for any stop, a lost player included. One whose own SDK is older than 1.5.54 (0.6.3 and earlier) calls the cleanup **without an argument** and has no `plugin.stopReason` (it reads `undefined`): the parameter is optional (`info?: DisposeInfo`), so read it as `info?.reason`.
|
|
18
|
+
- **`previous-crash`** (player 0.6.3) is documented on `VideoShaders`, `RenderOptions` and in the `PluginError` table: after a run of the player that ended while plugin shaders were drawing (a crash, the process being ended, a lost GPU device), the player refuses every non-empty shader list, the render options' `shaders` included, with `previous-crash` and a message naming the plugins; one such end keeps them off for the next start, two within a day until the GPU, its driver or the player changes, or for 7 days. Clearing still works, the player shows one OSD line itself, and plugins should not set the list again while it is refused (the SDK does not retry a `failed` handle). The render options send every value set so far with each `set`, the refused `shaders` included: include `shaders: []` in the next `set` to go on without them.
|
|
19
|
+
- **`TestHost`**: `previousCrash` (a message, default `null`) refuses non-empty `video.shaders` lists and render options `shaders` with `previous-crash` as the player does, after the files are checked and before the GPU check. `failVideoShaders(id, message, code?)` (`failed` by default, or `previous-crash`) and `failVideoRender("previous-crash", …)` are shortcuts that only set the handle's status: the real player refuses the request instead, which `previousCrash` plays. `startPlugin`'s `stop` still declares no parameter (`stop.length === 0`, `t.after(stop)`, `promise.then(stop)`) and takes an optional `{ reason }`; `disconnect()` stops the plugin as when the player is lost (`host-lost`; requests in flight and later ones are refused with `disabled`, as the runner does).
|
|
20
|
+
- **`TestHost` cleanup never fails a test.** `close()` (and so `stop()`) retries removing its temporary directory (`maxRetries: 5`, 100 ms apart) and leaves it to the system when Windows still refuses (an intermittent `ENOTEMPTY` on Windows CI, plugin repository run 41351136515).
|
|
21
|
+
- _Docs_: [The plugin model](docs/explanation/plugins.md#stopping), [Host and Node](docs/explanation/host-and-node.md#stopping-a-plugin), [Video and audio processing](docs/explanation/processing.md#shaders), [Test a plugin](docs/how-to/testing.md) and [Reference: testing](docs/reference/testing.md) describe them.
|
|
22
|
+
|
|
3
23
|
## 1.5.53
|
|
4
24
|
|
|
5
25
|
**Who must change: nobody.** No changes to the TypeScript API, `apiVersion` stays `vestra/1`, no new capability, no player version needed.
|
package/dist/docs-tree.json.gz
CHANGED
|
Binary file
|
package/dist/plugin/index.d.ts
CHANGED
|
@@ -19,7 +19,17 @@ export interface PluginInstance {
|
|
|
19
19
|
* `RuntimeOptions.strictDeclarations` is set.
|
|
20
20
|
*/
|
|
21
21
|
activated(): Promise<void>;
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Stop the plugin: dispose the root scope, then stop the runtime.
|
|
24
|
+
* `info.reason` (SDK 1.5.54) is why: the runner passes `runtime.stop`'s
|
|
25
|
+
* `data.reason` (planned for player 0.6.4), or `"host-lost"` when its input closed
|
|
26
|
+
* without a `runtime.stop`. It reaches `plugin.stopReason` and every
|
|
27
|
+
* `onDispose` cleanup; a reason the SDK does not know reads as
|
|
28
|
+
* `undefined`. A second call runs no cleanup again and keeps the first reason.
|
|
29
|
+
*/
|
|
30
|
+
dispose(info?: {
|
|
31
|
+
reason?: unknown;
|
|
32
|
+
}): Promise<void>;
|
|
23
33
|
}
|
|
24
34
|
export declare function createPlugin(transport: Transport, manifest: PluginManifest, directory: string, dataDirectory?: string,
|
|
25
35
|
/** `plugin.app` (the runner passes the player's `VESTRA_APP_INFO`). */
|
package/dist/plugin/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { failure, Runtime } from "./runtime.js";
|
|
1
|
+
import { failure, Runtime, stopReason } from "./runtime.js";
|
|
2
2
|
import { ScopeImpl } from "./scope.js";
|
|
3
3
|
import { playerOf } from "./player.js";
|
|
4
4
|
import { windowOf } from "./window.js";
|
|
@@ -27,7 +27,8 @@ options) {
|
|
|
27
27
|
return {
|
|
28
28
|
plugin: root,
|
|
29
29
|
activated: () => runtime.activated(),
|
|
30
|
-
dispose: async () => {
|
|
30
|
+
dispose: async (info) => {
|
|
31
|
+
runtime.stopping ??= { reason: stopReason(info?.reason) };
|
|
31
32
|
await root.dispose();
|
|
32
33
|
runtime.stop();
|
|
33
34
|
},
|
package/dist/plugin/packages.js
CHANGED
|
@@ -83,9 +83,11 @@ function installOptions(options) {
|
|
|
83
83
|
return {};
|
|
84
84
|
if (typeof options !== "object" || options === null)
|
|
85
85
|
throw failure("invalid", "install options must be an object");
|
|
86
|
-
const { source, replace } = options;
|
|
86
|
+
const { source, replace, enabled } = options;
|
|
87
87
|
if (replace !== undefined && typeof replace !== "boolean")
|
|
88
88
|
throw failure("invalid", "replace must be a boolean");
|
|
89
|
+
if (enabled !== undefined && typeof enabled !== "boolean")
|
|
90
|
+
throw failure("invalid", "enabled must be a boolean");
|
|
89
91
|
if (source !== undefined) {
|
|
90
92
|
const kinds = ["repository", "link", "file", "workshop"];
|
|
91
93
|
if (typeof source !== "object" ||
|
|
@@ -99,6 +101,7 @@ function installOptions(options) {
|
|
|
99
101
|
return {
|
|
100
102
|
...(source === undefined ? {} : { source: { ...source } }),
|
|
101
103
|
...(replace ? { replace: true } : {}),
|
|
104
|
+
...(enabled === undefined ? {} : { enabled }),
|
|
102
105
|
};
|
|
103
106
|
}
|
|
104
107
|
function candidate(scope, directory, checked) {
|
package/dist/plugin/runtime.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Json, LocalServices, Transport, Wire } from "../wire.ts";
|
|
2
|
-
import type { App, Disposable, Locale, PluginError, SystemInfo } from "./types.ts";
|
|
2
|
+
import type { App, Disposable, Locale, PluginError, StopReason, SystemInfo } from "./types.ts";
|
|
3
3
|
/**
|
|
4
4
|
* Why something listens, for the player's log line that says why a plugin
|
|
5
5
|
* keeps running (`events.subscribe { why }`, player 0.5.21): computed
|
|
@@ -46,6 +46,8 @@ export interface RuntimeOptions {
|
|
|
46
46
|
*/
|
|
47
47
|
strictDeclarations?: boolean;
|
|
48
48
|
}
|
|
49
|
+
/** A stop reason the runner passed on, if this SDK knows it. */
|
|
50
|
+
export declare function stopReason(value: unknown): StopReason | undefined;
|
|
49
51
|
export declare const same: (a: Json | undefined, b: Json | undefined) => boolean;
|
|
50
52
|
/** One value in flight, only the newest waiting. Each push learns its fate. */
|
|
51
53
|
export declare class Latest<T> {
|
|
@@ -73,6 +75,13 @@ export declare class Runtime {
|
|
|
73
75
|
readonly views: Map<string, (callback: string, args: Json[]) => void>;
|
|
74
76
|
readonly commands: Map<string, () => void>;
|
|
75
77
|
readonly services: Map<string, unknown>;
|
|
78
|
+
/**
|
|
79
|
+
* Set when the player stops the plugin (`PluginInstance.dispose`), before
|
|
80
|
+
* the root scope is disposed: `DisposeInfo` and `plugin.stopReason`.
|
|
81
|
+
*/
|
|
82
|
+
stopping: {
|
|
83
|
+
reason: StopReason | undefined;
|
|
84
|
+
} | undefined;
|
|
76
85
|
/** `plugin.app`, from the start-up handshake; `system()` asks the player. */
|
|
77
86
|
readonly app: App;
|
|
78
87
|
/** `plugin.i18n.locale`, from the start-up handshake; English when unknown. */
|
package/dist/plugin/runtime.js
CHANGED
|
@@ -96,6 +96,23 @@ const DECLARATION_REFUSALS = new Set([
|
|
|
96
96
|
"conflict",
|
|
97
97
|
"limit",
|
|
98
98
|
]);
|
|
99
|
+
/** The reasons `StopReason` names; anything else reads as `undefined`. */
|
|
100
|
+
const STOP_REASONS = new Set([
|
|
101
|
+
"quit",
|
|
102
|
+
"disable",
|
|
103
|
+
"uninstall",
|
|
104
|
+
"reload",
|
|
105
|
+
"upgrade",
|
|
106
|
+
"idle",
|
|
107
|
+
"failed",
|
|
108
|
+
"host-lost",
|
|
109
|
+
]);
|
|
110
|
+
/** A stop reason the runner passed on, if this SDK knows it. */
|
|
111
|
+
export function stopReason(value) {
|
|
112
|
+
return typeof value === "string" && STOP_REASONS.has(value)
|
|
113
|
+
? value
|
|
114
|
+
: undefined;
|
|
115
|
+
}
|
|
99
116
|
export const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
100
117
|
/** One value in flight, only the newest waiting. Each push learns its fate. */
|
|
101
118
|
export class Latest {
|
|
@@ -191,6 +208,11 @@ export class Runtime {
|
|
|
191
208
|
views = new Map();
|
|
192
209
|
commands = new Map();
|
|
193
210
|
services = new Map();
|
|
211
|
+
/**
|
|
212
|
+
* Set when the player stops the plugin (`PluginInstance.dispose`), before
|
|
213
|
+
* the root scope is disposed: `DisposeInfo` and `plugin.stopReason`.
|
|
214
|
+
*/
|
|
215
|
+
stopping;
|
|
194
216
|
/** `plugin.app`, from the start-up handshake; `system()` asks the player. */
|
|
195
217
|
app;
|
|
196
218
|
/** `plugin.i18n.locale`, from the start-up handshake; English when unknown. */
|
package/dist/plugin/scope.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { Change, Runtime } from "./runtime.ts";
|
|
2
|
-
import type { Disposable, Plugin, PluginError, Scope } from "./types.ts";
|
|
2
|
+
import type { Disposable, DisposeInfo, Plugin, PluginError, Scope, StopReason } from "./types.ts";
|
|
3
3
|
export declare class ScopeImpl implements Plugin {
|
|
4
4
|
#private;
|
|
5
5
|
readonly runtime: Runtime;
|
|
6
6
|
constructor(runtime: Runtime, parent?: ScopeImpl);
|
|
7
7
|
get id(): string;
|
|
8
8
|
get dataDirectory(): string;
|
|
9
|
+
get stopReason(): StopReason | undefined;
|
|
9
10
|
get disposed(): boolean;
|
|
10
11
|
readonly commands: import("./types.ts").Commands;
|
|
11
12
|
readonly when: import("./types.ts").When;
|
|
@@ -49,7 +50,7 @@ export declare class ScopeImpl implements Plugin {
|
|
|
49
50
|
add<T extends Disposable>(item: T): T;
|
|
50
51
|
/** Handles call this when disposed directly, so the scope forgets them. */
|
|
51
52
|
forget(item: Disposable): void;
|
|
52
|
-
onDispose(cleanup: () => void | Promise<void>): Disposable;
|
|
53
|
+
onDispose(cleanup: ((info?: DisposeInfo) => void | Promise<void>) | (() => void | Promise<void>)): Disposable;
|
|
53
54
|
onError(listener: (error: PluginError) => void): Disposable;
|
|
54
55
|
report(error: unknown): void;
|
|
55
56
|
/** Invoke plugin code on its behalf: report failures, then refresh bindings. */
|
package/dist/plugin/scope.js
CHANGED
|
@@ -39,6 +39,9 @@ export class ScopeImpl {
|
|
|
39
39
|
throw failure("unavailable", "the player provided no data directory");
|
|
40
40
|
return directory;
|
|
41
41
|
}
|
|
42
|
+
get stopReason() {
|
|
43
|
+
return this.runtime.stopping?.reason;
|
|
44
|
+
}
|
|
42
45
|
get disposed() {
|
|
43
46
|
return this.#disposed || (this.#parent?.disposed ?? false);
|
|
44
47
|
}
|
|
@@ -102,7 +105,12 @@ export class ScopeImpl {
|
|
|
102
105
|
return;
|
|
103
106
|
done = true;
|
|
104
107
|
this.forget(item);
|
|
105
|
-
|
|
108
|
+
const stopping = this.runtime.stopping;
|
|
109
|
+
await cleanup(Object.freeze(stopping
|
|
110
|
+
? stopping.reason === undefined
|
|
111
|
+
? { stopping: true }
|
|
112
|
+
: { stopping: true, reason: stopping.reason }
|
|
113
|
+
: { stopping: false }));
|
|
106
114
|
},
|
|
107
115
|
};
|
|
108
116
|
return this.add(item);
|
package/dist/plugin/testing.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ import type { SubtitleState } from "../subtitles.ts";
|
|
|
18
18
|
import { VIDEO_PROCESSING_LIMITS } from "../processing.ts";
|
|
19
19
|
import type { RenderingMode, RenderingPreference, RenderingReason, RenderSettings, UpscalingDecodingCause, UpscalingPlugin, UpscalingReason, UpscalingVideoSize, ViewSettings } from "./types.ts";
|
|
20
20
|
import type { MediaJob } from "../media.ts";
|
|
21
|
-
import type { LanguagePreference, Locale, Plugin, TemplateArgs, TextValue, ThemeId } from "./types.ts";
|
|
21
|
+
import type { LanguagePreference, Locale, Plugin, StopReason, TemplateArgs, TextValue, ThemeId } from "./types.ts";
|
|
22
22
|
import { LOCALES, LIST_SEPARATORS, TEXT_LIMITS } from "../i18n.ts";
|
|
23
23
|
/** Throw one of these from `host.handle(...)` to simulate a definite refusal. */
|
|
24
24
|
export { HostError };
|
|
@@ -1351,6 +1351,10 @@ export declare class TestHost implements Transport {
|
|
|
1351
1351
|
* or switches to software rendering (`unsupported`, which also sets
|
|
1352
1352
|
* `videoRender.gpu` to false and `rendering.reason` to `window-fallback`
|
|
1353
1353
|
* with `message`). Pushed only when this plugin is the owner.
|
|
1354
|
+
* `previous-crash` (SDK 1.5.54) is a shortcut that only sets the handle's
|
|
1355
|
+
* status and reaches `onError`: the real player never pushes it, it
|
|
1356
|
+
* refuses the `set` request itself. To test that, set
|
|
1357
|
+
* {@link TestHost.previousCrash}.
|
|
1354
1358
|
*
|
|
1355
1359
|
* @example Make a shader fail to compile
|
|
1356
1360
|
* The plugin's render options report `failed`, as when mpv rejects a
|
|
@@ -1372,7 +1376,7 @@ export declare class TestHost implements Transport {
|
|
|
1372
1376
|
* @capability None.
|
|
1373
1377
|
* @since 1.0.0
|
|
1374
1378
|
*/
|
|
1375
|
-
failVideoRender(code: "failed" | "unsupported", message: string): void;
|
|
1379
|
+
failVideoRender(code: "failed" | "unsupported" | "previous-crash", message: string): void;
|
|
1376
1380
|
/**
|
|
1377
1381
|
* The shader channel (`plugin.video.shaders(id)`, SDK 1.5.39): the lists
|
|
1378
1382
|
* the player holds for this plugin's handles by id (the real paths, after
|
|
@@ -1405,6 +1409,12 @@ export declare class TestHost implements Transport {
|
|
|
1405
1409
|
* rendering (`unsupported`; use {@link TestHost.failVideoRender}, which
|
|
1406
1410
|
* also tells the handles). Pushed only to a handle that holds shaders.
|
|
1407
1411
|
*
|
|
1412
|
+
* `code` (SDK 1.5.54, default `failed`) is the error code pushed.
|
|
1413
|
+
* `previous-crash` is a shortcut that only sets the handle's status and
|
|
1414
|
+
* reaches `onError`: the real player (0.6.3) never pushes it, it refuses
|
|
1415
|
+
* the `set` request itself after an abnormal end. To test that, set
|
|
1416
|
+
* {@link TestHost.previousCrash}.
|
|
1417
|
+
*
|
|
1408
1418
|
* @capability None.
|
|
1409
1419
|
*
|
|
1410
1420
|
* @example Make the plugin's shader fail to compile
|
|
@@ -1430,7 +1440,37 @@ export declare class TestHost implements Transport {
|
|
|
1430
1440
|
*
|
|
1431
1441
|
* @since 1.5.39
|
|
1432
1442
|
*/
|
|
1433
|
-
failVideoShaders(id: string, message: string): void;
|
|
1443
|
+
failVideoShaders(id: string, message: string, code?: "failed" | "previous-crash"): void;
|
|
1444
|
+
/**
|
|
1445
|
+
* Play a run after an abnormal end with plugin shaders on (player 0.6.3):
|
|
1446
|
+
* while set, a non-empty `video.shaders` list and a render options
|
|
1447
|
+
* `set` with a non-empty `shaders` list are refused with
|
|
1448
|
+
* `previous-crash` and this message; clearing still works. `null`
|
|
1449
|
+
* (default): shaders apply.
|
|
1450
|
+
*
|
|
1451
|
+
* @example Check that the plugin stops retrying while shaders are off
|
|
1452
|
+
* The plugin's shader handle turns `failed` with `previous-crash`, as
|
|
1453
|
+
* in the player after a crash while plugin shaders were drawing. The
|
|
1454
|
+
* player refuses the list itself (and shows its own OSD line), so the
|
|
1455
|
+
* plugin should report it once and not set the list again.
|
|
1456
|
+
* ```ts
|
|
1457
|
+
* import assert from "node:assert/strict";
|
|
1458
|
+
* import { join } from "node:path";
|
|
1459
|
+
* import { writeFileSync } from "node:fs";
|
|
1460
|
+
* declare const host: TestHost;
|
|
1461
|
+
* // ---cut---
|
|
1462
|
+
* host.previousCrash = "plugin shaders are off for this start";
|
|
1463
|
+
* const file = join(plugin.dataDirectory, "sharpen.glsl");
|
|
1464
|
+
* writeFileSync(file, "//!HOOK MAIN\n");
|
|
1465
|
+
* const shaders = plugin.video.shaders("sharpen");
|
|
1466
|
+
* shaders.set([file]); // refused: the previous run ended abnormally
|
|
1467
|
+
* await host.settle();
|
|
1468
|
+
* assert.equal(shaders.status.state, "failed");
|
|
1469
|
+
* ```
|
|
1470
|
+
*
|
|
1471
|
+
* @since 1.5.54
|
|
1472
|
+
*/
|
|
1473
|
+
previousCrash: string | null;
|
|
1434
1474
|
/**
|
|
1435
1475
|
* The player's processing (filter) state. Wire-level; may change in minor
|
|
1436
1476
|
* versions. `resources` holds each `video.filter` / `audio.filter` chain the
|
|
@@ -1942,6 +1982,11 @@ export declare class TestHost implements Transport {
|
|
|
1942
1982
|
/** Start time in seconds. */
|
|
1943
1983
|
start: number;
|
|
1944
1984
|
}[];
|
|
1985
|
+
/**
|
|
1986
|
+
* @internal `startPlugin`'s `disconnect()`: refuse every request, the
|
|
1987
|
+
* ones in flight included, as the runner does once the player is gone.
|
|
1988
|
+
*/
|
|
1989
|
+
loseConnection(): void;
|
|
1945
1990
|
/**
|
|
1946
1991
|
* `plugin.dataDirectory`: a fresh empty temporary directory, removed with
|
|
1947
1992
|
* the host on `stop()`. `plugin.storage.json` reads and writes real files
|
|
@@ -2391,7 +2436,7 @@ export declare class TestHost implements Transport {
|
|
|
2391
2436
|
method: "setEnabled" | "reload" | "uninstall" | "install";
|
|
2392
2437
|
/** The target plugin id. */
|
|
2393
2438
|
id: string;
|
|
2394
|
-
/** `setEnabled`'s value. */
|
|
2439
|
+
/** `setEnabled`'s value, or `install`'s `enabled` option when given. */
|
|
2395
2440
|
enabled?: boolean;
|
|
2396
2441
|
/** `uninstall`'s `removeData` option. */
|
|
2397
2442
|
removeData?: boolean;
|
|
@@ -2405,7 +2450,13 @@ export declare class TestHost implements Transport {
|
|
|
2405
2450
|
* `.vxp` file) → its manifest. Other paths fail with `missing`. `install`
|
|
2406
2451
|
* adds or upgrades the entry in {@link TestHost.packages} following the
|
|
2407
2452
|
* player's source rules: the same source upgrades, another source needs
|
|
2408
|
-
* `replace` (`conflict` otherwise), a built-in id is `unsupported`.
|
|
2453
|
+
* `replace` (`conflict` otherwise), a built-in id is `unsupported`. As
|
|
2454
|
+
* player 0.6.4 (planned), the installed entry's `state` follows
|
|
2455
|
+
* {@link "@vestra-player/sdk"!InstallOptions.enabled}: a first install
|
|
2456
|
+
* runs, an update of a `disabled` entry stays `disabled` unless `enabled`
|
|
2457
|
+
* is `true`, and `enabled: false` installs it `disabled`. Set
|
|
2458
|
+
* {@link TestHost.installKeepsDisabled} to `false` for the older players'
|
|
2459
|
+
* rule (every install runs).
|
|
2409
2460
|
*
|
|
2410
2461
|
* @example Offer a package file the plugin can install
|
|
2411
2462
|
* The plugin can validate and install `/downloads/someone.demo-1.0.0.vxp`
|
|
@@ -2422,6 +2473,32 @@ export declare class TestHost implements Transport {
|
|
|
2422
2473
|
* ```
|
|
2423
2474
|
*/
|
|
2424
2475
|
readonly candidates: Map<string, PackageManifest>;
|
|
2476
|
+
/**
|
|
2477
|
+
* Which player's install rule `packages.install` plays. `true` (default,
|
|
2478
|
+
* SDK 1.5.55): player 0.6.4 (planned), where an update or reinstall
|
|
2479
|
+
* keeps a `disabled` plugin disabled unless the plugin passes
|
|
2480
|
+
* {@link "@vestra-player/sdk"!InstallOptions.enabled}. `false`: players
|
|
2481
|
+
* up to 0.6.3, where every install runs the plugin and `enabled` is
|
|
2482
|
+
* ignored (it is still checked and recorded in
|
|
2483
|
+
* {@link TestHost.packageCalls}). A test written for the older rule that
|
|
2484
|
+
* updates a disabled plugin sets it to `false`; one that keeps testing
|
|
2485
|
+
* both runs once with each.
|
|
2486
|
+
*
|
|
2487
|
+
* @example Keep a test written for players up to 0.6.3
|
|
2488
|
+
* The plugin under test installs an update over a plugin the user
|
|
2489
|
+
* disabled and expects it to run afterwards, as players up to 0.6.3 do.
|
|
2490
|
+
* With `false` the host plays that rule; the default plays player 0.6.4,
|
|
2491
|
+
* where the update stays disabled.
|
|
2492
|
+
* ```ts
|
|
2493
|
+
* declare const host: TestHost;
|
|
2494
|
+
* // ---cut---
|
|
2495
|
+
* host.installKeepsDisabled = false; // every install runs, as before 0.6.4
|
|
2496
|
+
* ```
|
|
2497
|
+
*
|
|
2498
|
+
* @capability None.
|
|
2499
|
+
* @since 1.5.55
|
|
2500
|
+
*/
|
|
2501
|
+
installKeepsDisabled: boolean;
|
|
2425
2502
|
/**
|
|
2426
2503
|
* Remote images (`plugin.ui.markdown`, `plugin.ui.remoteImage`): address
|
|
2427
2504
|
* → a local PNG or JPEG file served for it, or `null` for a failing
|
|
@@ -2754,7 +2831,11 @@ export declare class TestHost implements Transport {
|
|
|
2754
2831
|
* @returns `host`: the {@link TestHost}; `plugin`: the object passed to
|
|
2755
2832
|
* `activate` (call the plugin API on it directly); `stop()`: dispose the
|
|
2756
2833
|
* plugin like the player stopping it (scopes released, stores flushed),
|
|
2757
|
-
* wait until the host is idle and remove the host's temporary files
|
|
2834
|
+
* wait until the host is idle and remove the host's temporary files;
|
|
2835
|
+
* `stop({ reason })` (SDK 1.5.54): `reason` is the `StopReason` the player
|
|
2836
|
+
* sends; `disconnect()` (SDK 1.5.54) stops it as when the player is lost
|
|
2837
|
+
* (`host-lost`, requests refused with `disabled`). Removing the
|
|
2838
|
+
* temporary files never fails a test.
|
|
2758
2839
|
*
|
|
2759
2840
|
* @example Test that a setting survives a restart
|
|
2760
2841
|
* The test turns on the 10-step option, stops the plugin, starts it again
|
|
@@ -2843,6 +2924,25 @@ export declare function startPlugin(activate: (plugin: Plugin) => unknown, optio
|
|
|
2843
2924
|
host: TestHost;
|
|
2844
2925
|
/** The `plugin` object passed to `activate`. */
|
|
2845
2926
|
plugin: Plugin;
|
|
2846
|
-
/**
|
|
2847
|
-
|
|
2927
|
+
/**
|
|
2928
|
+
* Stop the plugin, wait for the host, and remove temporary files.
|
|
2929
|
+
* `stop({ reason })` (SDK 1.5.54): `reason` is the reason the player
|
|
2930
|
+
* gives (`plugin.stopReason`, the `onDispose` cleanups'
|
|
2931
|
+
* `info.reason`); `stop()`: none (`undefined`), as with players up to
|
|
2932
|
+
* 0.6.3. It still takes no declared parameter, so it can be passed as
|
|
2933
|
+
* a callback (`t.after(stop)`, `promise.then(stop)`).
|
|
2934
|
+
*/
|
|
2935
|
+
stop: {
|
|
2936
|
+
(): Promise<void>;
|
|
2937
|
+
(options: {
|
|
2938
|
+
reason?: StopReason;
|
|
2939
|
+
}): Promise<void>;
|
|
2940
|
+
};
|
|
2941
|
+
/**
|
|
2942
|
+
* Lose the player (SDK 1.5.54): as when it crashes or is killed, the
|
|
2943
|
+
* runner stops the plugin with reason `host-lost`; requests still
|
|
2944
|
+
* waiting for their answer and every later request are refused with
|
|
2945
|
+
* `disabled`. Then waits and removes the temporary files like `stop`.
|
|
2946
|
+
*/
|
|
2947
|
+
disconnect(): Promise<void>;
|
|
2848
2948
|
}>;
|
package/dist/plugin/testing.js
CHANGED
|
@@ -1510,6 +1510,10 @@ export class TestHost {
|
|
|
1510
1510
|
* or switches to software rendering (`unsupported`, which also sets
|
|
1511
1511
|
* `videoRender.gpu` to false and `rendering.reason` to `window-fallback`
|
|
1512
1512
|
* with `message`). Pushed only when this plugin is the owner.
|
|
1513
|
+
* `previous-crash` (SDK 1.5.54) is a shortcut that only sets the handle's
|
|
1514
|
+
* status and reaches `onError`: the real player never pushes it, it
|
|
1515
|
+
* refuses the `set` request itself. To test that, set
|
|
1516
|
+
* {@link TestHost.previousCrash}.
|
|
1513
1517
|
*
|
|
1514
1518
|
* @example Make a shader fail to compile
|
|
1515
1519
|
* The plugin's render options report `failed`, as when mpv rejects a
|
|
@@ -1581,6 +1585,12 @@ export class TestHost {
|
|
|
1581
1585
|
* rendering (`unsupported`; use {@link TestHost.failVideoRender}, which
|
|
1582
1586
|
* also tells the handles). Pushed only to a handle that holds shaders.
|
|
1583
1587
|
*
|
|
1588
|
+
* `code` (SDK 1.5.54, default `failed`) is the error code pushed.
|
|
1589
|
+
* `previous-crash` is a shortcut that only sets the handle's status and
|
|
1590
|
+
* reaches `onError`: the real player (0.6.3) never pushes it, it refuses
|
|
1591
|
+
* the `set` request itself after an abnormal end. To test that, set
|
|
1592
|
+
* {@link TestHost.previousCrash}.
|
|
1593
|
+
*
|
|
1584
1594
|
* @capability None.
|
|
1585
1595
|
*
|
|
1586
1596
|
* @example Make the plugin's shader fail to compile
|
|
@@ -1606,14 +1616,44 @@ export class TestHost {
|
|
|
1606
1616
|
*
|
|
1607
1617
|
* @since 1.5.39
|
|
1608
1618
|
*/
|
|
1609
|
-
failVideoShaders(id, message) {
|
|
1619
|
+
failVideoShaders(id, message, code = "failed") {
|
|
1610
1620
|
if ((this.videoShaders.handles.get(id)?.length ?? 0) > 0)
|
|
1611
1621
|
this.emit("video.shaders.status", {
|
|
1612
1622
|
id,
|
|
1613
1623
|
state: "failed",
|
|
1614
|
-
error: { code
|
|
1624
|
+
error: { code, message },
|
|
1615
1625
|
});
|
|
1616
1626
|
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Play a run after an abnormal end with plugin shaders on (player 0.6.3):
|
|
1629
|
+
* while set, a non-empty `video.shaders` list and a render options
|
|
1630
|
+
* `set` with a non-empty `shaders` list are refused with
|
|
1631
|
+
* `previous-crash` and this message; clearing still works. `null`
|
|
1632
|
+
* (default): shaders apply.
|
|
1633
|
+
*
|
|
1634
|
+
* @example Check that the plugin stops retrying while shaders are off
|
|
1635
|
+
* The plugin's shader handle turns `failed` with `previous-crash`, as
|
|
1636
|
+
* in the player after a crash while plugin shaders were drawing. The
|
|
1637
|
+
* player refuses the list itself (and shows its own OSD line), so the
|
|
1638
|
+
* plugin should report it once and not set the list again.
|
|
1639
|
+
* ```ts
|
|
1640
|
+
* import assert from "node:assert/strict";
|
|
1641
|
+
* import { join } from "node:path";
|
|
1642
|
+
* import { writeFileSync } from "node:fs";
|
|
1643
|
+
* declare const host: TestHost;
|
|
1644
|
+
* // ---cut---
|
|
1645
|
+
* host.previousCrash = "plugin shaders are off for this start";
|
|
1646
|
+
* const file = join(plugin.dataDirectory, "sharpen.glsl");
|
|
1647
|
+
* writeFileSync(file, "//!HOOK MAIN\n");
|
|
1648
|
+
* const shaders = plugin.video.shaders("sharpen");
|
|
1649
|
+
* shaders.set([file]); // refused: the previous run ended abnormally
|
|
1650
|
+
* await host.settle();
|
|
1651
|
+
* assert.equal(shaders.status.state, "failed");
|
|
1652
|
+
* ```
|
|
1653
|
+
*
|
|
1654
|
+
* @since 1.5.54
|
|
1655
|
+
*/
|
|
1656
|
+
previousCrash = null;
|
|
1617
1657
|
/**
|
|
1618
1658
|
* The player's processing (filter) state. Wire-level; may change in minor
|
|
1619
1659
|
* versions. `resources` holds each `video.filter` / `audio.filter` chain the
|
|
@@ -2099,6 +2139,25 @@ export class TestHost {
|
|
|
2099
2139
|
#overrides = new Map();
|
|
2100
2140
|
#listeners = new Map();
|
|
2101
2141
|
#inFlight = 0;
|
|
2142
|
+
/** `disconnect()` of `startPlugin`: the player is gone. */
|
|
2143
|
+
#lost = false;
|
|
2144
|
+
#lose = () => { };
|
|
2145
|
+
/** Rejects when the player is lost: requests in flight fail with it. */
|
|
2146
|
+
#gone = (() => {
|
|
2147
|
+
const gone = new Promise((_, reject) => {
|
|
2148
|
+
this.#lose = reject;
|
|
2149
|
+
});
|
|
2150
|
+
gone.catch(() => { });
|
|
2151
|
+
return gone;
|
|
2152
|
+
})();
|
|
2153
|
+
/**
|
|
2154
|
+
* @internal `startPlugin`'s `disconnect()`: refuse every request, the
|
|
2155
|
+
* ones in flight included, as the runner does once the player is gone.
|
|
2156
|
+
*/
|
|
2157
|
+
loseConnection() {
|
|
2158
|
+
this.#lost = true;
|
|
2159
|
+
this.#lose(new HostError("disabled", "plugin stopped"));
|
|
2160
|
+
}
|
|
2102
2161
|
#next = { snapshot: 0, job: 0, producer: 0, source: 0, resolution: 0 };
|
|
2103
2162
|
#sourceBook = new Map();
|
|
2104
2163
|
/** The pending resolutions' stamps and operations (beside `resolutions`). */
|
|
@@ -2573,6 +2632,10 @@ export class TestHost {
|
|
|
2573
2632
|
* override from {@link TestHost.handle} or the default behaviour.
|
|
2574
2633
|
*/
|
|
2575
2634
|
async request(method, params = {}) {
|
|
2635
|
+
// After `disconnect()`: refused as the runner refuses them once the
|
|
2636
|
+
// player is gone.
|
|
2637
|
+
if (this.#lost)
|
|
2638
|
+
throw new HostError("disabled", "plugin is stopping");
|
|
2576
2639
|
const record = (params ?? {});
|
|
2577
2640
|
// The plugin runner refuses a request over the message limit before
|
|
2578
2641
|
// sending it (runtime/runner.ts): the player never sees it.
|
|
@@ -2583,10 +2646,15 @@ export class TestHost {
|
|
|
2583
2646
|
this.#inFlight++;
|
|
2584
2647
|
try {
|
|
2585
2648
|
await tick();
|
|
2649
|
+
if (this.#lost)
|
|
2650
|
+
throw new HostError("disabled", "plugin stopped");
|
|
2586
2651
|
const handler = this.#overrides.get(method) ?? this.#defaults[method];
|
|
2587
2652
|
if (!handler)
|
|
2588
2653
|
throw new HostError("unsupported", `test host has no ${method}`);
|
|
2589
|
-
|
|
2654
|
+
const work = Promise.resolve(handler(record));
|
|
2655
|
+
// `disconnect()` fails the requests still waiting, as the runner does.
|
|
2656
|
+
work.catch(() => { });
|
|
2657
|
+
return (await Promise.race([work, this.#gone]));
|
|
2590
2658
|
}
|
|
2591
2659
|
finally {
|
|
2592
2660
|
this.#inFlight--;
|
|
@@ -3031,7 +3099,13 @@ export class TestHost {
|
|
|
3031
3099
|
* `.vxp` file) → its manifest. Other paths fail with `missing`. `install`
|
|
3032
3100
|
* adds or upgrades the entry in {@link TestHost.packages} following the
|
|
3033
3101
|
* player's source rules: the same source upgrades, another source needs
|
|
3034
|
-
* `replace` (`conflict` otherwise), a built-in id is `unsupported`.
|
|
3102
|
+
* `replace` (`conflict` otherwise), a built-in id is `unsupported`. As
|
|
3103
|
+
* player 0.6.4 (planned), the installed entry's `state` follows
|
|
3104
|
+
* {@link "@vestra-player/sdk"!InstallOptions.enabled}: a first install
|
|
3105
|
+
* runs, an update of a `disabled` entry stays `disabled` unless `enabled`
|
|
3106
|
+
* is `true`, and `enabled: false` installs it `disabled`. Set
|
|
3107
|
+
* {@link TestHost.installKeepsDisabled} to `false` for the older players'
|
|
3108
|
+
* rule (every install runs).
|
|
3035
3109
|
*
|
|
3036
3110
|
* @example Offer a package file the plugin can install
|
|
3037
3111
|
* The plugin can validate and install `/downloads/someone.demo-1.0.0.vxp`
|
|
@@ -3048,6 +3122,32 @@ export class TestHost {
|
|
|
3048
3122
|
* ```
|
|
3049
3123
|
*/
|
|
3050
3124
|
candidates = new Map();
|
|
3125
|
+
/**
|
|
3126
|
+
* Which player's install rule `packages.install` plays. `true` (default,
|
|
3127
|
+
* SDK 1.5.55): player 0.6.4 (planned), where an update or reinstall
|
|
3128
|
+
* keeps a `disabled` plugin disabled unless the plugin passes
|
|
3129
|
+
* {@link "@vestra-player/sdk"!InstallOptions.enabled}. `false`: players
|
|
3130
|
+
* up to 0.6.3, where every install runs the plugin and `enabled` is
|
|
3131
|
+
* ignored (it is still checked and recorded in
|
|
3132
|
+
* {@link TestHost.packageCalls}). A test written for the older rule that
|
|
3133
|
+
* updates a disabled plugin sets it to `false`; one that keeps testing
|
|
3134
|
+
* both runs once with each.
|
|
3135
|
+
*
|
|
3136
|
+
* @example Keep a test written for players up to 0.6.3
|
|
3137
|
+
* The plugin under test installs an update over a plugin the user
|
|
3138
|
+
* disabled and expects it to run afterwards, as players up to 0.6.3 do.
|
|
3139
|
+
* With `false` the host plays that rule; the default plays player 0.6.4,
|
|
3140
|
+
* where the update stays disabled.
|
|
3141
|
+
* ```ts
|
|
3142
|
+
* declare const host: TestHost;
|
|
3143
|
+
* // ---cut---
|
|
3144
|
+
* host.installKeepsDisabled = false; // every install runs, as before 0.6.4
|
|
3145
|
+
* ```
|
|
3146
|
+
*
|
|
3147
|
+
* @capability None.
|
|
3148
|
+
* @since 1.5.55
|
|
3149
|
+
*/
|
|
3150
|
+
installKeepsDisabled = true;
|
|
3051
3151
|
/**
|
|
3052
3152
|
* Remote images (`plugin.ui.markdown`, `plugin.ui.remoteImage`): address
|
|
3053
3153
|
* → a local PNG or JPEG file served for it, or `null` for a failing
|
|
@@ -3424,7 +3524,20 @@ export class TestHost {
|
|
|
3424
3524
|
close() {
|
|
3425
3525
|
for (const producer of this.producers.values())
|
|
3426
3526
|
producer.server.close();
|
|
3427
|
-
|
|
3527
|
+
// Cleanup never fails a test: on Windows a file a closing handle still
|
|
3528
|
+
// holds makes the removal fail now and then (`ENOTEMPTY`, `EBUSY`,
|
|
3529
|
+
// `EPERM`); retry, then leave the directory to the system's cleanup.
|
|
3530
|
+
try {
|
|
3531
|
+
rmSync(this.#scratch, {
|
|
3532
|
+
recursive: true,
|
|
3533
|
+
force: true,
|
|
3534
|
+
maxRetries: 5,
|
|
3535
|
+
retryDelay: 100,
|
|
3536
|
+
});
|
|
3537
|
+
}
|
|
3538
|
+
catch {
|
|
3539
|
+
// Left in the temporary directory.
|
|
3540
|
+
}
|
|
3428
3541
|
}
|
|
3429
3542
|
// ------------------------------------------------------------ simulated host
|
|
3430
3543
|
#intent(intent) {
|
|
@@ -4575,6 +4688,12 @@ export class TestHost {
|
|
|
4575
4688
|
return null;
|
|
4576
4689
|
},
|
|
4577
4690
|
"video.render.set": (p) => {
|
|
4691
|
+
// The player's crash guard comes first (`processing_video.rs`).
|
|
4692
|
+
const listed = p.values?.shaders;
|
|
4693
|
+
if (this.previousCrash !== null &&
|
|
4694
|
+
Array.isArray(listed) &&
|
|
4695
|
+
listed.length > 0)
|
|
4696
|
+
throw new HostError("previous-crash", this.previousCrash);
|
|
4578
4697
|
if (!this.videoRender.gpu)
|
|
4579
4698
|
throw new HostError("unsupported", "render options need GPU rendering; the player renders in software");
|
|
4580
4699
|
const owner = this.videoRender.owner;
|
|
@@ -4610,6 +4729,9 @@ export class TestHost {
|
|
|
4610
4729
|
if (listProblem?.startsWith("at most"))
|
|
4611
4730
|
throw new HostError("limit", listProblem);
|
|
4612
4731
|
const paths = list.map((path) => ownFile(path, this.fileRoots, "shader", VIDEO_PROCESSING_LIMITS.shaderBytes));
|
|
4732
|
+
// The player's crash guard, after the files are checked.
|
|
4733
|
+
if (paths.length > 0 && this.previousCrash !== null)
|
|
4734
|
+
throw new HostError("previous-crash", this.previousCrash);
|
|
4613
4735
|
if (paths.length > 0 && !this.videoRender.gpu)
|
|
4614
4736
|
throw new HostError("unsupported", "shaders need GPU rendering; the player renders in software");
|
|
4615
4737
|
const mine = [...this.videoShaders.handles]
|
|
@@ -5015,6 +5137,10 @@ export class TestHost {
|
|
|
5015
5137
|
if (source.kind === "bundled")
|
|
5016
5138
|
throw new HostError("invalid", "source bundled is reserved");
|
|
5017
5139
|
const replace = p.replace === true;
|
|
5140
|
+
// As player 0.6.4: `enabled` is a boolean, or absent (`null` too).
|
|
5141
|
+
const requested = p.enabled ?? undefined;
|
|
5142
|
+
if (requested !== undefined && typeof requested !== "boolean")
|
|
5143
|
+
throw new HostError("invalid", "enabled must be a boolean");
|
|
5018
5144
|
const entry = this.packages.find((each) => each.id === manifest.id);
|
|
5019
5145
|
if (entry?.builtin)
|
|
5020
5146
|
throw new HostError("unsupported", "built-in plugins are updated with the player");
|
|
@@ -5030,12 +5156,19 @@ export class TestHost {
|
|
|
5030
5156
|
id: manifest.id,
|
|
5031
5157
|
source,
|
|
5032
5158
|
replace,
|
|
5159
|
+
...(requested === undefined ? {} : { enabled: requested }),
|
|
5033
5160
|
});
|
|
5161
|
+
// As player 0.6.4: a first install runs; an update or reinstall (any
|
|
5162
|
+
// source, `replace` included) keeps a disabled plugin disabled unless
|
|
5163
|
+
// `enabled` is given. Players up to 0.6.3 run every install.
|
|
5164
|
+
const enabled = !this.installKeepsDisabled
|
|
5165
|
+
? true
|
|
5166
|
+
: (requested ?? entry?.state !== "disabled");
|
|
5034
5167
|
const next = {
|
|
5035
5168
|
id: manifest.id,
|
|
5036
5169
|
version: manifest.version,
|
|
5037
5170
|
origin: "installed",
|
|
5038
|
-
state: "running",
|
|
5171
|
+
state: enabled ? "running" : "disabled",
|
|
5039
5172
|
error: null,
|
|
5040
5173
|
capabilities: [...manifest.capabilities],
|
|
5041
5174
|
directory: `/test-host/packages/${manifest.id}`,
|
|
@@ -5455,7 +5588,11 @@ function tellResidency(host) {
|
|
|
5455
5588
|
* @returns `host`: the {@link TestHost}; `plugin`: the object passed to
|
|
5456
5589
|
* `activate` (call the plugin API on it directly); `stop()`: dispose the
|
|
5457
5590
|
* plugin like the player stopping it (scopes released, stores flushed),
|
|
5458
|
-
* wait until the host is idle and remove the host's temporary files
|
|
5591
|
+
* wait until the host is idle and remove the host's temporary files;
|
|
5592
|
+
* `stop({ reason })` (SDK 1.5.54): `reason` is the `StopReason` the player
|
|
5593
|
+
* sends; `disconnect()` (SDK 1.5.54) stops it as when the player is lost
|
|
5594
|
+
* (`host-lost`, requests refused with `disabled`). Removing the
|
|
5595
|
+
* temporary files never fails a test.
|
|
5459
5596
|
*
|
|
5460
5597
|
* @example Test that a setting survives a restart
|
|
5461
5598
|
* The test turns on the 10-step option, stops the plugin, starts it again
|
|
@@ -5552,14 +5689,43 @@ export async function startPlugin(activate, options = {}) {
|
|
|
5552
5689
|
}
|
|
5553
5690
|
await host.settle();
|
|
5554
5691
|
tellResidency(host);
|
|
5692
|
+
// Zero declared parameters (`stop.length === 0`) and a `()` overload, so
|
|
5693
|
+
// `t.after(stop)`, `promise.then(stop)` and done-callback test runners
|
|
5694
|
+
// see the same function as before 1.5.54.
|
|
5695
|
+
const stop = async (...args) => {
|
|
5696
|
+
// Only an object with `reason` counts (a value `then` or a test
|
|
5697
|
+
// runner passes is ignored; an unknown reason reads as none).
|
|
5698
|
+
const first = args[0];
|
|
5699
|
+
const reason = typeof first === "object" && first !== null && "reason" in first
|
|
5700
|
+
? first.reason
|
|
5701
|
+
: undefined;
|
|
5702
|
+
await instance.dispose({ reason });
|
|
5703
|
+
await host.settle();
|
|
5704
|
+
host.close();
|
|
5705
|
+
};
|
|
5555
5706
|
return {
|
|
5556
5707
|
/** The test host the plugin talks to. */
|
|
5557
5708
|
host,
|
|
5558
5709
|
/** The `plugin` object passed to `activate`. */
|
|
5559
5710
|
plugin: instance.plugin,
|
|
5560
|
-
/**
|
|
5561
|
-
|
|
5562
|
-
|
|
5711
|
+
/**
|
|
5712
|
+
* Stop the plugin, wait for the host, and remove temporary files.
|
|
5713
|
+
* `stop({ reason })` (SDK 1.5.54): `reason` is the reason the player
|
|
5714
|
+
* gives (`plugin.stopReason`, the `onDispose` cleanups'
|
|
5715
|
+
* `info.reason`); `stop()`: none (`undefined`), as with players up to
|
|
5716
|
+
* 0.6.3. It still takes no declared parameter, so it can be passed as
|
|
5717
|
+
* a callback (`t.after(stop)`, `promise.then(stop)`).
|
|
5718
|
+
*/
|
|
5719
|
+
stop,
|
|
5720
|
+
/**
|
|
5721
|
+
* Lose the player (SDK 1.5.54): as when it crashes or is killed, the
|
|
5722
|
+
* runner stops the plugin with reason `host-lost`; requests still
|
|
5723
|
+
* waiting for their answer and every later request are refused with
|
|
5724
|
+
* `disabled`. Then waits and removes the temporary files like `stop`.
|
|
5725
|
+
*/
|
|
5726
|
+
async disconnect() {
|
|
5727
|
+
host.loseConnection();
|
|
5728
|
+
await instance.dispose({ reason: "host-lost" });
|
|
5563
5729
|
await host.settle();
|
|
5564
5730
|
host.close();
|
|
5565
5731
|
},
|
package/dist/plugin/types.d.ts
CHANGED
|
@@ -123,6 +123,7 @@ export interface Disposable {
|
|
|
123
123
|
* | `resolution` | A file or web resolver's result could not be opened; `message` has the reason. |
|
|
124
124
|
* | `revisionConflict` | {@link Packages}: the installed revision changed since it was read (another install or uninstall ran). Read again and retry; the SDK passes the revision it read, so this reaches plugins that act on old data. |
|
|
125
125
|
* | `source-geometry`, `source-geometry-unsupported` | The source's picture size or rotation could not be applied: invalid decoder dimensions / a rotation that is not a multiple of 90°. |
|
|
126
|
+
* | `previous-crash` | {@link VideoShaders}, {@link RenderOptions} `shaders` (player 0.6.3): plugin shaders are off because the player ended abnormally while they were drawing; clearing still works. Do not retry while refused (the player shows its own OSD line). |
|
|
126
127
|
* | `source-unavailable` | The source is not ready (still loading, or its time or native binding is not available yet). |
|
|
127
128
|
* | `unknown-expired` | The player no longer knows the operation or snapshot (it expired or was released). The SDK maps it to `unknownExpired` results where it can; do not resubmit the operation blindly. |
|
|
128
129
|
*
|
|
@@ -416,10 +417,36 @@ export interface Scope extends Disposable {
|
|
|
416
417
|
* plugin.onDispose(() => clearInterval(timer));
|
|
417
418
|
* ```
|
|
418
419
|
*
|
|
420
|
+
* @example Save a cache at once when the player is lost
|
|
421
|
+
* The plugin writes its cache file at once when the player crashed or
|
|
422
|
+
* was killed, instead of waiting for the next periodic save. The cleanup
|
|
423
|
+
* may get a {@link DisposeInfo}: `stopping` says the plugin itself stops
|
|
424
|
+
* (not a child scope), `reason` why (planned for player 0.6.4). Players
|
|
425
|
+
* whose SDK is older than 1.5.54 call the cleanup without an argument,
|
|
426
|
+
* so read it with `?.`.
|
|
427
|
+
* ```ts
|
|
428
|
+
* declare function saveCacheNow(): void;
|
|
429
|
+
* // ---cut---
|
|
430
|
+
* plugin.onDispose((info) => {
|
|
431
|
+
* // The player is gone: ask it nothing, keep what is local.
|
|
432
|
+
* if (info?.reason === "host-lost") saveCacheNow();
|
|
433
|
+
* });
|
|
434
|
+
* ```
|
|
435
|
+
*
|
|
436
|
+
* @remarks
|
|
437
|
+
* From SDK 1.5.54 in the player (and in `TestHost`) the cleanup is called
|
|
438
|
+
* with one argument, a {@link DisposeInfo}: whether the plugin stops, and
|
|
439
|
+
* why. A player whose own SDK is older calls it without one (`info` is
|
|
440
|
+
* `undefined`). A cleanup that takes no argument works as before; a
|
|
441
|
+
* function with optional parameters of its own that is passed directly
|
|
442
|
+
* (`onDispose(controller.abort.bind(controller))`) now receives the
|
|
443
|
+
* `DisposeInfo` as its first argument: wrap it (`() => controller.abort()`)
|
|
444
|
+
* when that matters.
|
|
445
|
+
*
|
|
419
446
|
* @capability None.
|
|
420
447
|
* @since 1.0.0
|
|
421
448
|
*/
|
|
422
|
-
onDispose(cleanup: () => void | Promise<void>): Disposable;
|
|
449
|
+
onDispose(cleanup: ((info?: DisposeInfo) => void | Promise<void>) | (() => void | Promise<void>)): Disposable;
|
|
423
450
|
/**
|
|
424
451
|
* Errors thrown by handlers that the SDK invoked (command `run`, window and
|
|
425
452
|
* panel callbacks, `onChange` listeners) in this scope. Without a listener
|
|
@@ -547,6 +574,83 @@ export interface Plugin extends Scope {
|
|
|
547
574
|
* none. See [How a plugin runs](../../docs/explanation/plugins.md#the-data-directory).
|
|
548
575
|
*/
|
|
549
576
|
readonly dataDirectory: string;
|
|
577
|
+
/**
|
|
578
|
+
* Why the player stops this plugin (SDK 1.5.54; the reasons are planned
|
|
579
|
+
* for player 0.6.4): set when the stop begins, before any
|
|
580
|
+
* {@link Scope.onDispose} cleanup runs. `undefined` while the plugin
|
|
581
|
+
* runs, and for every stop by an older player (one whose own SDK is older
|
|
582
|
+
* than 1.5.54 does not have the property at all). See {@link StopReason}.
|
|
583
|
+
*/
|
|
584
|
+
readonly stopReason: StopReason | undefined;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Why the player stopped the plugin ({@link DisposeInfo.reason},
|
|
588
|
+
* {@link Plugin.stopReason}).
|
|
589
|
+
*
|
|
590
|
+
* - `quit`: the player quits.
|
|
591
|
+
* - `disable`: the user disabled the plugin.
|
|
592
|
+
* - `uninstall`: the user uninstalls it.
|
|
593
|
+
* - `reload`: it is reloaded (from the plugin manager, or a development
|
|
594
|
+
* load with `--plugin`).
|
|
595
|
+
* - `upgrade`: an install or update replaces the running version; also a
|
|
596
|
+
* new version stopped because its install was cancelled (the previous
|
|
597
|
+
* version comes back).
|
|
598
|
+
* - `idle`: lazy activation parks it; its next use starts it again
|
|
599
|
+
* ([Idle plugins](../../docs/explanation/plugins.md#idle-plugins-are-stopped)).
|
|
600
|
+
* Lazy activation is off by default (`VESTRA_LAZY=1` turns it on); when
|
|
601
|
+
* on, it parks a plugin after a while without use, and parks one whose
|
|
602
|
+
* first run it judges lazy right after it is ready.
|
|
603
|
+
* - `failed`: the player stops it for a fault it can still tell the plugin
|
|
604
|
+
* about (planned to cover a malformed request, an unreadable or
|
|
605
|
+
* oversized line, an activation timeout, an uncaught exception the
|
|
606
|
+
* shared process attributes to this plugin).
|
|
607
|
+
* - `host-lost`: the plugin's runner saw its pipe to the player close
|
|
608
|
+
* without a stop: the player crashed or was killed. Requests are refused
|
|
609
|
+
* with `disabled`; clean up locally only. Player 0.6.4 sends `quit` on a
|
|
610
|
+
* normal exit; read `host-lost` as "the player is gone", not as proof of
|
|
611
|
+
* a crash.
|
|
612
|
+
*
|
|
613
|
+
* Some ends are no stop at all: the plugin gets no reason and its
|
|
614
|
+
* {@link Scope.onDispose} cleanups do not run. That is the case when its
|
|
615
|
+
* own process is killed because its input queue is full or a write to it
|
|
616
|
+
* failed, when its own process fails to activate (the runner exits without
|
|
617
|
+
* cleaning up), when the process has already exited, and when the shared
|
|
618
|
+
* runner is restarted as a whole (a hang, a fault it cannot attribute, an
|
|
619
|
+
* unreadable line of its own, a member's full input queue, the runner
|
|
620
|
+
* exiting): every member is killed with it. Keep state that must survive
|
|
621
|
+
* such an end in storage as it changes, not only in a cleanup.
|
|
622
|
+
*
|
|
623
|
+
* Planned for player 0.6.4: the player sends the reason with
|
|
624
|
+
* `runtime.stop` and its runner reports `host-lost`; the exact list may
|
|
625
|
+
* still change with that release. Players up to 0.6.3 give no reason for
|
|
626
|
+
* any stop, a lost player included (`undefined`); so does a stop that
|
|
627
|
+
* does not come from the player (a `TestHost` `stop()` without a reason).
|
|
628
|
+
* A value this SDK does not know reads as `undefined`.
|
|
629
|
+
*
|
|
630
|
+
* @player 0.6.4 (planned); older players give no reason (`undefined`).
|
|
631
|
+
* @since 1.5.54
|
|
632
|
+
* @category Plugin
|
|
633
|
+
*/
|
|
634
|
+
export type StopReason = "quit" | "disable" | "uninstall" | "reload" | "upgrade" | "idle" | "failed" | "host-lost";
|
|
635
|
+
/**
|
|
636
|
+
* What a {@link Scope.onDispose} cleanup is told (SDK 1.5.54). A player
|
|
637
|
+
* whose own SDK is older passes nothing, so the cleanup's parameter is
|
|
638
|
+
* optional.
|
|
639
|
+
*
|
|
640
|
+
* @since 1.5.54
|
|
641
|
+
* @category Plugin
|
|
642
|
+
*/
|
|
643
|
+
export interface DisposeInfo {
|
|
644
|
+
/**
|
|
645
|
+
* `true` when the plugin itself stops (the player stops it), `false`
|
|
646
|
+
* when the plugin's own code disposed this scope or one of its parents.
|
|
647
|
+
*/
|
|
648
|
+
readonly stopping: boolean;
|
|
649
|
+
/**
|
|
650
|
+
* Why the player stops the plugin (planned for player 0.6.4); `undefined` while
|
|
651
|
+
* `stopping` is false, and for every stop by an older player.
|
|
652
|
+
*/
|
|
653
|
+
readonly reason?: StopReason;
|
|
550
654
|
}
|
|
551
655
|
/**
|
|
552
656
|
* The state of a command's menu item, as the player shows it.
|
|
@@ -6716,7 +6820,13 @@ export interface RenderSettings {
|
|
|
6716
6820
|
* the player's first answer to `render()` (reported to `onError` too), and so is
|
|
6717
6821
|
* every `set`. Single owner, like {@link VideoView}. `set` never throws; the
|
|
6718
6822
|
* result shows in `status`. For shaders that several plugins add at the
|
|
6719
|
-
* same time, use {@link VideoShaders} (SDK 1.5.39).
|
|
6823
|
+
* same time, use {@link VideoShaders} (SDK 1.5.39). After a run of the
|
|
6824
|
+
* player that ended abnormally while plugin shaders were drawing, a `set`
|
|
6825
|
+
* whose merged values hold a non-empty `shaders` list is refused with
|
|
6826
|
+
* `previous-crash` (player 0.6.3; the rules are on {@link VideoShaders}).
|
|
6827
|
+
* Every `set` sends all the values set so far, the refused `shaders`
|
|
6828
|
+
* included, so include `shaders: []` in the next `set` to go on without
|
|
6829
|
+
* them.
|
|
6720
6830
|
*
|
|
6721
6831
|
* @capability processing.video, playback.read and events (like
|
|
6722
6832
|
* {@link VideoFilter})
|
|
@@ -6745,7 +6855,15 @@ export interface RenderOptions extends Disposable {
|
|
|
6745
6855
|
* `failed` with `unsupported` while the player renders in software;
|
|
6746
6856
|
* `failed` with `failed` when mpv cannot compile a shader (pushed later;
|
|
6747
6857
|
* while other plugins' {@link VideoShaders} are on too, the player
|
|
6748
|
-
* cannot tell whose shader failed and says so in the message)
|
|
6858
|
+
* cannot tell whose shader failed and says so in the message);
|
|
6859
|
+
* `failed` with `previous-crash` (player 0.6.3) when a `set` with a
|
|
6860
|
+
* non-empty `shaders` list comes after a run of the player that ended
|
|
6861
|
+
* abnormally while plugin shaders were drawing (the rules are the same
|
|
6862
|
+
* as for {@link VideoShaders}). The handle keeps the refused list among
|
|
6863
|
+
* its values and every `set` sends them all, so a later `set` without
|
|
6864
|
+
* `shaders` is refused again: include `shaders: []` in it
|
|
6865
|
+
* (`set({ shaders: [], toneMapping: "bt.2390" })`); `dispose` always
|
|
6866
|
+
* works.
|
|
6749
6867
|
*/
|
|
6750
6868
|
readonly status: FilterStatus;
|
|
6751
6869
|
/**
|
|
@@ -6805,6 +6923,20 @@ export interface RenderOptions extends Disposable {
|
|
|
6805
6923
|
* Players before 0.5.20 refuse the request (the handle is `failed`): declare
|
|
6806
6924
|
* `"minPlayerVersion": "0.5.20"`.
|
|
6807
6925
|
*
|
|
6926
|
+
* **After an abnormal end** (player 0.6.3): when the previous run of the
|
|
6927
|
+
* player ended while plugin shaders were drawing (a crash, the process
|
|
6928
|
+
* being ended, a lost GPU device), the player refuses every non-empty
|
|
6929
|
+
* list of every plugin with `previous-crash`, its message naming the
|
|
6930
|
+
* plugins whose shaders were on: one such end keeps them off for the next
|
|
6931
|
+
* start only; two within a day keep them off until the GPU, its driver or
|
|
6932
|
+
* the player changes, or for 7 days. Clearing (`[]`, `clear`,
|
|
6933
|
+
* `dispose`) still works. The player shows one OSD line about it itself;
|
|
6934
|
+
* the handle is `failed` with `previous-crash` and the SDK does not
|
|
6935
|
+
* retry it. Do not set the list again in a loop while it is refused, and
|
|
6936
|
+
* do not show an OSD line of your own for it; a later start of the player
|
|
6937
|
+
* applies lists again once the guard lets it. Players before 0.6.3 never
|
|
6938
|
+
* refuse with it.
|
|
6939
|
+
*
|
|
6808
6940
|
* @example Add a sharpening shader the plugin ships
|
|
6809
6941
|
* The video looks sharper on the GPU renderer; under software rendering
|
|
6810
6942
|
* the OSD says why it is not. The shader file lives in the plugin's
|
|
@@ -6835,7 +6967,8 @@ export interface VideoShaders extends Disposable {
|
|
|
6835
6967
|
*
|
|
6836
6968
|
* @remarks
|
|
6837
6969
|
* Never throws: a refused list shows in `status` (`invalid`, `missing`,
|
|
6838
|
-
* `limit`, `unsupported
|
|
6970
|
+
* `limit`, `unsupported`, and `previous-crash` from player 0.6.3: see
|
|
6971
|
+
* the interface's remarks) and goes to {@link Scope.onError}, and what the
|
|
6839
6972
|
* player held stays.
|
|
6840
6973
|
*
|
|
6841
6974
|
* @capability processing.video
|
|
@@ -8401,6 +8534,27 @@ export interface InstallOptions {
|
|
|
8401
8534
|
* reconnects to it.
|
|
8402
8535
|
*/
|
|
8403
8536
|
replace?: boolean;
|
|
8537
|
+
/**
|
|
8538
|
+
* Whether the plugin runs once installed (SDK 1.5.55). Left out, a first
|
|
8539
|
+
* install runs and an update or reinstall (from any source, `replace`
|
|
8540
|
+
* included) keeps the installed plugin's state: one the user or a store
|
|
8541
|
+
* disabled stays disabled, and only its files are installed (nothing
|
|
8542
|
+
* starts it, so the change commits without an `activating` step). `true`
|
|
8543
|
+
* enables and starts it, for example a version the store disabled that
|
|
8544
|
+
* an update fixes; if it fails to start, the old version is restored,
|
|
8545
|
+
* still disabled. `false` installs it disabled: a running version is
|
|
8546
|
+
* stopped and its session values go, as with {@link Packages.setEnabled}.
|
|
8547
|
+
* Built-in plugins always run (and cannot be installed over). A value
|
|
8548
|
+
* that is not a boolean is `invalid`.
|
|
8549
|
+
*
|
|
8550
|
+
* Planned for player 0.6.4: older players ignore it and an install there
|
|
8551
|
+
* always runs the plugin, so after a committed install call
|
|
8552
|
+
* {@link Packages.setEnabled} when the state matters.
|
|
8553
|
+
*
|
|
8554
|
+
* @player 0.6.4 (planned); older players ignore it (an install always
|
|
8555
|
+
* enables the plugin).
|
|
8556
|
+
*/
|
|
8557
|
+
enabled?: boolean;
|
|
8404
8558
|
}
|
|
8405
8559
|
/**
|
|
8406
8560
|
* A validated package directory, from {@link Packages.validate}.
|
|
@@ -8445,9 +8599,14 @@ export interface PackageCandidate {
|
|
|
8445
8599
|
* a built-in id fails with `unsupported` (built-ins update with the
|
|
8446
8600
|
* player). A reserved id (`vestra` or `vestra.*`, which every built-in
|
|
8447
8601
|
* uses) fails with `reserved` (player 0.4.1; already when validated), as
|
|
8448
|
-
* only the player installs built-in plugins.
|
|
8602
|
+
* only the player installs built-in plugins. An update keeps a disabled
|
|
8603
|
+
* plugin disabled unless `enabled` says otherwise (planned for player
|
|
8604
|
+
* 0.6.4; see {@link InstallOptions.enabled}).
|
|
8449
8605
|
*
|
|
8450
8606
|
* @capability plugins.manage
|
|
8607
|
+
* @throws `invalid` (rejects) An option is malformed: a `source` the
|
|
8608
|
+
* player does not accept, or a `replace` or `enabled` that is not a
|
|
8609
|
+
* boolean.
|
|
8451
8610
|
* @throws `conflict` (rejects) The plugin is installed from another source
|
|
8452
8611
|
* and `replace` is not set.
|
|
8453
8612
|
* @throws `unsupported` (rejects) A built-in plugin's id.
|
|
@@ -8470,7 +8629,19 @@ export interface PackageCandidate {
|
|
|
8470
8629
|
* declare const candidate: PackageCandidate;
|
|
8471
8630
|
* // ---cut---
|
|
8472
8631
|
* const change = await candidate.install({ replace: true });
|
|
8473
|
-
* await change.done; // "committed" once
|
|
8632
|
+
* await change.done; // "committed" once installed (running unless it stays disabled)
|
|
8633
|
+
* ```
|
|
8634
|
+
*
|
|
8635
|
+
* @example Enable a fixed version of a plugin the store disabled
|
|
8636
|
+
* A store disabled a version it revoked; the update that fixes it
|
|
8637
|
+
* should run again. Without `enabled` the update keeps the plugin
|
|
8638
|
+
* disabled (player 0.6.4); `true` enables it once installed. Older
|
|
8639
|
+
* players ignore the option and enable every install.
|
|
8640
|
+
* ```ts
|
|
8641
|
+
* declare const candidate: PackageCandidate;
|
|
8642
|
+
* // ---cut---
|
|
8643
|
+
* const change = await candidate.install({ enabled: true });
|
|
8644
|
+
* await change.done; // "committed" once the fixed version runs
|
|
8474
8645
|
* ```
|
|
8475
8646
|
*
|
|
8476
8647
|
* @since 1.0.0
|
package/dist/wire.d.ts
CHANGED
|
@@ -731,10 +731,15 @@ export declare function createWire(transport: Transport): {
|
|
|
731
731
|
/** Player 0.2.0: a `.vxp` file. */
|
|
732
732
|
validateArchive: (file: string) => Promise<PackageValidation>;
|
|
733
733
|
installed: (id: string) => Promise<InstalledPackage>;
|
|
734
|
-
/**
|
|
734
|
+
/**
|
|
735
|
+
* `source` and `replace` are sent only when given (player 0.2.0);
|
|
736
|
+
* `enabled` only when given (planned for player 0.6.4, older players
|
|
737
|
+
* ignore it).
|
|
738
|
+
*/
|
|
735
739
|
install: (token: string, expectedRevision: number, requestId: string, options?: {
|
|
736
740
|
source?: WireSource;
|
|
737
741
|
replace?: boolean;
|
|
742
|
+
enabled?: boolean;
|
|
738
743
|
}) => Promise<PackageOperation>;
|
|
739
744
|
operation: (operationId: string) => Promise<PackageOperation>;
|
|
740
745
|
cancel: (operationId: string) => Promise<PackageOperation>;
|
package/dist/wire.js
CHANGED
|
@@ -341,13 +341,20 @@ export function createWire(transport) {
|
|
|
341
341
|
file,
|
|
342
342
|
}),
|
|
343
343
|
installed: (id) => transport.request("packages.installed", { id }),
|
|
344
|
-
/**
|
|
344
|
+
/**
|
|
345
|
+
* `source` and `replace` are sent only when given (player 0.2.0);
|
|
346
|
+
* `enabled` only when given (planned for player 0.6.4, older players
|
|
347
|
+
* ignore it).
|
|
348
|
+
*/
|
|
345
349
|
install: (token, expectedRevision, requestId, options = {}) => transport.request("packages.install", {
|
|
346
350
|
token,
|
|
347
351
|
expectedRevision,
|
|
348
352
|
requestId,
|
|
349
353
|
...(options.source === undefined ? {} : { source: options.source }),
|
|
350
354
|
...(options.replace ? { replace: true } : {}),
|
|
355
|
+
...(options.enabled === undefined
|
|
356
|
+
? {}
|
|
357
|
+
: { enabled: options.enabled }),
|
|
351
358
|
}),
|
|
352
359
|
operation: (operationId) => transport.request("packages.operation", {
|
|
353
360
|
operationId,
|
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.55: 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. The documentation is a tree of short pages read by path, like `go doc`: the same pages and API as https://vestrasys.github.io/vestra-player-sdk/.
|
|
4
4
|
|
|
5
5
|
## Read the documentation
|
|
6
6
|
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
- `/API/Media`: 6 entries: CaptureOptions, Snapshot, ExportOptions, JobState, ExportJob, MediaCapture
|
|
33
33
|
- `/API/Packages`: 10 entries: PackageManifest, PackageSource, InstallOptions, PackageCandidate, PackageChangeStatus, PackageChange, PluginInfo, PluginDisplay, PluginWarning, Packages
|
|
34
34
|
- `/API/Playback`: 18 entries: MarkKind, TimelineMark, TimelineLayer, Timeline, AudioTrack, PlayerState, AudioDevice, Chapter, Conditional, Media, Scrub, StreamFrame, StreamOptions, StreamStartError, Stream,…
|
|
35
|
-
- `/API/Plugin`:
|
|
35
|
+
- `/API/Plugin`: 13 entries: Disposable, PluginError, Scope, Plugin, StopReason, DisposeInfo, WindowState, MainWindow, AppInfo, App, SystemInfo, DisplayInfo, Json
|
|
36
36
|
- `/API/Processing`: 18 entries: FilterStatus, VideoFilter, AudioFilter, ViewSettings, VideoView, RenderSettings, RenderOptions, VideoShaders, UpscalingReasonCode, UpscalingReason, UpscalingDecodingCause,…
|
|
37
37
|
- `/API/Services`: 15 entries: Clipboard, Power, ToolResult, Tools, JsonStore, Storage, SessionStorage, System, Secrets, RenderingMode, RenderingPreference, RenderingReasonCode, RenderingReason, RenderingState,…
|
|
38
38
|
- `/API/Sources`: 7 entries: ResolverOptions, WebResolverOptions, FileResolverOptions, ResolvedStream, Resolver, Resolvers, ResolvedMedia
|