aivee 0.1.0-alpha.0 → 0.1.0-dev.7025438

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 (73) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +118 -118
  3. package/docs/architecture.md +401 -401
  4. package/docs/aspect-ratio-presets.md +72 -72
  5. package/docs/releasing.md +19 -19
  6. package/docs/usage.md +1193 -1193
  7. package/package.json +52 -52
  8. package/requirements-otio.txt +1 -1
  9. package/src/capabilities.mjs +307 -307
  10. package/src/cli.mjs +646 -646
  11. package/src/doctor.mjs +133 -133
  12. package/src/edits/analysis-to-edl.mjs +43 -43
  13. package/src/edits/detect-silence.mjs +51 -51
  14. package/src/edits/find-mistake-candidates.mjs +42 -42
  15. package/src/edits/plan-shot-placements.mjs +105 -105
  16. package/src/edits/plan-silence-cuts.mjs +49 -49
  17. package/src/edits/retime-transcript.mjs +105 -105
  18. package/src/files.mjs +80 -80
  19. package/src/index.mjs +44 -44
  20. package/src/interchange/otio.mjs +204 -204
  21. package/src/interchange/otio_bridge.py +521 -521
  22. package/src/media/analysis-provider.mjs +75 -75
  23. package/src/media/compatibility.mjs +45 -45
  24. package/src/media/ffmpeg-capabilities.mjs +129 -129
  25. package/src/media/frame-rate.mjs +58 -58
  26. package/src/media/frame-time.mjs +22 -22
  27. package/src/media/preview-assets.mjs +350 -350
  28. package/src/media/probe.mjs +149 -149
  29. package/src/media/render-cuts.mjs +90 -90
  30. package/src/media/scene-detection.mjs +107 -107
  31. package/src/media/scene-review.mjs +205 -205
  32. package/src/project/assets.mjs +1194 -1194
  33. package/src/project/create.mjs +100 -100
  34. package/src/project/presets.mjs +44 -44
  35. package/src/render/checkpoints.mjs +192 -192
  36. package/src/render/formats.mjs +163 -163
  37. package/src/render/hyperframes.mjs +590 -590
  38. package/src/render/process.mjs +111 -111
  39. package/src/render/progress.mjs +46 -46
  40. package/src/render/timeline-control.mjs +53 -53
  41. package/src/timeline/assemble.mjs +119 -119
  42. package/src/timeline/audio-effects.mjs +75 -75
  43. package/src/timeline/audio-mastering.mjs +236 -236
  44. package/src/timeline/audio-mix.mjs +105 -105
  45. package/src/timeline/audio-track-effects.mjs +139 -139
  46. package/src/timeline/audio-volume.mjs +63 -63
  47. package/src/timeline/background.mjs +8 -8
  48. package/src/timeline/blend-mode.mjs +34 -34
  49. package/src/timeline/caption-layouts.mjs +61 -61
  50. package/src/timeline/captions.mjs +80 -80
  51. package/src/timeline/edit.mjs +965 -965
  52. package/src/timeline/fingerprint.mjs +6 -6
  53. package/src/timeline/from-edl.mjs +31 -31
  54. package/src/timeline/history-store.mjs +281 -281
  55. package/src/timeline/history.mjs +69 -69
  56. package/src/timeline/inspect.mjs +117 -117
  57. package/src/timeline/keyframes.mjs +158 -158
  58. package/src/timeline/layers.mjs +18 -18
  59. package/src/timeline/mask.mjs +53 -53
  60. package/src/timeline/project.mjs +52 -52
  61. package/src/timeline/render.mjs +759 -759
  62. package/src/timeline/sidechain-render.mjs +209 -209
  63. package/src/timeline/source-fit.mjs +15 -15
  64. package/src/timeline/speech-bubble.mjs +26 -26
  65. package/src/timeline/speed-ramp.mjs +188 -188
  66. package/src/timeline/track-state.mjs +22 -22
  67. package/src/timeline/transition.mjs +34 -34
  68. package/src/timeline/validate.mjs +322 -322
  69. package/src/transcript/corrections.mjs +21 -21
  70. package/src/transcript/normalize.mjs +17 -17
  71. package/src/transcript/provider.mjs +42 -42
  72. package/src/transcript/subtitles.mjs +57 -57
  73. package/src/transcript/validate.mjs +39 -39
