@vestra-player/sdk 1.5.38 → 1.5.40
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 +18 -0
- package/bin/vestra-plugin.mjs +5 -1
- package/dist/api-requests.js +1 -0
- package/dist/commands.d.ts +12 -2
- package/dist/commands.js +4 -2
- package/dist/host-capabilities.js +1 -0
- package/dist/player-runtime.d.ts +1 -0
- package/dist/player-runtime.js +1 -0
- package/dist/plugin/commands.js +41 -8
- package/dist/plugin/runtime.d.ts +31 -1
- package/dist/plugin/runtime.js +154 -12
- package/dist/plugin/scope.d.ts +2 -0
- package/dist/plugin/scope.js +4 -0
- package/dist/plugin/shaders.d.ts +9 -0
- package/dist/plugin/shaders.js +160 -0
- package/dist/plugin/testing.d.ts +120 -4
- package/dist/plugin/testing.js +320 -7
- package/dist/plugin/types.d.ts +443 -30
- package/dist/plugin/ui.js +6 -4
- package/dist/plugin/when.d.ts +46 -0
- package/dist/plugin/when.js +106 -0
- package/dist/processing.d.ts +118 -4
- package/dist/processing.js +139 -0
- package/dist/wire.d.ts +11 -4
- package/dist/wire.js +18 -3
- package/llms-full.txt +532 -32
- package/llms.txt +20 -20
- package/package.json +1 -1
package/llms-full.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @vestra-player/sdk 1.5.
|
|
1
|
+
# @vestra-player/sdk 1.5.40: 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
|
|
|
@@ -2709,6 +2709,8 @@ The table lists the API whose documentation names a player version, newest last:
|
|
|
2709
2709
|
| 0.5.12 | SystemInfo.rendering |
|
|
2710
2710
|
| 0.5.13 | SystemInfo.renderingReason, RenderingMode, RenderingPreference, RenderingReasonCode, RenderingReason, RenderingState, Rendering |
|
|
2711
2711
|
| 0.5.16 | UpscalingReasonCode, UpscalingReason, UpscalingState, VideoUpscaling |
|
|
2712
|
+
| 0.5.20 | VideoShaders |
|
|
2713
|
+
| 0.5.21 | Scope.when, When, Bindings.onChange |
|
|
2712
2714
|
|
|
2713
2715
|
The Slint library carries no version tags: `Theme.danger` needs player 0.3.0; `@vestra/icons.slint` and the palette `vestra-light` need 0.4.0 (Theme).
|
|
2714
2716
|
|
|
@@ -2730,7 +2732,7 @@ A plugin lists what it may do in the manifest's `capabilities` (Manifest). A cal
|
|
|
2730
2732
|
| `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 | |
|
|
2731
2733
|
| `ui.dialog` | Dialogs.confirm, Dialogs.openFile, Dialogs.openFiles, Dialogs.openDirectory, Dialogs.saveFile | — | Show file pickers and confirmations | |
|
|
2732
2734
|
| `plugins.manage` | Packages.validate, Packages.validateArchive, Packages.setEnabled, Packages.reload, Packages.uninstall | — | Install, uninstall and disable other plugins | yes |
|
|
2733
|
-
| `processing.video` | Plugin.video.filter, Plugin.video.view, Plugin.video.render | `playback.read` | Process the picture (filters, shaders) | |
|
|
2735
|
+
| `processing.video` | Plugin.video.filter, Plugin.video.view, Plugin.video.render, Plugin.video.shaders | `playback.read` | Process the picture (filters, shaders) | |
|
|
2734
2736
|
| `processing.audio` | Plugin.audio.filter | `playback.read`, `events` | Process the sound | |
|
|
2735
2737
|
| `subtitles.read` | Subtitles.state, Subtitles.onChange, Subtitles.refresh | — | Read subtitles | |
|
|
2736
2738
|
| `subtitles.control` | Subtitles.select, Subtitles.load, Subtitles.remove, Subtitles.setStyle | — | Load and change subtitles | |
|
|
@@ -2897,7 +2899,23 @@ RenderSettings; more shaders fail with `invalid`.
|
|
|
2897
2899
|
|
|
2898
2900
|
| Limit | Value | Meaning |
|
|
2899
2901
|
| --- | --- | --- |
|
|
2900
|
-
| Entries of `RenderSettings.shaders` | 16 | mpv user shaders (`.glsl`): absolute paths of existing files, at most 16, in order. |
|
|
2902
|
+
| Entries of `RenderSettings.shaders` | 16 | mpv user shaders (`.glsl`): absolute paths of existing files, at most 16, in order. They run before the shaders of every plugin's `VideoShaders` handles. |
|
|
2903
|
+
|
|
2904
|
+
#### Video primitives
|
|
2905
|
+
|
|
2906
|
+
The `convolution` and `lut3d` VideoNodes and the shader channel VideoShaders (player 0.5.20). Values beyond them fail with `invalid`; more shaders than `shaders` across all plugins fail with `limit`.
|
|
2907
|
+
|
|
2908
|
+
| Limit | Value | Meaning |
|
|
2909
|
+
| --- | --- | --- |
|
|
2910
|
+
| `VIDEO_PROCESSING_LIMITS.shaders` | 16 | Shaders across every plugin's `plugin.video.shaders()` handles. |
|
|
2911
|
+
| `VIDEO_PROCESSING_LIMITS.shaderBytes` | 16777216 (16 MiB) | Bytes of one shader file. |
|
|
2912
|
+
| `VIDEO_PROCESSING_LIMITS.shaderIdBytes` | 128 | Bytes of a shader handle's id. |
|
|
2913
|
+
| `VIDEO_PROCESSING_LIMITS.lutBytes` | 16777216 (16 MiB) | Bytes of one `lut3d` `.cube` file. |
|
|
2914
|
+
| `VIDEO_PROCESSING_LIMITS.lutSize` | 65 | Largest `LUT_3D_SIZE` of a `lut3d` file, points per axis. |
|
|
2915
|
+
| `VIDEO_PROCESSING_LIMITS.lutPathBytes` | 4096 (4 KiB) | Bytes of a `lut3d` file path. |
|
|
2916
|
+
| `VIDEO_PROCESSING_LIMITS.matrixValue` | 1024 | Largest magnitude of a `convolution` matrix entry. |
|
|
2917
|
+
| `VIDEO_PROCESSING_LIMITS.rdiv` | 256 | Largest `convolution` `rdiv`. |
|
|
2918
|
+
| `VIDEO_PROCESSING_LIMITS.bias` | 65535 | Largest `convolution` `bias`. |
|
|
2901
2919
|
|
|
2902
2920
|
#### Limits documented at their API
|
|
2903
2921
|
|
|
@@ -3880,6 +3898,10 @@ rest of the plugin keeps running.
|
|
|
3880
3898
|
interface Scope extends Disposable {
|
|
3881
3899
|
/** Menu commands and their default bindings (`commands`). */
|
|
3882
3900
|
readonly commands: Commands;
|
|
3901
|
+
/**
|
|
3902
|
+
* Conditions on the player's own state for a command's `enabled` and `checked` (SDK 1.5.40, player 0.5.21): the player evaluates them, so the menu follows playback even while the plugin is stopped. See When.
|
|
3903
|
+
*/
|
|
3904
|
+
readonly when: When;
|
|
3883
3905
|
/** Tool windows from `.slint` files (`ui.window`). */
|
|
3884
3906
|
readonly windows: Windows;
|
|
3885
3907
|
/** Docked panels: bottom bar, sidebar and backdrop (`ui.mount`). */
|
|
@@ -3889,7 +3911,7 @@ interface Scope extends Disposable {
|
|
|
3889
3911
|
/** Playback state and controls (`playback.read`, `events`, `playback.control`). */
|
|
3890
3912
|
readonly player: Player;
|
|
3891
3913
|
/**
|
|
3892
|
-
* Video processing: this plugin's filter chain, the display layer, the GPU render options and the GPU's video upscaler. The first
|
|
3914
|
+
* Video processing: this plugin's filter chain, the display layer, the GPU render options, this plugin's shaders and the GPU's video upscaler. The first four need `processing.video` and `playback.read`; the filter chain also needs `events` (without it the SDK never sees the media become ready and the chain stays `waiting`). The upscaler needs `video.upscaling` only.
|
|
3893
3915
|
*/
|
|
3894
3916
|
readonly video: {
|
|
3895
3917
|
/**
|
|
@@ -3910,6 +3932,15 @@ interface Scope extends Disposable {
|
|
|
3910
3932
|
* @throws Error without a code (throws) A second call in the same plugin while the first handle is not disposed.
|
|
3911
3933
|
*/
|
|
3912
3934
|
render(): RenderOptions;
|
|
3935
|
+
/**
|
|
3936
|
+
* This plugin's own GPU shaders named `id` (default `"shaders"`; needs `processing.video`; SDK 1.5.39, player 0.5.20). Unlike render(), every plugin may hold shader handles at the same time; the player joins their lists. Nothing is sent until the first `set()`.
|
|
3937
|
+
* @param id - Local name of the handle, 1–128 bytes, unique within the plugin.
|
|
3938
|
+
* @returns A VideoShaders owned by this scope.
|
|
3939
|
+
* @throws capability (throws) The manifest does not declare `processing.video`.
|
|
3940
|
+
* @throws invalid (throws) `id` is empty, longer than 128 bytes or has control characters.
|
|
3941
|
+
* @throws Error without a code (throws) A second handle with the same `id` while the first is not disposed.
|
|
3942
|
+
*/
|
|
3943
|
+
shaders(id?: string): VideoShaders;
|
|
3913
3944
|
/**
|
|
3914
3945
|
* The GPU's video upscaler (NVIDIA RTX Video Super Resolution on Windows; needs `video.upscaling`, player 0.5.16): whether it works here and why not, and turning it on or off for this plugin.
|
|
3915
3946
|
*/
|
|
@@ -4212,6 +4243,191 @@ plugin.commands.add({
|
|
|
4212
4243
|
});
|
|
4213
4244
|
```
|
|
4214
4245
|
|
|
4246
|
+
### Condition
|
|
4247
|
+
|
|
4248
|
+
A condition on the player's own state, from Scope.when, for a
|
|
4249
|
+
command's `enabled` or `checked`. The player evaluates it, again on
|
|
4250
|
+
every change of that state, whether or not the plugin runs.
|
|
4251
|
+
|
|
4252
|
+
Combine conditions with `and`, `or` and `not`; each returns a new
|
|
4253
|
+
condition and leaves the original as it was. A condition is data: it
|
|
4254
|
+
reads nothing in the plugin and keeps no listener, so a plugin whose
|
|
4255
|
+
menu state is made of conditions may be stopped while idle (lazy
|
|
4256
|
+
activation) and its menu stays current. A function (see Bindable)
|
|
4257
|
+
keeps working; one that reads `plugin.player.state` or another service's
|
|
4258
|
+
state keeps the plugin running so the SDK can evaluate it.
|
|
4259
|
+
|
|
4260
|
+
- @capability commands
|
|
4261
|
+
|
|
4262
|
+
```ts
|
|
4263
|
+
interface Condition {
|
|
4264
|
+
/**
|
|
4265
|
+
* True when this condition and every one of `others` hold.
|
|
4266
|
+
* @capability None (the command it is given to needs `commands`).
|
|
4267
|
+
* @example Combine two conditions with and
|
|
4268
|
+
* The item is enabled only while a file plays in fullscreen. `and`
|
|
4269
|
+
* returns a new condition; `when.playing` itself is unchanged.
|
|
4270
|
+
* ```ts
|
|
4271
|
+
* const { when } = plugin;
|
|
4272
|
+
* const playingFullscreen = when.playing.and(when.fullscreen);
|
|
4273
|
+
* ```
|
|
4274
|
+
*/
|
|
4275
|
+
and(...others: Condition[]): Condition;
|
|
4276
|
+
/**
|
|
4277
|
+
* True when this condition or one of `others` holds.
|
|
4278
|
+
* @capability None (the command it is given to needs `commands`).
|
|
4279
|
+
* @example Combine two conditions with or
|
|
4280
|
+
* The item is enabled while a file plays or is paused. `or` returns a
|
|
4281
|
+
* new condition that holds when either of the two holds.
|
|
4282
|
+
* ```ts
|
|
4283
|
+
* const { when } = plugin;
|
|
4284
|
+
* const active = when.playing.or(when.paused);
|
|
4285
|
+
* ```
|
|
4286
|
+
*/
|
|
4287
|
+
or(...others: Condition[]): Condition;
|
|
4288
|
+
/**
|
|
4289
|
+
* True when this condition does not hold.
|
|
4290
|
+
* @capability None (the command it is given to needs `commands`).
|
|
4291
|
+
* @example Negate a condition with not
|
|
4292
|
+
* An "Enter Fullscreen" item is enabled only outside fullscreen. `not`
|
|
4293
|
+
* returns the opposite condition; the player evaluates it on every
|
|
4294
|
+
* window change.
|
|
4295
|
+
* ```ts
|
|
4296
|
+
* const { when } = plugin;
|
|
4297
|
+
* const windowed = when.fullscreen.not();
|
|
4298
|
+
* ```
|
|
4299
|
+
*/
|
|
4300
|
+
not(): Condition;
|
|
4301
|
+
}
|
|
4302
|
+
```
|
|
4303
|
+
|
|
4304
|
+
Example: Enable an item while a frame can be saved
|
|
4305
|
+
|
|
4306
|
+
"Save Snapshot" is greyed out with its reason unless a media file is
|
|
4307
|
+
open at a frame the player can capture. The condition is sent with the
|
|
4308
|
+
command and evaluated by the player, so the item follows playback even
|
|
4309
|
+
while the plugin is stopped; with an `enabled` condition the player
|
|
4310
|
+
shows the `reason` only while the item is disabled.
|
|
4311
|
+
```ts
|
|
4312
|
+
const { when } = plugin;
|
|
4313
|
+
plugin.commands.add({
|
|
4314
|
+
id: "save",
|
|
4315
|
+
path: ["@video", "Save Snapshot"],
|
|
4316
|
+
enabled: when.can("snapshot").and(when.phase("ready", "ended"), when.stream.not()),
|
|
4317
|
+
reason: "No frame to capture", // shown only while disabled
|
|
4318
|
+
run: saveSnapshot,
|
|
4319
|
+
});
|
|
4320
|
+
```
|
|
4321
|
+
|
|
4322
|
+
### When
|
|
4323
|
+
|
|
4324
|
+
`plugin.when`: the vocabulary of conditions (SDK 1.5.40, player 0.5.21).
|
|
4325
|
+
It names only state the player owns: the source's phase, kind, pause
|
|
4326
|
+
and controls (the same names as PlayerState), fullscreen, the
|
|
4327
|
+
palette and the saved interface language.
|
|
4328
|
+
|
|
4329
|
+
Give a condition as a command's `enabled` or `checked`
|
|
4330
|
+
(CommandOptions, Command.set). The player evaluates it
|
|
4331
|
+
and shows the result in menus, on keys and on bar buttons, again
|
|
4332
|
+
whenever that state changes, also while the plugin is stopped because
|
|
4333
|
+
nothing used it. With an `enabled` condition the player shows the
|
|
4334
|
+
command's `reason` only while the condition is false (or the command
|
|
4335
|
+
is busy), so a fixed reason is enough. Players before 0.5.21 have no
|
|
4336
|
+
`plugin.when` (declare `"minPlayerVersion": "0.5.21"`).
|
|
4337
|
+
|
|
4338
|
+
State the vocabulary does not name (a plugin's own settings, an open
|
|
4339
|
+
window, a track list) stays a function or a value set with
|
|
4340
|
+
Command.set.
|
|
4341
|
+
|
|
4342
|
+
- @capability commands
|
|
4343
|
+
|
|
4344
|
+
```ts
|
|
4345
|
+
interface When {
|
|
4346
|
+
/** A source is open: any phase but `empty` (loading, ready, ended, error). */
|
|
4347
|
+
readonly open: Condition;
|
|
4348
|
+
/** The source is loaded and can play (`phase` is `ready`). */
|
|
4349
|
+
readonly ready: Condition;
|
|
4350
|
+
/** The source is ready and not paused. */
|
|
4351
|
+
readonly playing: Condition;
|
|
4352
|
+
/** The source is ready and paused. */
|
|
4353
|
+
readonly paused: Condition;
|
|
4354
|
+
/** The source is a generated stream (`PlayerState.kind` is `stream`). */
|
|
4355
|
+
readonly stream: Condition;
|
|
4356
|
+
/** The main window is fullscreen. */
|
|
4357
|
+
readonly fullscreen: Condition;
|
|
4358
|
+
/**
|
|
4359
|
+
* The source's `phase` is one of `phases`.
|
|
4360
|
+
* @throws TypeError (throws) No phase, or one that is not a phase.
|
|
4361
|
+
* @capability None (the command it is given to needs `commands`).
|
|
4362
|
+
* @example Enable an item while a file is loaded or finished
|
|
4363
|
+
* The item is enabled while the source is ready or has ended, as a
|
|
4364
|
+
* snapshot needs a frame. The phases are those of `PlayerState.phase`.
|
|
4365
|
+
* ```ts
|
|
4366
|
+
* const { when } = plugin;
|
|
4367
|
+
* const hasFrame = when.phase("ready", "ended");
|
|
4368
|
+
* ```
|
|
4369
|
+
*/
|
|
4370
|
+
phase(...phases: PlayerState["phase"][]): Condition;
|
|
4371
|
+
/**
|
|
4372
|
+
* The source allows `control`: `PlayerState.can[control]` is true.
|
|
4373
|
+
* @throws TypeError (throws) `control` is not one of `PlayerState.can`.
|
|
4374
|
+
* @capability None (the command it is given to needs `commands`).
|
|
4375
|
+
* @example Enable a seek item only where seeking works
|
|
4376
|
+
* "Jump to Middle" is greyed out on live streams that cannot seek.
|
|
4377
|
+
* The player reads its own `can.seek` for the current source.
|
|
4378
|
+
* ```ts
|
|
4379
|
+
* const { when } = plugin;
|
|
4380
|
+
* const seekable = when.can("seek");
|
|
4381
|
+
* ```
|
|
4382
|
+
*/
|
|
4383
|
+
can(control: keyof PlayerState["can"]): Condition;
|
|
4384
|
+
/**
|
|
4385
|
+
* The palette in use is `theme` (`plugin.appearance.theme`).
|
|
4386
|
+
* @throws TypeError (throws) `theme` is not a palette of the player.
|
|
4387
|
+
* @capability None (the command it is given to needs `commands`).
|
|
4388
|
+
* @example Check the light palette's item
|
|
4389
|
+
* The item carries a check mark while the light palette is in use,
|
|
4390
|
+
* whoever switched to it.
|
|
4391
|
+
* ```ts
|
|
4392
|
+
* const { when } = plugin;
|
|
4393
|
+
* const light = when.theme("vestra-light");
|
|
4394
|
+
* ```
|
|
4395
|
+
*/
|
|
4396
|
+
theme(theme: ThemeId): Condition;
|
|
4397
|
+
/**
|
|
4398
|
+
* The saved interface language is `language` (`plugin.appearance.language`: `"system"` or a locale).
|
|
4399
|
+
* @throws TypeError (throws) `language` is not `"system"` or a locale.
|
|
4400
|
+
* @capability None (the command it is given to needs `commands`).
|
|
4401
|
+
* @example Check the Follow System item
|
|
4402
|
+
* The item carries a check mark while the player follows the system's
|
|
4403
|
+
* language; the player reads its own saved preference.
|
|
4404
|
+
* ```ts
|
|
4405
|
+
* const { when } = plugin;
|
|
4406
|
+
* const followSystem = when.language("system");
|
|
4407
|
+
* ```
|
|
4408
|
+
*/
|
|
4409
|
+
language(language: LanguagePreference): Condition;
|
|
4410
|
+
}
|
|
4411
|
+
```
|
|
4412
|
+
|
|
4413
|
+
Example: Check a palette item and follow the palette
|
|
4414
|
+
|
|
4415
|
+
The Appearance submenu has one radio item per palette; the player
|
|
4416
|
+
checks the one in use, also after another plugin or the user changed
|
|
4417
|
+
it, and while this plugin is stopped. Nothing in the plugin listens to
|
|
4418
|
+
the palette.
|
|
4419
|
+
```ts
|
|
4420
|
+
const { when } = plugin;
|
|
4421
|
+
for (const [id, title] of [["vestra", "Dark"], ["vestra-light", "Light"]] as const)
|
|
4422
|
+
plugin.commands.add({
|
|
4423
|
+
id: `theme.${id}`,
|
|
4424
|
+
path: ["Settings", "Appearance", title],
|
|
4425
|
+
radio: true,
|
|
4426
|
+
checked: when.theme(id), // evaluated by the player, not the plugin
|
|
4427
|
+
run: () => plugin.appearance.set(id),
|
|
4428
|
+
});
|
|
4429
|
+
```
|
|
4430
|
+
|
|
4215
4431
|
### MenuId
|
|
4216
4432
|
|
|
4217
4433
|
A built-in top-level menu of the player, named in a command path as
|
|
@@ -4413,13 +4629,15 @@ interface CommandOptions {
|
|
|
4413
4629
|
* A button for this command on the bottom bar (SDK 1.5.29, player 0.5.10): an icon and a place, `{ icon: "store", order: 200 }`. Needs a `path`: the menu entry is where the command stays reachable when the bar shows no button (a bar without `BarButtons`, a narrow window, a limit). See CommandButton for where it shows, what it shows and the limits. Fails with `invalid` for an unknown icon name, an order outside 0–999 or a button without a path.
|
|
4414
4630
|
*/
|
|
4415
4631
|
button?: CommandButton;
|
|
4416
|
-
/** Whether the command can run (default true). */
|
|
4417
|
-
enabled?: Bindable<boolean
|
|
4418
|
-
/** Check mark (or radio dot with `radio`); `null` (default) draws none. */
|
|
4419
|
-
checked?: Bindable<boolean | null
|
|
4632
|
+
/** Whether the command can run (default true): a value, a function, or a Condition the player evaluates (`plugin.when`, SDK 1.5.40). */
|
|
4633
|
+
enabled?: Bindable<boolean> | Condition;
|
|
4634
|
+
/** Check mark (or radio dot with `radio`); `null` (default) draws none. A Condition (`plugin.when`, SDK 1.5.40) draws the mark while it holds. */
|
|
4635
|
+
checked?: Bindable<boolean | null> | Condition;
|
|
4420
4636
|
/** Busy state (default false); the player does not call `run` while busy. */
|
|
4421
4637
|
busy?: Bindable<boolean>;
|
|
4422
|
-
/**
|
|
4638
|
+
/**
|
|
4639
|
+
* Why the command is disabled or busy (shown to the user): English source text, or a Text for one with data in it (SDK 1.5.11). With an `enabled` Condition the player shows it only while the condition is false or the command is busy, so a fixed text is enough.
|
|
4640
|
+
*/
|
|
4423
4641
|
reason?: Bindable<TextValue | null>;
|
|
4424
4642
|
/**
|
|
4425
4643
|
* Run the command (menu item, binding, panel menu or `host.command(id).run()` in tests). It may return a promise; errors and rejections go to Scope.onError, and bound values are re-evaluated when it returns and when its promise settles.
|
|
@@ -4471,7 +4689,10 @@ interface Command extends Disposable {
|
|
|
4471
4689
|
* ```
|
|
4472
4690
|
*/
|
|
4473
4691
|
set(state: Partial<{
|
|
4474
|
-
[K in
|
|
4692
|
+
[K in "busy" | "reason"]: Bindable<CommandState[K]>;
|
|
4693
|
+
} & {
|
|
4694
|
+
enabled: Bindable<boolean> | Condition;
|
|
4695
|
+
checked: Bindable<boolean | null> | Condition;
|
|
4475
4696
|
}>): void;
|
|
4476
4697
|
/**
|
|
4477
4698
|
* Resolves when the host has registered the command. Rejects with `conflict` (path already taken), `invalid` or `limit` (more than 160 commands in this plugin, or 1024 in the whole player). You rarely need to await it: during activation a failure fails activation, later it goes to Scope.onError.
|
|
@@ -7455,8 +7676,10 @@ interface SystemInfo {
|
|
|
7455
7676
|
* Absent (`undefined`) on players before 0.5.12, which do not report it:
|
|
7456
7677
|
* the renderer is then unknown, not software.
|
|
7457
7678
|
*
|
|
7458
|
-
* Today the player renders on the GPU on macOS
|
|
7459
|
-
*
|
|
7679
|
+
* Today the player renders on the GPU on macOS, on Windows when the
|
|
7680
|
+
* user turned on Video ▸ GPU Rendering (Experimental) (since version 0.5.14;
|
|
7681
|
+
* off by default), and in software otherwise: on Windows by default, on
|
|
7682
|
+
* Linux and without a user interface (see
|
|
7460
7683
|
* Platforms); test this field
|
|
7461
7684
|
* rather than SystemInfo.os.
|
|
7462
7685
|
* @player 0.5.12; older players leave it out (declare `"minPlayerVersion": "0.5.12"` when the plugin relies on it).
|
|
@@ -8223,7 +8446,7 @@ What Player.stats reports. Every field is the backend's own value
|
|
|
8223
8446
|
```ts
|
|
8224
8447
|
interface PlaybackStats {
|
|
8225
8448
|
/**
|
|
8226
|
-
* The rendering path: "libmpv · GPU 渲染(OpenGL)" (GPU
|
|
8449
|
+
* The rendering path: "libmpv · GPU 渲染(OpenGL)" (GPU on macOS, the default there), "libmpv · GPU 渲染(Direct3D)" (GPU on Windows, when turned on; version 0.5.14 and later) or "libmpv · 软件渲染" (software: Windows by default, Linux, headless, fallback). The strings are the player's display text in the interface language (shown here in Chinese).
|
|
8227
8450
|
*/
|
|
8228
8451
|
backend: string;
|
|
8229
8452
|
/** The playback session number. */
|
|
@@ -9352,7 +9575,7 @@ interface RenderSettings {
|
|
|
9352
9575
|
targetPeak: number;
|
|
9353
9576
|
/** Per-scene peak estimation (where the GPU supports it). */
|
|
9354
9577
|
peakDetect: boolean;
|
|
9355
|
-
/** mpv user shaders (`.glsl`): absolute paths of existing files, at most 16, in order. */
|
|
9578
|
+
/** mpv user shaders (`.glsl`): absolute paths of existing files, at most 16, in order. They run before the shaders of every plugin's VideoShaders handles. */
|
|
9356
9579
|
shaders: string[];
|
|
9357
9580
|
/** Motion interpolation, resampled to the display's refresh rate. */
|
|
9358
9581
|
interpolation: boolean;
|
|
@@ -9365,19 +9588,23 @@ The GPU renderer's options, from `plugin.video.render()`
|
|
|
9365
9588
|
(`processing.video`). Single owner, like VideoView; the two are
|
|
9366
9589
|
independent handles.
|
|
9367
9590
|
|
|
9368
|
-
Platforms:
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9591
|
+
Platforms: macOS renders on the GPU by default; Windows does since
|
|
9592
|
+
version 0.5.14 of the player when the user turns on Video ▸ GPU Rendering
|
|
9593
|
+
(Experimental) (off by default, Rendering). Under software
|
|
9594
|
+
rendering (Windows by default, Linux, headless, fallback) every `set`
|
|
9595
|
+
fails with `unsupported`; SystemInfo.rendering tells which
|
|
9596
|
+
renderer draws without creating the handle. macOS OpenGL 4.1 has no
|
|
9372
9597
|
compute shaders, so `peakDetect` is accepted but has no effect; output
|
|
9373
9598
|
is always SDR.
|
|
9374
9599
|
|
|
9375
9600
|
GPU renderer options (tone mapping, peak detection, shaders,
|
|
9376
|
-
interpolation). Only the GPU renderer has them
|
|
9601
|
+
interpolation). Only the GPU renderer has them (macOS; Windows with GPU
|
|
9602
|
+
rendering turned on, version 0.5.14 and later);
|
|
9377
9603
|
under software rendering the handle is `failed` with `unsupported` after
|
|
9378
9604
|
the player's first answer to `render()` (reported to `onError` too), and so is
|
|
9379
9605
|
every `set`. Single owner, like VideoView. `set` never throws; the
|
|
9380
|
-
result shows in `status`.
|
|
9606
|
+
result shows in `status`. For shaders that several plugins add at the
|
|
9607
|
+
same time, use VideoShaders (SDK 1.5.39).
|
|
9381
9608
|
|
|
9382
9609
|
- @capability processing.video, playback.read and events (like VideoFilter)
|
|
9383
9610
|
|
|
@@ -9395,7 +9622,9 @@ interface RenderOptions extends Disposable {
|
|
|
9395
9622
|
* ```
|
|
9396
9623
|
*/
|
|
9397
9624
|
set(value: Partial<RenderSettings>): void;
|
|
9398
|
-
/**
|
|
9625
|
+
/**
|
|
9626
|
+
* `failed` with `unsupported` while the player renders in software; `failed` with `failed` when mpv cannot compile a shader (pushed later; while other plugins' VideoShaders are on too, the player cannot tell whose shader failed and says so in the message).
|
|
9627
|
+
*/
|
|
9399
9628
|
readonly status: FilterStatus;
|
|
9400
9629
|
/**
|
|
9401
9630
|
* The status changed (including later pushes such as shader compile errors).
|
|
@@ -9421,7 +9650,8 @@ On a GPU-rendering player HDR video is tone-mapped with BT.2390 for a
|
|
|
9421
9650
|
250-nit display; under software rendering the plugin hides its HDR menu.
|
|
9422
9651
|
`set` merges the values into the plugin's render options and the
|
|
9423
9652
|
player reports the result through `onStatus`: `failed` with
|
|
9424
|
-
`unsupported` means the player renders in software (
|
|
9653
|
+
`unsupported` means the player renders in software (Linux, or Windows
|
|
9654
|
+
while GPU rendering is off).
|
|
9425
9655
|
```ts
|
|
9426
9656
|
const render = plugin.video.render();
|
|
9427
9657
|
render.onStatus((s) => {
|
|
@@ -9430,6 +9660,127 @@ render.onStatus((s) => {
|
|
|
9430
9660
|
render.set({ toneMapping: "bt.2390", targetPeak: 250 }); // nits
|
|
9431
9661
|
```
|
|
9432
9662
|
|
|
9663
|
+
### VideoShaders
|
|
9664
|
+
|
|
9665
|
+
This plugin's own GPU shaders, from `plugin.video.shaders(id)`
|
|
9666
|
+
(`processing.video`, SDK 1.5.39, player 0.5.20): mpv user shaders
|
|
9667
|
+
(`.glsl`, `//!HOOK` …) that the GPU renderer runs on every frame.
|
|
9668
|
+
|
|
9669
|
+
Any number of plugins hold shader handles at the same time (unlike
|
|
9670
|
+
RenderOptions, which has one owner). The player joins every
|
|
9671
|
+
handle's list in a stable order, after the render options' own
|
|
9672
|
+
`shaders`: built-in plugins first, then installed plugins in the order
|
|
9673
|
+
they were first installed, then development loads (`--plugin`); ties by
|
|
9674
|
+
plugin id, and a plugin's handles by `id`. At most 16 shaders across all
|
|
9675
|
+
handles (`limit` beyond, nothing changes); the render options keep their
|
|
9676
|
+
own 16.
|
|
9677
|
+
|
|
9678
|
+
Files must be this plugin's own: absolute paths inside its package
|
|
9679
|
+
directory or Plugin.dataDirectory after following links, each
|
|
9680
|
+
at most 16 MiB. Elsewhere: `invalid`; a file that does not exist:
|
|
9681
|
+
`missing`. The player reads the files when the list is set.
|
|
9682
|
+
|
|
9683
|
+
GPU rendering only (macOS; Windows with Video ▸ GPU Rendering
|
|
9684
|
+
(Experimental) turned on): under software rendering a non-empty list is
|
|
9685
|
+
`failed` with `unsupported`, and a switch to software during the run
|
|
9686
|
+
pushes `unsupported` to every handle with shaders and removes every
|
|
9687
|
+
list (they no longer count against the 16); set them again once the
|
|
9688
|
+
player draws on the GPU (after a restart). Shaders act when a
|
|
9689
|
+
frame is drawn, so processed snapshots and exports never contain them
|
|
9690
|
+
(use a VideoNode for that). When mpv cannot compile a shader the
|
|
9691
|
+
handle turns `failed` with `failed` (pushed later); mpv does not say
|
|
9692
|
+
which file failed, so while several plugins' shaders are on, each of
|
|
9693
|
+
them hears it and the message says the player cannot tell whose.
|
|
9694
|
+
|
|
9695
|
+
`set` sends the whole list (last write wins); `clear` and `dispose`
|
|
9696
|
+
withdraw it; the player removes a stopped plugin's shaders itself.
|
|
9697
|
+
Players before 0.5.20 refuse the request (the handle is `failed`): declare
|
|
9698
|
+
`"minPlayerVersion": "0.5.20"`.
|
|
9699
|
+
|
|
9700
|
+
- @capability processing.video
|
|
9701
|
+
- @player 0.5.20; older players refuse the request.
|
|
9702
|
+
|
|
9703
|
+
```ts
|
|
9704
|
+
interface VideoShaders extends Disposable {
|
|
9705
|
+
/**
|
|
9706
|
+
* Replace this handle's shader list (last write wins); `[]` withdraws it.
|
|
9707
|
+
*
|
|
9708
|
+
* Never throws: a refused list shows in `status` (`invalid`, `missing`,
|
|
9709
|
+
* `limit`, `unsupported`) and goes to Scope.onError, and what the
|
|
9710
|
+
* player held stays.
|
|
9711
|
+
* @capability processing.video
|
|
9712
|
+
* @param paths - Absolute paths of shader files in this plugin's package or data directory, in the order they run.
|
|
9713
|
+
* @example Switch between two shader presets
|
|
9714
|
+
* The plugin offers a light and a strong preset; picking one replaces
|
|
9715
|
+
* the handle's list in one request. The files were written to the
|
|
9716
|
+
* plugin's data directory when the user imported them.
|
|
9717
|
+
* ```ts
|
|
9718
|
+
* const presets: Record<string, string[]> = {
|
|
9719
|
+
* light: [join(plugin.dataDirectory, "denoise.glsl")],
|
|
9720
|
+
* strong: [join(plugin.dataDirectory, "denoise.glsl"), join(plugin.dataDirectory, "sharpen.glsl")],
|
|
9721
|
+
* };
|
|
9722
|
+
* plugin.video.shaders("presets").set(presets.strong!);
|
|
9723
|
+
* ```
|
|
9724
|
+
*/
|
|
9725
|
+
set(paths: readonly string[]): void;
|
|
9726
|
+
/**
|
|
9727
|
+
* Withdraw this handle's shaders (the same as `set([])`).
|
|
9728
|
+
* @capability processing.video
|
|
9729
|
+
* @example Turn the plugin's shaders off from a menu item
|
|
9730
|
+
* A Video menu item turns the plugin's shader on and off. Clearing
|
|
9731
|
+
* keeps the handle, so the next pick sets the list again.
|
|
9732
|
+
* ```ts
|
|
9733
|
+
* const shaders = plugin.video.shaders();
|
|
9734
|
+
* let on = false;
|
|
9735
|
+
* plugin.commands.add({
|
|
9736
|
+
* id: "toggle-shader",
|
|
9737
|
+
* path: ["@video", "Sharpen (Shader)"],
|
|
9738
|
+
* checked: () => on,
|
|
9739
|
+
* run: () => {
|
|
9740
|
+
* on = !on;
|
|
9741
|
+
* // Clearing keeps the handle; the next pick sets the list again.
|
|
9742
|
+
* if (on) shaders.set([file]);
|
|
9743
|
+
* else shaders.clear();
|
|
9744
|
+
* },
|
|
9745
|
+
* });
|
|
9746
|
+
* ```
|
|
9747
|
+
*/
|
|
9748
|
+
clear(): void;
|
|
9749
|
+
/** Current status: `waiting` before ready media, `applying`, `applied`, or `failed` with the reason (see the remarks). */
|
|
9750
|
+
readonly status: FilterStatus;
|
|
9751
|
+
/**
|
|
9752
|
+
* The status changed, including later pushes (a compile failure, a switch to software rendering).
|
|
9753
|
+
* @capability processing.video
|
|
9754
|
+
* @example Report a shader that does not compile
|
|
9755
|
+
* The OSD shows mpv's message when the plugin's shader fails to compile.
|
|
9756
|
+
* The player pushes that failure after the list was accepted, so it
|
|
9757
|
+
* arrives through `onStatus`, not as a refused `set`.
|
|
9758
|
+
* ```ts
|
|
9759
|
+
* const shaders = plugin.video.shaders();
|
|
9760
|
+
* shaders.onStatus((status) => {
|
|
9761
|
+
* if (status.state === "failed") void plugin.osd.show("Shader: {}", [status.error.message]);
|
|
9762
|
+
* });
|
|
9763
|
+
* ```
|
|
9764
|
+
*/
|
|
9765
|
+
onStatus(listener: (status: FilterStatus) => void): Disposable;
|
|
9766
|
+
}
|
|
9767
|
+
```
|
|
9768
|
+
|
|
9769
|
+
Example: Add a sharpening shader the plugin ships
|
|
9770
|
+
|
|
9771
|
+
The video looks sharper on the GPU renderer; under software rendering
|
|
9772
|
+
the OSD says why it is not. The shader file lives in the plugin's
|
|
9773
|
+
package, and the player puts it after other plugins' shaders that come
|
|
9774
|
+
earlier in its order.
|
|
9775
|
+
```ts
|
|
9776
|
+
const shaders = plugin.video.shaders("sharpen");
|
|
9777
|
+
shaders.onStatus((s) => {
|
|
9778
|
+
// `unsupported`: software rendering; `failed`: mpv could not compile it.
|
|
9779
|
+
if (s.state === "failed") void plugin.osd.show("Sharpen: {}", [s.error.message]);
|
|
9780
|
+
});
|
|
9781
|
+
shaders.set([join(packageDirectory, "shaders", "sharpen.glsl")]);
|
|
9782
|
+
```
|
|
9783
|
+
|
|
9433
9784
|
### UpscalingReasonCode
|
|
9434
9785
|
|
|
9435
9786
|
Why the GPU's video upscaler does not work, as a stable kebab-case code.
|
|
@@ -11086,12 +11437,13 @@ interface Bindings {
|
|
|
11086
11437
|
*/
|
|
11087
11438
|
reset(ref?: BindingRef): Promise<void>;
|
|
11088
11439
|
/**
|
|
11089
|
-
*
|
|
11440
|
+
* The binding list changed (read `list()` again): a declaration or an override. At most every 200 ms; since player 0.5.21 only when the list itself changed, not when a plugin starts again with the same commands.
|
|
11090
11441
|
* @capability bindings.manage
|
|
11091
11442
|
* @example Refresh a key list when bindings change
|
|
11092
11443
|
* The plugin's key list stays current when the user changes a key in the
|
|
11093
|
-
* player or
|
|
11094
|
-
* at most every 200 ms; the plugin reads the
|
|
11444
|
+
* player or a plugin with other commands is installed. The listener
|
|
11445
|
+
* carries no data and runs at most every 200 ms; the plugin reads the
|
|
11446
|
+
* table again with `list`.
|
|
11095
11447
|
* ```ts
|
|
11096
11448
|
* plugin.bindings.onChange(async () => {
|
|
11097
11449
|
* const { commands } = await plugin.bindings.list(); // read the new table
|
|
@@ -11849,10 +12201,10 @@ RenderingReason.message for the rest. The codes of player 0.5.13:
|
|
|
11849
12201
|
|
|
11850
12202
|
| Code | Meaning |
|
|
11851
12203
|
| --- | --- |
|
|
11852
|
-
| `default` | Nothing ruled the GPU out; the platform's default. |
|
|
12204
|
+
| `default` | Nothing ruled the GPU out; the platform's default (macOS: GPU; Windows since version 0.5.14: software until the user turns on GPU rendering). |
|
|
11853
12205
|
| `pending` | GPU rendering is starting. |
|
|
11854
12206
|
| `headless` | The player runs without a user interface. |
|
|
11855
|
-
| `no-gpu-backend` | This player has no GPU renderer on this system (Windows
|
|
12207
|
+
| `no-gpu-backend` | This player has no GPU renderer on this system (Linux today; Windows has one since version 0.5.14, off by default, which reports `default`). |
|
|
11856
12208
|
| `env` | The environment chose it (`VESTRA_RENDER`, `VESTRA_SOFTWARE_RENDER`). |
|
|
11857
12209
|
| `preference` | The user chose software rendering (Rendering.setPreference); GPU chosen and available reports `default`. |
|
|
11858
12210
|
| `denylist` | The graphics driver is on the player's denylist. |
|
|
@@ -12217,6 +12569,28 @@ type VideoNode =
|
|
|
12217
12569
|
kind: "frameBlend";
|
|
12218
12570
|
/** Output frame rate, integer 24–120. */
|
|
12219
12571
|
fps: number;
|
|
12572
|
+
}
|
|
12573
|
+
/**
|
|
12574
|
+
* A convolution matrix of your own per plane (FFmpeg `convolution`, SDK 1.5.39, player 0.5.20): sharpen, blur, emboss or detect edges with a 3×3, 5×5 or 7×7 kernel, or a row or column kernel. `planes[i]` is plane `i` of the decoded picture: for the usual YUV video 0 is luma (brightness), 1 and 2 are the colour planes, 3 is alpha. A missing entry or `null` leaves that plane as it is; at least one entry has a matrix. Players before 0.5.20 refuse the kind with `invalid`.
|
|
12575
|
+
*/
|
|
12576
|
+
| {
|
|
12577
|
+
/** The node type. */
|
|
12578
|
+
kind: "convolution";
|
|
12579
|
+
/** 1–4 entries, by plane; `null` passes the plane through. */
|
|
12580
|
+
planes: (ConvolutionPlane | null)[];
|
|
12581
|
+
}
|
|
12582
|
+
/**
|
|
12583
|
+
* Recolour the picture with a 3D look-up table (FFmpeg `lut3d`, SDK 1.5.39, player 0.5.20): a `.cube` file the plugin ships in its package or wrote to its Plugin.dataDirectory. The player reads the file when the chain is set (and again for each new source, snapshot or export), so it must stay there while the chain uses it. The table is applied to RGB; the player converts the picture to RGB and back. Players before 0.5.20 refuse the kind with `invalid`.
|
|
12584
|
+
*/
|
|
12585
|
+
| {
|
|
12586
|
+
/** The node type. */
|
|
12587
|
+
kind: "lut3d";
|
|
12588
|
+
/**
|
|
12589
|
+
* Absolute path of a `.cube` file (`LUT_3D_SIZE` 2–65, at most 16 MiB) inside the plugin's package directory or data directory after following links; a path with `[` or `]` is refused. Outside those: `invalid`; a file that does not exist: `missing`.
|
|
12590
|
+
*/
|
|
12591
|
+
file: string;
|
|
12592
|
+
/** How colours between the table's points are computed; `tetrahedral` is FFmpeg's default. */
|
|
12593
|
+
interpolation: "nearest" | "trilinear" | "tetrahedral" | "pyramid" | "prism";
|
|
12220
12594
|
};
|
|
12221
12595
|
```
|
|
12222
12596
|
|
|
@@ -12238,6 +12612,27 @@ const chain: VideoNode[] = [
|
|
|
12238
12612
|
plugin.video.filter("picture").set(chain);
|
|
12239
12613
|
```
|
|
12240
12614
|
|
|
12615
|
+
### ConvolutionPlane
|
|
12616
|
+
|
|
12617
|
+
One plane's kernel of a `convolution` VideoNode: each output
|
|
12618
|
+
sample is `(sum of matrix × neighbourhood) × rdiv + bias`, clipped to the
|
|
12619
|
+
sample range.
|
|
12620
|
+
|
|
12621
|
+
```ts
|
|
12622
|
+
type ConvolutionPlane = {
|
|
12623
|
+
/**
|
|
12624
|
+
* `square`: `matrix` is a 3×3, 5×5 or 7×7 kernel, row by row (9, 25 or 49 entries); `row` / `column`: a horizontal / vertical kernel with an odd number of entries up to 49.
|
|
12625
|
+
*/
|
|
12626
|
+
mode: "square" | "row" | "column";
|
|
12627
|
+
/** Integers −1024–1024 (FFmpeg reads whole numbers only). */
|
|
12628
|
+
matrix: number[];
|
|
12629
|
+
/** Multiplier 0–256; 0 divides by the sum of the matrix (by 1 when it sums to 0), which keeps the brightness of a blur. */
|
|
12630
|
+
rdiv: number;
|
|
12631
|
+
/** Added after the multiplication, 0–65535 in the plane's sample units (128 is mid grey for 8-bit video). */
|
|
12632
|
+
bias: number;
|
|
12633
|
+
};
|
|
12634
|
+
```
|
|
12635
|
+
|
|
12241
12636
|
### AudioNode
|
|
12242
12637
|
|
|
12243
12638
|
One node of an AudioFilter chain, by `kind`. Values outside the
|
|
@@ -12594,7 +12989,18 @@ interface FakeCommand {
|
|
|
12594
12989
|
*/
|
|
12595
12990
|
reason?: string | null;
|
|
12596
12991
|
};
|
|
12597
|
-
/**
|
|
12992
|
+
/**
|
|
12993
|
+
* The conditions the plugin sent for `enabled` and `checked` (`plugin.when`, wire form), `undefined` without. Like the player, the host evaluates them in FakeCommand.state against `source`, `mainWindow.fullscreen`, `appearance` and `language`.
|
|
12994
|
+
*/
|
|
12995
|
+
when?: {
|
|
12996
|
+
/** The condition of `enabled`. */
|
|
12997
|
+
enabled?: Json;
|
|
12998
|
+
/** The condition of `checked`. */
|
|
12999
|
+
checked?: Json;
|
|
13000
|
+
};
|
|
13001
|
+
/**
|
|
13002
|
+
* The command state as the player shows it: what the plugin last sent (after the update pass), with the fields given as conditions (`plugin.when`) evaluated now. With an `enabled` condition, `reason` is `null` while the command is enabled and not busy.
|
|
13003
|
+
*/
|
|
12598
13004
|
state: {
|
|
12599
13005
|
/** `false` when the plugin disabled the command. */
|
|
12600
13006
|
enabled: boolean;
|
|
@@ -12814,6 +13220,10 @@ class TestHost implements Transport {
|
|
|
12814
13220
|
* The subscribed events the plugin really listens to: an `onChange` and the like, or a binding that reads the state. The SDK's own copies of the state (what keeps `plugin.player.state` current) are left out. Empty means the player may stop the plugin while nothing uses it (unless it holds something else: a window, a panel, a filter…).
|
|
12815
13221
|
*/
|
|
12816
13222
|
readonly listening: Set<string>;
|
|
13223
|
+
/**
|
|
13224
|
+
* Why the plugin listens to each event of TestHost.listening: every listener and every binding that reads its state, as the player's log says them (`a listener added at main.ts:12`, `command "Save" enabled is a function that reads plugin.player.state (main.ts:40) — use plugin.when`), in the order they came.
|
|
13225
|
+
*/
|
|
13226
|
+
readonly listeningWhy: Map<string, string[]>;
|
|
12817
13227
|
/**
|
|
12818
13228
|
* The player's source facts (phase, position, duration, paused, rate, audio tracks, loop, ...). Wire-level shape; change it with TestHost.setSource, which also broadcasts it.
|
|
12819
13229
|
*/
|
|
@@ -13394,6 +13804,39 @@ class TestHost implements Transport {
|
|
|
13394
13804
|
* ```
|
|
13395
13805
|
*/
|
|
13396
13806
|
failVideoRender(code: "failed" | "unsupported", message: string): void;
|
|
13807
|
+
/**
|
|
13808
|
+
* The shader channel (`plugin.video.shaders(id)`, SDK 1.5.39): the lists the player holds for this plugin's handles by id (the real paths, after following links), and how many shaders other plugins hold, which count against VIDEO_PROCESSING_LIMITS`.shaders` (`limit`). Files must be under TestHost.fileRoots; under software rendering (`videoRender.gpu = false`) a non-empty list fails with `unsupported`.
|
|
13809
|
+
*/
|
|
13810
|
+
readonly videoShaders: {
|
|
13811
|
+
/** This plugin's lists by handle id; a cleared handle has none. */
|
|
13812
|
+
handles: Map<string, string[]>;
|
|
13813
|
+
/** Shaders other plugins hold (0–16). */
|
|
13814
|
+
others: number;
|
|
13815
|
+
};
|
|
13816
|
+
/**
|
|
13817
|
+
* Where the plugin's own files may be: its package directory and its data directory (startPlugin sets both when the list is still empty). `lut3d` files and shader-channel files outside them fail with `invalid`, as in the player.
|
|
13818
|
+
*/
|
|
13819
|
+
fileRoots: string[];
|
|
13820
|
+
/**
|
|
13821
|
+
* What the player pushes to a shader handle when mpv cannot compile a shader (code `failed`; the player cannot tell whose shader failed, so every handle with shaders hears it) or the player switches to software rendering (`unsupported`; use TestHost.failVideoRender, which also tells the handles). Pushed only to a handle that holds shaders.
|
|
13822
|
+
* @capability None.
|
|
13823
|
+
* @example Make the plugin's shader fail to compile
|
|
13824
|
+
* The plugin's shader handle reports `failed`, as when mpv rejects a
|
|
13825
|
+
* shader. The host pushes the failure only to a handle that holds
|
|
13826
|
+
* shaders, which its accepted `set` made it; the SDK updates the
|
|
13827
|
+
* handle's `status` when the push arrives, so the test settles first.
|
|
13828
|
+
* ```ts
|
|
13829
|
+
* const file = join(plugin.dataDirectory, "sharpen.glsl");
|
|
13830
|
+
* writeFileSync(file, "//!HOOK MAIN\n");
|
|
13831
|
+
* const shaders = plugin.video.shaders("sharpen");
|
|
13832
|
+
* shaders.set([file]); // the handle now holds a shader
|
|
13833
|
+
* await host.settle();
|
|
13834
|
+
* host.failVideoShaders("sharpen", "shader compile failed: ERROR: 0:3: ...");
|
|
13835
|
+
* await host.settle();
|
|
13836
|
+
* assert.equal(shaders.status.state, "failed");
|
|
13837
|
+
* ```
|
|
13838
|
+
*/
|
|
13839
|
+
failVideoShaders(id: string, message: string): void;
|
|
13397
13840
|
/**
|
|
13398
13841
|
* The player's processing (filter) state. Wire-level; may change in minor versions. `resources` holds each `video.filter` / `audio.filter` chain the plugin applied, keyed by the SDK's resource id.
|
|
13399
13842
|
* @example Check the filter chain the plugin applied
|
|
@@ -13655,9 +14098,9 @@ class TestHost implements Transport {
|
|
|
13655
14098
|
* import activate from "../main.ts";
|
|
13656
14099
|
*
|
|
13657
14100
|
* const host = new TestHost();
|
|
13658
|
-
* host.videoRender.gpu = false; // software draws now (
|
|
14101
|
+
* host.videoRender.gpu = false; // software draws now (Linux)
|
|
13659
14102
|
* host.rendering.startedWith = "software";
|
|
13660
|
-
* host.rendering.reason = { code: "no-gpu-backend", message: "
|
|
14103
|
+
* host.rendering.reason = { code: "no-gpu-backend", message: "this player has no GPU renderer on Linux yet" };
|
|
13661
14104
|
* host.rendering.gpu = { available: false, reason: host.rendering.reason };
|
|
13662
14105
|
* const { stop } = await startPlugin(activate, {
|
|
13663
14106
|
* directory: new URL("..", import.meta.url),
|
|
@@ -13665,7 +14108,7 @@ class TestHost implements Transport {
|
|
|
13665
14108
|
* });
|
|
13666
14109
|
* const item = host.command("gpu-rendering");
|
|
13667
14110
|
* assert.equal(item.state.enabled, false);
|
|
13668
|
-
* assert.equal(item.state.reason, "
|
|
14111
|
+
* assert.equal(item.state.reason, "this player has no GPU renderer on Linux yet");
|
|
13669
14112
|
* await stop();
|
|
13670
14113
|
* ```
|
|
13671
14114
|
*/
|
|
@@ -14261,6 +14704,20 @@ class TestHost implements Transport {
|
|
|
14261
14704
|
* ```
|
|
14262
14705
|
*/
|
|
14263
14706
|
close(): void;
|
|
14707
|
+
/**
|
|
14708
|
+
* Why the player would keep this plugin running while nothing uses it (lazy activation, player 0.5.21), one line per reason, as the player's log says it: an event it listens to that is not one the player wakes it for (with what listens and where it was added), its mounted panel, its open windows. Empty: the player may stop it while idle and start it again on its next use. Filter chains, streams, jobs and the like (which also keep a plugin running) are not listed.
|
|
14709
|
+
* @capability None.
|
|
14710
|
+
* @example Check that a plugin can be stopped while idle
|
|
14711
|
+
* The test fails if the plugin keeps running for no reason after it
|
|
14712
|
+
* starts, and names the reason (a command whose `enabled` reads
|
|
14713
|
+
* `plugin.player.state`, say: give it a `plugin.when` condition). The
|
|
14714
|
+
* player stops such a plugin while nothing uses it and starts it again
|
|
14715
|
+
* on its next use, so it costs no process meanwhile.
|
|
14716
|
+
* ```ts
|
|
14717
|
+
* assert.deepEqual(host.keepsRunning, []);
|
|
14718
|
+
* ```
|
|
14719
|
+
*/
|
|
14720
|
+
get keepsRunning(): string[];
|
|
14264
14721
|
}
|
|
14265
14722
|
```
|
|
14266
14723
|
|
|
@@ -15100,6 +15557,49 @@ const IMAGE_LIMITS: Readonly<{
|
|
|
15100
15557
|
}>;
|
|
15101
15558
|
```
|
|
15102
15559
|
|
|
15560
|
+
### VIDEO_PROCESSING_LIMITS
|
|
15561
|
+
|
|
15562
|
+
The player's bounds for the video primitives of SDK 1.5.39 (player
|
|
15563
|
+
0.5.20): `convolution` and `lut3d` VideoNodes and the shader
|
|
15564
|
+
channel (`plugin.video.shaders()`). Values beyond them fail with
|
|
15565
|
+
`invalid`; more shaders than `shaders` fail with `limit`.
|
|
15566
|
+
|
|
15567
|
+
```ts
|
|
15568
|
+
const VIDEO_PROCESSING_LIMITS: Readonly<{
|
|
15569
|
+
/** Shaders across every plugin's `plugin.video.shaders()` handles (16). */
|
|
15570
|
+
shaders: 16;
|
|
15571
|
+
/** Bytes of one shader file (16 MiB). */
|
|
15572
|
+
shaderBytes: number;
|
|
15573
|
+
/** Bytes of a shader handle's id (128). */
|
|
15574
|
+
shaderIdBytes: 128;
|
|
15575
|
+
/** Bytes of one `lut3d` `.cube` file (16 MiB). */
|
|
15576
|
+
lutBytes: number;
|
|
15577
|
+
/** Largest `LUT_3D_SIZE` of a `lut3d` file, points per axis (65). */
|
|
15578
|
+
lutSize: 65;
|
|
15579
|
+
/** Bytes of a `lut3d` file path (4096). */
|
|
15580
|
+
lutPathBytes: 4096;
|
|
15581
|
+
/** Largest magnitude of a `convolution` matrix entry (1024). */
|
|
15582
|
+
matrixValue: 1024;
|
|
15583
|
+
/** Largest `convolution` `rdiv` (256). */
|
|
15584
|
+
rdiv: 256;
|
|
15585
|
+
/** Largest `convolution` `bias` (65535). */
|
|
15586
|
+
bias: 65535;
|
|
15587
|
+
}>;
|
|
15588
|
+
```
|
|
15589
|
+
|
|
15590
|
+
Example: Refuse a shader list the player would not take
|
|
15591
|
+
|
|
15592
|
+
The plugin checks a shader list against the player's limit before it
|
|
15593
|
+
sets it, and says so on the OSD instead of waiting for a `limit` failure.
|
|
15594
|
+
The limit counts every plugin's shaders, so the player still decides.
|
|
15595
|
+
```ts
|
|
15596
|
+
import { VIDEO_PROCESSING_LIMITS } from "@vestra-player/sdk/testing";
|
|
15597
|
+
|
|
15598
|
+
if (files.length > VIDEO_PROCESSING_LIMITS.shaders)
|
|
15599
|
+
void plugin.osd.show("Too many shaders");
|
|
15600
|
+
else plugin.video.shaders().set(files);
|
|
15601
|
+
```
|
|
15602
|
+
|
|
15103
15603
|
### HostError
|
|
15104
15604
|
|
|
15105
15605
|
A definite refusal from the player (or the plugin runner), with a stable
|
|
@@ -15359,7 +15859,7 @@ What Player.stats reports. Every field is the backend's own value
|
|
|
15359
15859
|
```ts
|
|
15360
15860
|
interface PlaybackStats {
|
|
15361
15861
|
/**
|
|
15362
|
-
* The rendering path: "libmpv · GPU 渲染(OpenGL)" (GPU
|
|
15862
|
+
* The rendering path: "libmpv · GPU 渲染(OpenGL)" (GPU on macOS, the default there), "libmpv · GPU 渲染(Direct3D)" (GPU on Windows, when turned on; version 0.5.14 and later) or "libmpv · 软件渲染" (software: Windows by default, Linux, headless, fallback). The strings are the player's display text in the interface language (shown here in Chinese).
|
|
15363
15863
|
*/
|
|
15364
15864
|
backend: string;
|
|
15365
15865
|
/** The playback session number. */
|