aivee 0.1.0-alpha.0 → 0.1.0-dev.59872e1

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 (74) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/LICENSE +21 -21
  3. package/README.md +120 -118
  4. package/docs/architecture.md +401 -401
  5. package/docs/aspect-ratio-presets.md +72 -72
  6. package/docs/releasing.md +19 -19
  7. package/docs/usage.md +1193 -1193
  8. package/package.json +52 -52
  9. package/requirements-otio.txt +1 -1
  10. package/src/capabilities.mjs +307 -307
  11. package/src/cli.mjs +646 -646
  12. package/src/doctor.mjs +133 -133
  13. package/src/edits/analysis-to-edl.mjs +43 -43
  14. package/src/edits/detect-silence.mjs +51 -51
  15. package/src/edits/find-mistake-candidates.mjs +42 -42
  16. package/src/edits/plan-shot-placements.mjs +105 -105
  17. package/src/edits/plan-silence-cuts.mjs +49 -49
  18. package/src/edits/retime-transcript.mjs +105 -105
  19. package/src/files.mjs +80 -80
  20. package/src/index.mjs +44 -44
  21. package/src/interchange/otio.mjs +204 -204
  22. package/src/interchange/otio_bridge.py +521 -521
  23. package/src/media/analysis-provider.mjs +75 -75
  24. package/src/media/compatibility.mjs +45 -45
  25. package/src/media/ffmpeg-capabilities.mjs +129 -129
  26. package/src/media/frame-rate.mjs +58 -58
  27. package/src/media/frame-time.mjs +22 -22
  28. package/src/media/preview-assets.mjs +350 -350
  29. package/src/media/probe.mjs +149 -149
  30. package/src/media/render-cuts.mjs +90 -90
  31. package/src/media/scene-detection.mjs +107 -107
  32. package/src/media/scene-review.mjs +205 -205
  33. package/src/project/assets.mjs +1194 -1194
  34. package/src/project/create.mjs +100 -100
  35. package/src/project/presets.mjs +44 -44
  36. package/src/render/checkpoints.mjs +192 -192
  37. package/src/render/formats.mjs +163 -163
  38. package/src/render/hyperframes.mjs +590 -590
  39. package/src/render/process.mjs +111 -111
  40. package/src/render/progress.mjs +46 -46
  41. package/src/render/timeline-control.mjs +53 -53
  42. package/src/timeline/assemble.mjs +119 -119
  43. package/src/timeline/audio-effects.mjs +75 -75
  44. package/src/timeline/audio-mastering.mjs +236 -236
  45. package/src/timeline/audio-mix.mjs +105 -105
  46. package/src/timeline/audio-track-effects.mjs +139 -139
  47. package/src/timeline/audio-volume.mjs +63 -63
  48. package/src/timeline/background.mjs +8 -8
  49. package/src/timeline/blend-mode.mjs +34 -34
  50. package/src/timeline/caption-layouts.mjs +62 -61
  51. package/src/timeline/captions.mjs +80 -80
  52. package/src/timeline/edit.mjs +965 -965
  53. package/src/timeline/fingerprint.mjs +6 -6
  54. package/src/timeline/from-edl.mjs +31 -31
  55. package/src/timeline/history-store.mjs +281 -281
  56. package/src/timeline/history.mjs +69 -69
  57. package/src/timeline/inspect.mjs +117 -117
  58. package/src/timeline/keyframes.mjs +158 -158
  59. package/src/timeline/layers.mjs +18 -18
  60. package/src/timeline/mask.mjs +53 -53
  61. package/src/timeline/project.mjs +52 -52
  62. package/src/timeline/render.mjs +759 -759
  63. package/src/timeline/sidechain-render.mjs +209 -209
  64. package/src/timeline/source-fit.mjs +15 -15
  65. package/src/timeline/speech-bubble.mjs +26 -26
  66. package/src/timeline/speed-ramp.mjs +188 -188
  67. package/src/timeline/track-state.mjs +22 -22
  68. package/src/timeline/transition.mjs +34 -34
  69. package/src/timeline/validate.mjs +322 -322
  70. package/src/transcript/corrections.mjs +21 -21
  71. package/src/transcript/normalize.mjs +17 -17
  72. package/src/transcript/provider.mjs +42 -42
  73. package/src/transcript/subtitles.mjs +57 -57
  74. 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
+ }