miriad-viz 0.13.2 → 0.13.4
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.
|
@@ -8,6 +8,23 @@ import {
|
|
|
8
8
|
// src/cli/from-curation-chain.ts
|
|
9
9
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
10
10
|
import { resolve } from "path";
|
|
11
|
+
var MAX_VIZ_SPEED = 10;
|
|
12
|
+
function validateNarrationGaps(narration) {
|
|
13
|
+
const errors = [];
|
|
14
|
+
for (let i = 0; i < narration.length - 1; i++) {
|
|
15
|
+
const current = narration[i];
|
|
16
|
+
const next = narration[i + 1];
|
|
17
|
+
const gap = next.progress - current.progress;
|
|
18
|
+
const minGap = current.clipDuration / MAX_VIZ_SPEED;
|
|
19
|
+
if (gap < minGap) {
|
|
20
|
+
const suggestedProgress = (current.progress + minGap).toFixed(1);
|
|
21
|
+
errors.push(
|
|
22
|
+
`"${current.id}" (${current.clipDuration.toFixed(1)}s clip) needs gap \u2265 ${minGap.toFixed(1)} but only has ${gap.toFixed(1)} before "${next.id}" \u2014 move "${next.id}" to at least progress ${suggestedProgress}`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return errors;
|
|
27
|
+
}
|
|
11
28
|
var DEFAULT_PAUSE_SEC = 0.5;
|
|
12
29
|
var DEFAULT_LEAD_IN_SEC = 1;
|
|
13
30
|
var DEFAULT_TAIL_OUT_SEC = 2;
|
|
@@ -16,6 +33,13 @@ function deriveTimingFromCuration(curation, script) {
|
|
|
16
33
|
if (narration.length === 0) {
|
|
17
34
|
throw new Error("viz-curation.json has no narration lines");
|
|
18
35
|
}
|
|
36
|
+
const overlapErrors = validateNarrationGaps(narration);
|
|
37
|
+
if (overlapErrors.length > 0) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Narration overlap: ${overlapErrors.length} line${overlapErrors.length > 1 ? "s" : ""} too close together (max vizSpeed: ${MAX_VIZ_SPEED}x):
|
|
40
|
+
${overlapErrors.join("\n")}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
19
43
|
const scriptMap = new Map(script.lines.map((l) => [l.id, l]));
|
|
20
44
|
const lines = [];
|
|
21
45
|
let wallClock = DEFAULT_LEAD_IN_SEC;
|
|
@@ -52,6 +76,23 @@ function deriveTimingFromCuration(curation, script) {
|
|
|
52
76
|
lines
|
|
53
77
|
};
|
|
54
78
|
}
|
|
79
|
+
var PILL_PROGRESS_TOLERANCE = 1;
|
|
80
|
+
function validateQuotePillSync(curation) {
|
|
81
|
+
const errors = [];
|
|
82
|
+
const { narration, pills } = curation;
|
|
83
|
+
for (const line of narration) {
|
|
84
|
+
if (line.type !== "quote") continue;
|
|
85
|
+
const hasMatchingPill = pills.some(
|
|
86
|
+
(pill) => pill.speaker === line.speaker && Math.abs(pill.progress - line.progress) <= PILL_PROGRESS_TOLERANCE
|
|
87
|
+
);
|
|
88
|
+
if (!hasMatchingPill) {
|
|
89
|
+
errors.push(
|
|
90
|
+
`${line.id} is a quote line at progress ${line.progress} but has no pill for speaker '${line.speaker}' \u2014 add a pill or keep the auto-generated anchor`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return errors;
|
|
95
|
+
}
|
|
55
96
|
function runFromCurationChain(dataDir) {
|
|
56
97
|
const curationPath = resolve(dataDir, "viz-curation.json");
|
|
57
98
|
const scriptPath = resolve(dataDir, "script.json");
|
|
@@ -83,6 +124,14 @@ function runFromCurationChain(dataDir) {
|
|
|
83
124
|
const message = err instanceof Error ? err.message : String(err);
|
|
84
125
|
return { success: false, error: `Invalid script.json: ${message}` };
|
|
85
126
|
}
|
|
127
|
+
const pillErrors = validateQuotePillSync(curation);
|
|
128
|
+
if (pillErrors.length > 0) {
|
|
129
|
+
return {
|
|
130
|
+
success: false,
|
|
131
|
+
error: `Quote-pill sync errors:
|
|
132
|
+
${pillErrors.map((e) => ` \u2022 ${e}`).join("\n")}`
|
|
133
|
+
};
|
|
134
|
+
}
|
|
86
135
|
let timing;
|
|
87
136
|
try {
|
|
88
137
|
timing = deriveTimingFromCuration(curation, script);
|
|
@@ -112,7 +161,11 @@ function runFromCurationChain(dataDir) {
|
|
|
112
161
|
].join("\n");
|
|
113
162
|
return { success: true, timingPath, previewTable };
|
|
114
163
|
}
|
|
164
|
+
|
|
115
165
|
export {
|
|
166
|
+
MAX_VIZ_SPEED,
|
|
167
|
+
validateNarrationGaps,
|
|
116
168
|
deriveTimingFromCuration,
|
|
169
|
+
validateQuotePillSync,
|
|
117
170
|
runFromCurationChain
|
|
118
171
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MAX_VIZ_SPEED,
|
|
3
|
+
deriveTimingFromCuration,
|
|
4
|
+
runFromCurationChain,
|
|
5
|
+
validateNarrationGaps,
|
|
6
|
+
validateQuotePillSync
|
|
7
|
+
} from "./chunk-4UVZ4276.js";
|
|
8
|
+
import "./chunk-SKRQW7PY.js";
|
|
9
|
+
import "./chunk-BD2KEZI4.js";
|
|
10
|
+
export {
|
|
11
|
+
MAX_VIZ_SPEED,
|
|
12
|
+
deriveTimingFromCuration,
|
|
13
|
+
runFromCurationChain,
|
|
14
|
+
validateNarrationGaps,
|
|
15
|
+
validateQuotePillSync
|
|
16
|
+
};
|
package/dist-cli/index.js
CHANGED
|
@@ -1948,7 +1948,7 @@ async function main() {
|
|
|
1948
1948
|
case "transform": {
|
|
1949
1949
|
const { projectDir, progress } = requireProject();
|
|
1950
1950
|
const { parseDuration } = await import("./parse-duration-NVLCEFAF.js");
|
|
1951
|
-
const { runTransform } = await import("./transform-
|
|
1951
|
+
const { runTransform } = await import("./transform-3OMHFHM7.js");
|
|
1952
1952
|
const padding = {};
|
|
1953
1953
|
if (typeof flags["pad-start"] === "string") {
|
|
1954
1954
|
padding.padStartMs = parseDuration(flags["pad-start"]);
|
|
@@ -1964,7 +1964,7 @@ async function main() {
|
|
|
1964
1964
|
const port = typeof flags.port === "string" ? Number.parseInt(flags.port, 10) : void 0;
|
|
1965
1965
|
if (flags["from-curation"] === true) {
|
|
1966
1966
|
const dataDir = resolve3(projectDir, progress.project.dataDir);
|
|
1967
|
-
const { runFromCurationChain } = await import("./from-curation-chain-
|
|
1967
|
+
const { runFromCurationChain } = await import("./from-curation-chain-7YL3EAVT.js");
|
|
1968
1968
|
console.log("\n\u{1F517} Running from-curation chain: curation \u2192 timing \u2192 transform \u2192 preview\n");
|
|
1969
1969
|
const chainResult = runFromCurationChain(dataDir);
|
|
1970
1970
|
if (!chainResult.success) {
|
|
@@ -1976,7 +1976,7 @@ async function main() {
|
|
|
1976
1976
|
console.log("");
|
|
1977
1977
|
console.log(chainResult.previewTable);
|
|
1978
1978
|
}
|
|
1979
|
-
const { runTransform } = await import("./transform-
|
|
1979
|
+
const { runTransform } = await import("./transform-3OMHFHM7.js");
|
|
1980
1980
|
await runTransform({ projectDir, progress });
|
|
1981
1981
|
const { runPreview: runPreview2 } = await import("./preview-7AGBBMPI.js");
|
|
1982
1982
|
await runPreview2({ projectDir, progress, port, noOpen: flags["no-open"] === true });
|
|
@@ -1996,7 +1996,7 @@ async function main() {
|
|
|
1996
1996
|
console.log("");
|
|
1997
1997
|
console.log(chainResult.previewTable);
|
|
1998
1998
|
}
|
|
1999
|
-
const { runTransform } = await import("./transform-
|
|
1999
|
+
const { runTransform } = await import("./transform-3OMHFHM7.js");
|
|
2000
2000
|
await runTransform({ projectDir, progress });
|
|
2001
2001
|
const { runPreview: runPreview2 } = await import("./preview-7AGBBMPI.js");
|
|
2002
2002
|
await runPreview2({ projectDir, progress, port, noOpen: flags["no-open"] === true });
|
|
@@ -18,9 +18,13 @@ import {
|
|
|
18
18
|
markInProgress,
|
|
19
19
|
writeProgress
|
|
20
20
|
} from "./chunk-GLABTDMO.js";
|
|
21
|
+
import {
|
|
22
|
+
validateNarrationGaps
|
|
23
|
+
} from "./chunk-4UVZ4276.js";
|
|
21
24
|
import {
|
|
22
25
|
TimingFileSchema
|
|
23
26
|
} from "./chunk-SKRQW7PY.js";
|
|
27
|
+
import "./chunk-BD2KEZI4.js";
|
|
24
28
|
|
|
25
29
|
// src/cli/guided/steps/transform.ts
|
|
26
30
|
import { resolve as resolve2 } from "path";
|
|
@@ -719,8 +723,18 @@ function runPipeline(dataDir, outDir, options) {
|
|
|
719
723
|
}
|
|
720
724
|
const curationPath = resolve(dataDir, "viz-curation.json");
|
|
721
725
|
if (existsSync(curationPath)) {
|
|
726
|
+
const curationRaw = JSON.parse(readFileSync(curationPath, "utf-8"));
|
|
727
|
+
const narration = curationRaw?.narration ?? [];
|
|
728
|
+
if (narration.length > 0) {
|
|
729
|
+
const gapErrors = validateNarrationGaps(narration);
|
|
730
|
+
if (gapErrors.length > 0) {
|
|
731
|
+
throw new Error(
|
|
732
|
+
`Narration overlap in viz-curation.json \u2014 ${gapErrors.length} line${gapErrors.length > 1 ? "s" : ""} too close:
|
|
733
|
+
${gapErrors.join("\n")}`
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
722
737
|
try {
|
|
723
|
-
const curationRaw = JSON.parse(readFileSync(curationPath, "utf-8"));
|
|
724
738
|
const pills = curationRaw?.pills ?? [];
|
|
725
739
|
if (pills.length > 0 && vizData.timeRange.durationMs > 0) {
|
|
726
740
|
const { start, durationMs } = vizData.timeRange;
|