@omelhorsite/video-sdk 0.2.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.
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # `@omelhorsite/video-sdk`
2
+
3
+ The engine of the omelhorsite video editor, as a library: the project model,
4
+ the math that puts every clip where it belongs, the effects, the karaoke
5
+ captions, the compiler that turns all of it into `ffmpeg` arguments, and the
6
+ client for the omelhorsite cloud.
7
+
8
+ ```sh
9
+ bun add @omelhorsite/video-sdk
10
+ ```
11
+
12
+ It is pure. Nothing here reads a file or spawns a process: files come in and
13
+ go out as `Blob` or `Uint8Array`, and rendering is a function that returns the
14
+ arguments for `ffmpeg`. That is what lets it run in Bun, Node 18+ and the
15
+ browser alike.
16
+
17
+ ## The project
18
+
19
+ A `Project` is `{ width, height, fps, assets[], tracks[] }`. Tracks render
20
+ bottom to top in array order; a clip is `{ start, duration, in }` in seconds on
21
+ the project timeline, and a visual clip carries a `transform` whose `x`/`y` are
22
+ the centre in normalised coordinates and whose `scale` multiplies the size the
23
+ asset gets when fitted inside the frame.
24
+
25
+ An `.omsv` file is a ZIP with that object and all of its media inside,
26
+ portable and self-contained.
27
+
28
+ ```ts
29
+ import { emptyProject, packProject, unpackProject, isZipData } from "@omelhorsite/video-sdk";
30
+
31
+ const project = emptyProject();
32
+ project.assets.push({ id: "a1", kind: "video", name: "clip.mp4", path: "clip.mp4", duration: 12 });
33
+ project.tracks[1].clips.push({
34
+ id: "c1", kind: "video", assetId: "a1", start: 0, duration: 4, in: 2,
35
+ transform: { x: 0.5, y: 0.5, scale: 1, opacity: 1 }, fadeIn: 0, fadeOut: 0, muted: false, volume: 1,
36
+ });
37
+
38
+ const bytes = packProject({ project, assetBytes: new Map([["a1", clipBytes]]), nowIso: new Date().toISOString() });
39
+ const { project: again, assetBytes } = unpackProject(bytes);
40
+ ```
41
+
42
+ ## Rendering
43
+
44
+ `compile` returns the `ffmpeg` argument list for the whole project in one
45
+ pass: layers, transforms, fades, text, effects and captions. Text arrives
46
+ rasterised, as PNGs the caller provides, so what was drawn is what gets
47
+ composited.
48
+
49
+ ```ts
50
+ import { compile, projectDuration } from "@omelhorsite/video-sdk";
51
+
52
+ const { args } = compile(project, { output: "out.mp4", preset: "slow", crf: 17, textImages });
53
+ // spawn("ffmpeg", args)
54
+ ```
55
+
56
+ `placement.ts` is the one place that decides where a clip goes (`fittedSize`,
57
+ `clipRect`, `visibleAt`); a preview drawn with the same functions matches the
58
+ export by construction.
59
+
60
+ ## Effects and captions
61
+
62
+ - `makeFlashClip(id, cutAt)` builds the flash transition that hides a cut;
63
+ `DEFAULT_FLASH` holds its parameters.
64
+ - Captions live in the project in source time (`project.captions[].words`,
65
+ `{ t0, t1, text }`), so cutting or reordering never invalidates them.
66
+ `timelineWords(project)` brings the words onto the timeline; `groupWords`
67
+ and `captionStates` do the karaoke grouping.
68
+ - `sliceProject(project, from, to)` cuts a window out of a project for a fast
69
+ partial render.
70
+
71
+ ## The omelhorsite cloud
72
+
73
+ `Cloud` signs in with the device grant, keeps whole projects in the account's
74
+ storage, and runs transcription and captions on the server. It takes a
75
+ `TokenStore` for the tokens and, when the host needs it, a `fetch`.
76
+
77
+ ```ts
78
+ import { Cloud, memoryTokenStore, describeCloudError } from "@omelhorsite/video-sdk";
79
+
80
+ const cloud = new Cloud({ store: memoryTokenStore() });
81
+ await cloud.login({ onPrompt: (p) => show(p.userCode, p.url) });
82
+
83
+ const saved = await cloud.uploadProject({ data: bytes, name: "promo" }, { onProgress: report });
84
+ const projects = await cloud.listProjects();
85
+ const { data } = await cloud.downloadProject(saved.id);
86
+
87
+ const tx = await cloud.transcribe({ data: wav, name: "voice.wav" }, { language: "pt" });
88
+ const captioned = await cloud.captions({ data: mp4, name: "render.mp4" }, { words: timelineWords(project) });
89
+ ```
90
+
91
+ `stringTokenStore` adapts anything that can keep a string (a file, a
92
+ keychain) into a `TokenStore`. `describeCloudError` turns any failure into a
93
+ sentence fit for a screen. Progress arrives per finished transfer, or per byte
94
+ where the runtime has `XMLHttpRequest` and no custom `fetch` was given.
95
+
96
+ ## Developing
97
+
98
+ ```sh
99
+ bun test # renders real mp4 files with the system ffmpeg
100
+ bun run typecheck
101
+ bun run check:isolate
102
+ bun run build
103
+ ```