@vestra-player/sdk 1.5.23 → 1.5.24

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.5.24
4
+
5
+ **Who must change: nobody.** Plugins built on earlier SDKs keep working unchanged: `when: "interlaced"` and `when: "all"` do what they did. Additive; `apiVersion` stays `vestra/1`. The addition needs player 0.5.4.
6
+
7
+ - **`VideoNode` `{ kind: "deinterlace", when: "auto", output: "field" }`** (player 0.5.4; requested by the video plugin, DEINT-AUTO-1: user ruling of 2026-10-27, Auto deinterlacing must cost nothing on progressive files). The player's own deinterlacer instead of BWDIF in the chain: decided per frame from the decoder's interlaced flag, with the decoder's hardware deinterlacer where frames stay on the GPU, one output frame per field; progressive frames pass untouched. It needs no frames in memory, so a chain with only this node keeps zero-copy hardware decoding, where any other node switches to a copy back to memory. It runs before every plugin's chain wherever it is written, and it is shared: on while any plugin's chain has one, off when the last one is removed or its plugin stops. `output: "frame"` with it, or another `deinterlace` node in the same chain, fails with `invalid`. Processed snapshots and exports deinterlace the same frames with BWDIF at field rate. Players before 0.5.4 refuse `auto` with `invalid`, so set `"minPlayerVersion": "0.5.4"`.
8
+ - **`TestHost`** validates the new value as the player does (`nodeProblem`).
9
+ - Docs: *Processing* (ordering and side effects). `llms.txt` and `llms-full.txt` regenerated.
10
+
3
11
  ## 1.5.23
4
12
 
5
13
  **Who must change: nobody.** Republishes 1.5.22 with a clean API documentation build; 1.5.22 was tagged but never published to npm: TypeDoc treats warnings as errors and failed on two `{@link}` labels in *Playback* that contained a brace (`Scrub.seek(seconds, { keyframes })`). Documentation only; the code is the same as 1.5.22 below.
@@ -102,13 +102,35 @@ export type VideoNode =
102
102
  /** Mirror vertically. */
103
103
  flipV: boolean;
104
104
  }
105
- /** BWDIF; `field` doubles the frame rate. */
105
+ /**
106
+ * Deinterlacing. `interlaced` and `all` are FFmpeg BWDIF in the chain;
107
+ * `field` doubles the frame rate.
108
+ *
109
+ * `when: "auto"` (player 0.5.4) is the player's own deinterlacer instead:
110
+ * decided per frame from the decoder's interlaced flag, with the decoder's
111
+ * hardware deinterlacer where frames stay on the GPU; progressive frames
112
+ * pass untouched. It needs no frames in memory, so a chain with only this
113
+ * node keeps zero-copy hardware decoding (any other node still needs a
114
+ * copy back to memory). It always outputs one frame per field, so it
115
+ * takes `output: "field"` only (`"frame"` fails with `invalid`). It runs
116
+ * before every plugin's chain wherever it is written, and is shared: on
117
+ * while any plugin's chain has one. A chain with it has no other
118
+ * `deinterlace` node. Snapshots and exports deinterlace the same frames
119
+ * with BWDIF at field rate. Players before 0.5.4 refuse `auto` with
120
+ * `invalid`.
121
+ */
106
122
  | {
107
123
  /** The node type. */
108
124
  kind: "deinterlace";
109
- /** `interlaced`: only frames flagged interlaced; `all`: every frame. */
110
- when: "interlaced" | "all";
111
- /** One frame per frame, or one per field (double rate). */
125
+ /**
126
+ * `interlaced`: only frames flagged interlaced; `all`: every frame;
127
+ * `auto`: the player's deinterlacer, on frames flagged interlaced.
128
+ */
129
+ when: "interlaced" | "all" | "auto";
130
+ /**
131
+ * One frame per frame, or one per field (double rate); `when: "auto"`
132
+ * takes `field` only.
133
+ */
112
134
  output: "frame" | "field";
113
135
  }
