@volcengine/amk-editor 0.0.2-beta.1 → 0.0.3

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 CHANGED
@@ -51,6 +51,9 @@ editor.requestMaterialImport('local', {
51
51
 
52
52
  // 从服务端重新拉取当前工程,并热更新素材与 Track(不写入撤销历史)。
53
53
  await editor.refreshProject();
54
+
55
+ // 查询由编辑器操作创建的异步任务;复用相同的 endpoint 和 getHeaders。
56
+ const exportTask = await editor.getTask('your-task-id');
54
57
  ```
55
58
  页面卸载时销毁:
56
59
  ```ts
@@ -62,15 +65,19 @@ editor.destroy();
62
65
  - 产物是单文件 ESM。依赖里的 AMD `define(["./core"])` 已在构建时剔除,避免 Next.js / webpack 误解析不存在的 `dist/core.js`。
63
66
  - `getHeaders` 可选,每次请求前都会重新调用;SDK 不解析、不缓存、不改写返回值,仅将其合并到发往 `endpoint` 的工程、素材、tools、tasks、导出等请求中。闭包内 Token 更新会自动用于下一次请求。
64
67
  - 本地上传、URL 导入由业务回调完成。返回的素材必须带业务主键 `source_material_id`,不必填写 `material_id`。
65
- - 播放地址、封面、雪碧图可能有时效。通过 `onRefreshPlayInfo` 返回新的访问地址。
68
+ - 播放地址、封面、雪碧图可能有时效。通过 `onRefreshPlayInfo` 返回新的访问地址;补丁按 `material_id`、`source_material_id`、无标识时数组顺序依次匹配,未返回的字段保持原值。
66
69
  - 可选实现 `onMaterialsImported`:素材成功入库后回调已合并 `material_id` 的列表;可用于把新素材挂到对话草稿。失败或取消不会触发。
67
70
  - 可选实现 `onPersistExtractUrls`:把抽帧得到的临时地址转存为业务长期地址,并随 `materialPatch` 写回自定义存储字段。
68
71
  - 可选配置 `export.qualityEnhancement: true` 以在导出弹窗展示「视频画质增强」;默认不展示。
69
72
  - 可选配置 `header.mount` 为外部 DOM 节点,将顶栏渲染到该节点(而不是编辑器内部),便于宿主做全宽顶栏;`header.show: false` 仍可完全隐藏顶栏。
70
73
  - 可选配置 `header.onTitleUpdateSuccess`:工程名 PATCH 成功后回调 `{ projectId, title }`。保存成功前编辑器继续展示旧名称;保存失败不会修改编辑器标题,也不会触发回调。
74
+ - 可选配置 `header.exportTaskList: false` 隐藏导出任务列表入口;默认开启。关闭后不会展示任务列表按钮,也不会启动导出任务列表查询与轮询。
71
75
  - `requestMaterialImport(type, options?)` 返回是否成功触发已配置入口;`local`、`url`、`system` 分别要求存在本地上传能力、`onUploadUrlMaterial`、`onUploadFromSystem`。外部入口不会复制上传逻辑,仍走素材面板相同的回调、TOS 签名、素材入库与元信息处理。可选的 `options.onMaterialsImported` 只对这一次外部触发有效,编辑器自身的导入按钮不会调用它。
72
76
  - `toolbar.customItems` 的只读态不会自动置灰。需要禁用时在该项 `disabled({ readonly })` 里自行返回 true(例如 `disabled: ({ readonly }) => readonly`)。录音中仍会全局锁定自定义项。内置 ASR 按钮在只读时仍会禁用。
73
77
  - `refreshProject()` 会等待当前保存队列结束,再重新拉取工程、刷新临时播放地址并热更新素材与 Track;远端 Track 不会写入本地撤销历史,也不会被自动保存回服务端。
78
+ - `getTask(taskId, options?)` 查询 `GET /api/v1/tasks/{task_id}`,复用编辑器初始化时的 `endpoint` 与 `getHeaders`;`options.signal` 可在宿主任务卡卸载时取消轮询请求。
79
+ - 配置 `projectId` 后,API Client 会把 `invokeTool`、`invokeSyncTool` 的成功响应以及 `getTask` 的轮询结果,以 schema v2 best-effort 上报到 `POST /api/v1/editing/projects/{project_id}/tasks`。记录包含 `task_id`、`tool_name`、同步/异步模式、状态、原始请求和响应;台账写入失败只输出 warning,不会让已经成功的 AMK 调用失败。轮询上报会省略 `request.input`,避免覆盖首次提交保存的请求参数。
80
+ - mediakit-studio `dev` 当前只提供上述任务台账写接口,尚无任务列表/详情 GET 接口。因此编辑器顶栏的导出记录仍只保留当前页面会话;如需刷新后读取历史导出列表,需要服务端补充按工程查询任务的接口。
74
81
  ## 样式隔离
75
82
  SDK 会随 `import '@volcengine/amk-editor'` 自动注入样式,导航栏、左侧分类、素材网格的布局由 SDK 自己负责。**接入方不必再写补丁 CSS 才能让界面正常显示。**
76
83
  编辑器挂在普通 DOM(`#track-video-editor`)里,不是 Shadow DOM。宿主页面里针对 `section` / `nav` / `aside` 的全局布局重置会穿透进来,把顶栏或左侧分类挤扁。
@@ -10,12 +10,18 @@ export declare class AmkEditor {
10
10
  private layout?;
11
11
  private exportModal?;
12
12
  private exportRequestInFlight?;
13
+ private readonly exportTaskPollingControllers;
14
+ private readonly exportTaskPollingTokens;
15
+ private exportTasks;
16
+ private exportTaskListRequest?;
13
17
  private importActions?;
14
18
  private asrProgressModal?;
15
19
  private confirmModal?;
16
20
  private emptyTrackConfirmInFlight?;
17
21
  private readonly apiClient;
18
22
  private readonly ready;
23
+ private readonly bootstrapAbortController;
24
+ private destroyed;
19
25
  private projectName?;
20
26
  private editorRevision;
21
27
  private projectHydrated;
@@ -25,6 +31,11 @@ export declare class AmkEditor {
25
31
  private emptyTrackSaveBlocked;
26
32
  private savedMaterials;
27
33
  private saveQueue;
34
+ /** 最近一次 GET 或成功 PUT 后的服务端 editor 深比较基线。 */
35
+ private latestEditorFingerprint;
36
+ private latestEditorHasTrack;
37
+ /** 正在等待或执行的 editor 文档快照,用于合并高频重复事件。 */
38
+ private readonly pendingEditorSaveFingerprints;
28
39
  private projectRefreshInFlight?;
29
40
  private pendingSaveCount;
30
41
  private readonly asrPollingControllers;
@@ -50,6 +61,8 @@ export declare class AmkEditor {
50
61
  private readonly thumbnailErrorInFlight;
51
62
  /** 前端抽帧封面缓存(不落盘):key = materialKey,value = dataURL */
52
63
  private readonly posterCache;
64
+ /** 本次会话签名 URL 对应的原始 TOS 身份,供产物落盘时保留重签依据。 */
65
+ private readonly tosIdentityBySignedUrl;
53
66
  /** 同一 URL 的封面抽帧只执行一次,避免初始化与上传回调重复请求。 */
54
67
  private readonly posterCaptureInFlight;
55
68
  /** 销毁编辑器时中断尚未完成的前端抽帧或 AMK 首帧轮询。 */
@@ -59,6 +72,10 @@ export declare class AmkEditor {
59
72
  /** 所有 TOS 素材共用一个刷新定时器,避免按素材创建大量 timer。 */
60
73
  private tosSignedUrlRefreshTimer?;
61
74
  private tosSignedUrlRefreshInFlight?;
75
+ /** 同一轮播放地址刷新合并 materialId,并串行执行各批次,避免并发覆盖轨道和 revision。 */
76
+ private readonly pendingPlayInfoRefreshes;
77
+ private playInfoRefreshBatchScheduled;
78
+ private playInfoRefreshQueue;
62
79
  private readonly handleVisibilityChange;
63
80
  private readonly handleTrackChanged;
64
81
  private readonly handleEditParamChanged;
@@ -69,6 +86,15 @@ export declare class AmkEditor {
69
86
  getI18n(): AmkI18nInstance;
70
87
  /** 通用工具调用:POST /api/v1/tools/{toolName} */
71
88
  invokeTool<T = unknown>(options: AmkInvokeToolParams): Promise<T>;
89
+ /**
90
+ * Queries an asynchronous AMK task through the editor's configured endpoint
91
+ * and headers. Intended for host UI cards that track tasks created by editor
92
+ * operations such as {@link requestExport}.
93
+ */
94
+ getTask<T = unknown>(taskId: string, options?: {
95
+ headers?: Record<string, string>;
96
+ signal?: AbortSignal;
97
+ }): Promise<T>;
72
98
  /**
73
99
  * Opens the existing export modal and resolves after the user confirms or cancels.
74
100
  * Intended for Agent frontend tools (`request_export_task`). Header export still
@@ -106,13 +132,19 @@ export declare class AmkEditor {
106
132
  * @param materialIdOrIds 单个 material_id、material_id 列表;省略则刷新当前全部有 material_id 的素材
107
133
  */
108
134
  refreshPlayInfo(materialIdOrIds?: string | string[]): Promise<MaterialItem | MaterialItem[] | undefined>;
135
+ private flushPlayInfoRefreshBatch;
109
136
  private refreshPlayInfoInternal;
137
+ /**
138
+ * 异步初始化入口:并行加载工程与预设,完成数据归一化后再挂载编辑器,
139
+ * 最后注册运行期的 URL 刷新和页面可见性监听。destroy() 可随时中断该流程。
140
+ */
110
141
  private bootstrap;
111
142
  /** 内置拉取工程:project + materials + editor → 归一化 → 组装 initialData。 */
112
143
  private resolveAndApplyProject;
113
144
  /**
114
145
  * 初始化时批量 refresh 素材(完整素材列表交给业务方),
115
- * 再把轨道元素里的 URL 回填到最新值,完成后回写 editor 文档。
146
+ * 再把轨道元素里的 URL 回填到当前运行时。仅 UserData 变化不回写 editor 文档;
147
+ * Source 等真实协议字段变化时才保存。
116
148
  */
117
149
  private refreshProjectOnInit;
118
150
  /** TOS 私有桶签名刷新优先,宿主刷新回调随后可继续覆盖播放信息。 */
@@ -122,6 +154,7 @@ export declare class AmkEditor {
122
154
  * URL 是运行时数据:保存工程,但不产生撤销步骤。
123
155
  */
124
156
  private applyTrackMaterialUrls;
157
+ private resolveTosSignedResourceUrls;
125
158
  private getExpiringTosMaterialIds;
126
159
  private scheduleTosSignedUrlRefresh;
127
160
  private refreshExpiringTosUrls;
@@ -130,6 +163,7 @@ export declare class AmkEditor {
130
163
  mount(): VeVeditorInstance | undefined;
131
164
  private mountVeVeditor;
132
165
  destroy(): void;
166
+ private assertNotDestroyed;
133
167
  private resolveConfig;
134
168
  /** 与 VeVeditor 同一套顶层扁平资源字段:boolean 面板开关 + 内部 xgplayer 组装 */
135
169
  private resolveVeVeditorOptions;
@@ -142,6 +176,7 @@ export declare class AmkEditor {
142
176
  */
143
177
  private deleteMaterial;
144
178
  private queueEditorSave;
179
+ private setLatestEditorSnapshot;
145
180
  private confirmAbnormalEmptyTrackSave;
146
181
  private enqueueEditorSaveWithCurrentEditParam;
147
182
  /**
@@ -149,6 +184,10 @@ export declare class AmkEditor {
149
184
  * 适配只作用于即将提交给导出接口的 editParam 副本,不写回工程或当前预览。
150
185
  */
151
186
  private submitExport;
187
+ private startExportTaskStatusPolling;
188
+ private refreshLatestExportTasks;
189
+ private loadLatestExportTasks;
190
+ private clearExportTaskPolling;
152
191
  private attach;
153
192
  private detach;
154
193
  private clearAsrPollingTimers;
@@ -252,6 +291,8 @@ export declare class AmkEditor {
252
291
  */
253
292
  private ensureMaterialExtractTasks;
254
293
  private createExtractFramesTask;
294
+ /** AMK 转存到私有 TOS 后,任务产物需先重签才能用于浏览器预览。 */
295
+ private signTaskOutputUrls;
255
296
  private startSpritePolling;
256
297
  private startPosterPolling;
257
298
  private buildSpriteFromExtractResult;
@@ -1,6 +1,8 @@
1
1
  import type { AmkEditorGetHeaders } from '../types';
2
2
  export type AmkApiClientOptions = {
3
3
  endpoint: string;
4
+ /** 当前工程 ID,用于统一注入 tools 请求的 callback_args。 */
5
+ projectId?: string;
4
6
  /** 每次请求前动态获取并原样透传给客户业务代理的请求头。 */
5
7
  getHeaders?: AmkEditorGetHeaders;
6
8
  /** 调用 tools 接口时写入请求体的产物存储目标,例如 `tos://bucket`。 */
@@ -16,13 +18,25 @@ export type AmkInvokeToolParams = {
16
18
  };
17
19
  export declare class AmkApiClient {
18
20
  private readonly baseUrl;
21
+ private readonly projectId?;
19
22
  private readonly getHeaders?;
20
23
  private readonly mediaOutputDestination?;
24
+ private readonly taskToolNames;
25
+ private readonly terminalTaskReports;
26
+ private syncTaskSequence;
21
27
  constructor(options: AmkApiClientOptions);
22
28
  getBaseUrl(): string;
23
29
  invokeTool<T = unknown>(options: AmkInvokeToolParams): Promise<T>;
24
30
  invokeSyncTool<T = unknown>(options: AmkInvokeToolParams): Promise<T>;
31
+ private createSyncTaskId;
32
+ private rememberTaskTool;
33
+ private reportTaskObservation;
25
34
  private mergeToolParams;
35
+ /**
36
+ * callback_args 约定以 JSON 字符串传输。保留调用方已有字段,并统一补充
37
+ * project_id,避免各个工具调用点重复组装且漏传。
38
+ */
39
+ private mergeCallbackArgs;
26
40
  /**
27
41
  * 查询异步任务:GET /api/v1/tasks/{task_id}
28
42
  * @see https://docs.volcengine.com/docs/6448/2278532
@@ -31,6 +45,14 @@ export declare class AmkApiClient {
31
45
  headers?: Record<string, string>;
32
46
  signal?: AbortSignal;
33
47
  }): Promise<T>;
48
+ /** 查询当前工程任务台账;服务端按 submitted_at 倒序返回。 */
49
+ listProjectTasks<T = unknown>(options?: {
50
+ toolNames?: string[];
51
+ page?: number;
52
+ pageSize?: number;
53
+ includeTotal?: boolean;
54
+ signal?: AbortSignal;
55
+ }): Promise<T>;
34
56
  requestJson<T = unknown>(path: string, options?: {
35
57
  method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
36
58
  headers?: Record<string, string>;
@@ -10,4 +10,6 @@ export type PresetLibraryItem = {
10
10
  default?: boolean;
11
11
  };
12
12
  export type PresetLibrary = Partial<Record<PresetLibraryResourceKey, PresetLibraryItem[]>>;
13
- export declare function fetchPresetLibrary(client: Pick<AmkApiClient, 'requestJson'>, resources: PresetLibraryResourceKey[]): Promise<PresetLibrary>;
13
+ export declare function fetchPresetLibrary(client: Pick<AmkApiClient, 'requestJson'>, resources: PresetLibraryResourceKey[], options?: {
14
+ signal?: AbortSignal;
15
+ }): Promise<PresetLibrary>;
@@ -8,8 +8,12 @@ export type SavedAmkEditor = {
8
8
  revision: number;
9
9
  editParam: Record<string, unknown>;
10
10
  };
11
- export declare function fetchAmkProjectMaterials(client: EditingApiClient, projectId: string): Promise<MaterialItem[]>;
12
- export declare function fetchAmkProject(client: EditingApiClient, projectId: string): Promise<LoadedAmkProject>;
11
+ export declare function fetchAmkProjectMaterials(client: EditingApiClient, projectId: string, options?: {
12
+ signal?: AbortSignal;
13
+ }): Promise<MaterialItem[]>;
14
+ export declare function fetchAmkProject(client: EditingApiClient, projectId: string, options?: {
15
+ signal?: AbortSignal;
16
+ }): Promise<LoadedAmkProject>;
13
17
  export declare function updateAmkProjectName(client: EditingApiClient, projectId: string, projectName: string): Promise<void>;
14
18
  /**
15
19
  * POST 一批新素材到 AMK 工程。
@@ -0,0 +1,15 @@
1
+ import { type TaskPollingController } from './task-polling';
2
+ export declare const EXPORT_TASK_POLL_INTERVAL_MS = 5000;
3
+ export declare const EXPORT_TASK_VISIBLE_WINDOW_MS: number;
4
+ export type ExportTaskPollingStatus = 'polling' | 'success' | 'failed';
5
+ export declare function resolveExportTaskPollingStatus(payload: unknown): ExportTaskPollingStatus;
6
+ export declare function resolveExportTaskProgress(payload: unknown): number | undefined;
7
+ export declare function resolveExportTaskOutputUrl(payload: unknown): string | undefined;
8
+ /** 服务端仍查最新 10 条,前端只展示提交时间位于最近 24 小时内的任务。 */
9
+ export declare function filterRecentExportTasks<T extends Record<string, unknown>>(items: T[], now?: number): T[];
10
+ export declare function startExportTaskPolling(options: {
11
+ taskId: string;
12
+ getTask: (taskId: string) => Promise<unknown>;
13
+ onStatusChange: (status: ExportTaskPollingStatus, payload?: unknown) => void | Promise<void>;
14
+ onError?: (error: unknown) => void | Promise<void>;
15
+ }): TaskPollingController;
package/dist/header.d.ts CHANGED
@@ -9,9 +9,13 @@ export interface CreateAmkEditorLayoutOptions {
9
9
  title: string;
10
10
  theme?: 'light' | 'dark';
11
11
  showHeader: boolean;
12
+ /** 是否展示导出任务列表入口,默认开启。 */
13
+ showExportTaskList?: boolean;
12
14
  /** When set, append the header here instead of inside the editor root. */
13
15
  headerMount?: HTMLElement;
14
16
  onExport: () => void;
17
+ /** 导出任务列表每次从关闭变为展开时触发。 */
18
+ onExportTasksOpen?: () => void | Promise<void>;
15
19
  /** 传入则展示返回按钮 */
16
20
  onBack?: () => void;
17
21
  /** 传入则展示关闭按钮 */
@@ -20,11 +24,27 @@ export interface CreateAmkEditorLayoutOptions {
20
24
  t?: AmkI18nInstance['t'];
21
25
  }
22
26
  export type AmkSaveStatus = 'saving' | 'saved';
27
+ export type AmkExportTaskStatus = 'idle' | 'polling' | 'success' | 'failed';
28
+ export type AmkExportTaskListItem = {
29
+ taskId: string;
30
+ name: string;
31
+ format: 'mp4' | 'mp3';
32
+ status: Exclude<AmkExportTaskStatus, 'idle'>;
33
+ /** 任务提交时间;历史任务来自任务列表接口的 submitted_at。 */
34
+ submittedAt?: string;
35
+ /** 服务端返回的任务进度(0-100);没有进度时使用不定长动画。 */
36
+ progress?: number;
37
+ /** 是否已在当前页面触发过浏览器下载。 */
38
+ downloaded?: boolean;
39
+ outputUrl?: string;
40
+ };
23
41
  export interface AmkEditorLayout {
24
42
  root: HTMLDivElement;
25
43
  workspace: HTMLDivElement;
26
44
  setTitle: (title: string) => void;
27
45
  setSaveStatus: (status: AmkSaveStatus) => void;
46
+ setExportTasks: (tasks: AmkExportTaskListItem[]) => void;
47
+ openExportTasks: () => void;
28
48
  destroy: () => void;
29
49
  }
30
50
  export declare function resolveHeaderTitle(options: ResolveHeaderTitleOptions): string;
@@ -16,6 +16,21 @@ export declare const AMK_I18N_KEYS: {
16
16
  readonly saving: "amk.header.saving";
17
17
  readonly saved: "amk.header.saved";
18
18
  readonly exportVideo: "amk.header.exportVideo";
19
+ readonly exportTasks: "amk.header.exportTasks";
20
+ readonly exportTaskTooltip: "amk.header.exportTaskTooltip";
21
+ readonly exportTaskPolling: "amk.header.exportTaskPolling";
22
+ readonly exportTaskSuccess: "amk.header.exportTaskSuccess";
23
+ readonly exportTaskFailed: "amk.header.exportTaskFailed";
24
+ readonly exportTaskEmpty: "amk.header.exportTaskEmpty";
25
+ readonly exportTaskDownloadReady: "amk.header.exportTaskDownloadReady";
26
+ readonly exportTaskDownload: "amk.header.exportTaskDownload";
27
+ readonly exportTaskDownloadAgain: "amk.header.exportTaskDownloadAgain";
28
+ readonly exportTaskDownloading: "amk.header.exportTaskDownloading";
29
+ readonly exportTaskDownloadFailed: "amk.header.exportTaskDownloadFailed";
30
+ readonly exportTaskDownloadCorsHint: "amk.header.exportTaskDownloadCorsHint";
31
+ readonly exportTaskView: "amk.header.exportTaskView";
32
+ readonly exportTaskVideo: "amk.header.exportTaskVideo";
33
+ readonly exportTaskAudio: "amk.header.exportTaskAudio";
19
34
  readonly titleRuleEmpty: "amk.header.titleRuleEmpty";
20
35
  readonly titleRuleMaxLength: "amk.header.titleRuleMaxLength";
21
36
  readonly titleRuleCharset: "amk.header.titleRuleCharset";