@vestra-player/sdk 1.5.15 → 1.5.17
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 +22 -0
- package/dist/capabilities.js +0 -1
- package/dist/plugin/packages.js +2 -4
- package/dist/plugin/testing.d.ts +0 -2
- package/dist/plugin/testing.js +1 -4
- package/dist/plugin/types.d.ts +10 -9
- package/dist/wire.d.ts +0 -2
- package/dist/wire.js +0 -5
- package/llms-full.txt +87 -17
- package/llms.txt +22 -22
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.17
|
|
4
|
+
|
|
5
|
+
**Package rollback is removed**, as announced in 1.5.16: the player keeps no previous plugin version. `apiVersion` stays `vestra/1` (a patch release, as when 1.5.8 removed `PackageCandidate.trial()`); plugins that do not manage other plugins need no change. A plugin manager that still calls `rollback` or reads `previousVersion` no longer compiles. Migration: [Migrating off package rollback](docs/how-to/rollback-migration.md).
|
|
6
|
+
|
|
7
|
+
- **Removed: `Packages.rollback`** and the wire method `packages.rollback`. The player already answers the method with `unsupported` ("unknown package method"). To go back to an older version of a third-party plugin, the user uninstalls it and installs that version's `.vxp`.
|
|
8
|
+
- **Removed: `PluginInfo.previousVersion`** and the wire field `PackageInfo.previousVersion`; `packages.list` and `packages.changed` no longer carry it.
|
|
9
|
+
- **Removed from `CAPABILITY_INFO["plugins.manage"].apis`**: `"Packages.rollback"`; the capability is unchanged otherwise.
|
|
10
|
+
- **`TestHost`**: `host.packages` entries have no `previousVersion` field (delete it from test data), and there is no `packages.rollback` handler.
|
|
11
|
+
- Unchanged: `revisionConflict`, and the `restoring` and `failedDegraded` statuses of `PackageChange`, which belong to recovery from a failed install.
|
|
12
|
+
- Docs: the Capabilities reference loses the deprecation note; *Migrating off package rollback* says what 1.5.17 removed; `llms.txt` and `llms-full.txt` regenerated.
|
|
13
|
+
|
|
14
|
+
## 1.5.16
|
|
15
|
+
|
|
16
|
+
**Package rollback is deprecated: the player keeps no previous plugin version.** `apiVersion` stays `vestra/1`; code still compiles, and plugins that do not manage other plugins need no change. 1.5.17 removes the deprecated symbols, as 1.5.8 removed `PackageCandidate.trial()` at patch level. Migration: [Migrating off package rollback](docs/how-to/rollback-migration.md).
|
|
17
|
+
|
|
18
|
+
- **Deprecated: `Packages.rollback`** (removed in 1.5.17). The player no longer has `packages.rollback` and answers it with `unsupported` ("unknown package method"); the SDK still forwards the call, so the error reaches the caller. To go back to an older version of a third-party plugin, the user uninstalls it and installs that version's `.vxp`.
|
|
19
|
+
- **Deprecated: `PluginInfo.previousVersion`** and the wire field `PackageInfo.previousVersion` (removed in 1.5.17): always `null`, since an upgrade replaces the package. The SDK reads a missing field as `null`.
|
|
20
|
+
- **Built-in plugins** run in place from the player installation: `PluginInfo.directory` of a built-in is `<install resources>/plugins/<id>` and read-only (plugins write only under `plugin.dataDirectory`); they can be disabled and enabled, not uninstalled (`unsupported`, as before).
|
|
21
|
+
- **`TestHost`** matches the player: `packages.list` and `packages.changed` report `previousVersion: null` for every plugin, an install no longer records the replaced version, and `packages.rollback` rejects with `unsupported` ("unknown package method"). The `previousVersion` field of `host.packages` entries is deprecated and ignored; it stays required (as `PluginInfo.previousVersion` does), so existing test code compiles unchanged.
|
|
22
|
+
- `"Packages.rollback"` stays in `CAPABILITY_INFO["plugins.manage"].apis` until 1.5.17; the Capabilities reference notes the deprecation.
|
|
23
|
+
- Docs: the new how-to *Migrating off package rollback (1.5.16)*; *Publish a plugin* no longer mentions a roll back button, installing an older version from the store or the user's rollback as a safety net; the data directory is "kept across restarts, disabling and upgrades"; `revisionConflict`, `PackageChange` and `PackageChangeStatus` speak of installs and uninstalls (the statuses are unchanged).
|
|
24
|
+
|
|
3
25
|
## 1.5.15
|
|
4
26
|
|
|
5
27
|
**How many frames really reach the screen, and how the video is decoded** (player 0.5.1); every player error code documented; the fullscreen bottom bar's new behaviour. Additive; `apiVersion` stays `vestra/1`, and plugins need no change.
|
package/dist/capabilities.js
CHANGED
package/dist/plugin/packages.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Package validation and
|
|
1
|
+
/** Package validation, install and uninstall (`plugins.manage`). */
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { failure, sleep, uncertain } from "./runtime.js";
|
|
@@ -62,7 +62,7 @@ class ChangeHandle {
|
|
|
62
62
|
this.#accept(await this.#scope.runtime.host.packages.cancel(this.#id));
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
/** Install and
|
|
65
|
+
/** Install and uninstall reuse the request id as the operation id after a lost reply. */
|
|
66
66
|
async function change(scope, submit, id) {
|
|
67
67
|
const host = scope.runtime.host.packages;
|
|
68
68
|
const installed = await host.installed(id);
|
|
@@ -126,7 +126,6 @@ export const packages = (scope) => ({
|
|
|
126
126
|
const checked = await scope.runtime.host.packages.validateArchive(path);
|
|
127
127
|
return candidate(scope, path, checked);
|
|
128
128
|
},
|
|
129
|
-
rollback: (id) => change(scope, (requestId, revision) => scope.runtime.host.packages.rollback(id, revision, requestId), id),
|
|
130
129
|
list: async () => (await scope.runtime.host.packages.list()).map(info),
|
|
131
130
|
onChange(listener) {
|
|
132
131
|
const watch = scope.runtime.on("packages.changed", (data) => scope.run(listener, data.map(info)), null);
|
|
@@ -152,7 +151,6 @@ const info = (plugin) => Object.freeze({
|
|
|
152
151
|
error: plugin.error,
|
|
153
152
|
capabilities: [...plugin.capabilities],
|
|
154
153
|
directory: plugin.directory,
|
|
155
|
-
previousVersion: plugin.previousVersion,
|
|
156
154
|
builtin: plugin.builtin === true,
|
|
157
155
|
source: plugin.source ? Object.freeze({ ...plugin.source }) : null,
|
|
158
156
|
dataDirectory: plugin.dataDirectory ?? null,
|
package/dist/plugin/testing.d.ts
CHANGED
|
@@ -1328,8 +1328,6 @@ export declare class TestHost implements Transport {
|
|
|
1328
1328
|
capabilities: string[];
|
|
1329
1329
|
/** The package directory. */
|
|
1330
1330
|
directory: string;
|
|
1331
|
-
/** The version a rollback would restore, `null` without one. */
|
|
1332
|
-
previousVersion: string | null;
|
|
1333
1331
|
/** Shipped with the player: `uninstall` and `install` are `unsupported`. */
|
|
1334
1332
|
builtin?: boolean;
|
|
1335
1333
|
/** Where it was installed from. */
|
package/dist/plugin/testing.js
CHANGED
|
@@ -1919,7 +1919,7 @@ export class TestHost {
|
|
|
1919
1919
|
*/
|
|
1920
1920
|
setPackages(list) {
|
|
1921
1921
|
this.packages = list;
|
|
1922
|
-
this.emit("packages.changed", structuredClone(
|
|
1922
|
+
this.emit("packages.changed", structuredClone(this.packages));
|
|
1923
1923
|
}
|
|
1924
1924
|
#split(key) {
|
|
1925
1925
|
const at = key.indexOf("/");
|
|
@@ -3205,9 +3205,6 @@ export class TestHost {
|
|
|
3205
3205
|
error: null,
|
|
3206
3206
|
capabilities: [...manifest.capabilities],
|
|
3207
3207
|
directory: `/test-host/packages/${manifest.id}`,
|
|
3208
|
-
previousVersion: entry && identity(entry.source) === identity(source)
|
|
3209
|
-
? entry.version
|
|
3210
|
-
: null,
|
|
3211
3208
|
builtin: false,
|
|
3212
3209
|
source,
|
|
3213
3210
|
dataDirectory: entry?.dataDirectory ?? null,
|
package/dist/plugin/types.d.ts
CHANGED
|
@@ -90,7 +90,7 @@ export interface Disposable {
|
|
|
90
90
|
* | `pending` | An operation with the same token is still running in the player. The SDK waits and asks again, so it rarely reaches a plugin. |
|
|
91
91
|
* | `producer` | `openStream`: the player refused or lost the generated stream; `message` has the reason. |
|
|
92
92
|
* | `resolution` | A file or web resolver's result could not be opened; `message` has the reason. |
|
|
93
|
-
* | `revisionConflict` | {@link Packages}: the installed revision changed since it was read (another install
|
|
93
|
+
* | `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. |
|
|
94
94
|
* | `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°. |
|
|
95
95
|
* | `source-unavailable` | The source is not ready (still loading, or its time or native binding is not available yet). |
|
|
96
96
|
* | `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. |
|
|
@@ -336,7 +336,7 @@ export interface Plugin extends Scope {
|
|
|
336
336
|
* A writable directory for this plugin's own files (settings, caches,
|
|
337
337
|
* remembered folders), created by the player before it starts the plugin.
|
|
338
338
|
* Each installation has its own directory (player 0.2.0): it is kept across
|
|
339
|
-
* restarts, disabling
|
|
339
|
+
* restarts, disabling and upgrades, and a reinstall of the same
|
|
340
340
|
* id from a different source gets a new, empty one. It is deleted only by
|
|
341
341
|
* `packages.uninstall(id, { removeData: true })`. No capability.
|
|
342
342
|
*
|
|
@@ -4693,10 +4693,10 @@ export interface PackageCandidate {
|
|
|
4693
4693
|
*/
|
|
4694
4694
|
install(options?: InstallOptions): Promise<PackageChange>;
|
|
4695
4695
|
}
|
|
4696
|
-
/** The progress of an install
|
|
4696
|
+
/** The progress of an install or uninstall. */
|
|
4697
4697
|
export type PackageChangeStatus = "preparing" | "activating" | "restoring" | "committed" | "cancelled" | "failed" | "failedDegraded" | "tooLate";
|
|
4698
4698
|
/**
|
|
4699
|
-
* An install
|
|
4699
|
+
* An install or uninstall in progress. The SDK never submits a
|
|
4700
4700
|
* change twice: after a lost reply it only queries the same request.
|
|
4701
4701
|
*/
|
|
4702
4702
|
export interface PackageChange {
|
|
@@ -4725,10 +4725,13 @@ export interface PluginInfo {
|
|
|
4725
4725
|
error: string | null;
|
|
4726
4726
|
/** Declared capabilities. */
|
|
4727
4727
|
capabilities: string[];
|
|
4728
|
-
/**
|
|
4728
|
+
/**
|
|
4729
|
+
* The package directory (read its `manifest.json` for display fields).
|
|
4730
|
+
* A built-in's directory is inside the player installation
|
|
4731
|
+
* (`<install resources>/plugins/<id>`) and read-only: a plugin writes only
|
|
4732
|
+
* under its own {@link Plugin.dataDirectory}.
|
|
4733
|
+
*/
|
|
4729
4734
|
directory: string;
|
|
4730
|
-
/** The version a rollback would restore, `null` without one. */
|
|
4731
|
-
previousVersion: string | null;
|
|
4732
4735
|
/**
|
|
4733
4736
|
* Shipped with the player: it can be disabled, not uninstalled or
|
|
4734
4737
|
* replaced. `false` from players before 0.2.0.
|
|
@@ -4827,8 +4830,6 @@ export interface Packages {
|
|
|
4827
4830
|
* fails with `reserved`, as in {@link Packages.validate}.
|
|
4828
4831
|
*/
|
|
4829
4832
|
validateArchive(file: string): Promise<PackageCandidate>;
|
|
4830
|
-
/** Restore a plugin's previous version (`plugins.manage`). */
|
|
4831
|
-
rollback(id: string): Promise<PackageChange>;
|
|
4832
4833
|
/** Every plugin: running, installed, and this run's development loads. No capability. */
|
|
4833
4834
|
list(): Promise<PluginInfo[]>;
|
|
4834
4835
|
/** The new list after any start, stop, failure, install or uninstall. No capability. */
|
package/dist/wire.d.ts
CHANGED
|
@@ -102,7 +102,6 @@ export interface PackageInfo {
|
|
|
102
102
|
error: string | null;
|
|
103
103
|
capabilities: string[];
|
|
104
104
|
directory: string;
|
|
105
|
-
previousVersion: string | null;
|
|
106
105
|
/** Player 0.2.0; absent from older players. */
|
|
107
106
|
builtin?: boolean;
|
|
108
107
|
source?: WireSource | null;
|
|
@@ -567,7 +566,6 @@ export declare function createWire(transport: Transport): {
|
|
|
567
566
|
source?: WireSource;
|
|
568
567
|
replace?: boolean;
|
|
569
568
|
}) => Promise<PackageOperation>;
|
|
570
|
-
rollback: (id: string, expectedRevision: number, requestId: string) => Promise<PackageOperation>;
|
|
571
569
|
operation: (operationId: string) => Promise<PackageOperation>;
|
|
572
570
|
cancel: (operationId: string) => Promise<PackageOperation>;
|
|
573
571
|
list: () => Promise<PackageInfo[]>;
|
package/dist/wire.js
CHANGED
|
@@ -278,11 +278,6 @@ export function createWire(transport) {
|
|
|
278
278
|
...(options.source === undefined ? {} : { source: options.source }),
|
|
279
279
|
...(options.replace ? { replace: true } : {}),
|
|
280
280
|
}),
|
|
281
|
-
rollback: (id, expectedRevision, requestId) => transport.request("packages.rollback", {
|
|
282
|
-
id,
|
|
283
|
-
expectedRevision,
|
|
284
|
-
requestId,
|
|
285
|
-
}),
|
|
286
281
|
operation: (operationId) => transport.request("packages.operation", {
|
|
287
282
|
operationId,
|
|
288
283
|
}),
|
package/llms-full.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @vestra-player/sdk 1.5.
|
|
1
|
+
# @vestra-player/sdk 1.5.17: 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
|
|
|
@@ -319,7 +319,7 @@ So `onDispose` cleanup must be quick and must not rely on the player. Media the
|
|
|
319
319
|
plugin.dataDirectory is a writable directory for the plugin's own files (settings, caches, remembered folders). It needs no capability; the player creates it before it starts the plugin.
|
|
320
320
|
|
|
321
321
|
- **One directory per installation.** Each fresh installation of a plugin gets its own directory (player 0.2.0). Installing the same id again from the same source is an upgrade and keeps the directory. Installing the same id from a different source, and confirming the replacement, gives the new plugin a new, empty directory; the old one is kept and reconnected if the user goes back to the first source later. A development load (`--plugin`) has a directory of its own.
|
|
322
|
-
- **Kept** when the plugin is disabled, reloaded
|
|
322
|
+
- **Kept** when the plugin is disabled, reloaded or upgraded. Uninstalling deletes it only when the user asks for that (uninstall with `removeData: true`); otherwise reinstalling from the same source reconnects it.
|
|
323
323
|
- **The path's layout is not part of the API.** Do not derive another plugin's directory from yours; a plugin manager reads it from PluginInfo.dataDirectory.
|
|
324
324
|
- **Write atomically.** Write to a temporary name and rename it, so a player that quits mid-write never leaves half a file; fall back to defaults when a file is missing or unreadable. For JSON settings use plugin.storage.json, which already does this (defaults, debounced atomic writes, a corrupt file moved aside).
|
|
325
325
|
- Reading the property throws `unavailable` when the player could not provide a directory.
|
|
@@ -1753,9 +1753,9 @@ Every member of `TestHost` is in the @vestra-player/sdk/testing reference.
|
|
|
1753
1753
|
|
|
1754
1754
|
## How-to: Publish a plugin
|
|
1755
1755
|
|
|
1756
|
-
**Goal:** your plugin appears in the player's plugin store, where users install, update and
|
|
1756
|
+
**Goal:** your plugin appears in the player's plugin store, where users install, update and uninstall it.
|
|
1757
1757
|
|
|
1758
|
-
The store reads the official plugin repository, [vestrasys/vestra-player-plugins](https://github.com/vestrasys/vestra-player-plugins). Every change to it is a pull request to its `main` branch, and **a merge is a release**: there are no beta channels, and CI
|
|
1758
|
+
The store reads the official plugin repository, [vestrasys/vestra-player-plugins](https://github.com/vestrasys/vestra-player-plugins). Every change to it is a pull request to its `main` branch, and **a merge is a release**: there are no beta channels, and CI and revocation are the safety net. Every plugin in it is a third-party plugin; only the plugins built into the player are official. The repository's [README](https://github.com/vestrasys/vestra-player-plugins#contributing-a-plugin) and [CONTRIBUTING](https://github.com/vestrasys/vestra-player-plugins/blob/main/CONTRIBUTING.md) hold the full rules; this page is the author's path through them.
|
|
1759
1759
|
|
|
1760
1760
|
#### Two ways in
|
|
1761
1761
|
|
|
@@ -1836,10 +1836,84 @@ After the merge, the repository's publish workflow builds the package, releases
|
|
|
1836
1836
|
The store shows the newest version's manifest fields, so what you write there is the plugin's page:
|
|
1837
1837
|
|
|
1838
1838
|
- **The list**: name and version, author and repository, the punchline. Search covers the name, id, author, punchline and tags; the list can be filtered by tag and sorted by name or by the date of the newest version. There are no download counts.
|
|
1839
|
-
- **The plugin's page**: the icon, name, author, punchline and the buttons (install, update,
|
|
1839
|
+
- **The plugin's page**: the icon, name, author, punchline and the buttons (install, update, uninstall). Three tabs show the README, the changelog and the version history (version, release date, `minPlayerVersion`, and whether the version is current, incompatible or revoked). The player keeps no previous version, and the store has no action to install an older one: a user who wants an older version uninstalls the plugin and installs that version's `.vxp` (Migrating off package rollback). A side column shows the version, the date, the license, a link to `sourceUrl` and the capabilities in plain words, the risky ones highlighted (the wording is the "Shown in the store as" column of Capabilities), with a fixed note that capabilities limit only the player API, not file or network access.
|
|
1840
1840
|
- **Before installing**, the user confirms a dialog that lists the capabilities; an update that adds capabilities lists the new ones. A plugin whose every version needs a newer player shows "needs Vestra Player x.y.z or later" instead of an install button.
|
|
1841
1841
|
- **The README** is rendered as Markdown; relative links and images resolve against the README's own address, and remote images are shown for the official repository.
|
|
1842
1842
|
|
|
1843
|
+
## How-to: Migrating off package rollback (1.5.16, removed in 1.5.17)
|
|
1844
|
+
|
|
1845
|
+
For plugins that manage other plugins (`plugins.manage`): stores, plugin managers, anything that called `packages.rollback` or read `PluginInfo.previousVersion`. Both were deprecated in 1.5.16 and **removed in 1.5.17**: with 1.5.17 a remaining use is a TypeScript compile error. A plugin that only uses its own API needs no change.
|
|
1846
|
+
|
|
1847
|
+
#### What changed
|
|
1848
|
+
|
|
1849
|
+
The player no longer keeps a previous version of a plugin. An upgrade replaces the package, so there is nothing to roll back to:
|
|
1850
|
+
|
|
1851
|
+
- There is no `packages.rollback`: the player answers the wire method with `unsupported` ("unknown package method"), and SDK 1.5.17 no longer has it.
|
|
1852
|
+
- `packages.list()` and `packages.onChange` entries have no `previousVersion` (always `null` with SDK 1.5.16, removed in 1.5.17).
|
|
1853
|
+
- Built-in plugins (`builtin: true`) run in place from the player installation. They can be disabled and enabled, never uninstalled or replaced; `uninstall` fails with `unsupported`. A built-in's `directory` is inside the installation (`<install resources>/plugins/<id>`) and read-only.
|
|
1854
|
+
- Third-party plugins can still be uninstalled.
|
|
1855
|
+
|
|
1856
|
+
Why: built-ins now run from the read-only installation instead of a copy, and an upgrade replaces the package instead of keeping the old one beside it. Going back to an older version becomes an ordinary uninstall and install that the user starts.
|
|
1857
|
+
|
|
1858
|
+
`TestHost` behaves the same way, so tests show it too.
|
|
1859
|
+
|
|
1860
|
+
#### 1. Remove rollback calls and `previousVersion` checks
|
|
1861
|
+
|
|
1862
|
+
Before:
|
|
1863
|
+
|
|
1864
|
+
```ts nocheck
|
|
1865
|
+
if (info.previousVersion !== null) showButton("Roll back", () => plugin.packages.rollback(info.id));
|
|
1866
|
+
```
|
|
1867
|
+
|
|
1868
|
+
After: delete both. There is no replacement call; nothing on the player can restore an earlier package. Remove the button, menu item or command, and any state, text or test that depends on `previousVersion`. With SDK 1.5.17 the compiler finds every remaining use.
|
|
1869
|
+
|
|
1870
|
+
#### 2. Going back to an older version
|
|
1871
|
+
|
|
1872
|
+
Managing plugins has exactly three operations: **install**, **uninstall** and **disable/enable**. There is no rollback, no "install older version" and no "Undo install".
|
|
1873
|
+
|
|
1874
|
+
To go back to an older version, the user uninstalls the plugin and installs the older version's `.vxp` file themselves, as two ordinary operations. Do not offer an action that does both, an older-version install from a store, or an "Undo" after an install (the official store and the AI window drop theirs). Uninstalling without `removeData` keeps the plugin's data, and a reinstall from the same source reconnects to it; a `.vxp` file is a different source from a repository, so a plugin first installed from the store gets a new, empty data directory.
|
|
1875
|
+
|
|
1876
|
+
#### 3. Remove "Undo install"
|
|
1877
|
+
|
|
1878
|
+
Remove any Undo action after an install, its texts and translations, and the code that only served it: copies of installed `.vxp` files, remembered "last install" state, and tests of it. A user who wants a plugin gone uninstalls it.
|
|
1879
|
+
|
|
1880
|
+
#### 4. Built-in plugins
|
|
1881
|
+
|
|
1882
|
+
Check `builtin` before offering actions:
|
|
1883
|
+
|
|
1884
|
+
```ts
|
|
1885
|
+
for (const info of await plugin.packages.list()) {
|
|
1886
|
+
const actions = info.builtin
|
|
1887
|
+
? [info.state === "disabled" ? "Enable" : "Disable"]
|
|
1888
|
+
: ["Disable", "Uninstall"];
|
|
1889
|
+
console.log(info.id, actions);
|
|
1890
|
+
}
|
|
1891
|
+
```
|
|
1892
|
+
|
|
1893
|
+
- Offer only disable and enable for a built-in (`packages.setEnabled`); never uninstall or install over it.
|
|
1894
|
+
- Never write into a built-in's `directory`, or into any plugin's `directory`: read `manifest.json` from it if you need to. A plugin writes only under its own plugin.dataDirectory.
|
|
1895
|
+
|
|
1896
|
+
#### 5. Tests
|
|
1897
|
+
|
|
1898
|
+
With SDK 1.5.17, `host.packages` entries have no `previousVersion` field: delete `previousVersion: null` (or any value) from test data, or the test no longer compiles. `TestHost` has no `packages.rollback`. Remove tests that exercised a rollback; there is nothing left to assert. (With 1.5.16 the field was still required but ignored, and `TestHost` answered rollback with `unsupported`.)
|
|
1899
|
+
|
|
1900
|
+
#### Timeline
|
|
1901
|
+
|
|
1902
|
+
| SDK | Player | What happens |
|
|
1903
|
+
| --- | --- | --- |
|
|
1904
|
+
| 1.5.16 | ships with it | `Packages.rollback` and `PluginInfo.previousVersion` (and the wire field) are `@deprecated`. The player and `TestHost` answer rollback with `unsupported` and list `previousVersion: null`. Code still compiles. |
|
|
1905
|
+
| 1.5.17 | ships with it | **Removed:** `Packages.rollback`, `PluginInfo.previousVersion`, the wire method `packages.rollback` and the wire field `previousVersion`, `TestHost`'s `previousVersion` field and its `packages.rollback` handler, and `Packages.rollback` in the `plugins.manage` capability. Remaining uses are TypeScript compile errors. |
|
|
1906
|
+
|
|
1907
|
+
`apiVersion` stays `vestra/1` in both releases, as when 1.5.8 removed `PackageCandidate.trial()`.
|
|
1908
|
+
|
|
1909
|
+
#### Before you hand it in
|
|
1910
|
+
|
|
1911
|
+
- [ ] No call to `packages.rollback`; no UI or logic gated on `previousVersion`.
|
|
1912
|
+
- [ ] Built-ins offer only disable and enable.
|
|
1913
|
+
- [ ] Nothing writes into a plugin's `directory`.
|
|
1914
|
+
- [ ] No Undo after an install, no older-version install; older versions go through uninstall and a user-chosen `.vxp`.
|
|
1915
|
+
- [ ] Tests compile and pass with SDK 1.5.17; no `previousVersion` is left in `host.packages` test data.
|
|
1916
|
+
|
|
1843
1917
|
## Guideline: Interface guidelines
|
|
1844
1918
|
|
|
1845
1919
|
These are the rules the player's built-in plugins follow for their windows, panels and messages. Follow them and your plugin looks like part of the player. They assume you know the five surfaces from Windows, panels, settings pages, dialogs and the OSD; token names, sizes, fonts, icons and widgets are listed in Theme.
|
|
@@ -2102,7 +2176,7 @@ A plugin lists what it may do in the manifest's `capabilities` (Manifest). A cal
|
|
|
2102
2176
|
| `ui.window` | Windows.create | — | Open tool windows | |
|
|
2103
2177
|
| `ui.mount` | Panels.mount, Panels.bottom, Panels.sidebar, Panels.backdrop, PanelSlot.onChange, Panel.onChange, BackdropSlot.onChange | — | Show panels in the bottom bar, the sidebar or behind the video | |
|
|
2104
2178
|
| `ui.dialog` | Dialogs.confirm, Dialogs.openFile, Dialogs.openFiles, Dialogs.openDirectory, Dialogs.saveFile | — | Show file pickers and confirmations | |
|
|
2105
|
-
| `plugins.manage` | Packages.validate, Packages.validateArchive, Packages.
|
|
2179
|
+
| `plugins.manage` | Packages.validate, Packages.validateArchive, Packages.setEnabled, Packages.uninstall | — | Install, uninstall and disable other plugins | yes |
|
|
2106
2180
|
| `processing.video` | Plugin.video.filter, Plugin.video.view, Plugin.video.render | `playback.read` | Process the picture (filters, shaders) | |
|
|
2107
2181
|
| `processing.audio` | Plugin.audio.filter | `playback.read`, `events` | Process the sound | |
|
|
2108
2182
|
| `subtitles.read` | Subtitles.state, Subtitles.onChange, Subtitles.refresh | — | Read subtitles | |
|
|
@@ -3070,7 +3144,7 @@ change).
|
|
|
3070
3144
|
| `pending` | An operation with the same token is still running in the player. The SDK waits and asks again, so it rarely reaches a plugin. |
|
|
3071
3145
|
| `producer` | `openStream`: the player refused or lost the generated stream; `message` has the reason. |
|
|
3072
3146
|
| `resolution` | A file or web resolver's result could not be opened; `message` has the reason. |
|
|
3073
|
-
| `revisionConflict` | Packages: the installed revision changed since it was read (another install
|
|
3147
|
+
| `revisionConflict` | 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. |
|
|
3074
3148
|
| `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°. |
|
|
3075
3149
|
| `source-unavailable` | The source is not ready (still loading, or its time or native binding is not available yet). |
|
|
3076
3150
|
| `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. |
|
|
@@ -3245,7 +3319,7 @@ interface Plugin extends Scope {
|
|
|
3245
3319
|
* A writable directory for this plugin's own files (settings, caches,
|
|
3246
3320
|
* remembered folders), created by the player before it starts the plugin.
|
|
3247
3321
|
* Each installation has its own directory (player 0.2.0): it is kept across
|
|
3248
|
-
* restarts, disabling
|
|
3322
|
+
* restarts, disabling and upgrades, and a reinstall of the same
|
|
3249
3323
|
* id from a different source gets a new, empty one. It is deleted only by
|
|
3250
3324
|
* `packages.uninstall(id, { removeData: true })`. No capability.
|
|
3251
3325
|
*
|
|
@@ -5885,7 +5959,7 @@ interface PackageCandidate {
|
|
|
5885
5959
|
|
|
5886
5960
|
### PackageChangeStatus
|
|
5887
5961
|
|
|
5888
|
-
The progress of an install
|
|
5962
|
+
The progress of an install or uninstall.
|
|
5889
5963
|
|
|
5890
5964
|
```ts
|
|
5891
5965
|
type PackageChangeStatus = "preparing" | "activating" | "restoring" | "committed" | "cancelled" | "failed" | "failedDegraded" | "tooLate";
|
|
@@ -5893,7 +5967,7 @@ type PackageChangeStatus = "preparing" | "activating" | "restoring" | "committed
|
|
|
5893
5967
|
|
|
5894
5968
|
### PackageChange
|
|
5895
5969
|
|
|
5896
|
-
An install
|
|
5970
|
+
An install or uninstall in progress. The SDK never submits a
|
|
5897
5971
|
change twice: after a lost reply it only queries the same request.
|
|
5898
5972
|
|
|
5899
5973
|
```ts
|
|
@@ -5929,10 +6003,10 @@ interface PluginInfo {
|
|
|
5929
6003
|
error: string | null;
|
|
5930
6004
|
/** Declared capabilities. */
|
|
5931
6005
|
capabilities: string[];
|
|
5932
|
-
/**
|
|
6006
|
+
/**
|
|
6007
|
+
* The package directory (read its `manifest.json` for display fields). A built-in's directory is inside the player installation (`<install resources>/plugins/<id>`) and read-only: a plugin writes only under its own Plugin.dataDirectory.
|
|
6008
|
+
*/
|
|
5933
6009
|
directory: string;
|
|
5934
|
-
/** The version a rollback would restore, `null` without one. */
|
|
5935
|
-
previousVersion: string | null;
|
|
5936
6010
|
/** Shipped with the player: it can be disabled, not uninstalled or replaced. `false` from players before 0.2.0. */
|
|
5937
6011
|
builtin: boolean;
|
|
5938
6012
|
/** Where an installed plugin came from; `null` for development loads. */
|
|
@@ -6006,8 +6080,6 @@ interface Packages {
|
|
|
6006
6080
|
* Validate a `.vxp` package file (`plugins.manage`, player 0.2.0): the same checks as Packages.validate after unpacking. Check the file's sha256 yourself before calling when you know it. A reserved id fails with `reserved`, as in Packages.validate.
|
|
6007
6081
|
*/
|
|
6008
6082
|
validateArchive(file: string): Promise<PackageCandidate>;
|
|
6009
|
-
/** Restore a plugin's previous version (`plugins.manage`). */
|
|
6010
|
-
rollback(id: string): Promise<PackageChange>;
|
|
6011
6083
|
/** Every plugin: running, installed, and this run's development loads. No capability. */
|
|
6012
6084
|
list(): Promise<PluginInfo[]>;
|
|
6013
6085
|
/** The new list after any start, stop, failure, install or uninstall. No capability. */
|
|
@@ -7344,8 +7416,6 @@ class TestHost implements Transport {
|
|
|
7344
7416
|
capabilities: string[];
|
|
7345
7417
|
/** The package directory. */
|
|
7346
7418
|
directory: string;
|
|
7347
|
-
/** The version a rollback would restore, `null` without one. */
|
|
7348
|
-
previousVersion: string | null;
|
|
7349
7419
|
/** Shipped with the player: `uninstall` and `install` are `unsupported`. */
|
|
7350
7420
|
builtin?: boolean;
|
|
7351
7421
|
/** Where it was installed from. */
|
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.17: 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. Everything a plugin creates (commands, windows, panels, filters, listeners…) is a handle that is released automatically when the plugin stops. L<n> are line numbers in llms-full.txt, next to this file: every page, declaration and example.
|
|
4
4
|
|
|
5
5
|
**Rules**
|
|
6
6
|
|
|
@@ -33,7 +33,7 @@ my-plugin/
|
|
|
33
33
|
}
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Fields: `id`, `version`, `apiVersion`, `entry`, `capabilities`, `name`, `author`, `punchline`, `description`, `tags`, `icon`, `readme`, `changelog`, `sourceUrl`, `license`, `minPlayerVersion`; unknown fields and capabilities are rejected. Rules and limits:
|
|
36
|
+
Fields: `id`, `version`, `apiVersion`, `entry`, `capabilities`, `name`, `author`, `punchline`, `description`, `tags`, `icon`, `readme`, `changelog`, `sourceUrl`, `license`, `minPlayerVersion`; unknown fields and capabilities are rejected. Rules and limits: L2100–2163.
|
|
37
37
|
|
|
38
38
|
**main.ts**
|
|
39
39
|
|
|
@@ -60,7 +60,7 @@ export default async function activate(plugin: Plugin) {
|
|
|
60
60
|
- `ui.window`: Windows.create
|
|
61
61
|
- `ui.mount`: Panels.mount/bottom/sidebar/backdrop, PanelSlot/Panel/BackdropSlot.onChange
|
|
62
62
|
- `ui.dialog`: Dialogs.confirm/openFile/openFiles/openDirectory/saveFile
|
|
63
|
-
- `plugins.manage`: Packages.validate/validateArchive/
|
|
63
|
+
- `plugins.manage`: Packages.validate/validateArchive/setEnabled/uninstall
|
|
64
64
|
- `processing.video`: Plugin.video.filter/video.view/video.render (also `playback.read`)
|
|
65
65
|
- `processing.audio`: Plugin.audio.filter (also `playback.read`, `events`)
|
|
66
66
|
- `subtitles.read`: Subtitles.state/onChange/refresh
|
|
@@ -76,9 +76,9 @@ export default async function activate(plugin: Plugin) {
|
|
|
76
76
|
- `ui.settings`: Settings.page/show
|
|
77
77
|
- `ui.theme`: Appearance.set/setLanguage
|
|
78
78
|
- `secrets`: Secrets.get/set/delete
|
|
79
|
-
- No capability: `tools`, `storage`, `system`, `app`, `ui` (images and Markdown), `i18n`, reading window.state, appearance.theme and packages.list. Details:
|
|
79
|
+
- No capability: `tools`, `storage`, `system`, `app`, `ui` (images and Markdown), `i18n`, reading window.state, appearance.theme and packages.list. Details: L2164–2206.
|
|
80
80
|
|
|
81
|
-
**Test**: `tests/*.test.ts` with `node:test`, run by `vestra-plugin test`. `startPlugin(activate, { directory: new URL("..", import.meta.url) })` from `@vestra-player/sdk/testing` returns `{ host, stop }`; `host` is a TestHost (an in-memory player): `host.setSource(…)`, `host.command(id).run()`, `host.press(key)`, `await host.settle()`, then assert on `host.osd`, `host.window(id).props`… Guide: L1588–1753; TestHost:
|
|
81
|
+
**Test**: `tests/*.test.ts` with `node:test`, run by `vestra-plugin test`. `startPlugin(activate, { directory: new URL("..", import.meta.url) })` from `@vestra-player/sdk/testing` returns `{ host, stop }`; `host` is a TestHost (an in-memory player): `host.setSource(…)`, `host.command(id).run()`, `host.press(key)`, `await host.settle()`, then assert on `host.osd`, `host.window(id).props`… Guide: L1588–1753; TestHost: L6747.
|
|
82
82
|
|
|
83
83
|
**Command line**
|
|
84
84
|
|
|
@@ -106,39 +106,39 @@ export default async function activate(plugin: Plugin) {
|
|
|
106
106
|
- `limit`: A player-wide limit was reached, a property value is over the limits of PropValue
|
|
107
107
|
- `reserved`: The id is `vestra` or starts with `vestra.`
|
|
108
108
|
- `versionMismatch`: The package needs a newer player or another `apiVersion`
|
|
109
|
-
- Others: PluginError, llms-full.txt
|
|
109
|
+
- Others: PluginError, llms-full.txt L3103.
|
|
110
110
|
|
|
111
111
|
## Reference
|
|
112
112
|
|
|
113
113
|
- [llms-full.txt](https://vestrasys.github.io/vestra-player-sdk/llms-full.txt), also node_modules/@vestra-player/sdk/llms-full.txt; the pages are on the [site](https://vestrasys.github.io/vestra-player-sdk/).
|
|
114
114
|
- Tutorials: Getting started L9–214
|
|
115
115
|
- Concepts: How a plugin runs L215–364; Commands, menus and key bindings L365–462; Playback state, controls and sources L463–585; Video and audio processing L586–663; Windows, panels, settings pages, dialogs and the OSD L664–950
|
|
116
|
-
- How-to: Keep a slider responsive while dragging L951–1125; Remember a window's state between runs L1126–1255; Translate a plugin L1256–1439; Migrate a plugin to English source text L1440–1587; Test a plugin L1588–1753; Publish a plugin L1754–1842
|
|
117
|
-
- Guidelines: Interface guidelines
|
|
118
|
-
- Reference: Manifest
|
|
119
|
-
- [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html):
|
|
120
|
-
- [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html):
|
|
116
|
+
- How-to: Keep a slider responsive while dragging L951–1125; Remember a window's state between runs L1126–1255; Translate a plugin L1256–1439; Migrate a plugin to English source text L1440–1587; Test a plugin L1588–1753; Publish a plugin L1754–1842; Migrating off package rollback (1.5.16, removed in 1.5.17) L1843–1916
|
|
117
|
+
- Guidelines: Interface guidelines L1917–2001; Menu guidelines L2002–2099
|
|
118
|
+
- Reference: Manifest L2100–2163; Capabilities L2164–2206; Limits L2207–2301; Commands, menus and bindings L2302–2770; Platforms L2771–2799; Theme L2800–3004; Command line L3005–3060
|
|
119
|
+
- [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L3061–6582
|
|
120
|
+
- [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html): L6583–7905
|
|
121
121
|
|
|
122
122
|
## API
|
|
123
123
|
|
|
124
124
|
Where each module of `plugin` (`plugin.<module>`) is declared:
|
|
125
125
|
|
|
126
|
-
- Plugin
|
|
127
|
-
- Commands
|
|
126
|
+
- Plugin L3295, Scope L3164, PluginError L3103, Disposable L3076
|
|
127
|
+
- Commands L3507; Windows L3712; Panels L4355; Dialogs L4641; Player L5089; video: VideoFilter L5354, VideoView L5417, RenderOptions L5459; audio: AudioFilter L5378; Subtitles L5557; media: MediaCapture L5717; Resolvers L5822; Packages L6062; Osd L5290; Host L5238; Clipboard L6177; Power L6197; Tools L6230; Storage L6273; System L6296; Secrets L6314; window: MainWindow L4478; App L4559; Bindings L6147; Ui L3599; Timeline L4435; Drops L4011; Appearance L4040; I18n L4182; Settings L4288.
|
|
128
128
|
|
|
129
129
|
## Examples
|
|
130
130
|
|
|
131
131
|
Tested plugins in [examples/](https://github.com/vestrasys/vestra-player-sdk/tree/main/examples):
|
|
132
132
|
|
|
133
|
-
- hello: The README Quick start: one menu command and an on-screen message.
|
|
134
|
-
- blur: A tool window with one slider that blurs the playing video.
|
|
135
|
-
- signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone.
|
|
136
|
-
- bookmark: A tool window bound to the playback state: remember the position and jump back to it.
|
|
137
|
-
- panel-menu: A sidebar panel with its own right-click menu.
|
|
138
|
-
- menus: Radio menu items, separator groups and a menu item whose title changes.
|
|
139
|
-
- theme-widgets: A tool window drawn with the player's theme and widgets.
|
|
140
|
-
- markdown: A window that shows a Markdown document.
|
|
141
|
-
- i18n: A plugin in the player's language: English source text, a Chinese translation.
|
|
133
|
+
- hello: The README Quick start: one menu command and an on-screen message. L8329–8418
|
|
134
|
+
- blur: A tool window with one slider that blurs the playing video. L8419–8680
|
|
135
|
+
- signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L8681–9013
|
|
136
|
+
- bookmark: A tool window bound to the playback state: remember the position and jump back to it. L9014–9198
|
|
137
|
+
- panel-menu: A sidebar panel with its own right-click menu. L9199–9404
|
|
138
|
+
- menus: Radio menu items, separator groups and a menu item whose title changes. L9405–9564
|
|
139
|
+
- theme-widgets: A tool window drawn with the player's theme and widgets. L9565–9728
|
|
140
|
+
- markdown: A window that shows a Markdown document. L9729–9923
|
|
141
|
+
- i18n: A plugin in the player's language: English source text, a Chinese translation. L9924–10091
|
|
142
142
|
|
|
143
143
|
## Optional
|
|
144
144
|
|