114
136
  /** Crop to / pad to a display aspect; x, y place it (−1 left/top … 1 right/bottom). */
@@ -19,7 +19,10 @@ const RULES = {
19
19
  deblock: { strength: ["weak", "strong"] },
20
20
  deband: { threshold: [0.003, 0.5], range: [1, 64] },
21
21
  transform: { rotate: [0, 90, 180, 270], flipH: BOOL, flipV: BOOL },
22
- deinterlace: { when: ["interlaced", "all"], output: ["frame", "field"] },
22
+ deinterlace: {
23
+ when: ["interlaced", "all", "auto"],
24
+ output: ["frame", "field"],
25
+ },
23
26
  reframe: { mode: ["crop", "pad"], aspect: [0.25, 4], x: [-1, 1], y: [-1, 1] },
24
27
  frameBlend: { fps: [24, 120] },
25
28
  volume: { gainDb: [-60, 0] },
@@ -73,7 +76,15 @@ export function nodeProblem(stage, nodes) {
73
76
  if (node.kind === "frameBlend" &&
74
77
  !Number.isInteger(node.fps))
75
78
  return "frameBlend.fps must be an integer";
79
+ if (node.kind === "deinterlace" &&
80
+ node.when === "auto" &&
81
+ node.output !== "field")
82
+ return "deinterlace when auto outputs one frame per field: output must be field";
76
83
  }
84
+ const deinterlace = nodes.filter((node) => node.kind === "deinterlace");
85
+ if (deinterlace.length > 1 &&
86
+ deinterlace.some((node) => node.when === "auto"))
87
+ return "a chain with a deinterlace node when auto has no other deinterlace node";
77
88
  if (nodes.filter((node) => node.kind === "frameBlend").length > 1)
78
89
  return "only one frameBlend node across all plugins";
79
90
  return null;
package/llms-full.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @vestra-player/sdk 1.5.23: full reference
1
+ # @vestra-player/sdk 1.5.24: 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
 
@@ -667,7 +667,8 @@ How smoothly the change shows depends on the field. Some fields, marked **live**
667
667
 
668
668
  - Video nodes act on the picture after the player has applied the container's rotation, so a portrait video is upright before a plugin's `transform` rotates it.
669
669
  - `frameBlend` (frame-rate conversion by blending) always runs **last**, after every plugin's chain, wherever it is written, so temporal filters such as denoise and deinterlace still see the original frames. It changes the frame rate of exports too, and costs a lot of CPU: decide from `stats()` whether to offer it.
670
- - Filters need frames in memory. While any plugin has a video chain, the player decodes in hardware with a copy back to memory instead of zero-copy decoding; when the last chain is removed it switches back. The decoder restarts at each switch, so the picture may pause briefly. stats().video.hwdec shows the decoder in use.
670
+ - Filters need frames in memory. While any plugin has a video chain, the player decodes in hardware with a copy back to memory instead of zero-copy decoding; when the last chain is removed it switches back. The decoder restarts at each switch, so the picture may pause briefly. stats().video.hwdec shows the decoder in use. The one exception is the next item.
671
+ - `{ kind: "deinterlace", when: "auto", output: "field" }` (player 0.5.4) is not a filter in the chain but the player's own deinterlacer. The player decides per frame: frames flagged interlaced are deinterlaced, at one frame per field, by the decoder's hardware deinterlacer where frames stay on the GPU (BWDIF otherwise); progressive frames pass untouched. It needs no frames in memory, so a chain with only this node keeps zero-copy decoding: this is the node for an "Auto" setting that must cost nothing on progressive files. It runs **first**, before every plugin's chain, wherever it is written, and it is shared: it stays on while any plugin's chain has one. `output: "frame"` fails with `invalid`, and so does another `deinterlace` node in the same chain; use `when: "interlaced"` (BWDIF in the chain, with a copy back to memory) for frame-rate output. Snapshots and exports deinterlace the same frames with BWDIF at field rate.
671
672
 
672
673
  #### The display layer
673
674
 
@@ -6561,13 +6562,29 @@ type VideoNode =
6561
6562
  /** Mirror vertically. */
6562
6563
  flipV: boolean;
6563
6564
  }
