@bylqwjc/media-editor-video 1.0.0

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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -0
  3. package/dist/chunk-EK7ODJWE.js +1 -0
  4. package/dist/index-CX2iAapm.d.ts +51 -0
  5. package/dist/index.d.ts +61 -0
  6. package/dist/index.js +2273 -0
  7. package/dist/lazy.d.ts +7 -0
  8. package/dist/lazy.js +1 -0
  9. package/dist/legacy.js +65 -0
  10. package/node_modules/@media-editor/client/LICENSE +21 -0
  11. package/node_modules/@media-editor/client/README.md +27 -0
  12. package/node_modules/@media-editor/client/dist/index.d.ts +24 -0
  13. package/node_modules/@media-editor/client/dist/index.js +1 -0
  14. package/node_modules/@media-editor/client/package.json +22 -0
  15. package/node_modules/@media-editor/core/LICENSE +21 -0
  16. package/node_modules/@media-editor/core/README.md +14 -0
  17. package/node_modules/@media-editor/core/dist/browser.d.ts +100 -0
  18. package/node_modules/@media-editor/core/dist/browser.js +3 -0
  19. package/node_modules/@media-editor/core/dist/chunk-MGKYVYOH.js +1 -0
  20. package/node_modules/@media-editor/core/dist/chunk-USY6UDGL.js +1 -0
  21. package/node_modules/@media-editor/core/dist/chunk-XCBM7P7N.js +1 -0
  22. package/node_modules/@media-editor/core/dist/engine.d.ts +359 -0
  23. package/node_modules/@media-editor/core/dist/engine.js +1 -0
  24. package/node_modules/@media-editor/core/dist/index.d.ts +264 -0
  25. package/node_modules/@media-editor/core/dist/index.js +1 -0
  26. package/node_modules/@media-editor/core/dist/platform-presets-B-o4C6uY.d.ts +80 -0
  27. package/node_modules/@media-editor/core/dist/state-B85vkf7_.d.ts +1027 -0
  28. package/node_modules/@media-editor/core/node_modules/zod/LICENSE +21 -0
  29. package/node_modules/@media-editor/core/node_modules/zod/README.md +208 -0
  30. package/node_modules/@media-editor/core/node_modules/zod/index.d.cts +4 -0
  31. package/node_modules/@media-editor/core/node_modules/zod/index.js +1 -0
  32. package/node_modules/@media-editor/core/node_modules/zod/package.json +74 -0
  33. package/node_modules/@media-editor/core/node_modules/zod/v3/ZodError.d.cts +164 -0
  34. package/node_modules/@media-editor/core/node_modules/zod/v3/ZodError.js +1 -0
  35. package/node_modules/@media-editor/core/node_modules/zod/v3/errors.d.cts +5 -0
  36. package/node_modules/@media-editor/core/node_modules/zod/v3/errors.js +1 -0
  37. package/node_modules/@media-editor/core/node_modules/zod/v3/external.d.cts +6 -0
  38. package/node_modules/@media-editor/core/node_modules/zod/v3/external.js +1 -0
  39. package/node_modules/@media-editor/core/node_modules/zod/v3/helpers/enumUtil.d.cts +8 -0
  40. package/node_modules/@media-editor/core/node_modules/zod/v3/helpers/enumUtil.js +0 -0
  41. package/node_modules/@media-editor/core/node_modules/zod/v3/helpers/errorUtil.d.cts +9 -0
  42. package/node_modules/@media-editor/core/node_modules/zod/v3/helpers/errorUtil.js +1 -0
  43. package/node_modules/@media-editor/core/node_modules/zod/v3/helpers/parseUtil.d.cts +78 -0
  44. package/node_modules/@media-editor/core/node_modules/zod/v3/helpers/parseUtil.js +1 -0
  45. package/node_modules/@media-editor/core/node_modules/zod/v3/helpers/partialUtil.d.cts +8 -0
  46. package/node_modules/@media-editor/core/node_modules/zod/v3/helpers/partialUtil.js +0 -0
  47. package/node_modules/@media-editor/core/node_modules/zod/v3/helpers/typeAliases.d.cts +2 -0
  48. package/node_modules/@media-editor/core/node_modules/zod/v3/helpers/typeAliases.js +0 -0
  49. package/node_modules/@media-editor/core/node_modules/zod/v3/helpers/util.d.cts +85 -0
  50. package/node_modules/@media-editor/core/node_modules/zod/v3/helpers/util.js +1 -0
  51. package/node_modules/@media-editor/core/node_modules/zod/v3/index.d.cts +4 -0
  52. package/node_modules/@media-editor/core/node_modules/zod/v3/index.js +1 -0
  53. package/node_modules/@media-editor/core/node_modules/zod/v3/locales/en.d.cts +3 -0
  54. package/node_modules/@media-editor/core/node_modules/zod/v3/locales/en.js +1 -0
  55. package/node_modules/@media-editor/core/node_modules/zod/v3/standard-schema.d.cts +102 -0
  56. package/node_modules/@media-editor/core/node_modules/zod/v3/standard-schema.js +0 -0
  57. package/node_modules/@media-editor/core/node_modules/zod/v3/types.d.cts +1031 -0
  58. package/node_modules/@media-editor/core/node_modules/zod/v3/types.js +1 -0
  59. package/node_modules/@media-editor/core/package.json +34 -0
  60. package/package.json +33 -0
