frameyard 0.0.0-stage → 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/CHANGELOG.md +69 -0
- package/GLOSSARY.md +145 -0
- package/LICENSE +21 -0
- package/README.md +311 -2
- package/dist/assets/executable.d.ts +16 -0
- package/dist/assets/executable.d.ts.map +1 -0
- package/dist/assets/executable.js +59 -0
- package/dist/assets/executable.js.map +1 -0
- package/dist/assets/files.d.ts +4 -0
- package/dist/assets/files.d.ts.map +1 -0
- package/dist/assets/files.js +30 -0
- package/dist/assets/files.js.map +1 -0
- package/dist/assets/integrations.d.ts +15 -0
- package/dist/assets/integrations.d.ts.map +1 -0
- package/dist/assets/integrations.js +15 -0
- package/dist/assets/integrations.js.map +1 -0
- package/dist/assets/process.d.ts +29 -0
- package/dist/assets/process.d.ts.map +1 -0
- package/dist/assets/process.js +97 -0
- package/dist/assets/process.js.map +1 -0
- package/dist/assets/provenance.d.ts +77 -0
- package/dist/assets/provenance.d.ts.map +1 -0
- package/dist/assets/provenance.js +302 -0
- package/dist/assets/provenance.js.map +1 -0
- package/dist/assets/transcribe.d.ts +38 -0
- package/dist/assets/transcribe.d.ts.map +1 -0
- package/dist/assets/transcribe.js +357 -0
- package/dist/assets/transcribe.js.map +1 -0
- package/dist/assets/tts.d.ts +29 -0
- package/dist/assets/tts.d.ts.map +1 -0
- package/dist/assets/tts.js +180 -0
- package/dist/assets/tts.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +795 -0
- package/dist/cli.js.map +1 -0
- package/dist/contracts.d.ts +106 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +297 -0
- package/dist/contracts.js.map +1 -0
- package/dist/doctor.d.ts +56 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +476 -0
- package/dist/doctor.js.map +1 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +18 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/preview/main.d.ts +19 -0
- package/dist/preview/main.d.ts.map +1 -0
- package/dist/preview/main.js +480 -0
- package/dist/preview/main.js.map +1 -0
- package/dist/project.d.ts +16 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +88 -0
- package/dist/project.js.map +1 -0
- package/dist/renderer/audio.d.ts +31 -0
- package/dist/renderer/audio.d.ts.map +1 -0
- package/dist/renderer/audio.js +88 -0
- package/dist/renderer/audio.js.map +1 -0
- package/dist/renderer/browser.d.ts +22 -0
- package/dist/renderer/browser.d.ts.map +1 -0
- package/dist/renderer/browser.js +175 -0
- package/dist/renderer/browser.js.map +1 -0
- package/dist/renderer/ffmpeg.d.ts +58 -0
- package/dist/renderer/ffmpeg.d.ts.map +1 -0
- package/dist/renderer/ffmpeg.js +268 -0
- package/dist/renderer/ffmpeg.js.map +1 -0
- package/dist/renderer/index.d.ts +72 -0
- package/dist/renderer/index.d.ts.map +1 -0
- package/dist/renderer/index.js +281 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/renderer/transcripts.d.ts +30 -0
- package/dist/renderer/transcripts.d.ts.map +1 -0
- package/dist/renderer/transcripts.js +86 -0
- package/dist/renderer/transcripts.js.map +1 -0
- package/dist/runtime/clock.d.ts +5 -0
- package/dist/runtime/clock.d.ts.map +1 -0
- package/dist/runtime/clock.js +16 -0
- package/dist/runtime/clock.js.map +1 -0
- package/dist/runtime/index.d.ts +29 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +289 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/random.d.ts +8 -0
- package/dist/runtime/random.d.ts.map +1 -0
- package/dist/runtime/random.js +21 -0
- package/dist/runtime/random.js.map +1 -0
- package/dist/scaffold.d.ts +3 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +176 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/server/preview-html.d.ts +2 -0
- package/dist/server/preview-html.d.ts.map +1 -0
- package/dist/server/preview-html.js +229 -0
- package/dist/server/preview-html.js.map +1 -0
- package/dist/server/server.d.ts +17 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +237 -0
- package/dist/server/server.js.map +1 -0
- package/dist/transcript.d.ts +44 -0
- package/dist/transcript.d.ts.map +1 -0
- package/dist/transcript.js +130 -0
- package/dist/transcript.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/docs/adr/0001-keep-composition-authoring-web-native.md +3 -0
- package/docs/adr/0002-make-frame-and-props-the-only-animation-state.md +3 -0
- package/docs/adr/0003-render-browser-frames-offline.md +3 -0
- package/docs/adr/0004-produce-local-ai-assets-outside-rendering.md +3 -0
- package/docs/v0.1-design.md +238 -0
- package/examples/demo/README.md +24 -0
- package/examples/demo/assets/chime.wav +0 -0
- package/examples/demo/compositions/demo.js +206 -0
- package/examples/demo/props/demo.json +5 -0
- package/examples/demo/video.config.js +5 -0
- package/package.json +73 -3
- package/skills/frameyard/README.md +35 -0
- package/skills/frameyard/SKILL.md +60 -0
- package/skills/frameyard/references/audio-assets.md +86 -0
- package/skills/frameyard/references/deterministic-authoring.md +107 -0
- package/skills/frameyard/references/failure-recovery.md +57 -0
- package/skills/frameyard/references/json-results.md +203 -0
- package/skills/frameyard/references/subtitles-karaoke.md +95 -0
- package/skills/frameyard/scripts/frameyard.mjs +60 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Subtitles and karaoke
|
|
2
|
+
|
|
3
|
+
Frameyard does not burn captions into videos and has no built-in subtitle renderer. Compositions render timed text themselves. Transcription produces a canonical transcript; SRT and VTT are derived exports.
|
|
4
|
+
|
|
5
|
+
## Canonical transcript
|
|
6
|
+
|
|
7
|
+
All timestamps are integer milliseconds relative to the source audio:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"schemaVersion": 1,
|
|
12
|
+
"text": "Hello world.",
|
|
13
|
+
"language": "en",
|
|
14
|
+
"source": {
|
|
15
|
+
"path": "assets/voice.wav",
|
|
16
|
+
"sha256": "64-character hex digest",
|
|
17
|
+
"bytes": 12345
|
|
18
|
+
},
|
|
19
|
+
"segments": [
|
|
20
|
+
{ "startMs": 0, "endMs": 1200, "text": "Hello world." }
|
|
21
|
+
],
|
|
22
|
+
"words": [
|
|
23
|
+
{ "startMs": 0, "endMs": 500, "text": "Hello" },
|
|
24
|
+
{ "startMs": 520, "endMs": 1100, "text": "world." }
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- `segments` are always present after a successful transcription.
|
|
30
|
+
- `words` is present only when `--words` requested measured timings.
|
|
31
|
+
- Never synthesize word timings by interpolating segment timestamps.
|
|
32
|
+
- `check` fails with `TRANSCRIPT_WORDS_UNAVAILABLE` when a composition declares `words: true` but the transcript has no word timings.
|
|
33
|
+
|
|
34
|
+
## Declaring transcript requirements
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
transcripts: [
|
|
38
|
+
{ src: "assets/voice.transcript.json", words: true },
|
|
39
|
+
],
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Transcript `src` paths are project-relative. `check`, `still`, and `render` validate declared transcript files. Preview loads the declarations but leaves visual presentation to the composition.
|
|
43
|
+
|
|
44
|
+
## Converting source time to frames
|
|
45
|
+
|
|
46
|
+
Given an audio clip:
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
const clip = {
|
|
50
|
+
src: "assets/voice.wav",
|
|
51
|
+
startFrame: 30,
|
|
52
|
+
trimStart: 0.25,
|
|
53
|
+
trimDuration: 4.2,
|
|
54
|
+
volume: 0.8,
|
|
55
|
+
};
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Convert a word or segment time with:
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
const FPS = 30;
|
|
62
|
+
const frame = clip.startFrame + Math.round((word.startMs / 1000 - clip.trimStart) * FPS);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Skip words before the trimmed source range or after `trimStart + trimDuration`. Do not divide by `durationInFrames` or assume a 30 fps composition.
|
|
66
|
+
|
|
67
|
+
A seek-safe karaoke update only reads the current frame:
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
function wordProgress(word, frame) {
|
|
71
|
+
const start = clip.startFrame + (word.startMs / 1000 - clip.trimStart) * FPS;
|
|
72
|
+
const end = clip.startFrame + (word.endMs / 1000 - clip.trimStart) * FPS;
|
|
73
|
+
return Math.max(0, Math.min(1, (frame - start) / Math.max(1, end - start)));
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Apply that value to text styling inside `update({ frame })`. Do not use timers, `requestAnimationFrame`, or transitions.
|
|
78
|
+
|
|
79
|
+
## Check and inspect
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
npx frameyard check karaoke --json
|
|
83
|
+
npx frameyard still karaoke --frame 120 --out karaoke-120.png --json
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Inspect several frames across a word boundary. A single still cannot prove that highlighting is synchronized.
|
|
87
|
+
|
|
88
|
+
## Derived exports
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
npx frameyard transcribe assets/voice.wav --model models/ggml-base.en.bin \
|
|
92
|
+
--out assets/voice.transcript.json --export srt,vtt --json
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Exports use the JSON output basename: `voice.transcript.srt` and `.vtt`. They are derived files, not the canonical contract. Regenerate them from the transcript rather than editing them by hand.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const localCli = path.resolve(scriptDir, "../../../dist/cli.js");
|
|
9
|
+
const configured = process.env.FRAMEYARD_CLI;
|
|
10
|
+
|
|
11
|
+
let command;
|
|
12
|
+
let args;
|
|
13
|
+
if (configured) {
|
|
14
|
+
if (!existsSync(configured)) {
|
|
15
|
+
process.stderr.write(`FRAMEYARD_CLI does not exist: ${configured}\n`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
if (configured.endsWith(".js") || configured.endsWith(".mjs")) {
|
|
19
|
+
command = process.execPath;
|
|
20
|
+
args = [configured, ...process.argv.slice(2)];
|
|
21
|
+
} else {
|
|
22
|
+
command = configured;
|
|
23
|
+
args = process.argv.slice(2);
|
|
24
|
+
}
|
|
25
|
+
} else if (existsSync(localCli)) {
|
|
26
|
+
command = process.execPath;
|
|
27
|
+
args = [localCli, ...process.argv.slice(2)];
|
|
28
|
+
} else {
|
|
29
|
+
command = "frameyard";
|
|
30
|
+
args = process.argv.slice(2);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const child = spawn(command, args, { stdio: "inherit" });
|
|
34
|
+
|
|
35
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
36
|
+
process.on(signal, () => {
|
|
37
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
38
|
+
child.kill(signal);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
child.once("error", (error) => {
|
|
44
|
+
if (error.code === "ENOENT") {
|
|
45
|
+
process.stderr.write(
|
|
46
|
+
"The frameyard CLI was not found. Run `npx frameyard`, add frameyard to PATH, or set FRAMEYARD_CLI to an installed CLI path.\n",
|
|
47
|
+
);
|
|
48
|
+
} else {
|
|
49
|
+
process.stderr.write(`Could not start the Frameyard CLI: ${error.message}\n`);
|
|
50
|
+
}
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
child.once("close", (code, signal) => {
|
|
55
|
+
if (signal) {
|
|
56
|
+
process.kill(process.pid, signal);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
process.exit(code ?? 1);
|
|
60
|
+
});
|