@vestra-player/sdk 1.6.8 → 1.6.10
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 -1
- package/dist/api-requests.js +5 -1
- package/dist/docs-tree.json.gz +0 -0
- package/dist/host-capabilities.js +4 -1
- package/dist/media.d.ts +43 -0
- package/dist/player-runtime.d.ts +1 -0
- package/dist/player-runtime.js +1 -0
- package/dist/plugin/media.js +46 -1
- package/dist/plugin/osd.d.ts +4 -0
- package/dist/plugin/osd.js +26 -1
- package/dist/plugin/testing.d.ts +117 -1
- package/dist/plugin/testing.js +256 -10
- package/dist/plugin/timeline.d.ts +40 -3
- package/dist/plugin/timeline.js +102 -10
- package/dist/plugin/types.d.ts +357 -10
- package/dist/wire.d.ts +32 -4
- package/dist/wire.js +8 -1
- package/llms.txt +5 -5
- package/package.json +1 -1
package/dist/plugin/types.d.ts
CHANGED
|
@@ -93,7 +93,7 @@ export interface Disposable {
|
|
|
93
93
|
* | `busy` | The player is still doing a previous call of the same kind (a dialog is open, a step is in flight). |
|
|
94
94
|
* | `conflict` | Another plugin owns the resource (the sequence provider, the display layer, the backdrop, a menu path), or this plugin already uses the id (a window or command id). |
|
|
95
95
|
* | `unavailable` | Not possible right now (no ready media, no user interface, fullscreen, a disabled core action). |
|
|
96
|
-
* | `unsupported` | Not possible on this system, build or source (a platform gap, software rendering, a source that cannot seek, a filter on a generated stream). |
|
|
96
|
+
* | `unsupported` | Not possible on this system, build or source (a platform gap, software rendering, a source that cannot seek, a filter on a generated stream, an export the player cannot make as asked: a stream copy, a source bitrate it cannot tell). |
|
|
97
97
|
* | `backend` | Loading or resolving failed; `message` is the backend's reason. |
|
|
98
98
|
* | `failed` | The operation ran and failed (`media.ready()` load error, a non-zero `tools.run` exit, the system refused). |
|
|
99
99
|
* | `replaced` | Accepted, but another source replaced it before it finished. |
|
|
@@ -114,6 +114,7 @@ export interface Disposable {
|
|
|
114
114
|
* | `unknown`, `rejected`, `expired` | After a transport problem the SDK could not confirm the outcome (does not happen in normal operation). |
|
|
115
115
|
* | `activation` | The plugin opened a tool window while its activation was still pending and the player could not track it. |
|
|
116
116
|
* | `command` | The player could not run or update a command it no longer knows for this plugin (removed meanwhile, or another plugin's). |
|
|
117
|
+
* | `export-failed` | An export job ({@link ExportJob}) failed: the code of its `outcome.error`, not a rejection; `message` has the worker's or FFmpeg's reason. |
|
|
117
118
|
* | `input-overflow` | `openStream` input: the player's queue of physical key transitions was full; the input was revoked and its keys released. |
|
|
118
119
|
* | `internal` | An internal step of the player failed (keeping an operation's result, a subtitle or media job); `message` has the reason. |
|
|
119
120
|
* | `javascript`, `slint` | {@link Packages.validate}: the package's entry module does not pass `node --check` / one of its Slint files does not compile; `message` has the diagnostics. |
|
|
@@ -4216,6 +4217,36 @@ export interface TimelineMark {
|
|
|
4216
4217
|
/** The manifest id of the plugin that published it. */
|
|
4217
4218
|
readonly plugin: string;
|
|
4218
4219
|
}
|
|
4220
|
+
/**
|
|
4221
|
+
* How a timeline range is drawn; readers choose colours by it.
|
|
4222
|
+
*
|
|
4223
|
+
* - `"selection"`: a span the user picked to act on, such as a clip to
|
|
4224
|
+
* export. The player's seek bar draws it in the accent colour.
|
|
4225
|
+
* - `"region"`: a part of the media the plugin describes, such as an intro
|
|
4226
|
+
* or a chapter's span. Drawn dimmer, under selections.
|
|
4227
|
+
*
|
|
4228
|
+
* @since 1.6.9
|
|
4229
|
+
* @category Playback
|
|
4230
|
+
*/
|
|
4231
|
+
export type RangeStyle = "selection" | "region";
|
|
4232
|
+
/**
|
|
4233
|
+
* One range on the seek bar (player 0.7.6).
|
|
4234
|
+
*
|
|
4235
|
+
* @since 1.6.9
|
|
4236
|
+
* @category Playback
|
|
4237
|
+
*/
|
|
4238
|
+
export interface TimelineRange {
|
|
4239
|
+
/** Seconds from the start. */
|
|
4240
|
+
readonly start: number;
|
|
4241
|
+
/** Seconds from the start, after `start`. */
|
|
4242
|
+
readonly end: number;
|
|
4243
|
+
/** How it is drawn. */
|
|
4244
|
+
readonly style: RangeStyle;
|
|
4245
|
+
/** Label, `null` without one. */
|
|
4246
|
+
readonly title: string | null;
|
|
4247
|
+
/** The manifest id of the plugin that published it. */
|
|
4248
|
+
readonly plugin: string;
|
|
4249
|
+
}
|
|
4219
4250
|
/**
|
|
4220
4251
|
* This plugin's own marks; disposing it removes them.
|
|
4221
4252
|
*
|
|
@@ -4273,6 +4304,54 @@ export interface TimelineLayer extends Disposable {
|
|
|
4273
4304
|
}[], options?: {
|
|
4274
4305
|
ifCurrent?: PlayerState;
|
|
4275
4306
|
}): void;
|
|
4307
|
+
/**
|
|
4308
|
+
* Replace the layer's ranges (last write wins; `[]` clears them), spans of
|
|
4309
|
+
* the media drawn on the seek bar (player 0.7.6). The marks and the ranges
|
|
4310
|
+
* of a layer are replaced on their own: `set` keeps the ranges and
|
|
4311
|
+
* `setRanges` keeps the marks. Disposing the layer, its scope or stopping
|
|
4312
|
+
* the plugin removes both, and a new source clears every layer.
|
|
4313
|
+
*
|
|
4314
|
+
* At most 100 ranges (`TIMELINE_LIMITS.ranges`), `0 ≤ start < end`, `end`
|
|
4315
|
+
* within the media while its duration is known, titles ≤ 100 characters.
|
|
4316
|
+
* Ranges may overlap. Failures go to `onError`; with `ifCurrent`, a
|
|
4317
|
+
* write for another source is dropped silently. A title is data or a
|
|
4318
|
+
* {@link Text} the player translates, as in `set`.
|
|
4319
|
+
*
|
|
4320
|
+
* Players before 0.7.6 build `plugin` from an SDK without `setRanges`, so
|
|
4321
|
+
* the call throws a `TypeError` there: declare
|
|
4322
|
+
* `"minPlayerVersion": "0.7.6"`.
|
|
4323
|
+
*
|
|
4324
|
+
* @example Show the clip range on the seek bar while a panel is open
|
|
4325
|
+
* The span the user picked shows on the seek bar in the accent colour
|
|
4326
|
+
* while the plugin's tab is showing, and goes when it is not. Two point
|
|
4327
|
+
* marks would show the ends but not the span between them.
|
|
4328
|
+
* ```ts
|
|
4329
|
+
* declare const panel: Panel;
|
|
4330
|
+
* declare const clip: { start: number; end: number };
|
|
4331
|
+
* // ---cut---
|
|
4332
|
+
* const layer = plugin.timeline.layer();
|
|
4333
|
+
* // Only while the tab is on screen; [] takes the range away.
|
|
4334
|
+
* const show = () =>
|
|
4335
|
+
* layer.setRanges(
|
|
4336
|
+
* panel.state?.effective
|
|
4337
|
+
* ? [{ start: clip.start, end: clip.end, style: "selection", title: "Clip" }]
|
|
4338
|
+
* : [],
|
|
4339
|
+
* { ifCurrent: plugin.player.state },
|
|
4340
|
+
* );
|
|
4341
|
+
* panel.onChange(show);
|
|
4342
|
+
* ```
|
|
4343
|
+
*
|
|
4344
|
+
* @capability timeline.marks
|
|
4345
|
+
* @since 1.6.9
|
|
4346
|
+
*/
|
|
4347
|
+
setRanges(ranges: {
|
|
4348
|
+
start: number;
|
|
4349
|
+
end: number;
|
|
4350
|
+
style: RangeStyle;
|
|
4351
|
+
title?: string | Text;
|
|
4352
|
+
}[], options?: {
|
|
4353
|
+
ifCurrent?: PlayerState;
|
|
4354
|
+
}): void;
|
|
4276
4355
|
}
|
|
4277
4356
|
/**
|
|
4278
4357
|
* `plugin.timeline`: marks on the seek bar. Every plugin publishes one
|
|
@@ -4303,6 +4382,38 @@ export interface TimelineLayer extends Disposable {
|
|
|
4303
4382
|
export interface Timeline {
|
|
4304
4383
|
/** Every layer merged, by time (`playback.read`). Empty on a new source. */
|
|
4305
4384
|
readonly marks: readonly TimelineMark[];
|
|
4385
|
+
/**
|
|
4386
|
+
* Every layer's ranges merged, by start (`playback.read`; player 0.7.6:
|
|
4387
|
+
* on older players `ranges` does not exist and reads `undefined`). Empty
|
|
4388
|
+
* on a new source. Kept current like `marks` (with `events`): read it in
|
|
4389
|
+
* a {@link View.bind} function and the view follows it. `onChange` runs
|
|
4390
|
+
* only when the marks change.
|
|
4391
|
+
*
|
|
4392
|
+
* @example Draw the published ranges in a seek bar of your own
|
|
4393
|
+
* A bottom bar draws a band for every range on the seek bar, whoever
|
|
4394
|
+
* published it, as fractions of the duration.
|
|
4395
|
+
* ```ts
|
|
4396
|
+
* declare const bar: View;
|
|
4397
|
+
* // ---cut---
|
|
4398
|
+
* bar.bind(() => {
|
|
4399
|
+
* // Reading `ranges` here runs the binding again when they change.
|
|
4400
|
+
* const duration = plugin.player.state.duration ?? 0;
|
|
4401
|
+
* return {
|
|
4402
|
+
* ranges: duration
|
|
4403
|
+
* ? plugin.timeline.ranges.map((range) => ({
|
|
4404
|
+
* from: range.start / duration,
|
|
4405
|
+
* to: range.end / duration,
|
|
4406
|
+
* selection: range.style === "selection",
|
|
4407
|
+
* }))
|
|
4408
|
+
* : [],
|
|
4409
|
+
* };
|
|
4410
|
+
* });
|
|
4411
|
+
* ```
|
|
4412
|
+
*
|
|
4413
|
+
* @capability playback.read
|
|
4414
|
+
* @since 1.6.9
|
|
4415
|
+
*/
|
|
4416
|
+
readonly ranges: readonly TimelineRange[];
|
|
4306
4417
|
/**
|
|
4307
4418
|
* The merged list changed (needs `events`; at most every 100 ms).
|
|
4308
4419
|
*
|
|
@@ -6700,12 +6811,19 @@ export interface Osd {
|
|
|
6700
6811
|
*
|
|
6701
6812
|
* @param text - The template, 1–200 characters once translated and
|
|
6702
6813
|
* formatted; line breaks become spaces.
|
|
6814
|
+
* With `options.actions` (player 0.7.6) the message ends in up to two
|
|
6815
|
+
* buttons, each running one of the plugin's own commands
|
|
6816
|
+
* ({@link OsdShowOptions}). Older players show the message without them.
|
|
6817
|
+
*
|
|
6703
6818
|
* @param args - Values for its placeholders (player 0.4.3; older players
|
|
6704
|
-
* show the template as it is).
|
|
6819
|
+
* show the template as it is). Pass `null` with a `Text` and options.
|
|
6820
|
+
* @param options - Action buttons after the text (SDK 1.6.9).
|
|
6705
6821
|
* @returns Resolves once the message is on screen.
|
|
6706
6822
|
*
|
|
6707
6823
|
* @throws `invalid` (rejects) The shown text is empty, only white space,
|
|
6708
|
-
* or longer than 200 characters, or `args` is not a {@link TemplateArgs}
|
|
6824
|
+
* or longer than 200 characters, or `args` is not a {@link TemplateArgs};
|
|
6825
|
+
* or there are more than 2 actions, a title is empty or longer than 40
|
|
6826
|
+
* characters once translated, or a command is not one of the plugin's.
|
|
6709
6827
|
* @throws `unavailable` (rejects) The player runs without a user interface.
|
|
6710
6828
|
* @throws `capability` (rejects) The manifest does not declare `ui.osd`.
|
|
6711
6829
|
*
|
|
@@ -6719,9 +6837,28 @@ export interface Osd {
|
|
|
6719
6837
|
* await plugin.osd.show("Speed {}×", [plugin.player.state.rate]);
|
|
6720
6838
|
* ```
|
|
6721
6839
|
*
|
|
6840
|
+
* @example Offer "Show in Folder" after saving a file
|
|
6841
|
+
* The OSD reads "Saved clip.mp4 [Show in Folder]" for about 4 seconds,
|
|
6842
|
+
* longer while the pointer is on it. A click runs the plugin's own
|
|
6843
|
+
* `reveal` command, which knows the last saved file; the message goes.
|
|
6844
|
+
* ```ts
|
|
6845
|
+
* declare const path: string;
|
|
6846
|
+
* // ---cut---
|
|
6847
|
+
* let saved = path; // the command acts on the last saved file
|
|
6848
|
+
* plugin.commands.add({
|
|
6849
|
+
* id: "reveal",
|
|
6850
|
+
* title: "Show Last Saved File in Folder",
|
|
6851
|
+
* enabled: () => saved !== "",
|
|
6852
|
+
* run: () => plugin.system.reveal(saved),
|
|
6853
|
+
* });
|
|
6854
|
+
* await plugin.osd.show("Saved {}", [saved.split(/[\\/]/).pop() ?? saved], {
|
|
6855
|
+
* actions: [{ title: "Show in Folder", command: "reveal" }], // one button
|
|
6856
|
+
* });
|
|
6857
|
+
* ```
|
|
6858
|
+
*
|
|
6722
6859
|
* @since 1.0.0
|
|
6723
6860
|
*/
|
|
6724
|
-
show(text: TextValue, args?: TemplateArgs): Promise<void>;
|
|
6861
|
+
show(text: TextValue, args?: TemplateArgs | null, options?: OsdShowOptions): Promise<void>;
|
|
6725
6862
|
/**
|
|
6726
6863
|
* Create this plugin's information block.
|
|
6727
6864
|
*
|
|
@@ -6748,6 +6885,54 @@ export interface Osd {
|
|
|
6748
6885
|
*/
|
|
6749
6886
|
panel(): OsdPanel;
|
|
6750
6887
|
}
|
|
6888
|
+
/**
|
|
6889
|
+
* One button at the end of an OSD message ({@link Osd.show}, player 0.7.6).
|
|
6890
|
+
*
|
|
6891
|
+
* @since 1.6.9
|
|
6892
|
+
* @category UI
|
|
6893
|
+
*/
|
|
6894
|
+
export interface OsdAction {
|
|
6895
|
+
/**
|
|
6896
|
+
* The button's label: English source text the player translates with the
|
|
6897
|
+
* plugin's catalog, or a {@link Text}; 1–40 characters once translated
|
|
6898
|
+
* (`OSD_LIMITS.actionChars`). A verb, as on a menu item: "Show in
|
|
6899
|
+
* Folder", "Copy", "Undo".
|
|
6900
|
+
*/
|
|
6901
|
+
title: TextValue;
|
|
6902
|
+
/**
|
|
6903
|
+
* The local id of one of this plugin's own commands
|
|
6904
|
+
* ({@link CommandOptions.id}), declared when the message is shown. A
|
|
6905
|
+
* click runs it as if the user picked it from a menu: a disabled command
|
|
6906
|
+
* does nothing. The command takes no arguments, so it acts on what the
|
|
6907
|
+
* plugin remembers (the file it just saved).
|
|
6908
|
+
*/
|
|
6909
|
+
command: string;
|
|
6910
|
+
}
|
|
6911
|
+
/**
|
|
6912
|
+
* Options of {@link Osd.show} (SDK 1.6.9).
|
|
6913
|
+
*
|
|
6914
|
+
* @remarks
|
|
6915
|
+
* A message with actions stays about 4 seconds instead of 2, and does not
|
|
6916
|
+
* fade while the pointer is on it and moving: it goes 2 seconds after the
|
|
6917
|
+
* pointer leaves or rests (the player's idle time), and at the latest 30
|
|
6918
|
+
* seconds after it showed, hovered or not. A click on a button runs its
|
|
6919
|
+
* command and removes the message.
|
|
6920
|
+
* It is still one line: the buttons follow the text. Buttons take no
|
|
6921
|
+
* keyboard focus; screen readers can press them. A newer message, from any
|
|
6922
|
+
* plugin or the player, replaces the message with its buttons, and the
|
|
6923
|
+
* buttons go when the plugin stops. While its buttons show, the plugin is
|
|
6924
|
+
* not stopped for being idle.
|
|
6925
|
+
*
|
|
6926
|
+
* Players before 0.7.6 ignore `actions` and show the text alone for 2
|
|
6927
|
+
* seconds, so do not make an action the only way to reach something.
|
|
6928
|
+
*
|
|
6929
|
+
* @since 1.6.9
|
|
6930
|
+
* @category UI
|
|
6931
|
+
*/
|
|
6932
|
+
export interface OsdShowOptions {
|
|
6933
|
+
/** At most 2 buttons (`OSD_LIMITS.actions`), in order after the text. */
|
|
6934
|
+
actions?: readonly OsdAction[];
|
|
6935
|
+
}
|
|
6751
6936
|
/**
|
|
6752
6937
|
* The state of a filter, display layer or render options handle.
|
|
6753
6938
|
* After `failed` nothing retries automatically until the next `set()`,
|
|
@@ -8110,11 +8295,30 @@ export interface Snapshot extends Disposable {
|
|
|
8110
8295
|
* Start exporting; the job runs in the host's offline worker.
|
|
8111
8296
|
* One snapshot can be exported several times.
|
|
8112
8297
|
*
|
|
8298
|
+
* A **stream copy** (`format: "copy"`, player 0.7.6) writes the video
|
|
8299
|
+
* stream and the snapshot's audio track as they are in the file, without
|
|
8300
|
+
* decoding: lossless and about as fast as the disk, but a copy can only
|
|
8301
|
+
* begin on a keyframe. It starts at the video's last keyframe at or before
|
|
8302
|
+
* the snapshot time (its first keyframe when the video itself starts
|
|
8303
|
+
* later; keyframes an MP4 edit list discards do not count) and is cut at
|
|
8304
|
+
* `end`, where it may run a few frames past `end`. An open GOP can write
|
|
8305
|
+
* a frame or two that present before the keyframe; `start` includes them.
|
|
8306
|
+
* The finished job's `outcome` gives the `start` and `end` actually
|
|
8307
|
+
* written; a plugin that previews the cut (with `plugin.tools.run`
|
|
8308
|
+
* and ffprobe's packet flags, say) shows those once the job is done.
|
|
8309
|
+
* Copies need a raw snapshot (effects and burned-in subtitles need a
|
|
8310
|
+
* re-encode) and leave subtitle tracks and chapters out. `auto` writes
|
|
8311
|
+
* MP4 for H.264, HEVC, AV1 or MPEG-4 video with AAC, MP3, AC-3, E-AC-3 or
|
|
8312
|
+
* ALAC audio (or none), and MKV for anything else. A copy the player
|
|
8313
|
+
* cannot make (no video stream, a cover image, an external audio track,
|
|
8314
|
+
* codecs `mp4` cannot hold) ends `failed` with the code `unsupported`.
|
|
8315
|
+
*
|
|
8113
8316
|
* @capability media.read and media.control
|
|
8114
8317
|
* @throws `released` (rejects) The snapshot was already released.
|
|
8115
8318
|
* @throws `invalid` (rejects) Bad options (`quality` outside 1–100, a
|
|
8116
8319
|
* relative destination, a clip `end` not after the snapshot time or
|
|
8117
|
-
* past the duration
|
|
8320
|
+
* past the duration, an {@link ExportEncoding} value outside its set,
|
|
8321
|
+
* `copy` from a processed snapshot).
|
|
8118
8322
|
* @throws `busy` (rejects) This plugin runs 2 exports (or the player 8).
|
|
8119
8323
|
* @throws `unavailable` (rejects) No FFmpeg, or the export worker could not
|
|
8120
8324
|
* start.
|
|
@@ -8134,18 +8338,48 @@ export interface Snapshot extends Disposable {
|
|
|
8134
8338
|
* if (end.status !== "completed") await plugin.osd.show("Export {}", [end.status]);
|
|
8135
8339
|
* ```
|
|
8136
8340
|
*
|
|
8341
|
+
* @example Cut without re-encoding and report where it starts
|
|
8342
|
+
* The clip is copied as it is in the file. A copy starts on a keyframe,
|
|
8343
|
+
* so the OSD says where it actually starts; a file the player cannot
|
|
8344
|
+
* copy ends with `unsupported`, and the plugin offers a re-encode instead.
|
|
8345
|
+
* ```ts
|
|
8346
|
+
* declare const snapshot: Snapshot;
|
|
8347
|
+
* // ---cut---
|
|
8348
|
+
* const job = await snapshot.export({ format: "copy", destination: "/tmp/clip.mp4", end: snapshot.time + 20 });
|
|
8349
|
+
* const state = await job.done;
|
|
8350
|
+
* const outcome = state.outcome;
|
|
8351
|
+
* // The copy starts on a keyframe: outcome.start says where.
|
|
8352
|
+
* if (outcome && "destination" in outcome)
|
|
8353
|
+
* await plugin.osd.show("Saved {} from {} s", [outcome.destination, (outcome.start ?? snapshot.time).toFixed(1)]);
|
|
8354
|
+
* // Codecs the container cannot hold, a cover image, …
|
|
8355
|
+
* else if (outcome && "error" in outcome && outcome.error.code === "unsupported")
|
|
8356
|
+
* await plugin.osd.show("Fast cut is not possible for this file");
|
|
8357
|
+
* ```
|
|
8358
|
+
*
|
|
8137
8359
|
* @since 1.0.0
|
|
8138
8360
|
*/
|
|
8139
8361
|
export(options: ExportOptions): Promise<ExportJob>;
|
|
8140
8362
|
}
|
|
8141
8363
|
/**
|
|
8142
8364
|
* One frame as PNG, JPEG (`quality` 1–100, default 90) or BMP (24-bit), or
|
|
8143
|
-
* a clip as MP4
|
|
8365
|
+
* a clip: re-encoded as MP4 (`mp4`, frame-accurate, optional
|
|
8366
|
+
* {@link ExportEncoding}) or copied without re-encoding (`copy`, player
|
|
8367
|
+
* 0.7.6, starting on a keyframe).
|
|
8144
8368
|
*
|
|
8145
8369
|
* `destination` is never overwritten if it exists. The extension does not
|
|
8146
|
-
* choose the format
|
|
8147
|
-
*
|
|
8148
|
-
*
|
|
8370
|
+
* choose the format (a copy's extension is set to its container).
|
|
8371
|
+
* `sampleTime` picks the frame (default: the snapshot time); `end` is the
|
|
8372
|
+
* clip's end in source seconds. On Windows the export worker needs FFmpeg
|
|
8373
|
+
* 6.1 or newer.
|
|
8374
|
+
*
|
|
8375
|
+
* **Which players have `copy`, `encoding` and the progress and range of
|
|
8376
|
+
* a job**: player 0.7.6 and later. Declare `"minPlayerVersion": "0.7.6"`,
|
|
8377
|
+
* or, to keep older players and offer these only where they exist, compare
|
|
8378
|
+
* {@link AppInfo.version | plugin.app.version} with `0.7.6`, as for any
|
|
8379
|
+
* newer API. Do not probe by trying: on an older player the `plugin`
|
|
8380
|
+
* object comes from that player's own, older SDK, which rejects `copy` with
|
|
8381
|
+
* `invalid` but builds a re-encode request without the `encoding` it does
|
|
8382
|
+
* not know, so the clip is encoded with the fixed settings and no error.
|
|
8149
8383
|
*
|
|
8150
8384
|
* @since 1.0.0
|
|
8151
8385
|
* @category Media
|
|
@@ -8167,19 +8401,132 @@ export type ExportOptions = {
|
|
|
8167
8401
|
/** Integer 1–100, default 90. */
|
|
8168
8402
|
quality?: number;
|
|
8169
8403
|
} | {
|
|
8170
|
-
/** A clip from the snapshot time. */
|
|
8404
|
+
/** A clip from the snapshot time, re-encoded as H.264 + AAC. */
|
|
8171
8405
|
format: "mp4";
|
|
8172
8406
|
/** Absolute path of the new file. */
|
|
8173
8407
|
destination: string;
|
|
8174
8408
|
/** Clip end, source seconds. */
|
|
8175
8409
|
end: number;
|
|
8410
|
+
/**
|
|
8411
|
+
* Encoder settings (player 0.7.6); without it, the settings before
|
|
8412
|
+
* they existed.
|
|
8413
|
+
*
|
|
8414
|
+
* @since 1.6.10
|
|
8415
|
+
*/
|
|
8416
|
+
encoding?: ExportEncoding;
|
|
8417
|
+
} | {
|
|
8418
|
+
/**
|
|
8419
|
+
* A clip copied stream by stream, without re-encoding (player 0.7.6):
|
|
8420
|
+
* lossless and about as fast as the disk. It starts on the video
|
|
8421
|
+
* keyframe at or before the snapshot time; see {@link Snapshot.export}.
|
|
8422
|
+
*
|
|
8423
|
+
* @since 1.6.10
|
|
8424
|
+
*/
|
|
8425
|
+
format: "copy";
|
|
8426
|
+
/**
|
|
8427
|
+
* Absolute path of the new file. The player gives it the extension of
|
|
8428
|
+
* the container it writes (`.mp4` or `.mkv`); the finished job's
|
|
8429
|
+
* `outcome.destination` is the actual path.
|
|
8430
|
+
*/
|
|
8431
|
+
destination: string;
|
|
8432
|
+
/** Clip end, source seconds. */
|
|
8433
|
+
end: number;
|
|
8434
|
+
/**
|
|
8435
|
+
* `auto` (default): MP4 when the codecs fit it, otherwise MKV. `mp4`
|
|
8436
|
+
* fails with `unsupported` when they do not; `mkv` always writes MKV.
|
|
8437
|
+
*/
|
|
8438
|
+
container?: "auto" | "mp4" | "mkv";
|
|
8176
8439
|
};
|
|
8440
|
+
/**
|
|
8441
|
+
* The encoder settings of a re-encoded clip (`format: "mp4"`, player
|
|
8442
|
+
* 0.7.6): a closed set of values, never encoder arguments. Every field is
|
|
8443
|
+
* optional; the defaults are the settings clips had before: CRF 18, the
|
|
8444
|
+
* fastest speed, the source's size and frame rate, AAC 192 kb/s, one thread.
|
|
8445
|
+
* A value outside its set rejects the export with `invalid`. `videoBitrate:
|
|
8446
|
+
* "source"` replaces the CRF with the source's bitrate, for a "Same as
|
|
8447
|
+
* source" preset.
|
|
8448
|
+
*
|
|
8449
|
+
* @example A small file
|
|
8450
|
+
* A 720p, 30 fps clip at a lower quality, encoded with the cores the player
|
|
8451
|
+
* can spare.
|
|
8452
|
+
* ```ts
|
|
8453
|
+
* declare const snapshot: Snapshot;
|
|
8454
|
+
* // ---cut---
|
|
8455
|
+
* // Each field is a ceiling or a choice from a closed set.
|
|
8456
|
+
* const job = await snapshot.export({
|
|
8457
|
+
* format: "mp4",
|
|
8458
|
+
* destination: "/tmp/small.mp4",
|
|
8459
|
+
* end: snapshot.time + 30,
|
|
8460
|
+
* encoding: { crf: 26, maxHeight: 720, fps: 30, audioBitrate: 128, threads: "auto" },
|
|
8461
|
+
* });
|
|
8462
|
+
* ```
|
|
8463
|
+
*
|
|
8464
|
+
* @since 1.6.10
|
|
8465
|
+
* @category Media
|
|
8466
|
+
*/
|
|
8467
|
+
export interface ExportEncoding {
|
|
8468
|
+
/**
|
|
8469
|
+
* H.264 quality, an integer 14–30 (x264's CRF): lower is a better picture
|
|
8470
|
+
* and a bigger file. Default 18.
|
|
8471
|
+
*/
|
|
8472
|
+
crf?: number;
|
|
8473
|
+
/**
|
|
8474
|
+
* Encoding effort: `faster` (default), `balanced` or `smaller` (slower,
|
|
8475
|
+
* a smaller file at the same quality); x264's `veryfast`, `medium` and
|
|
8476
|
+
* `slow`.
|
|
8477
|
+
*/
|
|
8478
|
+
speed?: "faster" | "balanced" | "smaller";
|
|
8479
|
+
/**
|
|
8480
|
+
* The tallest picture: a taller one is scaled down to this height,
|
|
8481
|
+
* keeping its shape; a shorter one is left as it is. Default: the
|
|
8482
|
+
* source's size.
|
|
8483
|
+
*/
|
|
8484
|
+
maxHeight?: 1080 | 720 | 480;
|
|
8485
|
+
/**
|
|
8486
|
+
* The highest frame rate: a faster source drops frames to it; a slower
|
|
8487
|
+
* one is left as it is. Default: the source's rate.
|
|
8488
|
+
*/
|
|
8489
|
+
fps?: 60 | 30;
|
|
8490
|
+
/** AAC audio bitrate in kb/s. Default 192. */
|
|
8491
|
+
audioBitrate?: 128 | 192 | 256;
|
|
8492
|
+
/**
|
|
8493
|
+
* `single` (default) encodes on one thread, leaving the other cores to
|
|
8494
|
+
* playback. `auto` lets the player use more: half the logical
|
|
8495
|
+
* processors, at most 8, so playback and the interface keep their rate.
|
|
8496
|
+
* From player 0.7.6 every export runs at below-normal priority (as
|
|
8497
|
+
* HandBrake and OBS run their encoders), with either setting.
|
|
8498
|
+
*/
|
|
8499
|
+
threads?: "single" | "auto";
|
|
8500
|
+
/**
|
|
8501
|
+
* `source`: aim at the source video's average bitrate instead of a
|
|
8502
|
+
* quality level (peaks up to 1.5 times it), so the file comes out about
|
|
8503
|
+
* the size of that stretch of the source: a "Same as source" preset. Not
|
|
8504
|
+
* together with `crf` (`invalid`). A source whose bitrate the player
|
|
8505
|
+
* cannot tell ends the job `failed` with `unsupported`.
|
|
8506
|
+
*/
|
|
8507
|
+
videoBitrate?: "source";
|
|
8508
|
+
}
|
|
8177
8509
|
/**
|
|
8178
8510
|
* An export job's state. `status` is `queued`, `running`, `cancelling`,
|
|
8179
8511
|
* `completed`, `failed` or `cancelled`; once terminal, `outcome` is
|
|
8180
8512
|
* `{ destination }`, `{ error: { code, message } }` or `{ cancelled: true }`.
|
|
8181
8513
|
* `facts` describes what the worker produced (versions, range, streams).
|
|
8182
8514
|
*
|
|
8515
|
+
* From player 0.7.6:
|
|
8516
|
+
* - a finished clip's `outcome` also has `start` and `end`, the source
|
|
8517
|
+
* times of its first frame and of the end of its last frame as written: a
|
|
8518
|
+
* copy's earliest written frame (its keyframe, or the leading frames of
|
|
8519
|
+
* an open GOP) and actual end; a re-encode's start and start
|
|
8520
|
+
* plus the encoded length.
|
|
8521
|
+
* - `progress` is `{ done, total }` in seconds of the clip while FFmpeg
|
|
8522
|
+
* writes it (at most 4 updates a second; the SDK reads it about every
|
|
8523
|
+
* 350 ms), `null` while unknown: before writing starts, and always for a
|
|
8524
|
+
* frame. Show an indeterminate bar then. After `done` reaches `total` the
|
|
8525
|
+
* player still checks the file before the job completes.
|
|
8526
|
+
* - `error.code` is `export-failed`, or `unsupported` for an export the
|
|
8527
|
+
* player cannot make as asked (a stream copy it cannot make, a
|
|
8528
|
+
* `videoBitrate: "source"` whose source bitrate it cannot tell).
|
|
8529
|
+
*
|
|
8183
8530
|
* @since 1.0.0
|
|
8184
8531
|
* @category Media
|
|
8185
8532
|
*/
|
package/dist/wire.d.ts
CHANGED
|
@@ -262,6 +262,19 @@ export interface WireMark {
|
|
|
262
262
|
title: string | null;
|
|
263
263
|
plugin: string;
|
|
264
264
|
}
|
|
265
|
+
/** One timeline range as the player merges them (player 0.7.6). */
|
|
266
|
+
export interface WireRange {
|
|
267
|
+
start: number;
|
|
268
|
+
end: number;
|
|
269
|
+
style: "selection" | "region";
|
|
270
|
+
title: string | null;
|
|
271
|
+
plugin: string;
|
|
272
|
+
}
|
|
273
|
+
/** `timeline.marks` and `timeline.changed`; `ranges` from player 0.7.6. */
|
|
274
|
+
export interface WireTimeline {
|
|
275
|
+
marks: WireMark[];
|
|
276
|
+
ranges?: WireRange[];
|
|
277
|
+
}
|
|
265
278
|
/** The backdrop as `layout.state` reports it; `null` without one. */
|
|
266
279
|
export interface LayoutBackdrop {
|
|
267
280
|
owner: number;
|
|
@@ -568,9 +581,7 @@ export declare function createWire(transport: Transport): {
|
|
|
568
581
|
};
|
|
569
582
|
/** Mark layers on the seek bar (`plugin.timeline`). */
|
|
570
583
|
timeline: {
|
|
571
|
-
marks: () => Promise<
|
|
572
|
-
marks: WireMark[];
|
|
573
|
-
}>;
|
|
584
|
+
marks: () => Promise<WireTimeline>;
|
|
574
585
|
set: (marks: {
|
|
575
586
|
time: number;
|
|
576
587
|
kind: string;
|
|
@@ -578,6 +589,15 @@ export declare function createWire(transport: Transport): {
|
|
|
578
589
|
}[], sourceId?: number) => Promise<{
|
|
579
590
|
applied: boolean;
|
|
580
591
|
}>;
|
|
592
|
+
/** Player 0.7.6. */
|
|
593
|
+
setRanges: (ranges: {
|
|
594
|
+
start: number;
|
|
595
|
+
end: number;
|
|
596
|
+
style: string;
|
|
597
|
+
title?: string | TextJson;
|
|
598
|
+
}[], sourceId?: number) => Promise<{
|
|
599
|
+
applied: boolean;
|
|
600
|
+
}>;
|
|
581
601
|
};
|
|
582
602
|
/** Chapters and the A-B loop of the current media. */
|
|
583
603
|
marks: {
|
|
@@ -666,7 +686,10 @@ export declare function createWire(transport: Transport): {
|
|
|
666
686
|
};
|
|
667
687
|
/** `text` is a template in the plugin's catalog, formatted with `args` (player 0.4.3). */
|
|
668
688
|
osd: {
|
|
669
|
-
show: (text: TextValueJson, args?: TemplateArgsJson
|
|
689
|
+
show: (text: TextValueJson, args?: TemplateArgsJson, actions?: {
|
|
690
|
+
title: TextValueJson;
|
|
691
|
+
command: string;
|
|
692
|
+
}[]) => Promise<void>;
|
|
670
693
|
panel: (text: TextValueJson | null, args?: TemplateArgsJson, maxColumns?: number) => Promise<void>;
|
|
671
694
|
};
|
|
672
695
|
layout: {
|
|
@@ -775,6 +798,11 @@ export declare function createWire(transport: Transport): {
|
|
|
775
798
|
} | {
|
|
776
799
|
kind: "rawMp4" | "processedMp4";
|
|
777
800
|
end: number;
|
|
801
|
+
encoding?: import("./media.ts").WireEncoding;
|
|
802
|
+
} | {
|
|
803
|
+
kind: "rawCopy";
|
|
804
|
+
end: number;
|
|
805
|
+
container?: "auto" | "mp4" | "mkv";
|
|
778
806
|
})) => Promise<{
|
|
779
807
|
jobId: number;
|
|
780
808
|
status: "queued";
|
package/dist/wire.js
CHANGED
|
@@ -156,6 +156,11 @@ export function createWire(transport) {
|
|
|
156
156
|
marks,
|
|
157
157
|
...(sourceId === undefined ? {} : { sourceId }),
|
|
158
158
|
}),
|
|
159
|
+
/** Player 0.7.6. */
|
|
160
|
+
setRanges: (ranges, sourceId) => transport.request("timeline.setRanges", {
|
|
161
|
+
ranges,
|
|
162
|
+
...(sourceId === undefined ? {} : { sourceId }),
|
|
163
|
+
}),
|
|
159
164
|
},
|
|
160
165
|
/** Chapters and the A-B loop of the current media. */
|
|
161
166
|
marks: {
|
|
@@ -245,9 +250,11 @@ export function createWire(transport) {
|
|
|
245
250
|
},
|
|
246
251
|
/** `text` is a template in the plugin's catalog, formatted with `args` (player 0.4.3). */
|
|
247
252
|
osd: {
|
|
248
|
-
show: (text, args) => transport.request("osd.show", {
|
|
253
|
+
show: (text, args, actions) => transport.request("osd.show", {
|
|
249
254
|
text,
|
|
250
255
|
...(args === undefined ? {} : { args }),
|
|
256
|
+
// Player 0.7.6; older players ignore it.
|
|
257
|
+
...(actions === undefined ? {} : { actions }),
|
|
251
258
|
}),
|
|
252
259
|
panel: (text, args, maxColumns) => transport.request("osd.panel", {
|
|
253
260
|
text,
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vestra Player SDK
|
|
2
2
|
|
|
3
|
-
> @vestra-player/sdk 1.6.
|
|
3
|
+
> @vestra-player/sdk 1.6.10: write plugins for Vestra Player in TypeScript (plugin API `vestra/1`). A plugin is a folder with `manifest.json` and an entry module whose default export `activate(plugin)` the player calls. The documentation is a tree of short pages read by path, like `go doc`: the same pages and API as https://vestrasys.github.io/vestra-player-sdk/.
|
|
4
4
|
|
|
5
5
|
## Read the documentation
|
|
6
6
|
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
## API
|
|
30
30
|
|
|
31
31
|
- `/API/Commands`: 15 entries: CommandState, Bindable, Condition, When, MenuId, BarButtonIcon, CommandButton, CommandOptions, Command, Commands, HostAction, Host, BindingRef, BindingInfo, Bindings
|
|
32
|
-
- `/API/Media`:
|
|
32
|
+
- `/API/Media`: 7 entries: CaptureOptions, Snapshot, ExportOptions, ExportEncoding, JobState, ExportJob, MediaCapture
|
|
33
33
|
- `/API/Packages`: 10 entries: PackageManifest, PackageSource, InstallOptions, PackageCandidate, PackageChangeStatus, PackageChange, PluginInfo, PluginDisplay, PluginWarning, Packages
|
|
34
|
-
- `/API/Playback`:
|
|
34
|
+
- `/API/Playback`: 20 entries: MarkKind, TimelineMark, RangeStyle, TimelineRange, TimelineLayer, Timeline, AudioTrack, PlayerState, AudioDevice, Chapter, Conditional, Media, Scrub, StreamFrame, StreamOptions,…
|
|
35
35
|
- `/API/Plugin`: 13 entries: Disposable, PluginError, Scope, Plugin, StopReason, DisposeInfo, WindowState, MainWindow, AppInfo, App, SystemInfo, DisplayInfo, Json
|
|
36
36
|
- `/API/Processing`: 18 entries: FilterStatus, VideoFilter, AudioFilter, ViewSettings, VideoView, RenderSettings, RenderOptions, VideoShaders, UpscalingReasonCode, UpscalingReason, UpscalingDecodingCause,…
|
|
37
37
|
- `/API/Services`: 15 entries: Clipboard, Power, ToolResult, Tools, JsonStore, Storage, SessionStorage, System, Secrets, RenderingMode, RenderingPreference, RenderingReasonCode, RenderingReason, RenderingState,…
|
|
38
38
|
- `/API/Sources`: 7 entries: ResolverOptions, WebResolverOptions, FileResolverOptions, ResolvedStream, Resolver, Resolvers, ResolvedMedia
|
|
39
39
|
- `/API/Subtitles`: 5 entries: SubtitleTrack, SubtitlesState, SubtitleConditional, Subtitles, SubtitleStyle
|
|
40
|
-
- `/API/UI`:
|
|
41
|
-
- `/API/Testing`:
|
|
40
|
+
- `/API/UI`: 44 entries: ImageRef, ImageOptions, MarkdownRef, MarkdownOptions, PropValue, PropData, Props, Ui, View, Window, Windows, SlotState, SlotPanel, PanelState, PanelSlot, SidebarSlot, Panel,…
|
|
41
|
+
- `/API/Testing`: 33 entries: CapabilityInfo, CAPABILITY_INFO, MESSAGE_LIMIT, COMMAND_LIMITS, MENU_IDS, BAR_BUTTON_ICONS, BAR_BUTTON_LIMITS, BuiltinSubmenu, BUILTIN_SUBMENUS, BuiltinCommand, BUILTIN_COMMANDS, LOCALES,…
|
|
42
42
|
- `/API/Docs`: 4 entries: DOCS_PAGE_BYTES, DocsOptions, docs, searchDocs
|