@@ -0,0 +1,359 @@
1
+ import * as zod from 'zod';
2
+ import { V as VideoEditStateT, O as OverlayT } from './state-B85vkf7_.js';
3
+
4
+ type TransitionT = {
5
+ type: 'none' | 'fade' | 'slide' | 'wipe' | 'flip' | 'clockWipe' | 'star' | 'circle' | 'rectangle';
6
+ duration: number;
7
+ };
8
+ type VideoEditStateWithTransitions = VideoEditStateT & {
9
+ transitions?: TransitionT[];
10
+ };
11
+
12
+ /** 视频源元数据(File/objectURL 是运行时的事,不进引擎;引擎只认 srcIdx + 元数据) */
13
+ interface VideoSourceMeta {
14
+ duration: number;
15
+ width: number;
16
+ height: number;
17
+ }
18
+ /** 音频源元数据 */
19
+ interface AudioSourceMeta {
20
+ duration: number;
21
+ }
22
+ interface MutOpts {
23
+ /** true = 瞬时(拖拽中间帧,不入 undo 栈) */
24
+ transient?: boolean;
25
+ }
26
+ type FilterKey = 'brightness' | 'contrast' | 'saturation' | 'temperature' | 'tint' | 'vibrance' | 'gamma' | 'highlights' | 'shadows' | 'whites' | 'blacks' | 'lightSense' | 'sharpen' | 'clarity' | 'grain' | 'fade' | 'vignette';
27
+ type EngineListener = (state: VideoEditStateWithTransitions) => void;
28
+ /** 导出计划:压缩后的状态 + 上传槽位映射(slot i 对应原 srcIdx = order[i]) */
29
+ interface ExportPlan {
30
+ state: VideoEditStateWithTransitions;
31
+ videoSrcOrder: number[];
32
+ audioSrcOrder: number[];
33
+ }
34
+ declare class VideoEditorEngine {
35
+ private history;
36
+ private videoSources;
37
+ private audioSources;
38
+ private listeners;
39
+ /** 瞬时拖拽序列的起始 base(拖拽前的提交态);null = 不在拖拽中 */
40
+ private transientBase;
41
+ constructor(initial?: VideoEditStateWithTransitions);
42
+ /** 当前编辑状态(引用稳定:仅在变更时换新对象,适配 useSyncExternalStore) */
43
+ getState(): VideoEditStateWithTransitions;
44
+ /** useSyncExternalStore 用别名 */
45
+ getSnapshot: () => VideoEditStateWithTransitions;
46
+ subscribe: (fn: EngineListener) => (() => void);
47
+ private emit;
48
+ canUndo(): boolean;
49
+ canRedo(): boolean;
50
+ undo(): void;
51
+ redo(): void;
52
+ /**
53
+ * 全部变更的唯一入口。mut 接收「计算基准态」返回新态。
54
+ * 瞬时:基准恒为 transientBase(拖拽前态),replacePresent;
55
+ * 提交:基准为 transientBase ?? present;若刚结束一段瞬时,先把 present 还原回 base 再 push,
56
+ * 使整段拖拽只占一步 undo。
57
+ */
58
+ private applyMut;
59
+ /**
60
+ * 登记一个视频源并入时间轴。首源:用其分辨率初始化 canvas 并重建 history(清空 undo);
61
+ * 后续源:追加整段 clip(pushHistory)。
62
+ */
63
+ registerVideoSource(meta: VideoSourceMeta): number;
64
+ /** 登记一个音频源并入音轨(整段、volume=1、startAt=0,clamp 后 push) */
65
+ registerAudioSource(meta: AudioSourceMeta): number;
66
+ getVideoSource(idx: number): VideoSourceMeta | undefined;
67
+ getAudioSource(idx: number): AudioSourceMeta | undefined;
68
+ /** 把已登记源再追加一段整段 clip(同源可多次引用) */
69
+ addClipFromSource(srcIdx: number): void;
70
+ splitAtPlayhead(outputTime: number): void;
71
+ removeClip(idx: number): void;
72
+ duplicateClip(idx: number): void;
73
+ reorderClips(from: number, to: number): void;
74
+ /** 拖把手改 in/out 到源时刻 srcT;拖拽中传 {transient:true},松手再调一次提交 */
75
+ trimClip(idx: number, edge: 'in' | 'out', srcT: number, opts?: MutOpts): void;
76
+ moveAudioClip(idx: number, startAt: number, opts?: MutOpts): void;
77
+ trimAudioClip(idx: number, edge: 'in' | 'out', t: number, opts?: MutOpts): void;
78
+ setAudioVolume(idx: number, volume: number, opts?: MutOpts): void;
79
+ removeAudioClip(idx: number): void;
80
+ /** 改一条音频片段并 clamp(volume 改动不需 clamp 几何,clampGeom=false 跳过) */
81
+ private patchAudio;
82
+ setCrop(crop: NonNullable<VideoEditStateWithTransitions['crop']>, opts?: MutOpts): void;
83
+ clearCrop(): void;
84
+ setScale(scale: NonNullable<VideoEditStateWithTransitions['scale']>): void;
85
+ clearScale(): void;
86
+ setRotation(deg: number, opts?: MutOpts): void;
87
+ /** 90° 步进(顺时针正);结果钳到 [-180,180] */
88
+ rotateBy(step: number): void;
89
+ flipX(): void;
90
+ flipY(): void;
91
+ setFilter(key: FilterKey, value: number, opts?: MutOpts): void;
92
+ setFilters(partial: Partial<VideoEditStateWithTransitions['filters']>, opts?: MutOpts): void;
93
+ setFilterPreset(id: string | undefined): void;
94
+ addOverlay(o: OverlayT): void;
95
+ updateOverlay(idx: number, patch: Partial<OverlayT>, opts?: MutOpts): void;
96
+ removeOverlay(idx: number): void;
97
+ /**
98
+ * 通用补丁:把 partial 浅合并进状态(UI 一次改多字段,如旋转同时重置 crop)。
99
+ * - `{transient:true}`:拖拽中间帧,基于拖拽前 base 合并、replacePresent,不入栈(= 原 patchLive)。
100
+ * - 默认:一次性提交(= commit(partial))。
101
+ */
102
+ patch(partial: Partial<VideoEditStateWithTransitions>, opts?: MutOpts): void;
103
+ /**
104
+ * 定格当前态为一步 undo(= 原 commit):保留 present(含拖拽末值),可再合并 partial;
105
+ * 若处于瞬时序列,先把 past 接到拖拽前 base 上,使整段拖拽只占一步。
106
+ */
107
+ commit(partial?: Partial<VideoEditStateWithTransitions>): void;
108
+ setKeepAudio(keep: boolean): void;
109
+ setPoster(time: number): void;
110
+ totalDuration(): number;
111
+ contentDuration(): number;
112
+ /** 输出时刻 → 所属片段与源内时刻(播放头定位) */
113
+ locate(outputTime: number): {
114
+ idx: number;
115
+ srcTime: number;
116
+ } | null;
117
+ usedVideoSources(): number[];
118
+ /**
119
+ * 生成导出计划:剔除未引用源、把 clips/audioClips 的 srcIdx 重映射到 [0..n),
120
+ * 返回压缩后的状态与上传槽位顺序(framework 按 order 取 File 上传)。
121
+ */
122
+ getExportPlan(): ExportPlan;
123
+ /** 校验当前(导出)状态是否符合 schema —— 提交前兜底 */
124
+ validate(): zod.SafeParseReturnType<{
125
+ clips: {
126
+ srcIdx: number;
127
+ in: number;
128
+ out: number;
129
+ }[];
130
+ canvas: {
131
+ w: number;
132
+ h: number;
133
+ };
134
+ rotation?: number | undefined;
135
+ audioClips?: {
136
+ srcIdx: number;
137
+ in: number;
138
+ out: number;
139
+ startAt: number;
140
+ volume: number;
141
+ }[] | undefined;
142
+ crop?: {
143
+ width: number;
144
+ x: number;
145
+ y: number;
146
+ height: number;
147
+ } | undefined;
148
+ scale?: {
149
+ width: number;
150
+ height: number;
151
+ fit: "contain" | "cover" | "force";
152
+ } | undefined;
153
+ flipX?: boolean | undefined;
154
+ flipY?: boolean | undefined;
155
+ filters?: {
156
+ brightness: number;
157
+ contrast: number;
158
+ saturation: number;
159
+ temperature?: number | undefined;
160
+ tint?: number | undefined;
161
+ vibrance?: number | undefined;
162
+ gamma?: number | undefined;
163
+ highlights?: number | undefined;
164
+ shadows?: number | undefined;
165
+ whites?: number | undefined;
166
+ blacks?: number | undefined;
167
+ lightSense?: number | undefined;
168
+ sharpen?: number | undefined;
169
+ clarity?: number | undefined;
170
+ grain?: number | undefined;
171
+ fade?: number | undefined;
172
+ vignette?: number | undefined;
173
+ } | undefined;
174
+ filterPreset?: string | undefined;
175
+ overlays?: ({
176
+ type: "pen";
177
+ points: number[];
178
+ color: string;
179
+ width: number;
180
+ rotation?: number | undefined;
181
+ } | {
182
+ type: "line";
183
+ color: string;
184
+ width: number;
185
+ x1: number;
186
+ y1: number;
187
+ x2: number;
188
+ y2: number;
189
+ rotation?: number | undefined;
190
+ } | {
191
+ type: "arrow";
192
+ color: string;
193
+ width: number;
194
+ x1: number;
195
+ y1: number;
196
+ x2: number;
197
+ y2: number;
198
+ rotation?: number | undefined;
199
+ } | {
200
+ type: "rect";
201
+ color: string;
202
+ width: number;
203
+ x: number;
204
+ y: number;
205
+ w: number;
206
+ h: number;
207
+ rotation?: number | undefined;
208
+ } | {
209
+ type: "ellipse";
210
+ color: string;
211
+ width: number;
212
+ x: number;
213
+ y: number;
214
+ w: number;
215
+ h: number;
216
+ rotation?: number | undefined;
217
+ } | {
218
+ type: "text";
219
+ color: string;
220
+ x: number;
221
+ y: number;
222
+ text: string;
223
+ fontSize: number;
224
+ rotation?: number | undefined;
225
+ } | {
226
+ type: "sticker";
227
+ x: number;
228
+ y: number;
229
+ w: number;
230
+ h: number;
231
+ rotation?: number | undefined;
232
+ emoji?: string | undefined;
233
+ src?: string | undefined;
234
+ })[] | undefined;
235
+ keepAudio?: boolean | undefined;
236
+ poster?: {
237
+ time: number;
238
+ } | undefined;
239
+ }, {
240
+ rotation: number;
241
+ clips: {
242
+ srcIdx: number;
243
+ in: number;
244
+ out: number;
245
+ }[];
246
+ audioClips: {
247
+ srcIdx: number;
248
+ in: number;
249
+ out: number;
250
+ startAt: number;
251
+ volume: number;
252
+ }[];
253
+ canvas: {
254
+ w: number;
255
+ h: number;
256
+ };
257
+ flipX: boolean;
258
+ flipY: boolean;
259
+ filters: {
260
+ brightness: number;
261
+ contrast: number;
262
+ saturation: number;
263
+ temperature: number;
264
+ tint: number;
265
+ vibrance: number;
266
+ gamma: number;
267
+ highlights: number;
268
+ shadows: number;
269
+ whites: number;
270
+ blacks: number;
271
+ lightSense: number;
272
+ sharpen: number;
273
+ clarity: number;
274
+ grain: number;
275
+ fade: number;
276
+ vignette: number;
277
+ };
278
+ keepAudio: boolean;
279
+ crop?: {
280
+ width: number;
281
+ x: number;
282
+ y: number;
283
+ height: number;
284
+ } | undefined;
285
+ scale?: {
286
+ width: number;
287
+ height: number;
288
+ fit: "contain" | "cover" | "force";
289
+ } | undefined;
290
+ filterPreset?: string | undefined;
291
+ overlays?: ({
292
+ type: "pen";
293
+ points: number[];
294
+ color: string;
295
+ width: number;
296
+ rotation?: number | undefined;
297
+ } | {
298
+ type: "line";
299
+ color: string;
300
+ width: number;
301
+ x1: number;
302
+ y1: number;
303
+ x2: number;
304
+ y2: number;
305
+ rotation?: number | undefined;
306
+ } | {
307
+ type: "arrow";
308
+ color: string;
309
+ width: number;
310
+ x1: number;
311
+ y1: number;
312
+ x2: number;
313
+ y2: number;
314
+ rotation?: number | undefined;
315
+ } | {
316
+ type: "rect";
317
+ color: string;
318
+ width: number;
319
+ x: number;
320
+ y: number;
321
+ w: number;
322
+ h: number;
323
+ rotation?: number | undefined;
324
+ } | {
325
+ type: "ellipse";
326
+ color: string;
327
+ width: number;
328
+ x: number;
329
+ y: number;
330
+ w: number;
331
+ h: number;
332
+ rotation?: number | undefined;
333
+ } | {
334
+ type: "text";
335
+ color: string;
336
+ x: number;
337
+ y: number;
338
+ text: string;
339
+ fontSize: number;
340
+ rotation?: number | undefined;
341
+ } | {
342
+ type: "sticker";
343
+ x: number;
344
+ y: number;
345
+ w: number;
346
+ h: number;
347
+ rotation?: number | undefined;
348
+ emoji?: string | undefined;
349
+ src?: string | undefined;
350
+ })[] | undefined;
351
+ poster?: {
352
+ time: number;
353
+ } | undefined;
354
+ }>;
355
+ /** 重置为给定状态(清空 history、源登记保留) */
356
+ reset(state?: VideoEditStateWithTransitions): void;
357
+ }
358
+
359
+ export { type AudioSourceMeta, type EngineListener, type ExportPlan, type FilterKey, type MutOpts, TransitionT, VideoEditStateWithTransitions, VideoEditorEngine, type VideoSourceMeta };
@@ -0,0 +1 @@
1
+ import{createHistory as h,defaultVideoEditState as p,canUndo as f,canRedo as v,undo as S,redo as C,replacePresent as c,pushHistory as l,clampAudioClip as d,totalDuration as u,splitAtOutput as A,removeClip as B,duplicateClip as g,reorderClips as x,trimClipEdge as I,contentDuration as P,locateOutput as V,usedVideoSrcIndices as y,remapSrcIndices as k,VideoEditState as w}from"./chunk-MGKYVYOH.js";function M(t){const i=Math.min(Math.max(Math.round(t),2),4096);return i%2===0?i:i-1}var m=t=>Math.min(Math.max(t,-180),180),E=class{constructor(t=p()){this.videoSources=[],this.audioSources=[],this.listeners=new Set,this.transientBase=null,this.getSnapshot=()=>this.history.present,this.subscribe=i=>(this.listeners.add(i),()=>this.listeners.delete(i)),this.history=h(t)}getState(){return this.history.present}emit(){const t=this.history.present;for(const i of this.listeners)i(t)}canUndo(){return f(this.history)}canRedo(){return v(this.history)}undo(){this.transientBase=null,this.history=S(this.history),this.emit()}redo(){this.transientBase=null,this.history=C(this.history),this.emit()}applyMut(t,i=!1){if(i)this.transientBase===null&&(this.transientBase=this.history.present),this.history=c(this.history,t(this.transientBase));else{const s=this.transientBase??this.history.present,r=t(s),e=this.transientBase?{...this.history,present:this.transientBase}:this.history;this.history=l(e,r),this.transientBase=null}this.emit()}registerVideoSource(t){const i=this.videoSources.length;this.videoSources.push(t);const s={srcIdx:i,in:0,out:t.duration};return i===0||this.history.present.clips.length===0?(this.transientBase=null,this.history=h({...p(),clips:[s],canvas:{w:M(t.width),h:M(t.height)},poster:{time:Math.min(1,t.duration/2)}}),this.emit()):this.applyMut(r=>({...r,clips:[...r.clips,s]})),i}registerAudioSource(t){const i=this.audioSources.length;return this.audioSources.push(t),this.applyMut(s=>{const r=new Set((s.audioClips??[]).map((n,a)=>Number.isInteger(n?.track)&&n.track>=0?n.track:a));let e=0;for(;r.has(e);)e+=1;const o=d({srcIdx:i,track:e,startAt:0,in:0,out:t.duration,volume:1},t.duration,u(s.clips));return{...s,audioClips:[...s.audioClips,o]}}),i}getVideoSource(t){return this.videoSources[t]}getAudioSource(t){return this.audioSources[t]}addClipFromSource(t){const i=this.videoSources[t];i&&this.applyMut(s=>({...s,clips:[...s.clips,{srcIdx:t,in:0,out:i.duration}]}))}splitAtPlayhead(t){this.applyMut(i=>({...i,clips:A(i.clips,t)}))}removeClip(t){this.applyMut(i=>({...i,clips:B(i.clips,t)}))}duplicateClip(t){this.applyMut(i=>({...i,clips:g(i.clips,t)}))}reorderClips(t,i){this.applyMut(s=>({...s,clips:x(s.clips,t,i)}))}trimClip(t,i,s,r){this.applyMut(e=>{const o=e.clips[t];if(!o)return e;const n=this.videoSources[o.srcIdx]?.duration??o.out;return{...e,clips:I(e.clips,t,i,s,n)}},r?.transient)}moveAudioClip(t,i,s){this.applyMut(r=>this.patchAudio(r,t,e=>({...e,startAt:i})),s?.transient)}trimAudioClip(t,i,s,r){this.applyMut(e=>this.patchAudio(e,t,o=>{if(i==="in"){const n=s-o.in;return{...o,in:s,startAt:o.startAt+n}}return{...o,out:s}}),r?.transient)}setAudioVolume(t,i,s){this.applyMut(r=>this.patchAudio(r,t,e=>({...e,volume:i}),!1),s?.transient)}removeAudioClip(t){this.applyMut(i=>({...i,audioClips:i.audioClips.filter((s,r)=>r!==t)}))}patchAudio(t,i,s,r=!0){const e=t.audioClips[i];if(!e)return t;let o=s(e);if(r){const n=this.audioSources[e.srcIdx]?.duration??e.out;o=d(o,n,u(t.clips))}return{...t,audioClips:t.audioClips.map((n,a)=>a===i?o:n)}}setCrop(t,i){this.applyMut(s=>({...s,crop:t}),i?.transient)}clearCrop(){this.applyMut(t=>({...t,crop:void 0}))}setScale(t){this.applyMut(i=>({...i,scale:t}))}clearScale(){this.applyMut(t=>({...t,scale:void 0}))}setRotation(t,i){this.applyMut(s=>({...s,rotation:m(t)}),i?.transient)}rotateBy(t){this.applyMut(i=>{let s=i.rotation+t;return s>180&&(s-=360),s<-180&&(s+=360),{...i,rotation:m(s)}})}flipX(){this.applyMut(t=>({...t,flipX:!t.flipX}))}flipY(){this.applyMut(t=>({...t,flipY:!t.flipY}))}setFilter(t,i,s){this.applyMut(r=>({...r,filters:{...r.filters,[t]:i}}),s?.transient)}setFilters(t,i){this.applyMut(s=>({...s,filters:{...s.filters,...t}}),i?.transient)}setFilterPreset(t){this.applyMut(i=>({...i,filterPreset:t}))}addOverlay(t){this.applyMut(i=>({...i,overlays:[...i.overlays??[],t]}))}updateOverlay(t,i,s){this.applyMut(r=>{const e=r.overlays??[];return e[t]?{...r,overlays:e.map((o,n)=>n===t?{...o,...i}:o)}:r},s?.transient)}removeOverlay(t){this.applyMut(i=>({...i,overlays:(i.overlays??[]).filter((s,r)=>r!==t)}))}patch(t,i){i?.transient?(this.transientBase===null&&(this.transientBase=this.history.present),this.history=c(this.history,{...this.transientBase,...t}),this.emit()):this.commit(t)}commit(t){const i=t?{...this.history.present,...t}:this.history.present,s=this.transientBase;this.transientBase=null,this.history=s?l({...this.history,present:s},i):l(this.history,i),this.emit()}setKeepAudio(t){this.applyMut(i=>({...i,keepAudio:t}))}setPoster(t){this.applyMut(i=>({...i,poster:{time:Math.max(0,t)}}))}totalDuration(){return u(this.history.present.clips)}contentDuration(){const t=this.history.present;return P(t.clips,t.audioClips,t.overlays)}locate(t){return V(this.history.present.clips,t)}usedVideoSources(){return y(this.history.present.clips)}getExportPlan(){const t=this.history.present,i=y(t.clips),s=[...new Set(t.audioClips.map(e=>e.srcIdx))].sort((e,o)=>e-o),r=new Map(s.map((e,o)=>[e,o]));return{state:{...t,clips:k(t.clips,i),audioClips:t.audioClips.map(e=>({...e,srcIdx:r.get(e.srcIdx)??0}))},videoSrcOrder:i,audioSrcOrder:s}}validate(){return w.safeParse(this.getExportPlan().state)}reset(t=p()){this.transientBase=null,this.history=h(t),this.emit()}};export{E as VideoEditorEngine};
@@ -0,0 +1,264 @@
1
+ import { A as AudioClipT, C as ClipT, V as VideoEditStateT } from './state-B85vkf7_.js';
2
+ export { a as AudioClip, b as Clip, c as Overlay, O as OverlayT, d as VideoEditState, e as defaultVideoEditState } from './state-B85vkf7_.js';
3
+ export { D as DEFAULT_PLATFORM_TARGETS, a as EDITOR_MESSAGES, E as EditorLocale, I as IMAGE_PLATFORM_PRESETS, b as IMAGE_RATIO_PRESETS, P as PlatformPreset, c as PlatformTarget, R as RatioPreset, S as SizePreset, V as VIDEO_PLATFORM_RATIO_PRESETS, d as VIDEO_RATIO_PRESETS, e as VIDEO_SIZE_PRESETS, f as VideoPlatformRatioPreset, g as buildImagePlatformPresets, h as buildVideoPlatformRatioPresets, i as buildVideoSizePresets, p as parseRatio, t as tEditor, j as toHant } from './platform-presets-B-o4C6uY.js';
4
+ import 'zod';
5
+
6
+ type TransitionT = {
7
+ type: 'none' | 'fade' | 'slide' | 'wipe' | 'flip' | 'clockWipe' | 'star' | 'circle' | 'rectangle';
8
+ duration: number;
9
+ };
10
+ type VideoEditStateWithTransitions = VideoEditStateT & {
11
+ transitions?: TransitionT[];
12
+ };
13
+
14
+ /**
15
+ * editor-core/timeline v2 —— 输出时间轴纯函数(多素材 clips/audioClips 操作)。
16
+ * 从 UI 抽出,Vue/React 壳共用;全部不可变(返回新数组)。
17
+ *
18
+ * 模型:clips 数组顺序 = 输出拼接顺序;输出时间轴 x = Σ前序片段时长。
19
+ * 所有 "t" 参数除特别说明外都是**输出时间**;trimClipEdge 的 srcT 是源时间。
20
+ */
21
+
22
+ declare const MIN_CLIP = 0.1;
23
+ /** 片段时长 */
24
+ declare function clipDuration(c: ClipT): number;
25
+ /** 输出总时长 */
26
+ declare function totalDuration(clips: ClipT[]): number;
27
+ /** 第 idx 个片段在输出时间轴上的起点(前缀和) */
28
+ declare function clipStartsAt(clips: ClipT[], idx: number): number;
29
+ /**
30
+ * 输出时间 t → 所属片段与源内时刻。
31
+ * 交界时刻归后段(前段已播完);t === total 归末段(srcTime = out);
32
+ * t 越界或 clips 为空返回 null。
33
+ */
34
+ declare function locateOutput(clips: ClipT[], t: number): {
35
+ idx: number;
36
+ srcTime: number;
37
+ } | null;
38
+ /** 在输出时刻 t 处把所属片段一分为二;距两端 <MIN_CLIP 返回原数组 */
39
+ declare function splitAtOutput(clips: ClipT[], t: number): ClipT[];
40
+ /** 删除第 idx 个片段;只剩一段时不可删,返回原数组 */
41
+ declare function removeClip(clips: ClipT[], idx: number): ClipT[];
42
+ /** 复制第 idx 个片段,插到原段之后 */
43
+ declare function duplicateClip(clips: ClipT[], idx: number): ClipT[];
44
+ /** 拖拽重排:把第 from 段移动到第 to 位 */
45
+ declare function reorderClips(clips: ClipT[], from: number, to: number): ClipT[];
46
+ /**
47
+ * 拖片段把手:把第 idx 段的 in/out 改到源时刻 srcT。
48
+ * in 钳 [0, out-MIN_CLIP];out 钳 [in+MIN_CLIP, srcDuration]。
49
+ */
50
+ declare function trimClipEdge(clips: ClipT[], idx: number, edge: 'in' | 'out', srcT: number, srcDuration: number): ClipT[];
51
+ /**
52
+ * 音乐片段钳制:in/out 在源时长内且 out-in≥MIN_CLIP;
53
+ * startAt ∈ [0, max(0, total-MIN_CLIP)] —— 允许尾部悬出输出末尾(导出 amix duration=first 截断)。
54
+ */
55
+ declare function clampAudioClip(a: AudioClipT, srcDuration: number, total: number): AudioClipT;
56
+ /**
57
+ * 时间轴内容时长 = max(视频总长, 音乐悬出末端)。
58
+ * 用于内容宽度/适应缩放/标尺——音乐尾部悬出视频末尾时也要看得见;
59
+ * 播放头/seek 仍以视频总长为界(导出 amix duration=first 同语义)。
60
+ */
61
+ declare function contentDuration(clips: ClipT[], audioClips?: AudioClipT[]): number;
62
+ /** clips 实际引用到的视频源序号(升序去重)——导出前压缩上传用 */
63
+ declare function usedVideoSrcIndices(clips: ClipT[]): number[];
64
+ /** 把 clips 的 srcIdx 重映射为 used 中的序号(与压缩后的上传文件序对齐) */
65
+ declare function remapSrcIndices(clips: ClipT[], used: number[]): ClipT[];
66
+
67
+ /**
68
+ * editor-core/ffmpeg-args v2 —— VideoEditState → FFmpeg 参数(纯函数,服务端执行)。
69
+ * 安全:只接受结构化 state + 服务端落盘的文件路径数组;文件内容不进命令行。
70
+ *
71
+ * 多素材模型:clips 数组序 = 输出拼接序;每片段先归一到画布
72
+ * (cover 居中裁剪 + setsar=1 + fps=30 + format=yuv420p,防 concat 流参数不齐),
73
+ * concat 后过全局画面链(flips → rotate → crop → scale → eq → 预设滤镜),最后 overlay。
74
+ * 音频:原声(keepAudio,无音轨源用 anullsrc 补齐) concat → 与音乐(atrim+volume+adelay)
75
+ * amix=duration=first(锚定视频长):normalize=0(不自动衰减)。
76
+ *
77
+ * 单视频源且无音乐时走 v1 快路径(-ss/-t 或单输入 filter_complex),隔离回归面。
78
+ */
79
+
80
+ /** 服务端按 srcIdx 顺序落盘后的路径与探测结果 */
81
+ interface FfmpegIo {
82
+ /** index 与 clip.srcIdx 对齐(客户端导出前已压缩重映射) */
83
+ videoPaths: string[];
84
+ /** 与 videoPaths 等长,probeHasAudio 结果 */
85
+ videoHasAudio: boolean[];
86
+ /** index 与 audioClip.srcIdx 对齐 */
87
+ audioPaths: string[];
88
+ overlayPath?: string;
89
+ }
90
+ /**
91
+ * v2 主入口:多视频源 concat + 音乐 amix + 全局链 + overlay。
92
+ * 输入序:视频 0..N-1 → 音乐 N..N+M-1 → overlay N+M。
93
+ */
94
+ declare function buildFfmpegArgs(state: VideoEditStateWithTransitions, io: FfmpegIo, output: string): string[];
95
+ /** 封面帧:在 time 处抽 1 帧 jpg */
96
+ declare function buildPosterArgs(input: string, time: number, output: string): string[];
97
+
98
+ /**
99
+ * editor-core/transform —— 旋转/坐标换算纯函数。
100
+ * 与 ffmpeg 的 rotw/roth 同公式,保证预览画布与导出尺寸一致。
101
+ */
102
+ declare const DEG2RAD: number;
103
+ /** 旋转 deg 度后的外接框尺寸(= ffmpeg rotate 滤镜 ow=rotw/oh=roth) */
104
+ declare function rotatedSize(w: number, h: number, deg: number): {
105
+ w: number;
106
+ h: number;
107
+ };
108
+ /** 角度归一化到 [0,360) */
109
+ declare function normalizeDeg(deg: number): number;
110
+ /** h264 偶数像素对齐 */
111
+ declare function evenize(n: number): number;
112
+ /** 把 rect 钳制在 bounds 内(裁剪框拖拽用) */
113
+ declare function clampRect(rect: {
114
+ x: number;
115
+ y: number;
116
+ width: number;
117
+ height: number;
118
+ }, bounds: {
119
+ w: number;
120
+ h: number;
121
+ }, minSize?: number): {
122
+ x: number;
123
+ y: number;
124
+ width: number;
125
+ height: number;
126
+ };
127
+ /** 给定纵横比,在 bounds 内求居中最大 rect(比例预设落框用) */
128
+ declare function centeredMaxRect(bounds: {
129
+ w: number;
130
+ h: number;
131
+ }, aspect: number): {
132
+ x: number;
133
+ y: number;
134
+ width: number;
135
+ height: number;
136
+ };
137
+ type FitMode = 'cover' | 'contain' | 'force';
138
+ /**
139
+ * scale 的 fit 语义 → 源/目标矩形对(预览画布与 ffmpeg 严格同语义):
140
+ * - cover:源侧取目标比例的居中最大区(= ffmpeg scale:force_original_aspect_ratio=increase + 居中 crop),不变形
141
+ * - contain:目标侧居中留黑边(= ffmpeg scale:decrease + pad)
142
+ * - force:双全幅拉伸(= ffmpeg scale=W:H)
143
+ * src 坐标相对 crop 区原点;dst 坐标相对输出原点。
144
+ */
145
+ declare function fitRects(crop: {
146
+ width: number;
147
+ height: number;
148
+ }, out: {
149
+ w: number;
150
+ h: number;
151
+ }, fit: FitMode): {
152
+ src: {
153
+ x: number;
154
+ y: number;
155
+ width: number;
156
+ height: number;
157
+ };
158
+ dst: {
159
+ x: number;
160
+ y: number;
161
+ width: number;
162
+ height: number;
163
+ };
164
+ };
165
+ /** 导出产物的最终输出尺寸(决定 overlay PNG 的分辨率) */
166
+ declare function outputSize(source: {
167
+ w: number;
168
+ h: number;
169
+ }, state: {
170
+ rotation: number;
171
+ crop?: {
172
+ width: number;
173
+ height: number;
174
+ } | undefined;
175
+ scale?: {
176
+ width: number;
177
+ height: number;
178
+ } | undefined;
179
+ }): {
180
+ w: number;
181
+ h: number;
182
+ };
183
+
184
+ /**
185
+ * editor-core/filters —— 预设滤镜表(数据驱动)。
186
+ * vf = ffmpeg 滤镜片段(导出真值);css = 浏览器预览近似。
187
+ * 全部使用 ffmpeg-static 6.x 标配滤镜(hue/eq/colorbalance/colorchannelmixer)。
188
+ */
189
+ interface FilterPreset {
190
+ id: string;
191
+ label: string;
192
+ vf: string;
193
+ css: string;
194
+ }
195
+ declare const FILTER_PRESETS: FilterPreset[];
196
+ declare function findFilterPreset(id: string | undefined): FilterPreset | undefined;
197
+ /**
198
+ * 拼出 canvas/CSS 用的 filter 字符串(预览与帧合成共用)。
199
+ * brightness/contrast/saturation 来自手调 [-1,1] → 1+v;叠加预设 css。
200
+ * gamma 无 CSS 等价(仅 ffmpeg 导出生效),此处不含。
201
+ */
202
+ declare function buildFilterCss(filters: {
203
+ brightness: number;
204
+ contrast: number;
205
+ saturation: number;
206
+ temperature: number;
207
+ tint: number;
208
+ vibrance: number;
209
+ gamma: number;
210
+ highlights: number;
211
+ shadows: number;
212
+ whites: number;
213
+ blacks: number;
214
+ lightSense: number;
215
+ sharpen: number;
216
+ clarity: number;
217
+ grain: number;
218
+ fade: number;
219
+ vignette: number;
220
+ }, presetId?: string): string;
221
+
222
+ /**
223
+ * editor-core/history —— 不可变快照 undo/redo 栈(纯函数)。
224
+ * 泛型,对 EditState 的 JSON 快照工作;壳层(React/Vue)只持有 History<T> 并 set 回去。
225
+ */
226
+ interface History<T> {
227
+ past: T[];
228
+ present: T;
229
+ future: T[];
230
+ }
231
+ declare function createHistory<T>(initial: T): History<T>;
232
+ /** 写入新状态(清空 redo 栈);浅比较相同引用则原样返回 */
233
+ declare function pushHistory<T>(h: History<T>, next: T): History<T>;
234
+ /**
235
+ * 替换当前态但不入栈(高频拖拽的中间帧用):
236
+ * 拖动过程 replacePresent 持续刷新,pointerup 时再 pushHistory 定格一步。
237
+ */
238
+ declare function replacePresent<T>(h: History<T>, next: T): History<T>;
239
+ declare function canUndo<T>(h: History<T>): boolean;
240
+ declare function canRedo<T>(h: History<T>): boolean;
241
+ declare function undo<T>(h: History<T>): History<T>;
242
+ declare function redo<T>(h: History<T>): History<T>;
243
+
244
+ /**
245
+ * Filerobot 图片编辑器的标注旋转补偿(纯函数,供 ImageEditor 注入 config.onAnnotationAdd)。
246
+ *
247
+ * 背景:Filerobot 把 rotation 加在整个 Konva DesignLayer 上(DesignLayerWrapper),
248
+ * 新标注挂进同一层会继承父层旋转 → 旋转图片后再贴文字,文字方向跟着转。
249
+ * onAnnotationAdd 是 5.0.0-beta.159 真实存在但未写进 index.d.ts 的 config 钩子:
250
+ * 源码 lib/actions/setAnnotation.js 在「新建标注」时调用 (annotation, 全量state),
251
+ * 把返回的 partial 合并进标注 —— 这里给新标注反向初始 rotation,与父层旋转叠加后视觉正向。
252
+ */
253
+ declare function compensateAnnotationRotation(ann: {
254
+ rotation?: number;
255
+ isDuplicated?: boolean;
256
+ }, state: {
257
+ adjustments?: {
258
+ rotation?: number;
259
+ };
260
+ }): {
261
+ rotation?: number;
262
+ };
263
+
264
+ export { AudioClipT, ClipT, DEG2RAD, FILTER_PRESETS, type FfmpegIo, type FilterPreset, type FitMode, type History, MIN_CLIP, TransitionT, VideoEditStateT, VideoEditStateWithTransitions, buildFfmpegArgs, buildFilterCss, buildPosterArgs, canRedo, canUndo, centeredMaxRect, clampAudioClip, clampRect, clipDuration, clipStartsAt, compensateAnnotationRotation, contentDuration, createHistory, duplicateClip, evenize, findFilterPreset, fitRects, locateOutput, normalizeDeg, outputSize, pushHistory, redo, remapSrcIndices, removeClip, reorderClips, replacePresent, rotatedSize, splitAtOutput, totalDuration, trimClipEdge, undo, usedVideoSrcIndices };
@@ -0,0 +1 @@
1
+ import{findFilterPreset as X,normalizeDeg as Y,outputSize as Z}from"./chunk-USY6UDGL.js";import{DEG2RAD as Bt,FILTER_PRESETS as Ut,buildFilterCss as Gt,centeredMaxRect as Kt,clampRect as Ht,evenize as Xt,findFilterPreset as Yt,fitRects as Zt,normalizeDeg as Jt,outputSize as Qt,rotatedSize as te}from"./chunk-USY6UDGL.js";import{AudioClip as ne,Clip as ie,MIN_CLIP as re,Overlay as se,VideoEditState as oe,canRedo as ae,canUndo as ce,clampAudioClip as ue,clipDuration as le,clipStartsAt as fe,contentDuration as he,createHistory as pe,defaultVideoEditState as me,duplicateClip as $e,locateOutput as de,pushHistory as ve,redo as ge,remapSrcIndices as be,removeClip as xe,reorderClips as Se,replacePresent as Te,splitAtOutput as Ae,totalDuration as Me,trimClipEdge as Ee,undo as Fe,usedVideoSrcIndices as ye}from"./chunk-MGKYVYOH.js";import{DEFAULT_PLATFORM_TARGETS as _e,EDITOR_MESSAGES as we,IMAGE_PLATFORM_PRESETS as Oe,IMAGE_RATIO_PRESETS as Re,VIDEO_PLATFORM_RATIO_PRESETS as Ne,VIDEO_RATIO_PRESETS as Ie,VIDEO_SIZE_PRESETS as Le,buildImagePlatformPresets as De,buildVideoPlatformRatioPresets as Ce,buildVideoSizePresets as je,compensateAnnotationRotation as ze,parseRatio as Ve,tEditor as ke,toHant as qe}from"./chunk-XCBM7P7N.js";function gt(t){const e=Y(t);if(e===0)return[];if(e===90)return["transpose=1"];if(e===180)return["transpose=1","transpose=1"];if(e===270)return["transpose=2"];const n=`${e}*PI/180`;return[`rotate=${n}:ow=rotw(${n}):oh=roth(${n}):c=black`]}function x(t){return Math.max(-1,Math.min(1,Number(t)||0))}function R(t){return Math.max(0,Math.min(1,Number(t)||0))}function J(t,e,n,i){const r=Math.round(Number(t));return Number.isFinite(r)?Math.min(n,Math.max(e,r)):i}function Q(t){return Math.min(Math.max(Number(t)||1,.5),2)}function bt(t){const e=[];let n=Q(t);for(;n<.5-1e-6||n>2+1e-6;)n<.5?(e.push("atempo=0.5"),n/=.5):(e.push("atempo=2"),n/=2);return Math.abs(n-1)>1e-4&&e.push(`atempo=${n.toFixed(4).replace(/0+$/,"").replace(/\.$/,"")}`),e}function _(t,e){const n=J(t,2,8192,e);return n%2===0?n:n-1}function w(t){return t?.exportSettings??null}function xt(t){const e=String(w(t)?.format??"mp4").toLowerCase();return e==="mov"||e==="mxf"?e:"mp4"}function tt(t){return J(w(t)?.frameRate,12,120,30)}function St(t){const e=Z(t.canvas,t),n=w(t);if(!n)return e;const i=Number.isFinite(Number(n.width))&&Number(n.width)>0,r=Number.isFinite(Number(n.height))&&Number(n.height)>0;if(!i&&!r)return e;const s=e.w>0&&e.h>0?e.w/e.h:1;if(i&&r)return{w:_(n.width,e.w),h:_(n.height,e.h)};if(i){const o=_(n.width,e.w);return{w:o,h:_(o/s,e.h)}}const a=_(n.height,e.h);return{w:_(a*s,e.w),h:a}}function Tt(t){const e=Number(w(t)?.videoBitrateKbps);return Number.isFinite(e)&&e>=100?Math.round(e):0}function At(t){const e=Number(w(t)?.audioBitrateKbps);return Number.isFinite(e)&&e>=32?Math.round(e):0}function Mt(t){return w(t)?.audioCodec==="mp3"?"libmp3lame":"aac"}function q(t){return Z(t.canvas,t)}function W(t){const e=q(t),n=St(t);return n.w===e.w&&n.h===e.h?[]:[`scale=${n.w}:${n.h}`]}function et(t){const e=x(t?.brightness),n=x(t?.contrast),i=x(t?.saturation),r=x(t?.gamma);if(e===0&&n===0&&i===0&&r===0)return null;const s=[];if(e!==0||n!==0){const o=T(Math.max(0,1+e)),u=T(Math.max(0,1+n)),c=`clip(((val*${o})-128)*${u}+128,0,255)`;s.push(`lutrgb=r='${c}':g='${c}':b='${c}'`)}const a=[];return i!==0&&a.push(`saturation=${1+i}`),r!==0&&a.push(`gamma=${(1+r*(r>0?1:.5)).toFixed(3)}`),a.length&&s.push(`eq=${a.join(":")}`),s.join(",")}function T(t){return`${Math.round(t*1e3)/1e3}`}function nt(t){const e=x(t?.temperature),n=x(t?.tint);if(e===0&&n===0)return null;const i=x(e*.18+n*.08),r=x(e*.02-n*.12),s=x(-e*.18+n*.08);return`colorbalance=rs=${T(i)}:gs=${T(r)}:bs=${T(s)}`}function it(t){const e=x(t?.vibrance);return e===0?null:`vibrance=intensity=${T(e)}`}function O(t,e,n){if(t===e)return n<t?0:1;const i=Math.max(0,Math.min(1,(n-t)/(e-t)));return i*i*(3-2*i)}function Et(t){const e=O(.08,.4,t),n=1-O(.6,.92,t);return Math.max(0,Math.min(1,e*n*2.2))}function Ft(t,e){const n=1-O(.18,.62,t),i=1-O(.04,.32,t),r=O(.38,.84,t),s=O(.7,.98,t),a=Et(t),o=e.shadows*n*.28+e.highlights*r*.24+e.blacks*i*.2+e.whites*s*.2+e.lightSense*a*.18+e.fade*(.12+n*.06);return Math.max(0,Math.min(1,t+o))}function rt(t){const e={highlights:x(t?.highlights),shadows:x(t?.shadows),whites:x(t?.whites),blacks:x(t?.blacks),lightSense:x(t?.lightSense),fade:R(t?.fade)};return Object.values(e).some(r=>Math.abs(r)>1e-4)?`curves=master='${[0,.18,.36,.5,.68,.84,1].map(r=>`${T(r)}/${T(Ft(r,e))}`).join(" ")}'`:null}function st(t){const e=[],n=R(t?.clarity),i=R(t?.sharpen);return n>1e-4&&e.push(`unsharp=lx=7:ly=7:la=${T(n*1.8)}:cx=7:cy=7:ca=0`),i>1e-4&&e.push(`unsharp=lx=3:ly=3:la=${T(.35+i*2.1)}:cx=3:cy=3:ca=${T(i*.55)}`),e}function ot(t){const e=R(t?.grain);return e<=1e-4?null:`noise=alls=${Math.round(6+e*26)}:allf=t`}function at(t){const e=R(t?.vignette);if(e<=1e-4)return null;const n=Math.max(.12,.42-e*.22);return`vignette=angle=${T(n)}*PI`}function B(t,e={}){const n=[];if(t.flipX&&n.push("hflip"),t.flipY&&n.push("vflip"),n.push(...gt(t.rotation)),t.crop){const{x:m,y:$,width:b,height:A}=t.crop;n.push(`crop=${b}:${A}:${m}:${$}`)}if(t.scale){const{width:m,height:$,fit:b}=t.scale;b==="force"?n.push(`scale=${m}:${$}`):b==="cover"?n.push(`scale=${m}:${$}:force_original_aspect_ratio=increase`,`crop=${m}:${$}`):n.push(`scale=${m}:${$}:force_original_aspect_ratio=decrease`,`pad=${m}:${$}:(ow-iw)/2:(oh-ih)/2`)}const i=et(t.filters);i&&n.push(i);const r=nt(t.filters);r&&n.push(r);const s=it(t.filters);s&&n.push(s);const a=rt(t.filters);a&&n.push(a),n.push(...st(t.filters));const o=ot(t.filters);o&&n.push(o);const u=at(t.filters);u&&n.push(u);const c=X(t.filterPreset);return c&&n.push(c.vf),e.includeExportScale!==!1&&n.push(...W(t)),n.push(`fps=${tt(t)}`),Y(t.rotation)%90!==0&&!t.scale&&n.push("scale=trunc(iw/2)*2:trunc(ih/2)*2"),n}var ct={fade:"fade",slide:"slideleft",wipe:"wipeleft",flip:"horzopen",clockWipe:"radial",star:"pixelize",circle:"circleopen",rectangle:"rectcrop"},yt={fade:"qsin",slide:"tri",wipe:"tri",flip:"exp",clockWipe:"qsin",star:"tri",circle:"qsin",rectangle:"qsin"};function N(t){return t.out-t.in}function ut(t,e,n){const i=t&&typeof t.type=="string"?t.type:"none",r=ct[i]?i:"none",s=Math.max(0,Math.min(1.2,e-.05,n-.05));if(r==="none"||s<=.05)return{type:"none",duration:0,effect:"fade",curve:"tri"};const a=Number(t?.duration),o=Math.min(Math.max(Number.isFinite(a)?a:.4,.1),s);return{type:r,duration:o,effect:ct[r],curve:yt[r]??"tri"}}function Pt(t,e){if(!Array.isArray(e)||e.length<2)return!1;for(let n=0;n<e.length-1;n++){const i=ut(t?.[n],N(e[n]),N(e[n+1]));if(i.type!=="none"&&i.duration>0)return!0}return!1}function _t(t,e){const n=[];let i=0;for(let r=0;r<t.length-1;r++){const s=ut(e?.[r],N(t[r]),N(t[r+1]));s.type!=="none"&&s.duration>0&&(n.push({start:i,end:r,transition:s}),i=r+1)}return n.push({start:i,end:t.length-1,transition:null}),n}function wt(t,e,n){let i=0;for(let r=e;r<=n;r++)i+=N(t[r]);return i}function Ot(t,e,n,i){const s=_t(e,n).map((c,f)=>{const m=wt(e,c.start,c.end);if(c.start===c.end)return{vLabel:`[v${c.start}]`,aLabel:i?`[a${c.start}]`:"",duration:m,transition:c.transition};const $=[];for(let S=c.start;S<=c.end;S++)$.push(i?`[v${S}][a${S}]`:`[v${S}]`);const b=`[sgv${f}]`,A=i?`[sga${f}]`:"";return t.push(`${$.join("")}concat=n=${c.end-c.start+1}:v=1:a=${i?1:0}${i?`${b}${A}`:b}`),{vLabel:b,aLabel:A,duration:m,transition:c.transition}});let a=s[0].vLabel,o=i?s[0].aLabel:"",u=s[0].duration;for(let c=1;c<s.length;c++){const f=s[c-1].transition,m=s[c],$=`[vx${c}]`,b=Math.max(0,u-f.duration);if(t.push(`${a}${m.vLabel}xfade=transition=${f.effect}:duration=${f.duration}:offset=${b}${$}`),a=$,i){const A=`[ax${c}]`;t.push(`${o}${m.aLabel}acrossfade=d=${f.duration}:c1=${f.curve}:c2=${f.curve}${A}`),o=A}u=u+m.duration-f.duration}return{vLabel:a,aLabel:o,duration:u}}function U(t,e){const n=xt(e),i=n==="mxf"?["-c:v","mpeg2video","-pix_fmt","yuv422p"]:["-c:v","libx264","-preset","veryfast","-pix_fmt","yuv420p","-movflags","+faststart"],r=Tt(e);if(r>0?i.push("-b:v",`${r}k`,"-maxrate",`${r}k`,"-bufsize",`${Math.max(r*2,512)}k`):n==="mxf"&&i.push("-q:v","2"),t)if(n==="mxf")i.push("-c:a","pcm_s16le");else{i.push("-c:a",Mt(e));const s=At(e);s>0&&i.push("-b:a",`${s}k`)}return n==="mxf"&&i.push("-f","mxf"),i}var C="aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=stereo",lt="anullsrc=r=44100:cl=stereo";function ft(t){return t>.02?`,afade=t=in:st=0:d=0.01,afade=t=out:st=${Math.round((t-.01)*1e3)/1e3}:d=0.01`:""}var Rt="alimiter=limit=0.95:level=disabled:latency=1",Nt=3,It=.5;function ht(t,e=0){const n=Math.max(e,Math.floor(Number(t)||0));return n%2===0?n:n-1}function Lt(t){const e=Number.isFinite(t?.startAt)?Math.max(0,Number(t.startAt)):0,n=Number(t?.endAt);return Number.isFinite(n)?{startAt:e,endAt:Math.max(e+It,n)}:{startAt:e,endAt:e+Nt}}function Dt(t,e){const n=Math.max(2,ht(e.w,2)),i=Math.max(2,ht(e.h,2));return!(n>1)||!(i>1)?null:{x:0,y:0,w:n,h:i}}function Ct(t){if(t?.type==="filter"){const u=X(t.filterPreset);return u?[u.vf]:[]}if(t?.type!=="adjust")return[];const e=[],n=et(t);n&&e.push(n);const i=nt(t);i&&e.push(i);const r=it(t);r&&e.push(r);const s=rt(t);s&&e.push(s),e.push(...st(t));const a=ot(t);a&&e.push(a);const o=at(t);return o&&e.push(o),e}function pt(t,e){const n=[];for(const i of t??[]){if(i?.type!=="filter"&&i?.type!=="adjust")continue;const r=Dt(i,e);if(!r)continue;const s=Ct(i);s.length&&n.push({rect:r,filters:s,...Lt(i)})}return n}function mt(t,e=1/0){const n=[];for(const i of t??[]){if(i?.type!=="pen"&&i?.type!=="line"&&i?.type!=="arrow"&&i?.type!=="rect"&&i?.type!=="ellipse"&&i?.type!=="text"&&i?.type!=="sticker")continue;const r=Number(i.startAt),s=Number(i.endAt);if(!(!Number.isFinite(r)||!Number.isFinite(s)||s<=r)&&(n.push({startAt:Math.max(0,r),endAt:Math.max(r+.01,s)}),n.length>=e))break}return n}function $t(t,e,n,i){let r=e;return i.forEach((s,a)=>{const o=`[vtxt${n+a}]`;t.push(`${r}[${n+a}:v]overlay=0:0:enable='between(t,${s.startAt.toFixed(3)},${s.endAt.toFixed(3)})'${o}`),r=o}),r}function dt(t,e,n){let i=e;return n.forEach((r,s)=>{const a=`[vfxsrc${s}]`,o=`[vfxbase${s}]`,u=`[vfxcrop${s}]`,c=`[vfx${s}]`,{rect:f}=r;t.push(`${i}split=2${a}${o}`),t.push(`${a}crop=${f.w}:${f.h}:${f.x}:${f.y},${r.filters.join(",")}${u}`),t.push(`${o}${u}overlay=${f.x}:${f.y}:enable='between(t,${r.startAt.toFixed(3)},${r.endAt.toFixed(3)})'${c}`),i=c}),i}function jt(t,e,n,i,r,s=[]){const a=B(t,{includeExportScale:!1}),o=t.clips,u=t.keepAudio&&r,c=o.length>=2,f=pt(t.overlays,q(t)),m=mt(t.overlays,s.length),$=s.slice(0,m.length);if(c||i||$.length||f.length){const g=[];let h;if(c){o.forEach((M,F)=>{g.push(`[0:v]trim=start=${M.in}:end=${M.out},setpts=PTS-STARTPTS[v${F}]`),u&&g.push(`[0:a]atrim=start=${M.in}:end=${M.out},asetpts=PTS-STARTPTS${ft(M.out-M.in)}[a${F}]`)});const d=o.map((M,F)=>u?`[v${F}][a${F}]`:`[v${F}]`).join(""),y=u?"[vcat][acat]":"[vcat]";g.push(`${d}concat=n=${o.length}:v=1:a=${u?1:0}${y}`),h="[vcat]"}else h="[0:v]";if(a.length&&(g.push(`${h}${a.join(",")}[vflt]`),h="[vflt]"),f.length&&(h=dt(g,h,f)),i?(g.push(`${h}[1:v]overlay=0:0[vout]`),h="[vout]"):h==="[0:v]"&&(g.push("[0:v]null[vout]"),h="[vout]"),$.length){const d=1+(i?1:0);h=$t(g,h,d,m)}const I=W(t);I.length&&(g.push(`${h}${I.join(",")}[vscaled]`),h="[vscaled]");const L=[];!c&&o.length===1&&L.push("-ss",String(o[0].in),"-t",String(o[0].out-o[0].in));const p=["-y",...L,"-i",e];i&&p.push("-i",i);for(const d of $)p.push("-i",d);return p.push("-filter_complex",g.join(";"),"-map",h),u&&p.push("-map",c?"[acat]":"0:a?"),p.push(...U(u,t)),p.push(n),p}const b=B(t),S=["-y",...["-ss",String(o[0].in),"-t",String(o[0].out-o[0].in)],"-i",e];return b.length&&S.push("-vf",b.join(",")),u||S.push("-an"),S.push(...U(u,t)),S.push(n),S}function zt(t,e,n){const{videoPaths:i,videoHasAudio:r,audioPaths:s,overlayPath:a,textOverlayPaths:o=[]}=e,u=t.audioClips??[],c=Pt(t.transitions,t.clips),f=mt(t.overlays,o.length),m=o.slice(0,f.length);if(i.length===1&&u.length===0&&!c)return jt(t,i[0],n,a,r[0]??!1,m);const $=i.length,b=s.length,A=t.canvas.w,S=t.canvas.h,g=t.clips,h=t.keepAudio&&g.some(l=>r[l.srcIdx]),I=tt(t),L=g.reduce((l,v)=>l+(v.out-v.in),0),p=[];g.forEach((l,v)=>{p.push(`[${l.srcIdx}:v]trim=start=${l.in}:end=${l.out},setpts=PTS-STARTPTS,scale=${A}:${S}:force_original_aspect_ratio=increase,crop=${A}:${S},setsar=1,fps=${I},format=yuv420p[v${v}]`),h&&(r[l.srcIdx]?p.push(`[${l.srcIdx}:a]atrim=start=${l.in}:end=${l.out},asetpts=PTS-STARTPTS${ft(l.out-l.in)},${C}[a${v}]`):p.push(`${lt},atrim=start=0:end=${l.out-l.in},asetpts=PTS-STARTPTS,${C}[a${v}]`))});let d="[vcat]",y=h?"[acat]":"",M=L;if(c){const l=Ot(p,g,t.transitions??[],h);d=l.vLabel,y=h?l.aLabel:"",M=l.duration}else{const l=g.map((v,P)=>h?`[v${P}][a${P}]`:`[v${P}]`).join("");p.push(`${l}concat=n=${g.length}:v=1:a=${h?1:0}${h?"[vcat][acat]":"[vcat]"}`)}if(!h&&u.length&&(p.push(`${lt},atrim=start=0:end=${M},asetpts=PTS-STARTPTS,${C}[acat]`),y="[acat]"),u.length){u.forEach((v,P)=>{const H=Math.max(0,Math.round(v.startAt*1e3)),z=Math.max(0,v.out-v.in),vt=Q(v.speed),V=Math.min(Math.max(Number(v.fadeIn)||0,0),z),k=Math.min(Math.max(Number(v.fadeOut)||0,0),Math.max(0,z-V)),D=[`atrim=start=${v.in}:end=${v.out}`,"asetpts=PTS-STARTPTS",...bt(vt),`volume=${v.volume}`];V>1e-4&&D.push(`afade=t=in:st=0:d=${V}`),k>1e-4&&D.push(`afade=t=out:st=${Math.max(0,z-k)}:d=${k}`),D.push(C,`adelay=${H}|${H}`),p.push(`[${$+P}:a]${D.join(",")}[mu${P}]`)});const l=`${y}${u.map((v,P)=>`[mu${P}]`).join("")}`;p.push(`${l}amix=inputs=${1+u.length}:duration=first:dropout_transition=0:normalize=0,${Rt}[aout]`),y="[aout]"}const F=B(t,{includeExportScale:!1}),G=pt(t.overlays,q(t));if(F.length&&(p.push(`${d}${F.join(",")}[vflt]`),d="[vflt]"),G.length&&(d=dt(p,d,G)),a&&(p.push(`${d}[${$+b}:v]overlay=0:0[vout]`),d="[vout]"),m.length){const l=$+b+(a?1:0);d=$t(p,d,l,f)}const K=W(t);K.length&&(p.push(`${d}${K.join(",")}[vscaled]`),d="[vscaled]");const E=["-y"];for(const l of i)E.push("-i",l);for(const l of s)E.push("-i",l);a&&E.push("-i",a);for(const l of m)E.push("-i",l);E.push("-filter_complex",p.join(";"),"-map",d);const j=!!y;return j&&E.push("-map",y),E.push(...U(j,t)),j&&E.push("-ar","44100","-ac","2"),E.push(n),E}function Vt(t,e,n){return["-y","-ss",String(e),"-i",t,"-frames:v","1","-q:v","2",n]}export{ne as AudioClip,ie as Clip,_e as DEFAULT_PLATFORM_TARGETS,Bt as DEG2RAD,we as EDITOR_MESSAGES,Ut as FILTER_PRESETS,Oe as IMAGE_PLATFORM_PRESETS,Re as IMAGE_RATIO_PRESETS,re as MIN_CLIP,se as Overlay,Ne as VIDEO_PLATFORM_RATIO_PRESETS,Ie as VIDEO_RATIO_PRESETS,Le as VIDEO_SIZE_PRESETS,oe as VideoEditState,zt as buildFfmpegArgs,Gt as buildFilterCss,De as buildImagePlatformPresets,Vt as buildPosterArgs,Ce as buildVideoPlatformRatioPresets,je as buildVideoSizePresets,ae as canRedo,ce as canUndo,Kt as centeredMaxRect,ue as clampAudioClip,Ht as clampRect,le as clipDuration,fe as clipStartsAt,ze as compensateAnnotationRotation,he as contentDuration,pe as createHistory,me as defaultVideoEditState,$e as duplicateClip,Xt as evenize,Yt as findFilterPreset,Zt as fitRects,de as locateOutput,Jt as normalizeDeg,Qt as outputSize,Ve as parseRatio,ve as pushHistory,ge as redo,be as remapSrcIndices,xe as removeClip,Se as reorderClips,Te as replacePresent,te as rotatedSize,Ae as splitAtOutput,ke as tEditor,qe as toHant,Me as totalDuration,Ee as trimClipEdge,Fe as undo,ye as usedVideoSrcIndices};