@vestra-player/sdk 1.5.29 → 1.5.30
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 +12 -0
- package/dist/capabilities.d.ts +13 -3
- package/dist/capabilities.js +13 -3
- package/dist/commands.d.ts +49 -9
- package/dist/commands.js +49 -9
- package/dist/index.d.ts +6 -1
- package/dist/palettes.d.ts +16 -2
- package/dist/palettes.js +16 -2
- package/dist/plugin/bindings.d.ts +27 -3
- package/dist/plugin/bindings.js +27 -3
- package/dist/plugin/osd.d.ts +13 -1
- package/dist/plugin/osd.js +13 -1
- package/dist/plugin/services.d.ts +46 -6
- package/dist/plugin/services.js +46 -6
- package/dist/plugin/testing.d.ts +353 -62
- package/dist/plugin/testing.js +343 -61
- package/dist/plugin/types.d.ts +781 -164
- package/dist/plugin/values.d.ts +27 -3
- package/dist/plugin/values.js +27 -3
- package/dist/processing.d.ts +16 -4
- package/dist/source-resolvers.d.ts +33 -8
- package/dist/subtitles.d.ts +19 -9
- package/dist/wire.d.ts +12 -3
- package/dist/wire.js +12 -3
- package/llms-full.txt +3312 -20
- package/llms.txt +20 -20
- package/package.json +1 -1
package/dist/plugin/values.d.ts
CHANGED
|
@@ -17,12 +17,36 @@ import type { Json } from "../wire.ts";
|
|
|
17
17
|
* bound (a playlist, a history) should send the rows near the view and say
|
|
18
18
|
* how many there are, not the whole list.
|
|
19
19
|
*
|
|
20
|
-
* @example
|
|
20
|
+
* @example Send a long playlist as a window of rows
|
|
21
|
+
* A list view shows any number of entries while each write stays small:
|
|
22
|
+
* the plugin sends only the rows around the scroll position plus the total,
|
|
23
|
+
* and the view reports where it scrolled to. After the `scrolled` handler
|
|
24
|
+
* returns, the SDK evaluates the `bind` function again and sends the new
|
|
25
|
+
* window. Each row object and each of its fields count as one value toward
|
|
26
|
+
* `values`, and so does the array holding them.
|
|
21
27
|
* ```ts
|
|
28
|
+
* declare const entries: { title: string; duration: number }[];
|
|
29
|
+
* import type { View } from "@vestra-player/sdk";
|
|
30
|
+
* declare const list: View;
|
|
31
|
+
* // ---cut---
|
|
22
32
|
* import { UI_VALUE_LIMITS } from "@vestra-player/sdk/testing";
|
|
23
33
|
*
|
|
24
|
-
* //
|
|
25
|
-
*
|
|
34
|
+
* // 200 rows, or fewer if the limits were smaller: 2 fields per row make
|
|
35
|
+
* // 3 values (the row and its fields), plus 1 for the array.
|
|
36
|
+
* const size = Math.min(
|
|
37
|
+
* 200,
|
|
38
|
+
* UI_VALUE_LIMITS.arrayRows,
|
|
39
|
+
* Math.floor((UI_VALUE_LIMITS.values - 1) / 3),
|
|
40
|
+
* );
|
|
41
|
+
* let first = 0;
|
|
42
|
+
* list.bind(() => ({
|
|
43
|
+
* total: entries.length,
|
|
44
|
+
* rows: entries.slice(first, first + size),
|
|
45
|
+
* }));
|
|
46
|
+
* // The Slint view reports its first visible row; the window follows it.
|
|
47
|
+
* list.on("scrolled", (row: number) => {
|
|
48
|
+
* first = Math.max(0, row - Math.floor(size / 2));
|
|
49
|
+
* });
|
|
26
50
|
* ```
|
|
27
51
|
*
|
|
28
52
|
* @since 1.5.10
|
package/dist/plugin/values.js
CHANGED
|
@@ -15,12 +15,36 @@
|
|
|
15
15
|
* bound (a playlist, a history) should send the rows near the view and say
|
|
16
16
|
* how many there are, not the whole list.
|
|
17
17
|
*
|
|
18
|
-
* @example
|
|
18
|
+
* @example Send a long playlist as a window of rows
|
|
19
|
+
* A list view shows any number of entries while each write stays small:
|
|
20
|
+
* the plugin sends only the rows around the scroll position plus the total,
|
|
21
|
+
* and the view reports where it scrolled to. After the `scrolled` handler
|
|
22
|
+
* returns, the SDK evaluates the `bind` function again and sends the new
|
|
23
|
+
* window. Each row object and each of its fields count as one value toward
|
|
24
|
+
* `values`, and so does the array holding them.
|
|
19
25
|
* ```ts
|
|
26
|
+
* declare const entries: { title: string; duration: number }[];
|
|
27
|
+
* import type { View } from "@vestra-player/sdk";
|
|
28
|
+
* declare const list: View;
|
|
29
|
+
* // ---cut---
|
|
20
30
|
* import { UI_VALUE_LIMITS } from "@vestra-player/sdk/testing";
|
|
21
31
|
*
|
|
22
|
-
* //
|
|
23
|
-
*
|
|
32
|
+
* // 200 rows, or fewer if the limits were smaller: 2 fields per row make
|
|
33
|
+
* // 3 values (the row and its fields), plus 1 for the array.
|
|
34
|
+
* const size = Math.min(
|
|
35
|
+
* 200,
|
|
36
|
+
* UI_VALUE_LIMITS.arrayRows,
|
|
37
|
+
* Math.floor((UI_VALUE_LIMITS.values - 1) / 3),
|
|
38
|
+
* );
|
|
39
|
+
* let first = 0;
|
|
40
|
+
* list.bind(() => ({
|
|
41
|
+
* total: entries.length,
|
|
42
|
+
* rows: entries.slice(first, first + size),
|
|
43
|
+
* }));
|
|
44
|
+
* // The Slint view reports its first visible row; the window follows it.
|
|
45
|
+
* list.on("scrolled", (row: number) => {
|
|
46
|
+
* first = Math.max(0, row - Math.floor(size / 2));
|
|
47
|
+
* });
|
|
24
48
|
* ```
|
|
25
49
|
*
|
|
26
50
|
* @since 1.5.10
|
package/dist/processing.d.ts
CHANGED
|
@@ -13,10 +13,17 @@ import type { Transport } from "./wire.ts";
|
|
|
13
13
|
* rotation): a portrait video is upright before `transform` runs. At most
|
|
14
14
|
* 64 nodes across all plugins.
|
|
15
15
|
*
|
|
16
|
-
* @example
|
|
16
|
+
* @example Brighten, sharpen and rotate the picture
|
|
17
|
+
* The video plays a little brighter and more saturated, slightly sharper
|
|
18
|
+
* and turned a quarter clockwise. The nodes run in array order; `set` sends
|
|
19
|
+
* the whole chain as this plugin's desired value (last write wins), and the
|
|
20
|
+
* player applies it to every source until it changes, needing
|
|
21
|
+
* `processing.video`, `playback.read` and `events`.
|
|
17
22
|
* ```ts
|
|
18
23
|
* const chain: VideoNode[] = [
|
|
24
|
+
* // Live fields: moving them later changes the picture in place.
|
|
19
25
|
* { kind: "eq", brightness: 0.05, contrast: 1.1, saturation: 1.2, gamma: 1 },
|
|
26
|
+
* // Not live: changing `amount` later rebuilds the stage (a one-frame stall).
|
|
20
27
|
* { kind: "sharpen", amount: 0.5, size: "small" },
|
|
21
28
|
* { kind: "transform", rotate: 90, flipH: false, flipV: false },
|
|
22
29
|
* ];
|
|
@@ -160,12 +167,17 @@ export type VideoNode =
|
|
|
160
167
|
* One node of an {@link AudioFilter} chain, by `kind`. Values outside the
|
|
161
168
|
* ranges fail with `invalid`; "live" fields change in place while playing.
|
|
162
169
|
*
|
|
163
|
-
* @example
|
|
170
|
+
* @example Lift the voices and play in mono
|
|
171
|
+
* Speech around 1 kHz is 3 dB louder and both speakers play the same mixed
|
|
172
|
+
* channel. `set` sends this plugin's whole audio chain (the nodes in order,
|
|
173
|
+
* plus an optional `delayMs`); the player joins it with other plugins'
|
|
174
|
+
* chains and applies it to every source (`processing.audio`,
|
|
175
|
+
* `playback.read`, `events`).
|
|
164
176
|
* ```ts
|
|
165
177
|
* plugin.audio.filter("sound").set({
|
|
166
178
|
* nodes: [
|
|
167
|
-
* { kind: "equalizer", frequency: 1000, q: 1, gainDb: 3 },
|
|
168
|
-
* { kind: "channels", mode: "mono" },
|
|
179
|
+
* { kind: "equalizer", frequency: 1000, q: 1, gainDb: 3 }, // +3 dB at 1 kHz
|
|
180
|
+
* { kind: "channels", mode: "mono" }, // both channels get (L + R) / 2
|
|
169
181
|
* ],
|
|
170
182
|
* });
|
|
171
183
|
* ```
|
|
@@ -11,15 +11,40 @@ export type ResolvedResource = {
|
|
|
11
11
|
* stream(s) behind a page URL. Both streams must use identical `headers`;
|
|
12
12
|
* different maps fail explicitly (nothing is dropped silently).
|
|
13
13
|
*
|
|
14
|
-
* @example
|
|
14
|
+
* @example Play a page whose video and audio are separate streams
|
|
15
|
+
* Opening a `https://video.example/…` page plays it with its title. When
|
|
16
|
+
* the user opens a URL whose scheme and host a web resolver declares, the
|
|
17
|
+
* player calls its `resolve(url, signal)`; the plugin looks up the streams
|
|
18
|
+
* and returns them, and the player plays `primary` with the separate
|
|
19
|
+
* `audio` track, sending the `headers` with every request for both.
|
|
15
20
|
* ```ts
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* }
|
|
21
|
+
* plugin.resolvers.add({
|
|
22
|
+
* id: "video-example",
|
|
23
|
+
* schemes: ["https"],
|
|
24
|
+
* hosts: ["video.example"],
|
|
25
|
+
* async resolve(url, signal) {
|
|
26
|
+
* // The site's own API (made up here) names the streams of a page.
|
|
27
|
+
* const api = `https://video.example/api/streams?page=${encodeURIComponent(url)}`;
|
|
28
|
+
* const response = await fetch(api, { signal });
|
|
29
|
+
* // The player shows a failed resolve's message to the user.
|
|
30
|
+
* if (!response.ok)
|
|
31
|
+
* throw new Error(`video.example answered ${response.status} for this page`);
|
|
32
|
+
* const info = (await response.json()) as {
|
|
33
|
+
* title: string;
|
|
34
|
+
* video: string;
|
|
35
|
+
* audio: string;
|
|
36
|
+
* };
|
|
37
|
+
* const headers = { Referer: url }; // identical for both streams
|
|
38
|
+
* const media: ResolvedMedia = {
|
|
39
|
+
* kind: "media",
|
|
40
|
+
* originalUrl: url,
|
|
41
|
+
* title: info.title,
|
|
42
|
+
* primary: { url: info.video, headers },
|
|
43
|
+
* audio: { url: info.audio, headers },
|
|
44
|
+
* };
|
|
45
|
+
* return media;
|
|
46
|
+
* },
|
|
47
|
+
* });
|
|
23
48
|
* ```
|
|
24
49
|
*/
|
|
25
50
|
export type ResolvedMedia = {
|
package/dist/subtitles.d.ts
CHANGED
|
@@ -16,20 +16,30 @@ export type SubtitleStamp = {
|
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
18
|
* A subtitle style layer ({@link Subtitles.setStyle}). Sizes are at the
|
|
19
|
-
* 1080p reference height and scale with the video.
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* 1080p reference height and scale with the video. Each plugin has one
|
|
20
|
+
* layer; when several plugins set one, the effective style is the layer of
|
|
21
|
+
* the plugin whose manifest id sorts last (the order of the calls does not
|
|
22
|
+
* matter). It also applies to the secondary subtitle.
|
|
22
23
|
*
|
|
23
|
-
* @example
|
|
24
|
+
* @example Show large yellow subtitles a quarter second earlier
|
|
25
|
+
* Subtitles turn yellow at size 44 on a 60 % opaque box, 6 % above the
|
|
26
|
+
* bottom of the video, and appear 250 ms earlier. `refresh` reads the
|
|
27
|
+
* subtitle state from the player, whose `fonts` lists the font ids a style
|
|
28
|
+
* may name; `setStyle` then sends this plugin's style layer
|
|
29
|
+
* (`subtitles.control`, `subtitles.read`). It shows unless another plugin
|
|
30
|
+
* with a manifest id that sorts later has a layer too: the player picks the
|
|
31
|
+
* effective layer by manifest id, not by which call came last.
|
|
24
32
|
* ```ts
|
|
33
|
+
* const { fonts } = await plugin.subtitles.refresh();
|
|
34
|
+
* if (!fonts.length) return; // no font to name: leave the style alone
|
|
25
35
|
* const style: SubtitleStyle = {
|
|
26
|
-
* fontId:
|
|
27
|
-
* fontSize: 44,
|
|
36
|
+
* fontId: fonts[0]!.id, // a font the player offers
|
|
37
|
+
* fontSize: 44, // at the 1080p reference height; scales with the video
|
|
28
38
|
* color: "yellow",
|
|
29
39
|
* outline: 2,
|
|
30
|
-
* marginPercent: 6,
|
|
31
|
-
* offsetMs: -250,
|
|
32
|
-
* box: 60,
|
|
40
|
+
* marginPercent: 6, // bottom margin, % of the video height
|
|
41
|
+
* offsetMs: -250, // negative: shown earlier
|
|
42
|
+
* box: 60, // background box opacity
|
|
33
43
|
* };
|
|
34
44
|
* await plugin.subtitles.setStyle(style);
|
|
35
45
|
* ```
|
package/dist/wire.d.ts
CHANGED
|
@@ -71,16 +71,25 @@ export interface UiCallback {
|
|
|
71
71
|
* `timeout`, `aborted`, `limit` (a player limit such as the command count),
|
|
72
72
|
* `disabled` (the plugin is stopping).
|
|
73
73
|
*
|
|
74
|
-
* @example
|
|
74
|
+
* @example Simulate the player refusing a clipboard write in a test
|
|
75
|
+
* The test proves the plugin sees a refused write as an error with code
|
|
76
|
+
* `failed`. `host.handle` replaces the test host's handler for one request
|
|
77
|
+
* method; the `HostError` it throws travels back like the player's own
|
|
78
|
+
* refusal, so the call rejects with that `code` and is not retried.
|
|
79
|
+
* `host.reset` puts the default handler back for later steps.
|
|
75
80
|
* ```ts
|
|
81
|
+
* import assert from "node:assert/strict";
|
|
76
82
|
* import { HostError, startPlugin } from "@vestra-player/sdk/testing";
|
|
83
|
+
* import activate from "../main.ts";
|
|
77
84
|
*
|
|
78
|
-
* const { host, plugin, stop } = await startPlugin(activate, {
|
|
85
|
+
* const { host, plugin, stop } = await startPlugin(activate, {
|
|
86
|
+
* directory: new URL("..", import.meta.url),
|
|
87
|
+
* });
|
|
79
88
|
* host.handle("clipboard.writeText", () => {
|
|
80
89
|
* throw new HostError("failed", "the system refused the write");
|
|
81
90
|
* });
|
|
82
91
|
* await assert.rejects(plugin.clipboard.writeText("x"), { code: "failed" });
|
|
83
|
-
* host.reset("clipboard.writeText");
|
|
92
|
+
* host.reset("clipboard.writeText"); // writes succeed again
|
|
84
93
|
* await stop();
|
|
85
94
|
* ```
|
|
86
95
|
*/
|
package/dist/wire.js
CHANGED
|
@@ -31,16 +31,25 @@ export const API_VERSION = "vestra/1";
|
|
|
31
31
|
* `timeout`, `aborted`, `limit` (a player limit such as the command count),
|
|
32
32
|
* `disabled` (the plugin is stopping).
|
|
33
33
|
*
|
|
34
|
-
* @example
|
|
34
|
+
* @example Simulate the player refusing a clipboard write in a test
|
|
35
|
+
* The test proves the plugin sees a refused write as an error with code
|
|
36
|
+
* `failed`. `host.handle` replaces the test host's handler for one request
|
|
37
|
+
* method; the `HostError` it throws travels back like the player's own
|
|
38
|
+
* refusal, so the call rejects with that `code` and is not retried.
|
|
39
|
+
* `host.reset` puts the default handler back for later steps.
|
|
35
40
|
* ```ts
|
|
41
|
+
* import assert from "node:assert/strict";
|
|
36
42
|
* import { HostError, startPlugin } from "@vestra-player/sdk/testing";
|
|
43
|
+
* import activate from "../main.ts";
|
|
37
44
|
*
|
|
38
|
-
* const { host, plugin, stop } = await startPlugin(activate, {
|
|
45
|
+
* const { host, plugin, stop } = await startPlugin(activate, {
|
|
46
|
+
* directory: new URL("..", import.meta.url),
|
|
47
|
+
* });
|
|
39
48
|
* host.handle("clipboard.writeText", () => {
|
|
40
49
|
* throw new HostError("failed", "the system refused the write");
|
|
41
50
|
* });
|
|
42
51
|
* await assert.rejects(plugin.clipboard.writeText("x"), { code: "failed" });
|
|
43
|
-
* host.reset("clipboard.writeText");
|
|
52
|
+
* host.reset("clipboard.writeText"); // writes succeed again
|
|
44
53
|
* await stop();
|
|
45
54
|
* ```
|
|
46
55
|
*/
|