@@ -1,163 +1,163 @@
1
- const EXTENSIONS = new Map([
2
- ['.mp4', 'mp4'],
3
- ['.webm', 'webm'],
4
- ['.mov', 'mov'],
5
- ['.gif', 'gif'],
6
- ]);
7
-
8
- export function resolveRenderFormat(outputPath, requestedFormat) {
9
- if (requestedFormat === 'png-sequence') return 'png-sequence';
10
- const extension = outputPath ? outputPath.slice(outputPath.lastIndexOf('.')).toLowerCase() : undefined;
11
- const inferred = EXTENSIONS.get(extension);
12
- const format = requestedFormat ?? inferred ?? 'mp4';
13
- if (!['mp4', 'webm', 'mov', 'gif', 'png-sequence'].includes(format)) {
14
- throw new TypeError('Format must be mp4, webm, mov, gif, or png-sequence.');
15
- }
16
- if (requestedFormat && inferred && requestedFormat !== inferred) {
17
- throw new TypeError(`Output extension ${extension} does not match requested ${requestedFormat} format.`);
18
- }
19
- if (outputPath && extension && !inferred) throw new TypeError(`Unsupported render output extension: ${extension}`);
20
- const resolvedExtension = `.${format}`;
21
- if (outputPath && format !== 'png-sequence' && extension !== resolvedExtension) {
22
- throw new TypeError(`Render output for ${format} must use the ${resolvedExtension} extension.`);
23
- }
24
- return format;
25
- }
26
-
27
- export function resolveEncoderArgs(format, options = {}) {
28
- const encoder = resolveVideoEncoder(format, options);
29
- if (encoder.codec) return [];
30
- if (encoder.crf !== undefined) return ['--crf', String(encoder.crf)];
31
- if (encoder.videoBitrate !== undefined) return ['--video-bitrate', encoder.videoBitrate];
32
- return [];
33
- }
34
-
35
- export function resolveColorProfile(profile, format) {
36
- if (profile === undefined) return undefined;
37
- if (profile === 'hdr10' && !['mp4', 'mov'].includes(format)) {
38
- throw new TypeError(`The hdr10 color profile requires MP4 or MOV output, not ${format}.`);
39
- }
40
- if (!['mp4', 'mov', 'webm'].includes(format)) {
41
- throw new TypeError(`Color profiles are supported for MP4, MOV, and WebM output, not ${format}.`);
42
- }
43
- const profiles = {
44
- 'rec709': {
45
- id: 'rec709',
46
- filter: 'colorspace=iall=bt709:space=bt709:primaries=bt709:trc=bt709:range=tv',
47
- colorSpace: 'bt709',
48
- colorTransfer: 'bt709',
49
- colorPrimaries: 'bt709',
50
- colorRange: 'tv',
51
- requiredFilter: 'colorspace',
52
- },
53
- 'rec2020-sdr': {
54
- id: 'rec2020-sdr',
55
- filter: 'colorspace=iall=bt709:space=bt2020nc:primaries=bt2020:trc=bt709:range=tv',
56
- colorSpace: 'bt2020nc',
57
- colorTransfer: 'bt709',
58
- colorPrimaries: 'bt2020',
59
- colorRange: 'tv',
60
- requiredFilter: 'colorspace',
61
- },
62
- hdr10: {
63
- id: 'hdr10',
64
- filter: 'zscale=pin=bt709:tin=bt709:min=bt709:rin=tv:p=bt2020:t=smpte2084:m=bt2020nc:r=tv:npl=203,format=yuv420p10le',
65
- colorSpace: 'bt2020nc',
66
- colorTransfer: 'smpte2084',
67
- colorPrimaries: 'bt2020',
68
- colorRange: 'tv',
69
- pixelFormat: 'yuv420p10le',
70
- requiredFilter: 'zscale',
71
- masteringDisplay: 'G(8500,39850)B(6550,2300)R(35400,14600)WP(15635,16450)L(10000000,1)',
72
- contentLightLevel: '203,203',
73
- },
74
- };
75
- const resolved = profiles[profile];
76
- if (!resolved) throw new TypeError(`Unsupported colorProfile ${String(profile)}; choose rec709, rec2020-sdr, or hdr10.`);
77
- return resolved;
78
- }
79
-
80
- export function resolveProfileEncoder(format, encoder, colorProfile) {
81
- if (colorProfile?.id === 'hdr10') {
82
- if (encoder.codec && encoder.codecId !== 'h265') {
83
- throw new TypeError('The hdr10 color profile requires the h265 codec.');
84
- }
85
- return encoder.codec ? encoder : { ...encoder, codecId: 'h265', codec: 'libx265' };
86
- }
87
- if (encoder.codec) return encoder;
88
- const codecId = format === 'webm' ? 'vp9' : 'h264';
89
- const codec = format === 'webm' ? 'libvpx-vp9' : 'libx264';
90
- return { ...encoder, codecId, codec };
91
- }
92
-
93
- export function resolveVideoEncoder(format, options = {}) {
94
- if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('Render options must be an object.');
95
- for (const field of Object.keys(options)) if (!['codec', 'crf', 'videoBitrate'].includes(field)) throw new TypeError(`Unsupported encoder option: ${field}.`);
96
- if (options.crf !== undefined && options.videoBitrate !== undefined) throw new TypeError('Choose either crf or videoBitrate, not both.');
97
-
98
- const codecs = format === 'webm'
99
- ? { vp9: 'libvpx-vp9' }
100
- : format === 'mov'
101
- ? { h264: 'libx264', h265: 'libx265', prores: 'prores_ks' }
102
- : format === 'mp4'
103
- ? { h264: 'libx264', h265: 'libx265' }
104
- : {};
105
- if (options.codec !== undefined && (typeof options.codec !== 'string' || codecs[options.codec] === undefined)) {
106
- throw new TypeError(`Render codec is unsupported for ${format}; choose one of: ${Object.keys(codecs).join(', ') || 'none'}.`);
107
- }
108
-
109
- if (options.crf !== undefined) {
110
- if (!['mp4', 'mov', 'webm'].includes(format)) throw new TypeError(`CRF control is not supported for ${format} output.`);
111
- const maximum = options.codec === 'vp9' || (format === 'webm' && options.codec === undefined) ? 63 : 51;
112
- if (options.codec === 'prores'
113
- || !Number.isSafeInteger(options.crf) || options.crf < 0 || options.crf > maximum) {
114
- throw new TypeError(`Render crf must be an integer from 0 to ${maximum} for ${options.codec ?? format}.`);
115
- }
116
- }
117
- if (options.videoBitrate !== undefined && (!['mp4', 'mov', 'webm'].includes(format)
118
- || options.codec === 'prores'
119
- || typeof options.videoBitrate !== 'string'
120
- || !/^(?:[1-9]\d{0,5})(?:\.\d{1,2})?[KMG]?$/.test(options.videoBitrate))) {
121
- throw new TypeError('Render videoBitrate must be a positive bitrate such as 800K, 5M, or 1.5M on a bitrate-based encoder.');
122
- }
123
-
124
- return {
125
- ...(options.codec ? { codec: codecs[options.codec], codecId: options.codec } : {}),
126
- ...(options.crf !== undefined ? { crf: options.crf } : {}),
127
- ...(options.videoBitrate !== undefined ? { videoBitrate: options.videoBitrate } : {}),
128
- };
129
- }
130
-
131
- export function resolveRenderControlArgs(options = {}) {
132
- if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('Render options must be an object.');
133
- for (const field of Object.keys(options)) {
134
- if (!['format', 'encoder', 'colorProfile', 'workers', 'lowMemoryMode', 'timeoutSeconds', 'resumeCheckpoint', 'checkpointMaxBytes', 'checkpointMaxAgeHours'].includes(field)) throw new TypeError(`Unsupported render option: ${field}.`);
135
- }
136
- if (options.workers !== undefined
137
- && options.workers !== 'auto'
138
- && (!Number.isSafeInteger(options.workers) || options.workers < 1 || options.workers > 64)) {
139
- throw new TypeError('Render workers must be auto or an integer from 1 to 64.');
140
- }
141
- if (options.lowMemoryMode !== undefined && typeof options.lowMemoryMode !== 'boolean') {
142
- throw new TypeError('Render lowMemoryMode must be a boolean.');
143
- }
144
- if (options.timeoutSeconds !== undefined
145
- && (!Number.isSafeInteger(options.timeoutSeconds) || options.timeoutSeconds < 1 || options.timeoutSeconds > 86_400)) {
146
- throw new TypeError('Render timeoutSeconds must be an integer from 1 to 86400.');
147
- }
148
- if (options.resumeCheckpoint !== undefined && typeof options.resumeCheckpoint !== 'boolean') {
149
- throw new TypeError('Render resumeCheckpoint must be a boolean.');
150
- }
151
- if (options.checkpointMaxBytes !== undefined
152
- && (!Number.isSafeInteger(options.checkpointMaxBytes) || options.checkpointMaxBytes < 1024 * 1024 || options.checkpointMaxBytes > 100 * 1024 ** 3)) {
153
- throw new TypeError('checkpointMaxBytes must be an integer from 1048576 to 107374182400.');
154
- }
155
- if (options.checkpointMaxAgeHours !== undefined
156
- && (!Number.isSafeInteger(options.checkpointMaxAgeHours) || options.checkpointMaxAgeHours < 1 || options.checkpointMaxAgeHours > 720)) {
157
- throw new TypeError('checkpointMaxAgeHours must be an integer from 1 to 720.');
158
- }
159
- return [
160
- ...(options.workers !== undefined ? ['--workers', String(options.workers)] : []),
161
- ...(options.lowMemoryMode === true ? ['--low-memory-mode'] : options.lowMemoryMode === false ? ['--no-low-memory-mode'] : []),
162
- ];
163
- }
1
+ const EXTENSIONS = new Map([
2
+ ['.mp4', 'mp4'],
3
+ ['.webm', 'webm'],
4
+ ['.mov', 'mov'],
5
+ ['.gif', 'gif'],
6
+ ]);
7
+
8
+ export function resolveRenderFormat(outputPath, requestedFormat) {
9
+ if (requestedFormat === 'png-sequence') return 'png-sequence';
10
+ const extension = outputPath ? outputPath.slice(outputPath.lastIndexOf('.')).toLowerCase() : undefined;
11
+ const inferred = EXTENSIONS.get(extension);
12
+ const format = requestedFormat ?? inferred ?? 'mp4';
13
+ if (!['mp4', 'webm', 'mov', 'gif', 'png-sequence'].includes(format)) {
14
+ throw new TypeError('Format must be mp4, webm, mov, gif, or png-sequence.');
15
+ }
16
+ if (requestedFormat && inferred && requestedFormat !== inferred) {
17
+ throw new TypeError(`Output extension ${extension} does not match requested ${requestedFormat} format.`);
18
+ }
19
+ if (outputPath && extension && !inferred) throw new TypeError(`Unsupported render output extension: ${extension}`);
20
+ const resolvedExtension = `.${format}`;
21
+ if (outputPath && format !== 'png-sequence' && extension !== resolvedExtension) {
22
+ throw new TypeError(`Render output for ${format} must use the ${resolvedExtension} extension.`);
23
+ }
24
+ return format;
25
+ }
26
+
27
+ export function resolveEncoderArgs(format, options = {}) {
28
+ const encoder = resolveVideoEncoder(format, options);
29
+ if (encoder.codec) return [];
30
+ if (encoder.crf !== undefined) return ['--crf', String(encoder.crf)];
31
+ if (encoder.videoBitrate !== undefined) return ['--video-bitrate', encoder.videoBitrate];
32
+ return [];
33
+ }
34
+
35
+ export function resolveColorProfile(profile, format) {
36
+ if (profile === undefined) return undefined;
37
+ if (profile === 'hdr10' && !['mp4', 'mov'].includes(format)) {
38
+ throw new TypeError(`The hdr10 color profile requires MP4 or MOV output, not ${format}.`);
39
+ }
40
+ if (!['mp4', 'mov', 'webm'].includes(format)) {
41
+ throw new TypeError(`Color profiles are supported for MP4, MOV, and WebM output, not ${format}.`);
42
+ }
43
+ const profiles = {
44
+ 'rec709': {
45
+ id: 'rec709',
46
+ filter: 'colorspace=iall=bt709:space=bt709:primaries=bt709:trc=bt709:range=tv',
47
+ colorSpace: 'bt709',
48
+ colorTransfer: 'bt709',
49
+ colorPrimaries: 'bt709',
50
+ colorRange: 'tv',
51
+ requiredFilter: 'colorspace',
52
+ },
53
+ 'rec2020-sdr': {
54
+ id: 'rec2020-sdr',
55
+ filter: 'colorspace=iall=bt709:space=bt2020nc:primaries=bt2020:trc=bt709:range=tv',
56
+ colorSpace: 'bt2020nc',
57
+ colorTransfer: 'bt709',
58
+ colorPrimaries: 'bt2020',
59
+ colorRange: 'tv',
60
+ requiredFilter: 'colorspace',
61
+ },
62
+ hdr10: {
63
+ id: 'hdr10',
64
+ filter: 'zscale=pin=bt709:tin=bt709:min=bt709:rin=tv:p=bt2020:t=smpte2084:m=bt2020nc:r=tv:npl=203,format=yuv420p10le',
65
+ colorSpace: 'bt2020nc',
66
+ colorTransfer: 'smpte2084',
67
+ colorPrimaries: 'bt2020',
68
+ colorRange: 'tv',
69
+ pixelFormat: 'yuv420p10le',
70
+ requiredFilter: 'zscale',
71
+ masteringDisplay: 'G(8500,39850)B(6550,2300)R(35400,14600)WP(15635,16450)L(10000000,1)',
72
+ contentLightLevel: '203,203',
73
+ },
74
+ };
75
+ const resolved = profiles[profile];
76
+ if (!resolved) throw new TypeError(`Unsupported colorProfile ${String(profile)}; choose rec709, rec2020-sdr, or hdr10.`);
77
+ return resolved;
78
+ }
79
+
80
+ export function resolveProfileEncoder(format, encoder, colorProfile) {
81
+ if (colorProfile?.id === 'hdr10') {
82
+ if (encoder.codec && encoder.codecId !== 'h265') {
83
+ throw new TypeError('The hdr10 color profile requires the h265 codec.');
84
+ }
85
+ return encoder.codec ? encoder : { ...encoder, codecId: 'h265', codec: 'libx265' };
86
+ }
87
+ if (encoder.codec) return encoder;
88
+ const codecId = format === 'webm' ? 'vp9' : 'h264';
89
+ const codec = format === 'webm' ? 'libvpx-vp9' : 'libx264';
90
+ return { ...encoder, codecId, codec };
91
+ }
92
+
93
+ export function resolveVideoEncoder(format, options = {}) {
94
+ if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('Render options must be an object.');
95
+ for (const field of Object.keys(options)) if (!['codec', 'crf', 'videoBitrate'].includes(field)) throw new TypeError(`Unsupported encoder option: ${field}.`);
96
+ if (options.crf !== undefined && options.videoBitrate !== undefined) throw new TypeError('Choose either crf or videoBitrate, not both.');
97
+
98
+ const codecs = format === 'webm'
99
+ ? { vp9: 'libvpx-vp9' }
100
+ : format === 'mov'
101
+ ? { h264: 'libx264', h265: 'libx265', prores: 'prores_ks' }
102
+ : format === 'mp4'
103
+ ? { h264: 'libx264', h265: 'libx265' }
104
+ : {};
105
+ if (options.codec !== undefined && (typeof options.codec !== 'string' || codecs[options.codec] === undefined)) {
106
+ throw new TypeError(`Render codec is unsupported for ${format}; choose one of: ${Object.keys(codecs).join(', ') || 'none'}.`);
107
+ }
108
+
109
+ if (options.crf !== undefined) {
110
+ if (!['mp4', 'mov', 'webm'].includes(format)) throw new TypeError(`CRF control is not supported for ${format} output.`);
111
+ const maximum = options.codec === 'vp9' || (format === 'webm' && options.codec === undefined) ? 63 : 51;
112
+ if (options.codec === 'prores'
113
+ || !Number.isSafeInteger(options.crf) || options.crf < 0 || options.crf > maximum) {
114
+ throw new TypeError(`Render crf must be an integer from 0 to ${maximum} for ${options.codec ?? format}.`);
115
+ }
116
+ }
117
+ if (options.videoBitrate !== undefined && (!['mp4', 'mov', 'webm'].includes(format)
118
+ || options.codec === 'prores'
119
+ || typeof options.videoBitrate !== 'string'
120
+ || !/^(?:[1-9]\d{0,5})(?:\.\d{1,2})?[KMG]?$/.test(options.videoBitrate))) {
121
+ throw new TypeError('Render videoBitrate must be a positive bitrate such as 800K, 5M, or 1.5M on a bitrate-based encoder.');
122
+ }
123
+
124
+ return {
125
+ ...(options.codec ? { codec: codecs[options.codec], codecId: options.codec } : {}),
126
+ ...(options.crf !== undefined ? { crf: options.crf } : {}),
127
+ ...(options.videoBitrate !== undefined ? { videoBitrate: options.videoBitrate } : {}),
128
+ };
129
+ }
130
+
131
+ export function resolveRenderControlArgs(options = {}) {
132
+ if (!options || typeof options !== 'object' || Array.isArray(options)) throw new TypeError('Render options must be an object.');
133
+ for (const field of Object.keys(options)) {
134
+ if (!['format', 'encoder', 'colorProfile', 'workers', 'lowMemoryMode', 'timeoutSeconds', 'resumeCheckpoint', 'checkpointMaxBytes', 'checkpointMaxAgeHours'].includes(field)) throw new TypeError(`Unsupported render option: ${field}.`);
135
+ }
136
+ if (options.workers !== undefined
137
+ && options.workers !== 'auto'
138
+ && (!Number.isSafeInteger(options.workers) || options.workers < 1 || options.workers > 64)) {
139
+ throw new TypeError('Render workers must be auto or an integer from 1 to 64.');
140
+ }
141
+ if (options.lowMemoryMode !== undefined && typeof options.lowMemoryMode !== 'boolean') {
142
+ throw new TypeError('Render lowMemoryMode must be a boolean.');
143
+ }
144
+ if (options.timeoutSeconds !== undefined
145
+ && (!Number.isSafeInteger(options.timeoutSeconds) || options.timeoutSeconds < 1 || options.timeoutSeconds > 86_400)) {
146
+ throw new TypeError('Render timeoutSeconds must be an integer from 1 to 86400.');
147
+ }
148
+ if (options.resumeCheckpoint !== undefined && typeof options.resumeCheckpoint !== 'boolean') {
149
+ throw new TypeError('Render resumeCheckpoint must be a boolean.');
150
+ }
151
+ if (options.checkpointMaxBytes !== undefined
152
+ && (!Number.isSafeInteger(options.checkpointMaxBytes) || options.checkpointMaxBytes < 1024 * 1024 || options.checkpointMaxBytes > 100 * 1024 ** 3)) {
153
+ throw new TypeError('checkpointMaxBytes must be an integer from 1048576 to 107374182400.');
154
+ }
155
+ if (options.checkpointMaxAgeHours !== undefined
156
+ && (!Number.isSafeInteger(options.checkpointMaxAgeHours) || options.checkpointMaxAgeHours < 1 || options.checkpointMaxAgeHours > 720)) {
157
+ throw new TypeError('checkpointMaxAgeHours must be an integer from 1 to 720.');
158
+ }
159
+ return [
160
+ ...(options.workers !== undefined ? ['--workers', String(options.workers)] : []),
161
+ ...(options.lowMemoryMode === true ? ['--low-memory-mode'] : options.lowMemoryMode === false ? ['--no-low-memory-mode'] : []),
162
+ ];
163
+ }