docs(core): [SITE-04] document subtitle loading and resource ownership

This commit is contained in:
Harvey Zhao committed 2026-09-16 07:18:10 +08:00
1 parent de45f5b978
commit bd3f3c0689
93 files changed
+3864 -2370

No files matched your search

@@ -420,6 +420,51 @@ art.on('ready', () => {
管理播放器的字幕功能
### 加载与切换 {#subtitle-contract}
`switch(url, option?)` 将本次选项浅合并到 `art.option.subtitle` 的配置之上,再以第一个参数覆盖 URL。它不会把本次选项写回 `art.option.subtitle`,也不会继承上一次 `switch` 的选项。`subtitle.option` 则保存最近一次开始加载的完整选项,可能对应尚未完成或失败的请求。
| 配置 | 默认值 | 实际作用 |
| --- | --- | --- |
| `url` | `''` | 请求地址;空值不清除已加载的轨道 |
| `name` | `''` | `switch` 成功提交后显示切换提示;轨道 label 仍取 `art.option.subtitle.name` 或 `Artplayer` |
| `type` | `''` | 显式 `vtt`、`srt`、`ass`,否则从 URL 推断;不按响应 MIME 推断 |
| `style` | `{}` | 设置字幕容器 CSS;后续设置不会自动清除旧 CSS |
| `encoding` | `'utf-8'` | `TextDecoder` 解码方式 |
| `escape` | `true` | 渲染读取 `art.option.subtitle.escape`;仅在本次 switch 中传入不会改变这个全局配置 |
| `onVttLoad` | 原样返回文本 | 同步转换 VTT 文本;普通函数的 this 为本次完整选项 |
识别的 SRT/ASS 先转为 WebVTT,再调用 `onVttLoad`,VTT 直接传入该回调;回调必须返回字符串,不等待 Promise。ASS 转换只保留基础文本与时间,不是完整 ASS 排版。未识别的类型仍会先 fetch/解码,但跳过回调并把原地址交给原生 track。
`switch` 返回 `Promise<string | null | undefined>`:成功返回提交的地址(转换后通常为 Blob URL),没有可用原生文本轨时为 null,空地址、被下一次请求替代或销毁取消时为 undefined。Promise 完成不等于原生 cue 已加载;需要在切换前订阅 `subtitleLoad(cues, option)`。`subtitle.url` 的 getter 返回当前 track 的地址,setter 启动异步切换但不能等待结果。
新请求取消旧请求,并阻止迟到结果覆盖当前字幕;销毁也会结算等待中的请求。活动请求的 fetch、解码、转换错误会拒绝直接调用的 Promise,并更新提示;构造和 URL setter 会处理其内部拒绝。原生 track 后续加载失败只更新提示,不会追溯拒绝已经完成的 switch。失败或空 URL 不保证删除旧轨道,因此隐藏字幕请用 `subtitle.show = false`。
### 轨道、渲染和清理 {#subtitle-runtime}
`textTrack` 读取视频的第一个 TextTrack,而不是按语言或 kind 搜索;代理媒体没有该能力时可能为 undefined。`cues` 和 `activeCues` 每次返回新数组,cue 对象本身保持引用;不可用或禁用时为空数组。`SubtitleCue.text` 是字幕文本,`originalStartTime`/`originalEndTime` 是调整偏移时保存的原时间;track 上的可选 `offset` 保存当前偏移。这些元数据不会因读取数组而复制。
`update()` 同步重绘当前活动 cue,不是通用组件的更新方法,也不重新下载字幕。没有活动 cue 时只清空视图;否则先发出 `subtitleBeforeUpdate`,按非空行生成 `.art-subtitle-line[data-group]`,再发出 `subtitleAfterUpdate`。渲染时以播放器配置决定转义;关闭 escape 时,cue 内容会作为可信 HTML 插入。监听器中的切换、重绘或销毁会阻止过期的外层渲染继续提交。
`show`/`toggle()` 控制播放器的 `art-subtitle-show` 类并发出 `subtitle` 布尔事件,不暂停字幕下载或轨道。`style(object)` 和 `style(key, value)` 返回字幕容器节点。管理器的 `name` 为 `subtitle`;`destroyEvent` 是当前 cuechange 监听器的清理函数,不是销毁整个字幕管理器的方法。
底层 `init(fullOption)` 不补齐配置,通常应使用 `switch`;`createTrack(kind, url)` 直接替换原生轨道,不下载转换、不合并选项,返回 undefined。新轨道以 hidden 模式工作,其 load 事件触发 `subtitleLoad`。替换会释放旧监听器;核心创建的 Blob URL 在被替换或播放器销毁时回收,调用者传入的 URL 仍由调用者管理。不要在 switch 刚返回时回收核心返回的 Blob URL。WebKit 原生全屏切换可能重建 track 并重新触发加载,不能把字幕加载视为只发生一次。
根入口保留历史 style 的 void、switch 的 `Promise<string>` 和继承组件 update 声明。需要准确返回值与 `update()` 时使用 `artplayer/runtime`;其 `Subtitle` 是管理器类型,根入口 `Subtitle` 是配置类型。继承成员不表示字幕支持像 layers 一样添加自定义条目。
```ts
import Artplayer from 'artplayer/runtime';
import type { SubtitleCue } from 'artplayer/runtime';
const art = new Artplayer({ container: '.artplayer-app', url: '/assets/sample/video.mp4' });
art.on('subtitleLoad', (cues: SubtitleCue[]) => console.info(cues.length));
const node: HTMLDivElement = art.subtitle.style({ color: 'red' });
const loading: Promise<string | null | undefined> = art.subtitle.switch('/assets/sample/subtitle.srt');
void loading.catch(console.error);
art.subtitle.update();
void node;
```
- `url` 属性设置和返回当前字幕地址
- `style` 方法设置当前字幕的样式
- `switch` 方法设置当前字幕地址和选项
@@ -421,6 +421,51 @@ art.on('ready', () => {
Manages the player's subtitle functionality
### Loading and switching {#subtitle-contract}
`switch(url, option?)` shallowly merges this call's options over `art.option.subtitle`, then overrides the URL with its first argument. It does not write the options back to `art.option.subtitle` or inherit options from the previous switch. `subtitle.option` holds the complete options for the most recently started load, which may still be pending or may have failed.
| Option | Default | Behavior |
| --- | --- | --- |
| `url` | `''` | Request URL; an empty value does not clear the existing track |
| `name` | `''` | Switch notice after a successful submission; the track label still uses `art.option.subtitle.name` or `Artplayer` |
| `type` | `''` | Explicit `vtt`, `srt`, or `ass`, otherwise inferred from the URL, not the response MIME type |
| `style` | `{}` | Subtitle container CSS; later assignments do not automatically clear earlier styles |
| `encoding` | `'utf-8'` | TextDecoder encoding |
| `escape` | `true` | Rendering reads `art.option.subtitle.escape`; a switch-only override does not change that setting |
| `onVttLoad` | Return the text unchanged | Synchronously transforms VTT text; an ordinary function receives the complete options as this |
Recognized SRT/ASS is converted to WebVTT before `onVttLoad`; VTT is passed directly to it. The callback must return a string; promises are not awaited. ASS conversion retains basic text and timing, not full ASS layout. Unrecognized types still undergo fetch and decoding, but skip the callback and pass the original URL to the native track.
`switch` returns `Promise<string | null | undefined>`: the submitted URL on success (usually a Blob URL after conversion), null without an available native text track, or undefined for an empty URL, superseded request, or cancellation on destruction. Fulfillment does not mean native cues have loaded. Subscribe to `subtitleLoad(cues, option)` before switching. The `subtitle.url` getter returns the current track URL; its setter starts a switch without exposing a Promise.
A new request cancels its predecessor and prevents late results from replacing current subtitles; destruction also settles pending requests. Active fetch, decoding, or conversion errors reject direct calls and update the notice. Construction and the URL setter observe their internal rejections. A later native track failure only updates the notice; it cannot reject an already fulfilled switch. Errors and empty URLs do not guarantee removal of the old track. Use `subtitle.show = false` to hide subtitles.
### Tracks, rendering, and cleanup {#subtitle-runtime}
`textTrack` reads the video's first TextTrack rather than searching by language or kind; proxy media without that capability may return undefined. `cues` and `activeCues` return fresh arrays containing the original cue objects, or empty arrays when unavailable or disabled. `SubtitleCue.text` contains the caption; `originalStartTime`/`originalEndTime` preserve the original times when adjusting an offset, and the track's optional `offset` holds the current offset. Reading an array does not clone this metadata.
`update()` synchronously redraws active cues. It is not the generic component update method and does not download subtitles again. With no active cues it only clears the view; otherwise it emits `subtitleBeforeUpdate`, creates `.art-subtitle-line[data-group]` elements for nonempty lines, then emits `subtitleAfterUpdate`. Rendering uses the player's escape setting. With escaping disabled, cue contents are inserted as trusted HTML. Switching, redrawing, or destroying inside a listener prevents the stale outer render from committing.
`show`/`toggle()` control the player's `art-subtitle-show` class and emit the boolean `subtitle` event; they do not stop downloads or the track. `style(object)` and `style(key, value)` return the subtitle container. The manager's `name` is `subtitle`. `destroyEvent` cleans up the current cuechange listener; it does not destroy the entire subtitle manager.
Low-level `init(fullOption)` does not fill in missing configuration; normally use `switch`. `createTrack(kind, url)` directly replaces the native track without downloading, converting, or merging options, and returns undefined. The new track uses hidden mode and its load event emits `subtitleLoad`. Replacement releases old listeners. Core-generated Blob URLs are revoked on replacement or player destruction; caller-supplied URLs remain caller-owned. Do not revoke a core-returned Blob URL as soon as switch fulfills. WebKit native fullscreen transitions may recreate the track and trigger another load, so loading is not a one-time event.
The root entry retains historical void style returns, `Promise<string>` switch returns, and the inherited component update declaration. Use `artplayer/runtime` for accurate returns and `update()`. Its `Subtitle` describes the manager; the root `Subtitle` describes configuration. Inherited members do not imply support for adding custom entries as with layers.
```ts
import Artplayer from 'artplayer/runtime';
import type { SubtitleCue } from 'artplayer/runtime';
const art = new Artplayer({ container: '.artplayer-app', url: '/assets/sample/video.mp4' });
art.on('subtitleLoad', (cues: SubtitleCue[]) => console.info(cues.length));
const node: HTMLDivElement = art.subtitle.style({ color: 'red' });
const loading: Promise<string | null | undefined> = art.subtitle.switch('/assets/sample/subtitle.srt');
void loading.catch(console.error);
art.subtitle.update();
void node;
```
- The `url` property sets and returns the current subtitle URL
- The `style` method sets the style of the current subtitle
- The `switch` method sets the current subtitle URL and options