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,100 +1,100 @@
1
- import { copyFileSync, existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
2
- import { dirname, join, resolve } from 'node:path';
3
- import { createRequire } from 'node:module';
4
- import { resolveCanvasPreset } from './presets.mjs';
5
- import { DEFAULT_SOURCE_FIT, validateSourceFit } from '../timeline/source-fit.mjs';
6
- import { createProjectTimeline } from '../timeline/project.mjs';
7
- import { normalizeFrameRate } from '../media/frame-rate.mjs';
8
- import { DEFAULT_PROJECT_BACKGROUND_COLOR, validateProjectBackgroundColor } from '../timeline/background.mjs';
9
-
10
- const require = createRequire(import.meta.url);
11
-
12
- export function createVideoProject(root, slug, options = {}) {
13
- if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) {
14
- throw new TypeError('Use a lowercase kebab-case slug, not a path.');
15
- }
16
- const { fps: requestedFps = 30, durationSeconds = 10, sourceFit = DEFAULT_SOURCE_FIT, backgroundColor = DEFAULT_PROJECT_BACKGROUND_COLOR } = options;
17
- if (options.preset !== undefined && (options.width !== undefined || options.height !== undefined)) {
18
- throw new TypeError('Choose a canvas preset or custom width and height, not both.');
19
- }
20
- const preset = options.preset === undefined ? undefined : resolveCanvasPreset(options.preset);
21
- const width = preset?.width ?? options.width ?? 1920;
22
- const height = preset?.height ?? options.height ?? 1080;
23
- validateSourceFit(sourceFit, 'sourceFit');
24
- validateProjectBackgroundColor(backgroundColor, 'backgroundColor');
25
- if (![width, height].every((value) => Number.isSafeInteger(value) && value > 0)) {
26
- throw new TypeError('Width and height must be positive integers.');
27
- }
28
- const fps = normalizeFrameRate(requestedFps);
29
- if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) {
30
- throw new TypeError('durationSeconds must be a positive number.');
31
- }
32
- const projectPath = resolve(root, 'video-projects', slug);
33
- if (existsSync(projectPath)) throw new Error(`Project already exists: ${slug}`);
34
- const projectsPath = join(root, 'video-projects');
35
-
36
- const project = {
37
- schemaVersion: 1,
38
- id: slug,
39
- name: slug,
40
- width,
41
- height,
42
- fps,
43
- durationSeconds,
44
- sourceFit,
45
- backgroundColor,
46
- ...(preset ? { canvasPreset: preset.id } : {}),
47
- };
48
- const directories = ['assets', 'transcripts', 'edits', 'compositions', 'renders'];
49
- mkdirSync(projectsPath, { recursive: true });
50
- mkdirSync(projectPath);
51
- try {
52
- for (const directory of directories) mkdirSync(join(projectPath, directory));
53
- const gsapPath = join(dirname(require.resolve('gsap')), 'gsap.min.js');
54
- copyFileSync(gsapPath, join(projectPath, 'assets', 'gsap.min.js'));
55
- writeFileSync(join(projectPath, 'meta.json'), `${JSON.stringify(project, null, 2)}\n`, { flag: 'wx' });
56
- writeFileSync(join(projectPath, 'hyperframes.json'), `${JSON.stringify({
57
- '$schema': 'https://hyperframes.heygen.com/schema/hyperframes.json',
58
- paths: { blocks: 'compositions', components: 'compositions/components', assets: 'assets' },
59
- }, null, 2)}\n`, { flag: 'wx' });
60
- writeFileSync(join(projectPath, 'index.html'), starterHtml(project), { flag: 'wx' });
61
- writeFileSync(join(projectPath, 'timeline.json'), `${JSON.stringify(createProjectTimeline(projectPath), null, 2)}\n`, { flag: 'wx' });
62
- } catch (error) {
63
- rmSync(projectPath, { recursive: true, force: true });
64
- throw error;
65
- }
66
- return projectPath;
67
- }
68
-
69
- function starterHtml({ name, width, height, durationSeconds, backgroundColor }) {
70
- const background = backgroundColor ?? 'transparent';
71
- return `<!doctype html>
72
- <html lang="en">
73
- <head>
74
- <meta charset="utf-8">
75
- <meta name="viewport" content="width=${width}, initial-scale=1">
76
- <title>${escapeHtml(name)}</title>
77
- <link rel="icon" href="data:,">
78
- <style>html,body,#root{box-sizing:border-box;margin:0;width:${width}px;height:${height}px}body{background:${background};color:#f5f5f5;font:48px system-ui,sans-serif}#root{display:grid;place-items:center}</style>
79
- <script src="assets/gsap.min.js"></script>
80
- </head>
81
- <body>
82
- <div id="root" data-composition-id="root" data-start="0" data-duration="${durationSeconds}" data-width="${width}" data-height="${height}">
83
- <div id="title" class="clip" data-start="0" data-duration="${durationSeconds}" data-track-index="0">${escapeHtml(name)}</div>
84
- </div>
85
- <script>
86
- const timeline = gsap.timeline({ paused: true });
87
- timeline.from('#title', { opacity: 0, y: 24, duration: 0.8, ease: 'power2.out' });
88
- window.__timelines = window.__timelines || {};
89
- window.__timelines.root = timeline;
90
- </script>
91
- </body>
92
- </html>
93
- `;
94
- }
95
-
96
- function escapeHtml(value) {
97
- return value.replace(/[&<>"']/g, (character) => ({
98
- '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
99
- })[character]);
100
- }
1
+ import { copyFileSync, existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join, resolve } from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import { resolveCanvasPreset } from './presets.mjs';
5
+ import { DEFAULT_SOURCE_FIT, validateSourceFit } from '../timeline/source-fit.mjs';
6
+ import { createProjectTimeline } from '../timeline/project.mjs';
7
+ import { normalizeFrameRate } from '../media/frame-rate.mjs';
8
+ import { DEFAULT_PROJECT_BACKGROUND_COLOR, validateProjectBackgroundColor } from '../timeline/background.mjs';
9
+
10
+ const require = createRequire(import.meta.url);
11
+
12
+ export function createVideoProject(root, slug, options = {}) {
13
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug)) {
14
+ throw new TypeError('Use a lowercase kebab-case slug, not a path.');
15
+ }
16
+ const { fps: requestedFps = 30, durationSeconds = 10, sourceFit = DEFAULT_SOURCE_FIT, backgroundColor = DEFAULT_PROJECT_BACKGROUND_COLOR } = options;
17
+ if (options.preset !== undefined && (options.width !== undefined || options.height !== undefined)) {
18
+ throw new TypeError('Choose a canvas preset or custom width and height, not both.');
19
+ }
20
+ const preset = options.preset === undefined ? undefined : resolveCanvasPreset(options.preset);
21
+ const width = preset?.width ?? options.width ?? 1920;
22
+ const height = preset?.height ?? options.height ?? 1080;
23
+ validateSourceFit(sourceFit, 'sourceFit');
24
+ validateProjectBackgroundColor(backgroundColor, 'backgroundColor');
25
+ if (![width, height].every((value) => Number.isSafeInteger(value) && value > 0)) {
26
+ throw new TypeError('Width and height must be positive integers.');
27
+ }
28
+ const fps = normalizeFrameRate(requestedFps);
29
+ if (!Number.isFinite(durationSeconds) || durationSeconds <= 0) {
30
+ throw new TypeError('durationSeconds must be a positive number.');
31
+ }
32
+ const projectPath = resolve(root, 'video-projects', slug);
33
+ if (existsSync(projectPath)) throw new Error(`Project already exists: ${slug}`);
34
+ const projectsPath = join(root, 'video-projects');
35
+
36
+ const project = {
37
+ schemaVersion: 1,
38
+ id: slug,
39
+ name: slug,
40
+ width,
41
+ height,
42
+ fps,
43
+ durationSeconds,
44
+ sourceFit,
45
+ backgroundColor,
46
+ ...(preset ? { canvasPreset: preset.id } : {}),
47
+ };
48
+ const directories = ['assets', 'transcripts', 'edits', 'compositions', 'renders'];
49
+ mkdirSync(projectsPath, { recursive: true });
50
+ mkdirSync(projectPath);
51
+ try {
52
+ for (const directory of directories) mkdirSync(join(projectPath, directory));
53
+ const gsapPath = join(dirname(require.resolve('gsap')), 'gsap.min.js');
54
+ copyFileSync(gsapPath, join(projectPath, 'assets', 'gsap.min.js'));
55
+ writeFileSync(join(projectPath, 'meta.json'), `${JSON.stringify(project, null, 2)}\n`, { flag: 'wx' });
56
+ writeFileSync(join(projectPath, 'hyperframes.json'), `${JSON.stringify({
57
+ '$schema': 'https://hyperframes.heygen.com/schema/hyperframes.json',
58
+ paths: { blocks: 'compositions', components: 'compositions/components', assets: 'assets' },
59
+ }, null, 2)}\n`, { flag: 'wx' });
60
+ writeFileSync(join(projectPath, 'index.html'), starterHtml(project), { flag: 'wx' });
61
+ writeFileSync(join(projectPath, 'timeline.json'), `${JSON.stringify(createProjectTimeline(projectPath), null, 2)}\n`, { flag: 'wx' });
62
+ } catch (error) {
63
+ rmSync(projectPath, { recursive: true, force: true });
64
+ throw error;
65
+ }
66
+ return projectPath;
67
+ }
68
+
69
+ function starterHtml({ name, width, height, durationSeconds, backgroundColor }) {
70
+ const background = backgroundColor ?? 'transparent';
71
+ return `<!doctype html>
72
+ <html lang="en">
73
+ <head>
74
+ <meta charset="utf-8">
75
+ <meta name="viewport" content="width=${width}, initial-scale=1">
76
+ <title>${escapeHtml(name)}</title>
77
+ <link rel="icon" href="data:,">
78
+ <style>html,body,#root{box-sizing:border-box;margin:0;width:${width}px;height:${height}px}body{background:${background};color:#f5f5f5;font:48px system-ui,sans-serif}#root{display:grid;place-items:center}</style>
79
+ <script src="assets/gsap.min.js"></script>
80
+ </head>
81
+ <body>
82
+ <div id="root" data-composition-id="root" data-start="0" data-duration="${durationSeconds}" data-width="${width}" data-height="${height}">
83
+ <div id="title" class="clip" data-start="0" data-duration="${durationSeconds}" data-track-index="0">${escapeHtml(name)}</div>
84
+ </div>
85
+ <script>
86
+ const timeline = gsap.timeline({ paused: true });
87
+ timeline.from('#title', { opacity: 0, y: 24, duration: 0.8, ease: 'power2.out' });
88
+ window.__timelines = window.__timelines || {};
89
+ window.__timelines.root = timeline;
90
+ </script>
91
+ </body>
92
+ </html>
93
+ `;
94
+ }
95
+
96
+ function escapeHtml(value) {
97
+ return value.replace(/[&<>"']/g, (character) => ({
98
+ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
99
+ })[character]);
100
+ }
@@ -1,44 +1,44 @@
1
- const presets = [
2
- {
3
- id: 'vertical',
4
- aspectRatio: '9:16',
5
- width: 1080,
6
- height: 1920,
7
- platforms: ['TikTok', 'Instagram Reels and Stories', 'Facebook Reels and Stories', 'YouTube Shorts'],
8
- },
9
- {
10
- id: 'portrait-feed',
11
- aspectRatio: '4:5',
12
- width: 1080,
13
- height: 1350,
14
- platforms: ['Instagram feed', 'LinkedIn feed'],
15
- },
16
- {
17
- id: 'square',
18
- aspectRatio: '1:1',
19
- width: 1080,
20
- height: 1080,
21
- platforms: ['TikTok', 'Instagram feed', 'LinkedIn feed'],
22
- },
23
- {
24
- id: 'landscape',
25
- aspectRatio: '16:9',
26
- width: 1920,
27
- height: 1080,
28
- platforms: ['YouTube', 'LinkedIn feed'],
29
- },
30
- ].map((preset) => Object.freeze({ ...preset, platforms: Object.freeze(preset.platforms) }));
31
-
32
- export const CANVAS_PRESETS = Object.freeze(presets);
33
-
34
- export function listCanvasPresets() {
35
- return CANVAS_PRESETS;
36
- }
37
-
38
- export function resolveCanvasPreset(id) {
39
- const preset = CANVAS_PRESETS.find((candidate) => candidate.id === id);
40
- if (!preset) {
41
- throw new TypeError(`Unknown canvas preset "${id}". Available presets: ${CANVAS_PRESETS.map(({ id: name }) => name).join(', ')}.`);
42
- }
43
- return preset;
44
- }
1
+ const presets = [
2
+ {
3
+ id: 'vertical',
4
+ aspectRatio: '9:16',
5
+ width: 1080,
6
+ height: 1920,
7
+ platforms: ['TikTok', 'Instagram Reels and Stories', 'Facebook Reels and Stories', 'YouTube Shorts'],
8
+ },
9
+ {
10
+ id: 'portrait-feed',
11
+ aspectRatio: '4:5',
12
+ width: 1080,
13
+ height: 1350,
14
+ platforms: ['Instagram feed', 'LinkedIn feed'],
15
+ },
16
+ {
17
+ id: 'square',
18
+ aspectRatio: '1:1',
19
+ width: 1080,
20
+ height: 1080,
21
+ platforms: ['TikTok', 'Instagram feed', 'LinkedIn feed'],
22
+ },
23
+ {
24
+ id: 'landscape',
25
+ aspectRatio: '16:9',
26
+ width: 1920,
27
+ height: 1080,
28
+ platforms: ['YouTube', 'LinkedIn feed'],
29
+ },
30
+ ].map((preset) => Object.freeze({ ...preset, platforms: Object.freeze(preset.platforms) }));
31
+
32
+ export const CANVAS_PRESETS = Object.freeze(presets);
33
+
34
+ export function listCanvasPresets() {
35
+ return CANVAS_PRESETS;
36
+ }
37
+
38
+ export function resolveCanvasPreset(id) {
39
+ const preset = CANVAS_PRESETS.find((candidate) => candidate.id === id);
40
+ if (!preset) {
41
+ throw new TypeError(`Unknown canvas preset "${id}". Available presets: ${CANVAS_PRESETS.map(({ id: name }) => name).join(', ')}.`);
42
+ }
43
+ return preset;
44
+ }