6564
- /** BWDIF; `field` doubles the frame rate. */
6565
+ /**
6566
+ * Deinterlacing. `interlaced` and `all` are FFmpeg BWDIF in the chain;
6567
+ * `field` doubles the frame rate.
6568
+ *
6569
+ * `when: "auto"` (player 0.5.4) is the player's own deinterlacer instead:
6570
+ * decided per frame from the decoder's interlaced flag, with the decoder's
6571
+ * hardware deinterlacer where frames stay on the GPU; progressive frames
6572
+ * pass untouched. It needs no frames in memory, so a chain with only this
6573
+ * node keeps zero-copy hardware decoding (any other node still needs a
6574
+ * copy back to memory). It always outputs one frame per field, so it
6575
+ * takes `output: "field"` only (`"frame"` fails with `invalid`). It runs
6576
+ * before every plugin's chain wherever it is written, and is shared: on
6577
+ * while any plugin's chain has one. A chain with it has no other
6578
+ * `deinterlace` node. Snapshots and exports deinterlace the same frames
6579
+ * with BWDIF at field rate. Players before 0.5.4 refuse `auto` with
6580
+ * `invalid`.
6581
+ */
6565
6582
  | {
6566
6583
  /** The node type. */
6567
6584
  kind: "deinterlace";
6568
- /** `interlaced`: only frames flagged interlaced; `all`: every frame. */
6569
- when: "interlaced" | "all";
6570
- /** One frame per frame, or one per field (double rate). */
6585
+ /** `interlaced`: only frames flagged interlaced; `all`: every frame; `auto`: the player's deinterlacer, on frames flagged interlaced. */
6586
+ when: "interlaced" | "all" | "auto";
6587
+ /** One frame per frame, or one per field (double rate); `when: "auto"` takes `field` only. */
6571
6588
  output: "frame" | "field";
6572
6589
  }
6573
6590
  /** Crop to / pad to a display aspect; x, y place it (−1 left/top … 1 right/bottom). */
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vestra Player SDK
2
2
 
3
- > @vestra-player/sdk 1.5.23: 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.
3
+ > @vestra-player/sdk 1.5.24: 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: L2127–2192.
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: L2128–2193.
37
37
 
38
38
  **main.ts**
39
39
 
