@linker-design-plus/timeline-track 2.0.24 → 2.1.1
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/README.md +43 -129
- package/dist/components/panel/ClipConfigPanel.d.ts +2 -0
- package/dist/components/panel/ClipConfigPanelRenderer.d.ts +2 -0
- package/dist/core/controllers/index.d.ts +1 -0
- package/dist/core/controllers/previewBackend.d.ts +21 -7
- package/dist/core/controllers/previewPendingOverlayRenderer.d.ts +1 -2
- package/dist/core/controllers/previewRecoveryExecution.d.ts +25 -0
- package/dist/core/controllers/previewSlotCoordinator.d.ts +18 -0
- package/dist/core/controllers/previewSlotLifecycle.d.ts +41 -0
- package/dist/core/controllers/previewSlotPolicy.d.ts +59 -0
- package/dist/core/controllers/previewTrackPlanner.d.ts +24 -0
- package/dist/core/controllers/timelineClipConfigController.d.ts +2 -0
- package/dist/core/controllers/timelinePreviewSession.d.ts +31 -8
- package/dist/core/controllers/timelinePreviewStateController.d.ts +6 -15
- package/dist/core/facade/timelineManager.d.ts +21 -9
- package/dist/core/history/history.d.ts +1 -1
- package/dist/core/history/timelineHistoryExecutor.d.ts +1 -0
- package/dist/core/history/timelineHistoryRecorder.d.ts +2 -2
- package/dist/core/models/constants.d.ts +14 -0
- package/dist/core/models/types.d.ts +15 -7
- package/dist/index.cjs.js +115 -115
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +5379 -4701
- package/dist/utils/diagnostics/types.d.ts +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,24 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
基于 TypeScript、Konva 和 Vue demo 的时间线编辑组件库,对外以 `TimelineManager` 为统一入口,提供多轨片段编辑、拖拽/拉伸/分割、撤销重做、选中态同步、封面渲染和预览挂载能力。时间轴主体仍由 Konva 驱动,播放指针使用独立的 SVG/DOM 覆盖层渲染。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- 主线重构已经完成,仓库已从“双轨实现 + 大量兼容层”收敛到单一轨道组件 `Track`
|
|
8
|
-
- `TimelineManager` 仍然是 façade,但内部已经拆分为 `stores`、`commands`、`controllers`、`tracks`、`presentation`
|
|
9
|
-
- 当前发布包实际内置的预览实现是 `DomPreviewBackend`
|
|
10
|
-
- `previewBackend` 配置仍然保留,但当前实现会统一解析到 `dom`,不要把 `canvas` / `auto` 视为已交付能力
|
|
11
|
-
- 仓库内保留了较完整的回归测试,适合继续做功能收敛和架构瘦身
|
|
12
|
-
|
|
13
|
-
## 审计结论
|
|
14
|
-
|
|
15
|
-
本轮已完成一次针对仓库现状的维护梳理,并同步修正了以下残留:
|
|
16
|
-
|
|
17
|
-
- 修复了 3 处测试残留,使 `pnpm test` 与 `pnpm exec tsc -p tsconfig.json --noEmit` 恢复通过
|
|
18
|
-
- 清理了 `pixi` 预览类型和 demo 入口中的过期选项
|
|
19
|
-
- 把 README 与重构路线文档改为“当前真实状态”,不再把未交付的 canvas 预览写成已完成能力
|
|
20
|
-
- 把 `build` 脚本改为直接调用本地 `tsc`,避免 `npm run build:types` 带来的环境告警
|
|
21
|
-
|
|
22
|
-
更细的维护记录见 [docs/maintenance-audit.md](./docs/maintenance-audit.md)。
|
|
5
|
+
当前版本已经收敛到单一 `Track` 实现。预览侧真实交付的 backend 只有 `DomPreviewBackend`;`previewBackend` 配置仍保留兼容输入,但运行时会统一解析到 `dom`。
|
|
23
6
|
|
|
24
7
|
## 安装
|
|
25
8
|
|
|
@@ -66,35 +49,17 @@ timeline.on('history_change', (_event, data) => {
|
|
|
66
49
|
});
|
|
67
50
|
```
|
|
68
51
|
|
|
69
|
-
### 预览资源缓存
|
|
70
|
-
|
|
71
|
-
预览资源缓存默认关闭。开启后,可被前端 `fetch` 读取且不超过 100MB 的 `http:` / `https:` 音视频文件会被缓存到浏览器存储中,用于后续预览播放。默认策略为 30 天 TTL、10GB 应用级容量上限、优先 OPFS,并在 OPFS 不可用时降级到 IndexedDB Blob。缓存失败会自动回退原始媒体 URL。
|
|
72
|
-
|
|
73
|
-
```ts
|
|
74
|
-
const timeline = new TimelineManager({
|
|
75
|
-
container,
|
|
76
|
-
previewBackend: 'dom',
|
|
77
|
-
resourceCache: {
|
|
78
|
-
enabled: true,
|
|
79
|
-
resolveMode: 'prefer-fast-start'
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
第一版不会缓存 HLS manifest / 分片,也不会缓存超过 100MB 的媒体文件。
|
|
85
|
-
|
|
86
52
|
## 核心能力
|
|
87
53
|
|
|
88
|
-
-
|
|
54
|
+
- 多轨时间线编辑,支持视频轨、音频轨和文本片段
|
|
89
55
|
- 片段拖拽、拉伸、分割、跨轨移动和重叠避让
|
|
90
|
-
-
|
|
56
|
+
- 多选、框选与批量拖拽
|
|
91
57
|
- 播放头、缩放、滚动和时间同步
|
|
92
58
|
- 撤销 / 重做 / 历史变更通知
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
- 草稿导入导出、音视频分离/恢复等编辑工作流
|
|
59
|
+
- 预览挂载、比例同步和当前激活片段解析
|
|
60
|
+
- 草稿导入导出、音视频分离 / 恢复等编辑工作流
|
|
96
61
|
|
|
97
|
-
##
|
|
62
|
+
## API 概览
|
|
98
63
|
|
|
99
64
|
常用方法:
|
|
100
65
|
|
|
@@ -104,30 +69,30 @@ const timeline = new TimelineManager({
|
|
|
104
69
|
- `setSpeed(speed)` / `getSpeed()`
|
|
105
70
|
- `addClip(config)` / `addClips(configs)`
|
|
106
71
|
- `updateClip(clipId, updates)` / `removeClip(clipId)`
|
|
107
|
-
- `selectClip(clipId)` / `
|
|
72
|
+
- `selectClip(clipId)` / `setSelection(ids)` / `clearSelection()`
|
|
108
73
|
- `splitCurrentClip()` / `removeClipGaps()`
|
|
109
|
-
- `getRenderedHeight()`
|
|
110
74
|
- `undo()` / `redo()` / `clearHistory()`
|
|
111
75
|
- `attachPreview(containerOrConfig)` / `detachPreview()`
|
|
112
|
-
- `exportTimeline()` / `
|
|
76
|
+
- `exportTimeline()` / `importTimeline(data)`
|
|
113
77
|
|
|
114
|
-
|
|
78
|
+
配音配置面板:
|
|
115
79
|
|
|
116
|
-
- `
|
|
117
|
-
- `
|
|
118
|
-
-
|
|
80
|
+
- `attachClipConfig(container, options)` 返回 `TimelineClipConfigController`
|
|
81
|
+
- 内建 `generateVoiceBatch` 流程会自动展示 / 关闭“配音生成中”全屏 loading
|
|
82
|
+
- 外部自定义配音流程也可以手动调用 `controller.showVoiceGenerationLoading()` 和 `controller.hideVoiceGenerationLoading()`
|
|
119
83
|
|
|
120
|
-
|
|
84
|
+
```ts
|
|
85
|
+
const controller = timeline.attachClipConfig(clipConfigContainer, {
|
|
86
|
+
voiceCatalog
|
|
87
|
+
});
|
|
121
88
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
- 撤销:`Mod+Z`
|
|
130
|
-
- 还原:`Mod+Shift+Z`,Windows 另支持 `Ctrl+Y`
|
|
89
|
+
controller.showVoiceGenerationLoading();
|
|
90
|
+
try {
|
|
91
|
+
await customGenerateVoice();
|
|
92
|
+
} finally {
|
|
93
|
+
controller.hideVoiceGenerationLoading();
|
|
94
|
+
}
|
|
95
|
+
```
|
|
131
96
|
|
|
132
97
|
常用事件:
|
|
133
98
|
|
|
@@ -137,83 +102,30 @@ const timeline = new TimelineManager({
|
|
|
137
102
|
- `clip_added`
|
|
138
103
|
- `clip_removed`
|
|
139
104
|
- `clip_updated`
|
|
140
|
-
- `clip_selected`
|
|
141
105
|
- `selected_clip_change`
|
|
142
106
|
- `history_change`
|
|
143
107
|
- `track_duration_change`
|
|
144
108
|
- `buffering_state_change`
|
|
145
109
|
- `source_loading_change`
|
|
146
110
|
|
|
147
|
-
##
|
|
148
|
-
|
|
149
|
-
```ts
|
|
150
|
-
interface ClipConfig {
|
|
151
|
-
id?: string;
|
|
152
|
-
type?: 'video' | 'audio';
|
|
153
|
-
externalId?: string;
|
|
154
|
-
src: string;
|
|
155
|
-
name: string;
|
|
156
|
-
isMuted?: boolean;
|
|
157
|
-
startTime?: number;
|
|
158
|
-
duration: number;
|
|
159
|
-
startTimeAtSource?: number;
|
|
160
|
-
endTimeAtSource?: number;
|
|
161
|
-
sourceDuration?: number;
|
|
162
|
-
thumbnails?: string[];
|
|
163
|
-
style?: Record<string, any>;
|
|
164
|
-
visualTransform?: {
|
|
165
|
-
x: number;
|
|
166
|
-
y: number;
|
|
167
|
-
scale: number;
|
|
168
|
-
};
|
|
169
|
-
trackId?: string;
|
|
170
|
-
volume?: number;
|
|
171
|
-
}
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
## 预览实现说明
|
|
111
|
+
## 兼容与边界
|
|
175
112
|
|
|
176
|
-
-
|
|
177
|
-
- `
|
|
178
|
-
-
|
|
113
|
+
- 当前代码路径只会创建 `DomPreviewBackend`
|
|
114
|
+
- `canvas` / `auto` 仍可作为兼容输入传入,但不应视为已交付能力
|
|
115
|
+
- 挂载预览且当前时间命中视频片段时,时间轴会消费预览视频的真实媒体时钟
|
|
116
|
+
- 空白段、纯音频、文本或未挂载预览时,播放仍回退到 `TimelineManager` 的 wall-clock 推进
|
|
179
117
|
|
|
180
|
-
##
|
|
118
|
+
## 预览资源缓存
|
|
181
119
|
|
|
182
|
-
|
|
183
|
-
flowchart TB
|
|
184
|
-
API["Public API\nsrc/index.ts"] --> FACADE["TimelineManager\nsrc/core/facade/timelineManager.ts"]
|
|
185
|
-
FACADE --> STORES["Stores\nTimeline / Selection / Playback / Viewport"]
|
|
186
|
-
FACADE --> COMMANDS["Commands\nTimelineCommands"]
|
|
187
|
-
FACADE --> CONTROLLERS["Controllers\nworkflow / selection / duration / playback / events"]
|
|
188
|
-
FACADE --> TRACKS["Tracks\nTrackManager / Bridge / Collection"]
|
|
189
|
-
FACADE --> PRESENTATION["Presentation\nTimelinePresentationAdapter"]
|
|
190
|
-
FACADE --> PREVIEW["Preview\nDomPreviewBackend"]
|
|
191
|
-
FACADE --> COMPONENTS["Presentation Views\nTimeline / Track / Clip / Panels / ManagedPlayhead"]
|
|
192
|
-
```
|
|
120
|
+
预览资源缓存默认关闭。开启后,前端可直接 `fetch` 的音视频资源会被缓存到浏览器存储中,用于后续预览复用;缓存失败时会自动回退原始媒体 URL。完整边界和配置说明见 [docs/resource-cache.md](./docs/resource-cache.md)。
|
|
193
121
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
## 目录结构
|
|
202
|
-
|
|
203
|
-
```text
|
|
204
|
-
src/
|
|
205
|
-
components/ 时间轴视图、SVG/DOM 覆盖层与交互 helper
|
|
206
|
-
core/ stores / commands / controllers / facade / models
|
|
207
|
-
utils/ 渲染、时间和日志工具
|
|
208
|
-
styles/ 样式入口
|
|
209
|
-
index.ts 对外导出
|
|
210
|
-
docs/
|
|
211
|
-
interaction-model.md
|
|
212
|
-
maintenance-audit.md
|
|
213
|
-
refactor-roadmap.md
|
|
214
|
-
demo/
|
|
215
|
-
App.vue
|
|
216
|
-
main.ts
|
|
122
|
+
```ts
|
|
123
|
+
const timeline = new TimelineManager({
|
|
124
|
+
container,
|
|
125
|
+
resourceCache: {
|
|
126
|
+
enabled: true
|
|
127
|
+
}
|
|
128
|
+
});
|
|
217
129
|
```
|
|
218
130
|
|
|
219
131
|
## 开发
|
|
@@ -226,9 +138,11 @@ pnpm test
|
|
|
226
138
|
pnpm exec tsc -p tsconfig.json --noEmit
|
|
227
139
|
```
|
|
228
140
|
|
|
229
|
-
##
|
|
141
|
+
## 文档
|
|
230
142
|
|
|
143
|
+
- [docs/README.md](./docs/README.md): 文档入口
|
|
144
|
+
- [docs/architecture-overview.md](./docs/architecture-overview.md): 当前架构、模块边界、运行链路
|
|
145
|
+
- [docs/implementation-notes.md](./docs/implementation-notes.md): selection、preview、resource cache、draft 的实现笔记
|
|
146
|
+
- [docs/resource-cache.md](./docs/resource-cache.md): 预览资源缓存的能力边界与配置
|
|
231
147
|
- [docs/interaction-model.md](./docs/interaction-model.md): 指针交互分层和拖拽约束
|
|
232
|
-
- [docs/
|
|
233
|
-
- [docs/maintenance-audit.md](./docs/maintenance-audit.md): 本轮仓库审计、验证结果和依赖建议
|
|
234
|
-
- [docs/review-remediation-plan.md](./docs/review-remediation-plan.md): 基于审查结论的修复计划与阶段性落地建议
|
|
148
|
+
- [docs/preview-playback-recovery-flow.md](./docs/preview-playback-recovery-flow.md): 预览媒体时钟驱动播放、buffering 阻塞与恢复续播流程
|
|
@@ -105,6 +105,8 @@ export declare class ClipConfigPanel {
|
|
|
105
105
|
setClip(clip: Clip | null): void;
|
|
106
106
|
setSelectionState(selectionState: ClipConfigPanelSelectionState): void;
|
|
107
107
|
setPreferredTab(tab: 'voice' | null): void;
|
|
108
|
+
showVoiceGenerationLoading(): void;
|
|
109
|
+
hideVoiceGenerationLoading(): void;
|
|
108
110
|
setVoiceGenerationBusy(isBusy: boolean): void;
|
|
109
111
|
destroy(): void;
|
|
110
112
|
private render;
|
|
@@ -13,4 +13,5 @@ export { TimelinePreviewStateController, type TimelinePendingPreviewState } from
|
|
|
13
13
|
export { TimelineKeyboardShortcutsController, type TimelineKeyboardShortcutsControllerCallbacks } from './timelineKeyboardShortcutsController';
|
|
14
14
|
export * from './timelineSelectionController';
|
|
15
15
|
export * from './timelineTrackMutationController';
|
|
16
|
+
export { TimelineClipConfigController } from './timelineClipConfigController';
|
|
16
17
|
export { TimelineTrackInfoPanelController } from './timelineTrackInfoPanelController';
|
|
@@ -15,8 +15,6 @@ export interface TimelinePreviewSyncPayload {
|
|
|
15
15
|
syncRequestId?: number;
|
|
16
16
|
}
|
|
17
17
|
export interface TimelinePreviewBackendCallbacks {
|
|
18
|
-
onBufferingStateChange?: (isBuffering: boolean) => void;
|
|
19
|
-
onSourceLoadingChange?: (pending: number) => void;
|
|
20
18
|
onSyncProcessed?: (syncRequestId?: number) => void;
|
|
21
19
|
onRuntimeStateChange?: (state: PreviewRuntimeState) => void;
|
|
22
20
|
onAspectRatioChange?: (aspectRatio: PreviewAspectRatio) => void;
|
|
@@ -29,15 +27,31 @@ export interface TimelinePreviewBackendCallbacks {
|
|
|
29
27
|
/** 预览层文字交互(点击字幕或文字变换控件)时若时间线处于播放中,用于请求暂停 */
|
|
30
28
|
onPauseIfPlaying?: () => void;
|
|
31
29
|
}
|
|
30
|
+
export type PreviewLoadingStatus = 'idle' | 'loading' | 'ready' | 'failed';
|
|
31
|
+
export type PreviewLoadingReason = 'syncing' | 'resolving-source' | 'seeking' | 'media-pending' | 'failed' | null;
|
|
32
|
+
export interface PreviewLoadingState {
|
|
33
|
+
status: PreviewLoadingStatus;
|
|
34
|
+
isLoading: boolean;
|
|
35
|
+
reason: PreviewLoadingReason;
|
|
36
|
+
}
|
|
32
37
|
export interface PreviewPendingState {
|
|
33
|
-
mode: 'seek' | 'scrub';
|
|
38
|
+
mode: 'seek' | 'scrub' | 'playback';
|
|
34
39
|
targetTime: TimeMs;
|
|
35
|
-
|
|
36
|
-
isBuffering: boolean;
|
|
40
|
+
loading: PreviewLoadingState;
|
|
37
41
|
errorMessage: string | null;
|
|
38
42
|
}
|
|
39
43
|
export type PreviewRuntimePhase = 'idle' | 'awaiting-sync' | 'awaiting-media' | 'ready' | 'failed';
|
|
40
44
|
export type PreviewSlotPhase = 'idle' | 'binding' | 'primed' | 'active' | 'recovering' | 'failed';
|
|
45
|
+
export type PreviewClockStatus = 'unavailable' | 'priming' | 'running' | 'blocked' | 'failed';
|
|
46
|
+
export type PreviewClockBlockedReason = 'no-video-clock' | 'syncing' | 'resolving-source' | 'seeking' | 'media-pending' | 'media-paused' | 'media-stalled' | 'failed' | null;
|
|
47
|
+
export interface PreviewClockState {
|
|
48
|
+
status: PreviewClockStatus;
|
|
49
|
+
timelineTime: TimeMs | null;
|
|
50
|
+
mediaTime: TimeMs | null;
|
|
51
|
+
clipId: string | null;
|
|
52
|
+
trackId: string | null;
|
|
53
|
+
reason: PreviewClockBlockedReason;
|
|
54
|
+
}
|
|
41
55
|
export interface PreviewSlotDiagnostic {
|
|
42
56
|
trackId: string;
|
|
43
57
|
role: 'current' | 'preload';
|
|
@@ -52,8 +66,8 @@ export interface PreviewSlotDiagnostic {
|
|
|
52
66
|
export interface PreviewRuntimeState {
|
|
53
67
|
syncRequestId?: number;
|
|
54
68
|
phase: PreviewRuntimePhase;
|
|
55
|
-
|
|
56
|
-
|
|
69
|
+
loading: PreviewLoadingState;
|
|
70
|
+
clock?: PreviewClockState;
|
|
57
71
|
errorMessage: string | null;
|
|
58
72
|
slots: PreviewSlotDiagnostic[];
|
|
59
73
|
}
|
|
@@ -3,16 +3,15 @@ import type { DomRenderer } from '../renderers/domRenderer';
|
|
|
3
3
|
export interface PreviewPendingOverlayViewModel {
|
|
4
4
|
pendingState: PreviewPendingState | null;
|
|
5
5
|
runtimeState: PreviewRuntimeState;
|
|
6
|
-
isBuffering: boolean;
|
|
7
6
|
}
|
|
8
7
|
export declare class PreviewPendingOverlayRenderer implements DomRenderer<PreviewPendingOverlayViewModel> {
|
|
9
8
|
private container;
|
|
10
9
|
private statusElement;
|
|
11
|
-
private detailElement;
|
|
12
10
|
private actionElement;
|
|
13
11
|
private readonly onRetry;
|
|
14
12
|
constructor(onRetry: () => void);
|
|
15
13
|
mount(container: HTMLElement): void;
|
|
16
14
|
update(viewModel: PreviewPendingOverlayViewModel): void;
|
|
15
|
+
private resolveLoadingStatusText;
|
|
17
16
|
destroy(): void;
|
|
18
17
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ActiveClipPlaybackInfo } from '../models/types';
|
|
2
|
+
import { type PreviewSlotRecoveryReason } from './previewSlotPolicy';
|
|
3
|
+
export interface PreviewRecoveryAttemptInput {
|
|
4
|
+
entry: ActiveClipPlaybackInfo;
|
|
5
|
+
desiredSource: string;
|
|
6
|
+
reason: PreviewSlotRecoveryReason;
|
|
7
|
+
previousRecoveryKey: string | null;
|
|
8
|
+
previousRetryCount: number;
|
|
9
|
+
maxImmediateRecoveryRetries: number;
|
|
10
|
+
recoveryTimeBucketMs: number;
|
|
11
|
+
isBackgroundPreload: boolean;
|
|
12
|
+
}
|
|
13
|
+
export type PreviewRecoveryAttemptResult = {
|
|
14
|
+
status: 'continue';
|
|
15
|
+
recoveryKey: string;
|
|
16
|
+
retryCount: number;
|
|
17
|
+
} | {
|
|
18
|
+
status: 'park';
|
|
19
|
+
message: string;
|
|
20
|
+
parkReason: 'retries-exhausted';
|
|
21
|
+
} | {
|
|
22
|
+
status: 'fail';
|
|
23
|
+
message: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function resolveRecoveryAttempt(input: PreviewRecoveryAttemptInput): PreviewRecoveryAttemptResult;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ActiveClipPlaybackInfo, PlayState } from '../models/types';
|
|
2
|
+
import type { PreviewSlotTarget } from './previewTrackPlanner';
|
|
3
|
+
export interface PreviewSlotLike {
|
|
4
|
+
role: 'current' | 'preload';
|
|
5
|
+
trackId: string;
|
|
6
|
+
entry: ActiveClipPlaybackInfo | null;
|
|
7
|
+
requestedPlayState: PlayState;
|
|
8
|
+
}
|
|
9
|
+
export interface TrackSlotPairLike<TSlot extends PreviewSlotLike = PreviewSlotLike> {
|
|
10
|
+
current: TSlot;
|
|
11
|
+
preload: TSlot;
|
|
12
|
+
}
|
|
13
|
+
export declare function slotHasEntry(slot: PreviewSlotLike, clipId?: string | null): boolean;
|
|
14
|
+
export declare function shouldSwapTrackSlotsForPlan(slots: TrackSlotPairLike, currentEntry: ActiveClipPlaybackInfo | null): boolean;
|
|
15
|
+
export declare function swapTrackSlotPair<TSlot extends PreviewSlotLike>(slots: TrackSlotPairLike<TSlot>): TrackSlotPairLike<TSlot>;
|
|
16
|
+
export declare function buildSlotKey(slot: Pick<PreviewSlotLike, 'trackId' | 'role'>): string;
|
|
17
|
+
export declare function buildDeferredPreloadTarget(slot: PreviewSlotLike, syncRequestId?: number): PreviewSlotTarget | null;
|
|
18
|
+
export declare function resolveSlotFromKey<TSlot extends PreviewSlotLike>(trackSlots: Map<string, TrackSlotPairLike<TSlot>>, slotKey: string): TSlot | null;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { PlayState } from '../models/types';
|
|
2
|
+
import type { PreviewSlotPhase } from './previewBackend';
|
|
3
|
+
export interface PreviewSlotRecoveryStateLike {
|
|
4
|
+
isActive: boolean;
|
|
5
|
+
phase: PreviewSlotPhase;
|
|
6
|
+
errorMessage: string | null;
|
|
7
|
+
retryCount: number;
|
|
8
|
+
recoveryKey: string | null;
|
|
9
|
+
forceRecover: boolean;
|
|
10
|
+
isLoading: boolean;
|
|
11
|
+
isResolvingSource: boolean;
|
|
12
|
+
isBuffering: boolean;
|
|
13
|
+
isSeeking: boolean;
|
|
14
|
+
consecutiveStalledCount: number;
|
|
15
|
+
expectedEmptiedEvents: number;
|
|
16
|
+
recoverableEventCount: number;
|
|
17
|
+
loadStartedSinceRecover: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface PreviewTrackedCurrentVideoSlotStatus {
|
|
20
|
+
failed: boolean;
|
|
21
|
+
resolving: boolean;
|
|
22
|
+
seeking: boolean;
|
|
23
|
+
mediaUnready: boolean;
|
|
24
|
+
ready: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function resetSlotRecoveryTrackingState(slot: PreviewSlotRecoveryStateLike, forceRecover?: boolean): void;
|
|
27
|
+
export declare function clearSlotState(slot: PreviewSlotRecoveryStateLike): void;
|
|
28
|
+
export declare function parkPreloadSlotState(slot: PreviewSlotRecoveryStateLike): void;
|
|
29
|
+
export declare function settleResolvedSourceWithoutRecoveryState(slot: Pick<PreviewSlotRecoveryStateLike, 'isActive' | 'phase' | 'isResolvingSource' | 'isLoading' | 'isBuffering' | 'errorMessage'>): void;
|
|
30
|
+
export declare function finalizeAppliedSlotPhase(slot: Pick<PreviewSlotRecoveryStateLike, 'isActive' | 'isLoading' | 'isSeeking' | 'phase'>): void;
|
|
31
|
+
export declare function beginDeferredPreloadState(slot: Pick<PreviewSlotRecoveryStateLike, 'phase' | 'forceRecover' | 'isLoading' | 'isResolvingSource' | 'isSeeking' | 'isBuffering' | 'errorMessage'>, hasAssignedSource: boolean): void;
|
|
32
|
+
export declare function reconcileTrackedCurrentVideoSlotState(slot: PreviewSlotRecoveryStateLike, input: {
|
|
33
|
+
hasDesiredSource: boolean;
|
|
34
|
+
sourceMatched: boolean;
|
|
35
|
+
withinSeekThreshold: boolean;
|
|
36
|
+
readyState: number;
|
|
37
|
+
haveCurrentDataState: number;
|
|
38
|
+
}): PreviewTrackedCurrentVideoSlotStatus;
|
|
39
|
+
export declare function failSlotState(slot: Pick<PreviewSlotRecoveryStateLike, 'phase' | 'isLoading' | 'isResolvingSource' | 'isSeeking' | 'isBuffering' | 'errorMessage' | 'forceRecover' | 'expectedEmptiedEvents'>, errorMessage: string): void;
|
|
40
|
+
export declare function beginPreloadPrimingState(slot: Pick<PreviewSlotRecoveryStateLike, 'isActive' | 'isBuffering' | 'isSeeking'>): void;
|
|
41
|
+
export declare function beginSlotRecoveryState(slot: PreviewSlotRecoveryStateLike, playState: PlayState): void;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ActiveClipPlaybackInfo, PlayState } from '../models/types';
|
|
2
|
+
import type { PreviewSlotPhase } from './previewBackend';
|
|
3
|
+
export type PreviewSlotRecoveryReason = 'reconcile' | 'stalled' | 'abort' | 'emptied' | 'error';
|
|
4
|
+
export interface ResolvedSlotSourceLike {
|
|
5
|
+
stableSourceUrl: string;
|
|
6
|
+
url: string;
|
|
7
|
+
objectUrl: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface ReusableBoundSlotSourceInput {
|
|
10
|
+
currentSourceKey: string;
|
|
11
|
+
nextSourceKey: string;
|
|
12
|
+
desiredSource: string | null;
|
|
13
|
+
stableSourceUrl: string | null;
|
|
14
|
+
objectUrl: string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface PreviewSlotRecoveryNeedInput {
|
|
17
|
+
desiredSource: string;
|
|
18
|
+
configuredSource: string | null;
|
|
19
|
+
assignedSource: string | null;
|
|
20
|
+
forceRecover: boolean;
|
|
21
|
+
isActive: boolean;
|
|
22
|
+
isLoading: boolean;
|
|
23
|
+
isSeeking: boolean;
|
|
24
|
+
isBuffering: boolean;
|
|
25
|
+
phase: PreviewSlotPhase;
|
|
26
|
+
networkState: number | null;
|
|
27
|
+
readyState: number | null;
|
|
28
|
+
networkEmptyState: number;
|
|
29
|
+
haveCurrentDataState: number;
|
|
30
|
+
}
|
|
31
|
+
export interface PreviewSlotRecoveryNeedResult {
|
|
32
|
+
needsRecovery: boolean;
|
|
33
|
+
recoveryReason: string | null;
|
|
34
|
+
actualSource: string | null;
|
|
35
|
+
configuredSource: string | null;
|
|
36
|
+
shouldWarn: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function buildRecoveryKey(entry: ActiveClipPlaybackInfo, desiredSource: string, reason: PreviewSlotRecoveryReason, recoveryTimeBucketMs: number): string;
|
|
39
|
+
export declare function getReusableBoundSlotSource(input: ReusableBoundSlotSourceInput): ResolvedSlotSourceLike | null;
|
|
40
|
+
export declare function doesSlotSourceMatchDesired(configuredSource: string | null, assignedSource: string | null, desiredSource: string | null): boolean;
|
|
41
|
+
export declare function evaluateSlotRecoveryNeed(input: PreviewSlotRecoveryNeedInput): PreviewSlotRecoveryNeedResult;
|
|
42
|
+
export declare function getSlotSeekThresholdSeconds(role: 'current' | 'preload', playbackRate: number): number;
|
|
43
|
+
export declare function shouldHardResetRecoveredSource(previousActualSource: string | null, reason: PreviewSlotRecoveryReason, retryCount: number): boolean;
|
|
44
|
+
export declare function buildCurrentSlotTarget(input: {
|
|
45
|
+
role: 'current' | 'preload';
|
|
46
|
+
entry: ActiveClipPlaybackInfo;
|
|
47
|
+
requestedPlayState: PlayState;
|
|
48
|
+
isActive: boolean;
|
|
49
|
+
playbackRate: number;
|
|
50
|
+
syncRequestId?: number;
|
|
51
|
+
}): {
|
|
52
|
+
role: 'current' | 'preload';
|
|
53
|
+
entry: ActiveClipPlaybackInfo;
|
|
54
|
+
playState: PlayState;
|
|
55
|
+
speed: number;
|
|
56
|
+
visible: boolean;
|
|
57
|
+
zIndex: number;
|
|
58
|
+
syncRequestId?: number;
|
|
59
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ActiveClipPlaybackInfo, TrackType } from '../models/types';
|
|
2
|
+
import type { TimelinePreviewSyncPayload } from './previewBackend';
|
|
3
|
+
export interface PreviewSlotTarget {
|
|
4
|
+
role: 'current' | 'preload';
|
|
5
|
+
entry: ActiveClipPlaybackInfo | null;
|
|
6
|
+
playState: TimelinePreviewSyncPayload['playState'];
|
|
7
|
+
speed: number;
|
|
8
|
+
visible: boolean;
|
|
9
|
+
zIndex: number;
|
|
10
|
+
syncRequestId?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface PreviewTrackPlan {
|
|
13
|
+
trackId: string;
|
|
14
|
+
kind: TrackType;
|
|
15
|
+
order: number;
|
|
16
|
+
current: PreviewSlotTarget;
|
|
17
|
+
preload: PreviewSlotTarget;
|
|
18
|
+
}
|
|
19
|
+
export interface ExistingPreviewTrack {
|
|
20
|
+
trackId: string;
|
|
21
|
+
kind: TrackType;
|
|
22
|
+
order: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function projectPreviewPlan(payload: TimelinePreviewSyncPayload, existingTracks: ExistingPreviewTrack[]): PreviewTrackPlan[];
|
|
@@ -18,6 +18,8 @@ export declare class TimelineClipConfigController {
|
|
|
18
18
|
updateFromExternal(): void;
|
|
19
19
|
destroy(): void;
|
|
20
20
|
setPreferredTab(tab: 'voice' | null): void;
|
|
21
|
+
showVoiceGenerationLoading(): void;
|
|
22
|
+
hideVoiceGenerationLoading(): void;
|
|
21
23
|
setVoiceGenerationBusy(isBusy: boolean): void;
|
|
22
24
|
private buildSelectionState;
|
|
23
25
|
private resolveUpdateTargets;
|
|
@@ -4,8 +4,6 @@ import type { ResourceCacheManager } from '../resources/resourceCache';
|
|
|
4
4
|
import type { PreviewPendingState, PreviewRuntimeState } from './previewBackend';
|
|
5
5
|
import { type DiagnosticEmitInput, type DiagnosticsCenter } from '../../utils/diagnostics';
|
|
6
6
|
interface TimelinePreviewSessionCallbacks {
|
|
7
|
-
onBufferingStateChange?: (isBuffering: boolean) => void;
|
|
8
|
-
onSourceLoadingChange?: (pending: number) => void;
|
|
9
7
|
onSyncProcessed?: (syncRequestId?: number) => void;
|
|
10
8
|
onRuntimeStateChange?: (state: PreviewRuntimeState) => void;
|
|
11
9
|
onAspectRatioChange?: (aspectRatio: PreviewAspectRatio) => void;
|
|
@@ -54,9 +52,9 @@ export declare class TimelinePreviewSession {
|
|
|
54
52
|
private readonly textPreviewEntries;
|
|
55
53
|
private audioContext;
|
|
56
54
|
private masterGainNode;
|
|
57
|
-
private
|
|
58
|
-
private
|
|
59
|
-
private
|
|
55
|
+
private loadingProbeTimeoutId;
|
|
56
|
+
private clockProbeTimeoutId;
|
|
57
|
+
private playbackGroupSuspended;
|
|
60
58
|
private lastRuntimeSignature;
|
|
61
59
|
private lastRuntimeState;
|
|
62
60
|
private lastSettledSyncRequestId;
|
|
@@ -81,13 +79,13 @@ export declare class TimelinePreviewSession {
|
|
|
81
79
|
private aspectRatioProbe;
|
|
82
80
|
private aspectRatioProbeSrc;
|
|
83
81
|
private aspectRatioProbeResolveToken;
|
|
84
|
-
private isAspectRatioProbeLoading;
|
|
85
82
|
private pendingState;
|
|
86
83
|
private activeSyncRequestId;
|
|
87
84
|
private isSyncProjecting;
|
|
88
85
|
private readonly deferredPreloadSlotKeys;
|
|
89
86
|
private deferredPreloadFlushScheduled;
|
|
90
87
|
private lastSyncedPlayState;
|
|
88
|
+
private pendingClockAlignmentTargetTime;
|
|
91
89
|
constructor(callbacks?: TimelinePreviewSessionCallbacks, dependencies?: TimelinePreviewSessionDependencies);
|
|
92
90
|
private requestPauseIfPlaying;
|
|
93
91
|
private emitDiagnostic;
|
|
@@ -117,11 +115,13 @@ export declare class TimelinePreviewSession {
|
|
|
117
115
|
private resolvePlayableSlotSource;
|
|
118
116
|
private emitRuntimeSourceDiagnostic;
|
|
119
117
|
private getReusableResolvedSlotSource;
|
|
118
|
+
private getReusableBoundSlotSource;
|
|
120
119
|
private slotNeedsRecovery;
|
|
121
120
|
private recoverSlot;
|
|
122
121
|
private finishSlotRecovery;
|
|
123
122
|
private getSlotKey;
|
|
124
123
|
private isActiveCurrentSlot;
|
|
124
|
+
private isTrackedActiveCurrentVideoSlot;
|
|
125
125
|
private hasBlockingActiveCurrentSlot;
|
|
126
126
|
private shouldDeferPreloadTarget;
|
|
127
127
|
private shouldDeferPreloadRecovery;
|
|
@@ -143,6 +143,9 @@ export declare class TimelinePreviewSession {
|
|
|
143
143
|
private failSlot;
|
|
144
144
|
private configureAudioRouting;
|
|
145
145
|
private syncCurrentSlot;
|
|
146
|
+
private resetSlotPlaybackProgressProbe;
|
|
147
|
+
private updateSlotPlaybackProgressState;
|
|
148
|
+
private maybeResumeRequestedPlayback;
|
|
146
149
|
private preparePreloadSlot;
|
|
147
150
|
private setSlotVisible;
|
|
148
151
|
private getAudioContext;
|
|
@@ -156,9 +159,29 @@ export declare class TimelinePreviewSession {
|
|
|
156
159
|
private tryResolveAutoAspectRatioFromSlot;
|
|
157
160
|
private handleResolvedAutoAspectRatio;
|
|
158
161
|
private refreshRuntimeState;
|
|
159
|
-
private
|
|
160
|
-
private
|
|
162
|
+
private buildPreviewClockState;
|
|
163
|
+
private buildPreviewClockStateFromSlot;
|
|
164
|
+
private resolveClockBlockedReason;
|
|
165
|
+
private getPreviewClockSourceSlot;
|
|
166
|
+
private buildPreviewClockSourceCandidate;
|
|
167
|
+
private comparePreviewClockSourceCandidates;
|
|
168
|
+
private isPreviewClockCandidateAlignedToTarget;
|
|
169
|
+
private getPreviewClockTargetAlignmentThresholdMs;
|
|
170
|
+
private isPreviewClockSlotAlignedToPendingTarget;
|
|
171
|
+
private updatePendingClockAlignmentTarget;
|
|
172
|
+
private syncActivePlaybackGroupSuspension;
|
|
173
|
+
private getSlotMediaTimeMs;
|
|
174
|
+
private getSlotTimelineTime;
|
|
161
175
|
private refreshPendingOverlay;
|
|
176
|
+
private buildPreviewLoadingState;
|
|
177
|
+
private evaluateTrackedCurrentVideoSlot;
|
|
178
|
+
private doesSlotSourceMatchDesired;
|
|
179
|
+
private getSlotSeekThresholdSeconds;
|
|
180
|
+
private finalizeSourceResolutionResult;
|
|
181
|
+
private syncLoadingProbe;
|
|
182
|
+
private syncClockProbe;
|
|
183
|
+
private clearLoadingProbe;
|
|
184
|
+
private clearClockProbe;
|
|
162
185
|
private handlePreviewTransformChange;
|
|
163
186
|
private refreshVisualLayout;
|
|
164
187
|
private syncTextPreviewEntries;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TimeMs } from '../models/types';
|
|
2
|
-
import type { PreviewPendingState, PreviewRuntimeState } from './previewBackend';
|
|
2
|
+
import type { PreviewLoadingState, PreviewPendingState, PreviewRuntimeState } from './previewBackend';
|
|
3
3
|
export interface TimelinePendingPreviewState {
|
|
4
4
|
mode: 'seek' | 'scrub';
|
|
5
5
|
targetTime: TimeMs;
|
|
@@ -13,36 +13,26 @@ export interface TimelinePendingPreviewState {
|
|
|
13
13
|
}
|
|
14
14
|
interface TimelinePreviewStateControllerCallbacks {
|
|
15
15
|
applyPendingState: (state: PreviewPendingState | null) => void;
|
|
16
|
-
|
|
17
|
-
emitSourceLoadingStateChange: () => void;
|
|
16
|
+
emitLoadingStateChange: (state: PreviewLoadingState) => void;
|
|
18
17
|
resumePlayback: () => void;
|
|
19
18
|
requestPreviewSync: () => void;
|
|
20
19
|
}
|
|
21
20
|
interface TimelinePreviewStateControllerOptions {
|
|
22
21
|
callbacks: TimelinePreviewStateControllerCallbacks;
|
|
23
|
-
pendingTimeoutMs?: number;
|
|
24
22
|
failedOverlayDelayMs?: number;
|
|
25
23
|
}
|
|
26
24
|
export declare class TimelinePreviewStateController {
|
|
27
25
|
private readonly callbacks;
|
|
28
|
-
private readonly pendingTimeoutMs;
|
|
29
26
|
private readonly failedOverlayDelayMs;
|
|
30
|
-
private
|
|
31
|
-
private _previewBuffering;
|
|
32
|
-
private _previewAwaitingMedia;
|
|
27
|
+
private _previewLoadingState;
|
|
33
28
|
private _pendingPreviewState;
|
|
34
29
|
private _nextPendingPreviewSyncRequestId;
|
|
35
30
|
constructor(options: TimelinePreviewStateControllerOptions);
|
|
36
|
-
get
|
|
37
|
-
set previewSourceLoadingCount(value: number);
|
|
38
|
-
get previewBuffering(): boolean;
|
|
39
|
-
set previewBuffering(value: boolean);
|
|
31
|
+
get previewLoadingState(): PreviewLoadingState;
|
|
40
32
|
get pendingPreviewState(): TimelinePendingPreviewState | null;
|
|
41
33
|
set pendingPreviewState(value: TimelinePendingPreviewState | null);
|
|
42
34
|
get nextPendingPreviewSyncRequestId(): number;
|
|
43
35
|
set nextPendingPreviewSyncRequestId(value: number);
|
|
44
|
-
handleBufferingStateChange(isBuffering: boolean): void;
|
|
45
|
-
handleSourceLoadingChange(pending: number): void;
|
|
46
36
|
handleRuntimeStateChange(state: PreviewRuntimeState): void;
|
|
47
37
|
markSyncProcessed(syncRequestId?: number): void;
|
|
48
38
|
beginPendingPreview(time: TimeMs, mode: 'seek' | 'scrub', resumePlayOnReady: boolean): void;
|
|
@@ -51,7 +41,8 @@ export declare class TimelinePreviewStateController {
|
|
|
51
41
|
retryPendingPreview(): void;
|
|
52
42
|
clearPendingPreviewState(): void;
|
|
53
43
|
resetPreviewRuntimeState(): void;
|
|
54
|
-
private
|
|
44
|
+
private resolvePendingLoadingState;
|
|
45
|
+
private buildPlaybackLoadingOverlayState;
|
|
55
46
|
private clearPendingPreviewTimeout;
|
|
56
47
|
private scheduleFailedOverlay;
|
|
57
48
|
private clearFailedOverlayTimeout;
|