docs(site): [SITE-04] document thumbnail extraction and subtitle selection

This commit is contained in:
Harvey Zhao committed 2026-09-16 02:57:15 +08:00
1 parent 94adb77fd6
commit 3317230f2e
94 files changed
+4654 -1908

No files matched your search

+293
View File
@@ -4931,6 +4931,95 @@ Named types include `Option`, `UpdateOption`, the old `Result`, `RuntimeResult`
Actual desktop tests cover audio/video playback, pause, seeking, updates and destruction. They do not establish support for every mobile device, proxy player, audio format or long-running synchronization combination. Check browser decoding capabilities and the project's validation records for the applicable scope.
===== packages/artplayer-vitepress/docs/en/plugin/auto-thumbnail.md =====
# Automatic Thumbnails
[简体中文](../../plugin/auto-thumbnail.md)
Read frames with a separate video element, progressively create a JPEG sprite sheet in the browser, and update the player's progress-bar thumbnails. This page describes the unreleased refactor branch; the online example and unpinned npm/CDN packages are not the current candidate.
## Installation and example
```sh
yarn add artplayer artplayer-plugin-auto-thumbnail
```
```js
import Artplayer from 'artplayer';
import artplayerPluginAutoThumbnail from 'artplayer-plugin-auto-thumbnail';
```
For script usage, load ArtPlayer before `dist/artplayer-plugin-auto-thumbnail.js`; the global is `artplayerPluginAutoThumbnail`. This preserves the [original online example](https://artplayer.org/?libs=./uncompiled/artplayer-plugin-auto-thumbnail/index.js&example=auto.thumbnail). The factory requires an options object; use `{}` for defaults.
<div className="run-code" data-libs="./uncompiled/artplayer-plugin-auto-thumbnail/index.js">▶ Run Code</div>
```js
// npm i artplayer-plugin-auto-thumbnail
// import artplayerPluginAutoThumbnail from 'artplayer-plugin-auto-thumbnail';
const art = new Artplayer({
container: '.artplayer-app',
url: '/assets/sample/video.mp4',
plugins: [
artplayerPluginAutoThumbnail({
//
}),
],
})
```
## Options and generation
| Field | Type | Default | Meaning |
| --- | --- | --- | --- |
| `url` | `string` | Current `art.option.url` | Media read by the separate decoder; an explicit nonempty URL takes precedence |
| `width` | `number` | `160` | Width of each thumbnail cell in pixels |
| `number` | `number` | `100` | Target sample count; normally use a positive integer |
| `scale` | `number` | `1` | Preview display scale passed to the player; does not reduce the generated canvas size |
| `height` | `number` | Derived from video aspect ratio | Historical ignored option accepted by the precise runtime type |
Defaults preserve `value || default` behavior: `width: 0`, for example, uses 160. After fallback, width and number must convert to finite positive values. Historical JavaScript numeric strings and fractional counts are retained, but typed examples use numbers and positive integer counts. Height is `Math.floor(width * videoHeight / videoWidth)`, with ten columns per row. Media needs finite positive duration and dimensions; infinite-duration live streams do not qualify.
Sampling uses `duration * index / number`, starting at index 0 and excluding the media endpoint. Duration and sheet dimensions are captured at the separate video's metadata event. Each drawn cell is followed by encoding the whole JPEG and publishing a new `art.thumbnails` configuration. Later cells can therefore remain empty while generation is in progress.
Larger width and count increase canvas allocation, decoding and repeated encoding cost. Dimension checks are not a browser memory budget. Choose values appropriate for the media and device, or use prebuilt [VTT thumbnails](./vtt-thumbnail.md).
## Registration, source changes and cleanup
The factory returns an asynchronous registrar whose result contains only `name: 'artplayerPluginAutoThumbnail'`. Its Promise resolves after installing player subscriptions, before extraction. It does not confirm that media was read successfully. There are no public progress, extraction-completion, update, stop or destroy methods.
Each `video:loadedmetadata` event starts extraction and rereads the original options object, so later mutations affect the next run. Installation does not replay metadata events that already occurred. `restart` cancels the old job; a subsequent metadata event starts another. Old frame or encoding callbacks cannot overwrite the newer job.
Metadata acquisition, each frame wait and each JPEG encoding operation have separate 30-second limits, not one total extraction deadline. Media, canvas and encoding failures clean up the current job, report through `console.warn` and retain the last usable preview. They cannot reject an already-resolved registration Promise.
Completion or cancellation releases the separate video and canvas. The final image URL remains until a usable replacement is installed or the player is destroyed. Only generated URLs are revoked; application-owned thumbnail URLs are not. Destroy also removes subscriptions. Directly invoking a retained registrar after player destruction returns the same name without allocating a decoder; it does not override core `plugins.add()` destruction checks.
## Media access and browser limits
The separate video uses `crossOrigin = 'anonymous'`, is muted, and is never explicitly played. Media must support native browser loading and Canvas pixel access, including appropriate server CORS headers. Player custom loaders, SDKs, request headers and proxies are not installed into this decoder; supply a directly readable media `url` when necessary.
The hidden video is attached to the document with a rendered box; it is not an additional visible player. Actual decoding and first-frame correctness remain browser-dependent. The current Windows WebKit first-frame issue is still unresolved. Types, navigation and other browser results do not close it or establish physical Safari/mobile support.
## Compatible TypeScript entries
The root and `/legacy` preserve npm 1.1.0 synchronous result declarations and required options; the root Option does not contain height. For the actual Promise, historical height input or `.default` self-alias types, use `/runtime`, which loads the same implementation:
```ts
import type Artplayer from 'artplayer';
import autoThumbnail from 'artplayer-plugin-auto-thumbnail/runtime';
import type { Option, Result } from 'artplayer-plugin-auto-thumbnail/runtime';
const options: Option = { width: 160, number: 100, scale: 1 };
async function registerThumbnails(art: Artplayer): Promise<Result> {
return await autoThumbnail(options)(art); // Registration only, not extraction completion.
}
```
Runtime exports `Option`, `Result`, `Factory` and `RuntimeFactory`. Factory is a plain asynchronous factory; RuntimeFactory adds the writable `.default` self-alias. CommonJS runtime supports direct and `.default(...)` calls. Earlier 1.0.x `export =` and height declarations differ from 1.1.0; use the runtime `import = require` form when migrating those imports. Root NodeNext namespace behavior remains unchanged; use runtime for an accurate callable default import. Classic Node10 default imports need `esModuleInterop`.
===== packages/artplayer-vitepress/docs/en/plugin/chapter.md =====
# Video chapters
@@ -5812,6 +5901,210 @@ Public `Option`, `Config`, `QualityLevel`, `AudioTrack` and `Result` types are e
The refactor tests Hls.js 1.5.17 and 1.7.2 with local media and real workers; these are tested points, not a newly declared supported range. Firefox grouped-stream crashes and a separate switching stall remain under investigation. Windows Playwright WebKit lacks the MSE path used in those tests; it does not establish Safari/iOS native-HLS acceptance. Verify your actual streams and target devices before adopting the unpublished refactor.
===== packages/artplayer-vitepress/docs/en/plugin/multiple-subtitles.md =====
# Multiple Subtitles
[简体中文](../../plugin/multiple-subtitles.md)
Download several subtitle files, retain their individual cue times, and merge selected tracks into the player's subtitles. Display multiple languages and select or reorder them by name. This page describes the unreleased refactor branch; online examples and unpinned packages are not the current candidate.
## Installation and example
```sh
yarn add artplayer artplayer-plugin-multiple-subtitles
```
```js
import Artplayer from 'artplayer';
import artplayerPluginMultipleSubtitles from 'artplayer-plugin-multiple-subtitles';
```
For script usage, load ArtPlayer before `dist/artplayer-plugin-multiple-subtitles.js`; the global is `artplayerPluginMultipleSubtitles`. The following preserves the [online example](https://artplayer.org/?libs=./uncompiled/artplayer-plugin-multiple-subtitles/index.js&example=multiple.subtitles), including its selection menu and styles. The application configures that menu; the plugin does not create one automatically.
<div className="run-code" data-libs="./uncompiled/artplayer-plugin-multiple-subtitles/index.js">▶ Run Code</div>
```js
// npm i artplayer-plugin-multiple-subtitles
// import artplayerPluginMultipleSubtitles from 'artplayer-plugin-multiple-subtitles';
var art = new Artplayer({
container: '.artplayer-app',
url: '/assets/sample/video.mp4',
setting: true,
plugins: [
artplayerPluginMultipleSubtitles({
subtitles: [
{
name: 'chinese',
url: '/assets/sample/subtitle.cn.srt',
},
{
name: 'japanese',
url: '/assets/sample/subtitle.jp.srt',
},
],
}),
],
settings: [
{
width: 200,
html: 'Subtitle',
tooltip: 'Double',
icon: '<img width="22" height="22" src="/assets/img/subtitle.svg">',
selector: [
{
html: 'Display',
tooltip: 'Show',
switch: true,
onSwitch(item) {
item.tooltip = item.switch ? 'Hide' : 'Show'
// 显示/隐藏字幕
// Show/hide subtitles
art.subtitle.show = !item.switch
return !item.switch
},
},
{
html: 'Reverse',
tooltip: 'Off',
switch: false,
onSwitch(item) {
item.tooltip = item.switch ? 'Off' : 'On'
// 修改字幕顺序
// Change the order of subtitles
if (item.switch) {
art.plugins.multipleSubtitles.tracks(['chinese', 'japanese'])
}
else {
art.plugins.multipleSubtitles.tracks(['japanese', 'chinese'])
}
return !item.switch
},
},
{
default: true,
html: 'Double',
name: 'double',
},
{
html: 'Chinese',
name: 'chinese',
},
{
html: 'Japanese',
name: 'japanese',
},
],
onSelect(item) {
if (item.name === 'double') {
// 重置字幕
// Reset subtitles
art.plugins.multipleSubtitles.reset()
}
else {
// 显示单个字幕
// Show single subtitle
art.plugins.multipleSubtitles.tracks([item.name])
}
return item.html
},
},
],
})
// 自定义你自己的样式,请勿复制以下代码
// Customize your own style, please do not copy the following code
const style = `
.art-subtitle-chinese {
color: red;
font-size: 18px;
}
.art-subtitle-japanese {
color: yellow;
font-size: 12px;
}
`
const $style = document.getElementById('artplayer-subtitle-style')
if ($style) {
$style.textContent = style
}
else {
const $style = document.createElement('style')
$style.id = 'artplayer-subtitle-style'
$style.textContent = style
document.head.appendChild($style)
}
```
## Track options
The factory requires an options object containing a `subtitles` array. Runtime `{}` uses an empty array; historical root types still require the field. Each `TrackOption` has these fields:
| Field | Type | Default or behavior |
| --- | --- | --- |
| `url` | `string` | Subtitle URL loaded with browser fetch; provide a valid URL despite the historical optional type |
| `name` | `string` | Selection and CSS name; use a unique simple identifier such as `chinese` |
| `type` | `'vtt' \| 'srt' \| 'ass'` | Explicit value takes precedence over the URL extension |
| `encoding` | `string` | Defaults to `'utf-8'`, passed to TextDecoder |
| `onParser` | `(...args: object[]) => object` | Retained historical declaration; the implementation does not call it |
Files download concurrently, then decode and merge. Cross-origin servers must allow fetch. SRT and ASS use core conversion utilities to produce VTT; full ASS layout and animation are not preserved. Use JASSUB when full ASS rendering is needed. Unknown types produce empty content. Parsing is best-effort, and diagnostics do not necessarily discard all usable cues.
Unsuccessful HTTP responses and thrown decoding/conversion errors reject registration and release sibling requests. Track metadata is read from the original configuration after downloads, not a deep copy; avoid mutating the array or track objects during installation.
## Registration and selection
Registration is asynchronous and returns `{ name: 'multipleSubtitles', tracks, reset }`. The registered result is `art.plugins.multipleSubtitles`, not the global factory name. Await registration before directly using its result; readiness of that result does not guarantee the player has finished loading subtitles.
| Call | Result |
| --- | --- |
| `tracks(['chinese', 'japanese'])` | Select tracks in the caller's name order |
| `tracks(['japanese'])` | Select only that track |
| `tracks()` or `tracks([])` | Clear the selection |
| `reset()` | Restore all originally downloaded tracks in their original order |
Both methods return `undefined` synchronously. Unknown names retain the historical synchronous TypeError. Repeated names select the first matching track on each lookup without deduplication; use unique configured names. Merging follows selection order but does not rewrite cue timestamps, so reordering cannot synchronize mistimed translations.
Each selection creates a new VTT Blob URL, initializes player subtitles and releases the previous owned URL. Async host installation failures warn and clean up the failed resource; void methods cannot be awaited for subtitle readiness. Video source changes do not refetch tracks, and reset does not reload the server files. There is no public update, reload or separate destroy method.
## Styling and resource ownership
Content is wrapped with `.art-subtitle-<name>`, such as the example's `.art-subtitle-chinese` and `.art-subtitle-japanese`. Names enter HTML class markup; use application-defined simple identifiers. Selection sets `art.option.subtitle.escape = false` and supplies the subtitle URL, type and onVttLoad. Establish ownership when combining it with other subtitle managers, since later writes replace that configuration.
Literal cue text, entities and supported markup are handled separately. Inline timestamps remain in cue data, while captions still display whole cues; this does not introduce karaoke highlighting. Older cores receive a multiple-active-cue display adapter. Install custom subtitle DOM listeners after plugin registration so the plugin's later view update does not overwrite them.
Player destruction cancels requests, removes subscriptions and releases generated URLs. Pending registration settles with an inert result; retained tracks/reset calls become no-ops. Destroy does not restore the shared escape option, because another consumer may have changed it. Fonts, historical core combinations and physical Safari/mobile display still need their own validation.
## Compatible TypeScript entries
The root and `/legacy` preserve the latest published 1.2.0 factory shape: required `{ subtitles: TrackOption[] }` and a synchronous name-only `LegacyResult`. This retains historical extraction and replacement functions. For the actual Promise and selection methods, use `/runtime`, pointing to the same implementation:
```ts
import type Artplayer from 'artplayer';
import multipleSubtitles from 'artplayer-plugin-multiple-subtitles/runtime';
import type { Result, RuntimeOption } from 'artplayer-plugin-multiple-subtitles/runtime';
const options: RuntimeOption = {
subtitles: [{ url: '/subtitles/en.vtt', name: 'en' }],
};
async function selectSubtitles(art: Artplayer): Promise<Result> {
const result = await multipleSubtitles(options)(art);
result.tracks(['en']);
result.reset();
return result;
}
```
Root named types are `TrackOption`, `Option`, `RuntimeOption`, `LegacyResult`, `Result`, `Factory` and `RuntimeFactory`. Runtime exports TrackOption, RuntimeOption, Result and RuntimeFactory. RuntimeFactory also describes the writable `.default` self-alias; the historical Factory does not require it.
The 1.0/1.1 export-assignment declarations conflict with 1.2's default-module shape; the approved policy retains 1.2 at the root. NodeNext ESM root types expose the factory at `root.default`; use runtime for accurate default calls. Classic CommonJS without interop can use runtime `import = require`; classic default imports need `esModuleInterop`. Historical JavaScript `.default(...)` calls remain supported; an old incorrect declaration does not establish a runtime call that never worked.
===== packages/artplayer-vitepress/docs/en/plugin/vtt-thumbnail.md =====
# VTT Thumbnail