@linker-design-plus/timeline-track 2.0.15 → 2.0.16
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 +17 -0
- package/dist/core/controllers/previewBackend.d.ts +2 -0
- package/dist/core/controllers/timelinePreviewSession.d.ts +11 -0
- package/dist/core/facade/timelineManager.d.ts +5 -0
- package/dist/core/models/types.d.ts +16 -1
- package/dist/core/resources/previewMediaSourceResolver.d.ts +4 -0
- package/dist/core/resources/resourceCache/cacheKey.d.ts +5 -0
- package/dist/core/resources/resourceCache/http.d.ts +5 -0
- package/dist/core/resources/resourceCache/index.d.ts +7 -0
- package/dist/core/resources/resourceCache/metadataStore.d.ts +11 -0
- package/dist/core/resources/resourceCache/resourceCacheManager.d.ts +39 -0
- package/dist/core/resources/resourceCache/storage.d.ts +24 -0
- package/dist/core/resources/resourceCache/types.d.ts +85 -0
- package/dist/index.cjs.js +52 -52
- package/dist/index.es.js +4577 -3802
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,6 +66,23 @@ timeline.on('history_change', (_event, data) => {
|
|
|
66
66
|
});
|
|
67
67
|
```
|
|
68
68
|
|
|
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
|
+
|
|
69
86
|
## 核心能力
|
|
70
87
|
|
|
71
88
|
- 多轨时间线编辑,支持视频轨和音频轨
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ActiveClipPlaybackInfo, Clip, ClipVisualTransform, PlayState, PreviewAspectRatio, PreviewMediaSource, PreviewSourceResolver, TextPreviewFontConfig, TimeMs } from '../models/types';
|
|
2
2
|
import type { SourceMediaRegistry } from '../resources/sourceMediaRegistry';
|
|
3
|
+
import type { ResourceCacheManager } from '../resources/resourceCache';
|
|
3
4
|
import type { DiagnosticsCenter, DiagnosticEmitInput } from '../../utils/diagnostics';
|
|
4
5
|
export interface TimelinePreviewSyncPayload {
|
|
5
6
|
activeClips: ActiveClipPlaybackInfo[];
|
|
@@ -72,6 +73,7 @@ export interface TimelinePreviewBackendOptions {
|
|
|
72
73
|
previewSourceResolver?: PreviewSourceResolver;
|
|
73
74
|
textPreviewFont?: TextPreviewFontConfig | null;
|
|
74
75
|
sourceMediaRegistry?: SourceMediaRegistry;
|
|
76
|
+
resourceCacheManager?: ResourceCacheManager | null;
|
|
75
77
|
diagnostics?: DiagnosticsCenter;
|
|
76
78
|
getDiagnosticsContext?: () => Partial<DiagnosticEmitInput>;
|
|
77
79
|
rootClassName?: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ActiveClipPlaybackInfo, ClipVisualTransform, PlayState, PreviewAspectRatio, PreviewSourceResolver, TextPreviewFontConfig, TimeMs, TrackType } from '../models/types';
|
|
2
2
|
import type { SourceMediaRegistry } from '../resources/sourceMediaRegistry';
|
|
3
|
+
import type { ResourceCacheManager } from '../resources/resourceCache';
|
|
3
4
|
import type { PreviewPendingState, PreviewRuntimeState } from './previewBackend';
|
|
4
5
|
import { type DiagnosticEmitInput, type DiagnosticsCenter } from '../../utils/diagnostics';
|
|
5
6
|
interface TimelinePreviewSessionCallbacks {
|
|
@@ -24,6 +25,7 @@ interface TimelinePreviewSessionDependencies {
|
|
|
24
25
|
previewSourceResolver?: PreviewSourceResolver;
|
|
25
26
|
textPreviewFont?: TextPreviewFontConfig | null;
|
|
26
27
|
sourceMediaRegistry?: SourceMediaRegistry;
|
|
28
|
+
resourceCacheManager?: ResourceCacheManager | null;
|
|
27
29
|
}
|
|
28
30
|
export interface TimelinePreviewSyncPayload {
|
|
29
31
|
activeClips: ActiveClipPlaybackInfo[];
|
|
@@ -94,12 +96,19 @@ export declare class TimelinePreviewSession {
|
|
|
94
96
|
private createSlot;
|
|
95
97
|
private resetSlotRecoveryTracking;
|
|
96
98
|
private destroySlot;
|
|
99
|
+
private releaseSlotObjectUrl;
|
|
100
|
+
private clearPendingRuntimeSource;
|
|
101
|
+
private rememberSlotObjectUrl;
|
|
102
|
+
private revokeResolvedObjectUrl;
|
|
97
103
|
private getTrackSlots;
|
|
98
104
|
private swapTrackSlots;
|
|
99
105
|
private applyTrackPlan;
|
|
100
106
|
private applySlotTarget;
|
|
107
|
+
private settleResolvedSourceWithoutRecovery;
|
|
101
108
|
private resolveDesiredSource;
|
|
102
109
|
private decorateSlotSourceUrl;
|
|
110
|
+
private resolvePlayableSlotSource;
|
|
111
|
+
private getReusableResolvedSlotSource;
|
|
103
112
|
private slotNeedsRecovery;
|
|
104
113
|
private recoverSlot;
|
|
105
114
|
private finishSlotRecovery;
|
|
@@ -114,7 +123,9 @@ export declare class TimelinePreviewSession {
|
|
|
114
123
|
private scheduleDeferredPreloadFlush;
|
|
115
124
|
private flushDeferredPreloads;
|
|
116
125
|
private applyResolvedSlotState;
|
|
126
|
+
private buildCurrentTargetForSlot;
|
|
117
127
|
private isCurrentRequest;
|
|
128
|
+
private isCurrentSourceTarget;
|
|
118
129
|
private shouldIgnoreExpectedEmptied;
|
|
119
130
|
private shouldIgnoreExpectedAbort;
|
|
120
131
|
private shouldIgnoreClearedSlotRecoverableEvent;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type TimelinePreviewBackend } from '../controllers';
|
|
2
|
+
import { type TimelineResourceCacheStats } from '../resources/resourceCache';
|
|
2
3
|
import { TimelineConfig, Clip, ClipConfig, TimeMs, PlayState, Action, TimelineEvent, EventListener as TimelineEventListener, TimelineExportData, Track as TrackEntity, ThumbnailProvider, TrackInsertionPlacement, TrackType, ActiveClipPlaybackInfo, PreviewAspectRatio, PreviewMountConfig, PreviewBackendType, SelectedClipAudioAction, ClipConfigVoicePanelOptions } from '../models';
|
|
3
4
|
import type { ResolvedPlaybackPlan } from '../controllers/timelinePlaybackResolver';
|
|
4
5
|
import { TimelineClipConfigController } from '../controllers/timelineClipConfigController';
|
|
@@ -78,6 +79,7 @@ export declare class TimelineManager {
|
|
|
78
79
|
private playbackAttemptId;
|
|
79
80
|
private lastPreviewSyncedPlayState;
|
|
80
81
|
private previewSyncInteractionMode;
|
|
82
|
+
private readonly resourceCacheManager;
|
|
81
83
|
constructor(config?: Partial<TimelineConfig>);
|
|
82
84
|
private createPlaybackAttemptId;
|
|
83
85
|
private refreshPlaybackAttempt;
|
|
@@ -506,6 +508,9 @@ export declare class TimelineManager {
|
|
|
506
508
|
* @returns 从 0 到最后一个音视频 clip 终点的时长(毫秒)
|
|
507
509
|
*/
|
|
508
510
|
getTrackTotalDuration(): TimeMs;
|
|
511
|
+
getResourceCacheStats(): Promise<TimelineResourceCacheStats>;
|
|
512
|
+
cleanupResourceCache(): Promise<void>;
|
|
513
|
+
clearResourceCache(): Promise<void>;
|
|
509
514
|
destroy(): void;
|
|
510
515
|
private initKeyboardShortcuts;
|
|
511
516
|
/** 清除历史堆栈 */
|
|
@@ -37,7 +37,12 @@ export interface HlsFmp4PreviewMediaSource {
|
|
|
37
37
|
mimeType: string;
|
|
38
38
|
kind: 'hls-fmp4';
|
|
39
39
|
}
|
|
40
|
-
export
|
|
40
|
+
export interface AudioPreviewMediaSource {
|
|
41
|
+
url: string;
|
|
42
|
+
mimeType: string;
|
|
43
|
+
kind: 'audio';
|
|
44
|
+
}
|
|
45
|
+
export type PreviewMediaSource = Mp4PreviewMediaSource | HlsFmp4PreviewMediaSource | AudioPreviewMediaSource;
|
|
41
46
|
export type PreviewSourceResolver = (clip: Clip) => PreviewMediaSource | null | Promise<PreviewMediaSource | null>;
|
|
42
47
|
export interface TextPreviewFontConfig {
|
|
43
48
|
fontName: string;
|
|
@@ -190,6 +195,15 @@ export interface TimelineKeyboardShortcutsConfig {
|
|
|
190
195
|
}
|
|
191
196
|
export declare const defaultDarkTheme: Theme;
|
|
192
197
|
export declare function resolveTheme(theme?: ThemeConfig): Theme;
|
|
198
|
+
export interface TimelineResourceCacheConfig {
|
|
199
|
+
enabled?: boolean;
|
|
200
|
+
maxEntryBytes?: number;
|
|
201
|
+
maxTotalBytes?: number;
|
|
202
|
+
ttlMs?: number;
|
|
203
|
+
preferStorage?: 'opfs' | 'indexeddb';
|
|
204
|
+
resolveMode?: 'prefer-fast-start' | 'prefer-cache-ready';
|
|
205
|
+
allowedMimeTypes?: string[];
|
|
206
|
+
}
|
|
193
207
|
export interface TimelineConfig {
|
|
194
208
|
duration: TimeMs;
|
|
195
209
|
zoom: number;
|
|
@@ -206,6 +220,7 @@ export interface TimelineConfig {
|
|
|
206
220
|
thumbnailProvider?: ThumbnailProvider;
|
|
207
221
|
previewBackend?: PreviewBackendType;
|
|
208
222
|
previewSourceResolver?: PreviewSourceResolver;
|
|
223
|
+
resourceCache?: boolean | TimelineResourceCacheConfig;
|
|
209
224
|
textPreviewFont?: TextPreviewFontConfig | null;
|
|
210
225
|
draftData?: TimelineExportData;
|
|
211
226
|
keyboardShortcuts?: false | TimelineKeyboardShortcutsConfig;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ResolveResourceInput, ResourceCacheConfig, ResourceCacheOptions } from './types';
|
|
2
|
+
export declare function normalizeResourceCacheOptions(config: ResourceCacheConfig | undefined): ResourceCacheOptions;
|
|
3
|
+
export declare function normalizeResourceUrl(url: string): string;
|
|
4
|
+
export declare function buildResourceCacheKey(input: Pick<ResolveResourceInput, 'sourceKey' | 'url'>): Promise<string>;
|
|
5
|
+
export declare function shouldBypassResourceCache(input: ResolveResourceInput, allowedMimeTypes?: string[]): boolean;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './types';
|
|
2
|
+
export { IndexedDbResourceMetadataStore, createMemoryMetadataStore } from './metadataStore';
|
|
3
|
+
export { IndexedDbResourceStorage, OpfsResourceStorage, createMemoryResourceStorage, createResourceCacheStorage } from './storage';
|
|
4
|
+
export { buildResourceCacheKey, normalizeResourceCacheOptions, normalizeResourceUrl, shouldBypassResourceCache } from './cacheKey';
|
|
5
|
+
export { FetchResourceCacheHttpClient } from './http';
|
|
6
|
+
export { ResourceCacheManager } from './resourceCacheManager';
|
|
7
|
+
export type { ResourceCacheManagerDependencies } from './resourceCacheManager';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CachedResourceMetadata, ResourceCacheMetadataStore } from './types';
|
|
2
|
+
export declare class IndexedDbResourceMetadataStore implements ResourceCacheMetadataStore {
|
|
3
|
+
private dbPromise;
|
|
4
|
+
private getDb;
|
|
5
|
+
get(cacheKey: string): Promise<CachedResourceMetadata | null>;
|
|
6
|
+
put(metadata: CachedResourceMetadata): Promise<void>;
|
|
7
|
+
delete(cacheKey: string): Promise<void>;
|
|
8
|
+
clear(): Promise<void>;
|
|
9
|
+
list(): Promise<CachedResourceMetadata[]>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createMemoryMetadataStore(initial?: CachedResourceMetadata[]): ResourceCacheMetadataStore;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ObjectUrlAdapter, ResolveResourceInput, ResolvedCachedResource, ResourceCacheConfig, ResourceCacheHttpClient, ResourceCacheMetadataStore, ResourceCacheStats, ResourceCacheStorage } from './types';
|
|
2
|
+
export interface ResourceCacheManagerDependencies {
|
|
3
|
+
options?: ResourceCacheConfig;
|
|
4
|
+
metadataStore?: ResourceCacheMetadataStore;
|
|
5
|
+
storage?: ResourceCacheStorage;
|
|
6
|
+
http?: ResourceCacheHttpClient;
|
|
7
|
+
objectUrl?: ObjectUrlAdapter;
|
|
8
|
+
now?: () => number;
|
|
9
|
+
cacheKeyBuilder?: (input: Pick<ResolveResourceInput, 'sourceKey' | 'url'>) => Promise<string>;
|
|
10
|
+
onEvent?: (eventName: string, data: Record<string, unknown>) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare class ResourceCacheManager {
|
|
13
|
+
private readonly options;
|
|
14
|
+
private readonly metadataStore;
|
|
15
|
+
private readonly storagePromise;
|
|
16
|
+
private readonly http;
|
|
17
|
+
private readonly objectUrl;
|
|
18
|
+
private readonly now;
|
|
19
|
+
private readonly cacheKeyBuilder;
|
|
20
|
+
private readonly onEvent?;
|
|
21
|
+
private readonly objectUrls;
|
|
22
|
+
private readonly inflightDownloads;
|
|
23
|
+
constructor(dependencies?: ResourceCacheManagerDependencies);
|
|
24
|
+
resolve(input: ResolveResourceInput): Promise<ResolvedCachedResource>;
|
|
25
|
+
resolvePlayableUrl(input: ResolveResourceInput): Promise<ResolvedCachedResource>;
|
|
26
|
+
revokeObjectUrl(url: string): void;
|
|
27
|
+
cleanupExpired(): Promise<void>;
|
|
28
|
+
clear(): Promise<void>;
|
|
29
|
+
getStats(): Promise<ResourceCacheStats>;
|
|
30
|
+
private resolveExpired;
|
|
31
|
+
private probeCacheable;
|
|
32
|
+
private probeAndDownload;
|
|
33
|
+
private getOrCreateDownload;
|
|
34
|
+
private downloadAndStore;
|
|
35
|
+
private evictForWrite;
|
|
36
|
+
private deleteEntry;
|
|
37
|
+
private blobResult;
|
|
38
|
+
private emitEvent;
|
|
39
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ResourceCacheStorage, ResourceCacheStorageKind } from './types';
|
|
2
|
+
export declare class IndexedDbResourceStorage implements ResourceCacheStorage {
|
|
3
|
+
readonly kind: "indexeddb";
|
|
4
|
+
private dbPromise;
|
|
5
|
+
private getDb;
|
|
6
|
+
read(cacheKey: string): Promise<Blob | null>;
|
|
7
|
+
write(cacheKey: string, blob: Blob): Promise<void>;
|
|
8
|
+
delete(cacheKey: string): Promise<void>;
|
|
9
|
+
clear(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare class OpfsResourceStorage implements ResourceCacheStorage {
|
|
12
|
+
readonly kind: "opfs";
|
|
13
|
+
private directoryPromise;
|
|
14
|
+
private getDirectory;
|
|
15
|
+
ensureReady(): Promise<boolean>;
|
|
16
|
+
read(cacheKey: string): Promise<Blob | null>;
|
|
17
|
+
write(cacheKey: string, blob: Blob): Promise<void>;
|
|
18
|
+
delete(cacheKey: string): Promise<void>;
|
|
19
|
+
clear(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export declare function createMemoryResourceStorage(kind?: ResourceCacheStorageKind): ResourceCacheStorage;
|
|
22
|
+
export declare function createResourceCacheStorage(options: {
|
|
23
|
+
preferStorage: ResourceCacheStorageKind;
|
|
24
|
+
}): Promise<ResourceCacheStorage>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export type ResourceCacheStorageKind = 'opfs' | 'indexeddb';
|
|
2
|
+
export type ResourceCacheResolveMode = 'prefer-fast-start' | 'prefer-cache-ready';
|
|
3
|
+
export type ResourceCacheStatus = 'hit' | 'miss' | 'bypass' | 'stale-revalidated' | 'fallback';
|
|
4
|
+
export interface ResourceCacheOptions {
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
maxEntryBytes: number;
|
|
7
|
+
maxTotalBytes: number;
|
|
8
|
+
ttlMs: number;
|
|
9
|
+
preferStorage: ResourceCacheStorageKind;
|
|
10
|
+
resolveMode: ResourceCacheResolveMode;
|
|
11
|
+
allowedMimeTypes: string[];
|
|
12
|
+
}
|
|
13
|
+
export type ResourceCacheConfig = boolean | Partial<ResourceCacheOptions>;
|
|
14
|
+
export interface ResolveResourceInput {
|
|
15
|
+
url: string;
|
|
16
|
+
mimeType?: string | null;
|
|
17
|
+
kind?: string | null;
|
|
18
|
+
sourceKey?: string | null;
|
|
19
|
+
}
|
|
20
|
+
export interface ResolvedCachedResource {
|
|
21
|
+
url: string;
|
|
22
|
+
cacheStatus: ResourceCacheStatus;
|
|
23
|
+
sourceUrl: string;
|
|
24
|
+
sizeBytes?: number;
|
|
25
|
+
objectUrl?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ResourceProbeResult {
|
|
28
|
+
url: string;
|
|
29
|
+
ok: boolean;
|
|
30
|
+
status: number;
|
|
31
|
+
mimeType: string | null;
|
|
32
|
+
sizeBytes: number | null;
|
|
33
|
+
etag: string | null;
|
|
34
|
+
lastModified: string | null;
|
|
35
|
+
acceptRanges: string | null;
|
|
36
|
+
}
|
|
37
|
+
export interface CachedResourceMetadata {
|
|
38
|
+
cacheKey: string;
|
|
39
|
+
sourceUrl: string;
|
|
40
|
+
mimeType: string | null;
|
|
41
|
+
sizeBytes: number;
|
|
42
|
+
etag: string | null;
|
|
43
|
+
lastModified: string | null;
|
|
44
|
+
contentLength: number | null;
|
|
45
|
+
storageKind: ResourceCacheStorageKind;
|
|
46
|
+
createdAt: number;
|
|
47
|
+
updatedAt: number;
|
|
48
|
+
lastAccessedAt: number;
|
|
49
|
+
expiresAt: number;
|
|
50
|
+
}
|
|
51
|
+
export interface ResourceCacheStats {
|
|
52
|
+
entryCount: number;
|
|
53
|
+
totalBytes: number;
|
|
54
|
+
maxTotalBytes: number;
|
|
55
|
+
}
|
|
56
|
+
export interface TimelineResourceCacheStats extends ResourceCacheStats {
|
|
57
|
+
enabled: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface ResourceCacheStorage {
|
|
60
|
+
readonly kind: ResourceCacheStorageKind;
|
|
61
|
+
read(cacheKey: string): Promise<Blob | null>;
|
|
62
|
+
write(cacheKey: string, blob: Blob): Promise<void>;
|
|
63
|
+
delete(cacheKey: string): Promise<void>;
|
|
64
|
+
clear(): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
export interface ResourceCacheMetadataStore {
|
|
67
|
+
get(cacheKey: string): Promise<CachedResourceMetadata | null>;
|
|
68
|
+
put(metadata: CachedResourceMetadata): Promise<void>;
|
|
69
|
+
delete(cacheKey: string): Promise<void>;
|
|
70
|
+
clear(): Promise<void>;
|
|
71
|
+
list(): Promise<CachedResourceMetadata[]>;
|
|
72
|
+
}
|
|
73
|
+
export interface ResourceCacheHttpClient {
|
|
74
|
+
probe(url: string): Promise<ResourceProbeResult>;
|
|
75
|
+
download(url: string): Promise<Blob>;
|
|
76
|
+
}
|
|
77
|
+
export interface ObjectUrlAdapter {
|
|
78
|
+
createObjectURL(blob: Blob): string;
|
|
79
|
+
revokeObjectURL(url: string): void;
|
|
80
|
+
}
|
|
81
|
+
export declare const RESOURCE_CACHE_DEFAULT_MAX_ENTRY_BYTES: number;
|
|
82
|
+
export declare const RESOURCE_CACHE_DEFAULT_MAX_TOTAL_BYTES: number;
|
|
83
|
+
export declare const RESOURCE_CACHE_DEFAULT_TTL_MS: number;
|
|
84
|
+
export declare const RESOURCE_CACHE_DEFAULT_ALLOWED_MIME_TYPES: string[];
|
|
85
|
+
export declare const RESOURCE_CACHE_DEFAULT_OPTIONS: ResourceCacheOptions;
|