@@ -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: L2193–2235.
79
+ - No capability: `tools`, `storage`, `system`, `app`, `ui` (images and Markdown), `i18n`, reading window.state, appearance.theme and packages.list. Details: L2194–2236.
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: L1613–1778; TestHost: L6883.
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: L1614–1779; TestHost: L6900.
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 L3134.
109
+ - Others: PluginError, llms-full.txt L3135.
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
- - Concepts: How a plugin runs L215–364; Commands, menus and key bindings L365–462; Playback state, controls and sources L463–608; Video and audio processing L609–686; Windows, panels, settings pages, dialogs and the OSD L687–975
116
- - How-to: Keep a slider responsive while dragging L976–1150; Remember a window's state between runs L1151–1280; Translate a plugin L1281–1464; Migrate a plugin to English source text L1465–1612; Test a plugin L1613–1778; Publish a plugin L1779–1867; Migrating off package rollback (1.5.16, removed in 1.5.17) L1868–1943
117
- - Guidelines: Interface guidelines L1944–2028; Menu guidelines L2029–2126
118
- - Reference: Manifest L2127–2192; Capabilities L2193–2235; Limits L2236–2330; Commands, menus and bindings L2331–2799; Platforms L2800–2828; Theme L2829–3035; Command line L3036–3091
119
- - [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L3092–6718
120
- - [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html): L6719–8092
115
+ - Concepts: How a plugin runs L215–364; Commands, menus and key bindings L365–462; Playback state, controls and sources L463–608; Video and audio processing L609–687; Windows, panels, settings pages, dialogs and the OSD L688–976
116
+ - How-to: Keep a slider responsive while dragging L977–1151; Remember a window's state between runs L1152–1281; Translate a plugin L1282–1465; Migrate a plugin to English source text L1466–1613; Test a plugin L1614–1779; Publish a plugin L1780–1868; Migrating off package rollback (1.5.16, removed in 1.5.17) L1869–1944
117
+ - Guidelines: Interface guidelines L1945–2029; Menu guidelines L2030–2127
118
+ - Reference: Manifest L2128–2193; Capabilities L2194–2236; Limits L2237–2331; Commands, menus and bindings L2332–2800; Platforms L2801–2829; Theme L2830–3036; Command line L3037–3092
119
+ - [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L3093–6735
120
+ - [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html): L6736–8109
121
121
 
122
122
  ## API
123
123
 
124
124
  Where each module of `plugin` (`plugin.<module>`) is declared:
125
125
 
126
- - Plugin L3326, Scope L3195, PluginError L3134, Disposable L3107
127
- - Commands L3538; Windows L3768; Panels L4411; Dialogs L4697; Player L5164; video: VideoFilter L5471, VideoView L5534, RenderOptions L5576; audio: AudioFilter L5495; Subtitles L5674; media: MediaCapture L5834; Resolvers L5939; Packages L6181; Osd L5407; Host L5338; Clipboard L6313; Power L6333; Tools L6366; Storage L6409; System L6432; Secrets L6450; window: MainWindow L4534; App L4615; Bindings L6283; Ui L3643; Timeline L4491; Drops L4067; Appearance L4096; I18n L4238; Settings L4344.
126
+ - Plugin L3327, Scope L3196, PluginError L3135, Disposable L3108
127
+ - Commands L3539; Windows L3769; Panels L4412; Dialogs L4698; Player L5165; video: VideoFilter L5472, VideoView L5535, RenderOptions L5577; audio: AudioFilter L5496; Subtitles L5675; media: MediaCapture L5835; Resolvers L5940; Packages L6182; Osd L5408; Host L5339; Clipboard L6314; Power L6334; Tools L6367; Storage L6410; System L6433; Secrets L6451; window: MainWindow L4535; App L4616; Bindings L6284; Ui L3644; Timeline L4492; Drops L4068; Appearance L4097; I18n L4239; Settings L4345.
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. L8516–8605
134
- - blur: A tool window with one slider that blurs the playing video. L8606–8867
135
- - signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L8868–9200
136
- - bookmark: A tool window bound to the playback state: remember the position and jump back to it. L9201–9385
137
- - panel-menu: A sidebar panel with its own right-click menu. L9386–9591
138
- - menus: Radio menu items, separator groups and a menu item whose title changes. L9592–9751
139
- - theme-widgets: A tool window drawn with the player's theme and widgets. L9752–9915
140
- - markdown: A window that shows a Markdown document. L9916–10110
141
- - i18n: A plugin in the player's language: English source text, a Chinese translation. L10111–10278
133
+ - hello: The README Quick start: one menu command and an on-screen message. L8533–8622
134
+ - blur: A tool window with one slider that blurs the playing video. L8623–8884
135
+ - signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L8885–9217
136
+ - bookmark: A tool window bound to the playback state: remember the position and jump back to it. L9218–9402
137
+ - panel-menu: A sidebar panel with its own right-click menu. L9403–9608
138
+ - menus: Radio menu items, separator groups and a menu item whose title changes. L9609–9768
139
+ - theme-widgets: A tool window drawn with the player's theme and widgets. L9769–9932
140
+ - markdown: A window that shows a Markdown document. L9933–10127
141
+ - i18n: A plugin in the player's language: English source text, a Chinese translation. L10128–10295
142
142
 
143
143
  ## Optional
144
144
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vestra-player/sdk",
3
- "version": "1.5.23",
3
+ "version": "1.5.24",
4
4
  "description": "Plugin API types, test host and plugin CLI for Vestra Player (apiVersion vestra/1)",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {