@hyperframes/engine 0.1.12 → 0.1.14
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/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/services/videoFrameExtractor.d.ts.map +1 -1
- package/dist/services/videoFrameExtractor.js +29 -5
- package/dist/services/videoFrameExtractor.js.map +1 -1
- package/dist/utils/ffprobe.d.ts +14 -0
- package/dist/utils/ffprobe.d.ts.map +1 -1
- package/dist/utils/ffprobe.js +139 -101
- package/dist/utils/ffprobe.js.map +1 -1
- package/package.json +2 -2
- package/scripts/test-fitTextFontSize-browser.ts +131 -0
- package/src/index.ts +2 -0
- package/src/services/videoFrameExtractor.ts +29 -5
- package/src/utils/ffprobe.ts +157 -120
package/dist/index.d.ts
CHANGED
|
@@ -44,6 +44,6 @@ export type { AudioElement, AudioTrack, MixResult } from "./services/audioMixer.
|
|
|
44
44
|
export { calculateOptimalWorkers, distributeFrames, executeParallelCapture, mergeWorkerFrames, getSystemResources, type WorkerTask, type WorkerResult, type ParallelProgress, } from "./services/parallelCoordinator.js";
|
|
45
45
|
export { createFileServer, type FileServerOptions, type FileServerHandle, } from "./services/fileServer.js";
|
|
46
46
|
export { quantizeTimeToFrame, MEDIA_VISUAL_STYLE_PROPERTIES } from "@hyperframes/core";
|
|
47
|
-
export { extractVideoMetadata, extractAudioMetadata, type VideoMetadata, type AudioMetadata, } from "./utils/ffprobe.js";
|
|
47
|
+
export { extractVideoMetadata, extractAudioMetadata, analyzeKeyframeIntervals, type VideoMetadata, type AudioMetadata, type KeyframeAnalysis, } from "./utils/ffprobe.js";
|
|
48
48
|
export { downloadToTemp, isHttpUrl } from "./utils/urlDownloader.js";
|
|
49
49
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,YAAY,EACV,UAAU,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAG/E,OAAO,EACL,cAAc,EACd,cAAc,EACd,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,6BAA6B,EAC7B,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,EACb,sBAAsB,EACtB,wBAAwB,EACxB,eAAe,EACf,KAAK,gBAAgB,GACtB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,KAAK,UAAU,GAChB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAEhG,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,GACxB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,cAAc,EACd,sBAAsB,EACtB,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACvF,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAG1F,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EACL,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAEvF,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,aAAa,EAClB,KAAK,aAAa,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,YAAY,EACV,UAAU,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAG/E,OAAO,EACL,cAAc,EACd,cAAc,EACd,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,6BAA6B,EAC7B,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,EACb,sBAAsB,EACtB,wBAAwB,EACxB,eAAe,EACf,KAAK,gBAAgB,GACtB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,KAAK,UAAU,GAChB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAEhG,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,GACxB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,cAAc,EACd,sBAAsB,EACtB,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACvF,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAG1F,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACtB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EACL,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAEvF,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,6 @@ export { calculateOptimalWorkers, distributeFrames, executeParallelCapture, merg
|
|
|
50
50
|
export { createFileServer, } from "./services/fileServer.js";
|
|
51
51
|
// ── Utilities ──────────────────────────────────────────────────────────────────
|
|
52
52
|
export { quantizeTimeToFrame, MEDIA_VISUAL_STYLE_PROPERTIES } from "@hyperframes/core";
|
|
53
|
-
export { extractVideoMetadata, extractAudioMetadata, } from "./utils/ffprobe.js";
|
|
53
|
+
export { extractVideoMetadata, extractAudioMetadata, analyzeKeyframeIntervals, } from "./utils/ffprobe.js";
|
|
54
54
|
export { downloadToTemp, isHttpUrl } from "./utils/urlDownloader.js";
|
|
55
55
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAYH,kFAAkF;AAClF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAqB,MAAM,aAAa,CAAC;AAE/E,kFAAkF;AAClF,OAAO,EACL,cAAc,EACd,cAAc,EACd,wBAAwB,EACxB,eAAe,EACf,mBAAmB,GAIpB,MAAM,8BAA8B,CAAC;AAEtC,mFAAmF;AACnF,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,6BAA6B,GAG9B,MAAM,4BAA4B,CAAC;AAEpC,mFAAmF;AACnF,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,EACb,sBAAsB,EACtB,wBAAwB,EACxB,eAAe,GAEhB,MAAM,iCAAiC,CAAC;AAEzC,kFAAkF;AAClF,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GAEjB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,qBAAqB,EACrB,wBAAwB,GAKzB,MAAM,gCAAgC,CAAC;AAExC,kFAAkF;AAClF,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,cAAc,EACd,sBAAsB,EACtB,gBAAgB,GAKjB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAGvF,kFAAkF;AAClF,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,GAInB,MAAM,mCAAmC,CAAC;AAE3C,kFAAkF;AAClF,OAAO,EACL,gBAAgB,GAGjB,MAAM,0BAA0B,CAAC;AAElC,kFAAkF;AAClF,OAAO,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAEvF,OAAO,EACL,oBAAoB,EACpB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAYH,kFAAkF;AAClF,OAAO,EAAE,aAAa,EAAE,cAAc,EAAqB,MAAM,aAAa,CAAC;AAE/E,kFAAkF;AAClF,OAAO,EACL,cAAc,EACd,cAAc,EACd,wBAAwB,EACxB,eAAe,EACf,mBAAmB,GAIpB,MAAM,8BAA8B,CAAC;AAEtC,mFAAmF;AACnF,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,6BAA6B,GAG9B,MAAM,4BAA4B,CAAC;AAEpC,mFAAmF;AACnF,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,EACb,sBAAsB,EACtB,wBAAwB,EACxB,eAAe,GAEhB,MAAM,iCAAiC,CAAC;AAEzC,kFAAkF;AAClF,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GAEjB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,qBAAqB,EACrB,wBAAwB,GAKzB,MAAM,gCAAgC,CAAC;AAExC,kFAAkF;AAClF,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,EACrB,cAAc,EACd,sBAAsB,EACtB,gBAAgB,GAKjB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAGvF,kFAAkF;AAClF,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,GAInB,MAAM,mCAAmC,CAAC;AAE3C,kFAAkF;AAClF,OAAO,EACL,gBAAgB,GAGjB,MAAM,0BAA0B,CAAC;AAElC,kFAAkF;AAClF,OAAO,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAEvF,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,GAIzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"videoFrameExtractor.d.ts","sourceRoot":"","sources":["../../src/services/videoFrameExtractor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,aAAa,CAAC;IACxB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"videoFrameExtractor.d.ts","sourceRoot":"","sources":["../../src/services/videoFrameExtractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,7 +14,16 @@ import { DEFAULT_CONFIG } from "../config.js";
|
|
|
14
14
|
export function parseVideoElements(html) {
|
|
15
15
|
const videos = [];
|
|
16
16
|
const { document } = parseHTML(html);
|
|
17
|
-
|
|
17
|
+
// Union: original "video[id][src]" (backward compat) + "video[src][data-start]"
|
|
18
|
+
// (sub-composition videos that have timing but no explicit id).
|
|
19
|
+
const videoEls = Array.from(new Set([
|
|
20
|
+
...Array.from(document.querySelectorAll("video[id][src]")),
|
|
21
|
+
...Array.from(document.querySelectorAll("video[src][data-start]")),
|
|
22
|
+
]));
|
|
23
|
+
videoEls.forEach((el, i) => {
|
|
24
|
+
if (!el.id)
|
|
25
|
+
el.id = `hf-video-${i}`;
|
|
26
|
+
});
|
|
18
27
|
for (const el of videoEls) {
|
|
19
28
|
const id = el.getAttribute("id");
|
|
20
29
|
const src = el.getAttribute("src");
|
|
@@ -22,13 +31,27 @@ export function parseVideoElements(html) {
|
|
|
22
31
|
continue;
|
|
23
32
|
const startAttr = el.getAttribute("data-start");
|
|
24
33
|
const endAttr = el.getAttribute("data-end");
|
|
34
|
+
const durationAttr = el.getAttribute("data-duration");
|
|
25
35
|
const mediaStartAttr = el.getAttribute("data-media-start");
|
|
26
36
|
const hasAudioAttr = el.getAttribute("data-has-audio");
|
|
37
|
+
const start = startAttr ? parseFloat(startAttr) : 0;
|
|
38
|
+
// Derive end from data-end → data-start+data-duration → Infinity (natural duration).
|
|
39
|
+
// The caller (htmlCompiler) clamps Infinity to the composition's absoluteEnd.
|
|
40
|
+
let end = 0;
|
|
41
|
+
if (endAttr) {
|
|
42
|
+
end = parseFloat(endAttr);
|
|
43
|
+
}
|
|
44
|
+
else if (durationAttr) {
|
|
45
|
+
end = start + parseFloat(durationAttr);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
end = Infinity; // no explicit bounds — play for the full natural video duration
|
|
49
|
+
}
|
|
27
50
|
videos.push({
|
|
28
51
|
id,
|
|
29
52
|
src,
|
|
30
|
-
start
|
|
31
|
-
end
|
|
53
|
+
start,
|
|
54
|
+
end,
|
|
32
55
|
mediaStart: mediaStartAttr ? parseFloat(mediaStartAttr) : 0,
|
|
33
56
|
hasAudio: hasAudioAttr === "true",
|
|
34
57
|
});
|
|
@@ -146,8 +169,9 @@ export async function extractAllVideoFrames(videos, baseDir, options, signal, co
|
|
|
146
169
|
return { error: { videoId: video.id, error: `Video file not found: ${videoPath}` } };
|
|
147
170
|
}
|
|
148
171
|
let videoDuration = video.end - video.start;
|
|
149
|
-
// Fallback: if no data-duration/data-end was specified
|
|
150
|
-
|
|
172
|
+
// Fallback: if no data-duration/data-end was specified (end is Infinity or 0),
|
|
173
|
+
// probe the actual video file to get its natural duration.
|
|
174
|
+
if (!Number.isFinite(videoDuration) || videoDuration <= 0) {
|
|
151
175
|
const metadata = await extractVideoMetadata(videoPath);
|
|
152
176
|
const sourceDuration = metadata.durationSeconds - video.mediaStart;
|
|
153
177
|
videoDuration = sourceDuration > 0 ? sourceDuration : metadata.durationSeconds;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"videoFrameExtractor.js","sourceRoot":"","sources":["../../src/services/videoFrameExtractor.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"videoFrameExtractor.js","sourceRoot":"","sources":["../../src/services/videoFrameExtractor.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/ffprobe.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export interface VideoMetadata {
|
|
|
5
5
|
fps: number;
|
|
6
6
|
videoCodec: string;
|
|
7
7
|
hasAudio: boolean;
|
|
8
|
+
/** True when r_frame_rate and avg_frame_rate differ significantly (>10%), indicating variable frame rate. */
|
|
9
|
+
isVFR: boolean;
|
|
8
10
|
}
|
|
9
11
|
export interface AudioMetadata {
|
|
10
12
|
durationSeconds: number;
|
|
@@ -15,4 +17,16 @@ export interface AudioMetadata {
|
|
|
15
17
|
}
|
|
16
18
|
export declare function extractVideoMetadata(filePath: string): Promise<VideoMetadata>;
|
|
17
19
|
export declare function extractAudioMetadata(filePath: string): Promise<AudioMetadata>;
|
|
20
|
+
export interface KeyframeAnalysis {
|
|
21
|
+
avgIntervalSeconds: number;
|
|
22
|
+
maxIntervalSeconds: number;
|
|
23
|
+
keyframeCount: number;
|
|
24
|
+
isProblematic: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Check keyframe intervals in a video file. Intervals > 2s cause seeking
|
|
28
|
+
* issues in the headless renderer and audio/video desync. Videos from
|
|
29
|
+
* yt-dlp --download-sections or screen recordings often have sparse keyframes.
|
|
30
|
+
*/
|
|
31
|
+
export declare function analyzeKeyframeIntervals(filePath: string): Promise<KeyframeAnalysis>;
|
|
18
32
|
//# sourceMappingURL=ffprobe.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ffprobe.d.ts","sourceRoot":"","sources":["../../src/utils/ffprobe.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ffprobe.d.ts","sourceRoot":"","sources":["../../src/utils/ffprobe.ts"],"names":[],"mappings":"AA4CA,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,6GAA6G;IAC7G,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAkCD,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA0CnF;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAoCnF;AAED,MAAM,WAAW,gBAAgB;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;CACxB;AAID;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAY1F"}
|
package/dist/utils/ffprobe.js
CHANGED
|
@@ -1,4 +1,42 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
|
+
/** Spawn ffprobe with given args, return stdout. Throws on non-zero exit or missing binary. */
|
|
3
|
+
function runFfprobe(args) {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
const proc = spawn("ffprobe", args);
|
|
6
|
+
let stdout = "";
|
|
7
|
+
let stderr = "";
|
|
8
|
+
proc.stdout.on("data", (data) => {
|
|
9
|
+
stdout += data.toString();
|
|
10
|
+
});
|
|
11
|
+
proc.stderr.on("data", (data) => {
|
|
12
|
+
stderr += data.toString();
|
|
13
|
+
});
|
|
14
|
+
proc.on("close", (code) => {
|
|
15
|
+
if (code !== 0) {
|
|
16
|
+
reject(new Error(`[FFmpeg] ffprobe exited with code ${code}: ${stderr}`));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
resolve(stdout);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
proc.on("error", (err) => {
|
|
23
|
+
if (err.code === "ENOENT") {
|
|
24
|
+
reject(new Error("[FFmpeg] ffprobe not found. Please install FFmpeg."));
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
reject(err);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function parseProbeJson(stdout) {
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(stdout);
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
throw new Error(`[FFmpeg] Failed to parse ffprobe output: ${e instanceof Error ? e.message : e}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
2
40
|
const videoMetadataCache = new Map();
|
|
3
41
|
const audioMetadataCache = new Map();
|
|
4
42
|
function parseFrameRate(frameRateStr) {
|
|
@@ -15,11 +53,10 @@ function parseFrameRate(frameRateStr) {
|
|
|
15
53
|
}
|
|
16
54
|
export async function extractVideoMetadata(filePath) {
|
|
17
55
|
const cached = videoMetadataCache.get(filePath);
|
|
18
|
-
if (cached)
|
|
56
|
+
if (cached)
|
|
19
57
|
return cached;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const args = [
|
|
58
|
+
const probePromise = (async () => {
|
|
59
|
+
const stdout = await runFfprobe([
|
|
23
60
|
"-v",
|
|
24
61
|
"quiet",
|
|
25
62
|
"-print_format",
|
|
@@ -27,54 +64,26 @@ export async function extractVideoMetadata(filePath) {
|
|
|
27
64
|
"-show_format",
|
|
28
65
|
"-show_streams",
|
|
29
66
|
filePath,
|
|
30
|
-
];
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const hasAudio = output.streams.some((s) => s.codec_type === "audio");
|
|
53
|
-
const fps = parseFrameRate(videoStream.avg_frame_rate) || parseFrameRate(videoStream.r_frame_rate);
|
|
54
|
-
const durationSeconds = output.format.duration ? parseFloat(output.format.duration) : 0;
|
|
55
|
-
const metadata = {
|
|
56
|
-
durationSeconds,
|
|
57
|
-
width: videoStream.width || 0,
|
|
58
|
-
height: videoStream.height || 0,
|
|
59
|
-
fps,
|
|
60
|
-
videoCodec: videoStream.codec_name || "unknown",
|
|
61
|
-
hasAudio,
|
|
62
|
-
};
|
|
63
|
-
resolve(metadata);
|
|
64
|
-
}
|
|
65
|
-
catch (parseError) {
|
|
66
|
-
reject(new Error(`[FFmpeg] Failed to parse ffprobe output: ${parseError instanceof Error ? parseError.message : parseError}`));
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
ffprobe.on("error", (err) => {
|
|
70
|
-
if (err.code === "ENOENT") {
|
|
71
|
-
reject(new Error("[FFmpeg] ffprobe not found. Please install FFmpeg."));
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
reject(err);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
});
|
|
67
|
+
]);
|
|
68
|
+
const output = parseProbeJson(stdout);
|
|
69
|
+
const videoStream = output.streams.find((s) => s.codec_type === "video");
|
|
70
|
+
if (!videoStream)
|
|
71
|
+
throw new Error("[FFmpeg] No video stream found");
|
|
72
|
+
const rFps = parseFrameRate(videoStream.r_frame_rate);
|
|
73
|
+
const avgFps = parseFrameRate(videoStream.avg_frame_rate);
|
|
74
|
+
const fps = avgFps || rFps;
|
|
75
|
+
// VFR: r_frame_rate (max/nominal) differs from avg_frame_rate (actual average) by >10%
|
|
76
|
+
const isVFR = rFps > 0 && avgFps > 0 && Math.abs(rFps - avgFps) / Math.max(rFps, avgFps) > 0.1;
|
|
77
|
+
return {
|
|
78
|
+
durationSeconds: output.format.duration ? parseFloat(output.format.duration) : 0,
|
|
79
|
+
width: videoStream.width || 0,
|
|
80
|
+
height: videoStream.height || 0,
|
|
81
|
+
fps,
|
|
82
|
+
videoCodec: videoStream.codec_name || "unknown",
|
|
83
|
+
hasAudio: output.streams.some((s) => s.codec_type === "audio"),
|
|
84
|
+
isVFR,
|
|
85
|
+
};
|
|
86
|
+
})();
|
|
78
87
|
videoMetadataCache.set(filePath, probePromise);
|
|
79
88
|
probePromise.catch(() => {
|
|
80
89
|
if (videoMetadataCache.get(filePath) === probePromise) {
|
|
@@ -85,11 +94,10 @@ export async function extractVideoMetadata(filePath) {
|
|
|
85
94
|
}
|
|
86
95
|
export async function extractAudioMetadata(filePath) {
|
|
87
96
|
const cached = audioMetadataCache.get(filePath);
|
|
88
|
-
if (cached)
|
|
97
|
+
if (cached)
|
|
89
98
|
return cached;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const args = [
|
|
99
|
+
const probePromise = (async () => {
|
|
100
|
+
const stdout = await runFfprobe([
|
|
93
101
|
"-v",
|
|
94
102
|
"quiet",
|
|
95
103
|
"-print_format",
|
|
@@ -97,51 +105,20 @@ export async function extractAudioMetadata(filePath) {
|
|
|
97
105
|
"-show_format",
|
|
98
106
|
"-show_streams",
|
|
99
107
|
filePath,
|
|
100
|
-
];
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
try {
|
|
116
|
-
const output = JSON.parse(stdout);
|
|
117
|
-
const audioStream = output.streams.find((s) => s.codec_type === "audio");
|
|
118
|
-
if (!audioStream) {
|
|
119
|
-
reject(new Error("[FFmpeg] No audio stream found"));
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
const durationSeconds = output.format.duration ? parseFloat(output.format.duration) : 0;
|
|
123
|
-
const metadata = {
|
|
124
|
-
durationSeconds,
|
|
125
|
-
sampleRate: audioStream.sample_rate ? parseInt(audioStream.sample_rate) : 44100,
|
|
126
|
-
channels: audioStream.channels || 2,
|
|
127
|
-
audioCodec: audioStream.codec_name || "unknown",
|
|
128
|
-
bitrate: output.format.bit_rate ? parseInt(output.format.bit_rate) : undefined,
|
|
129
|
-
};
|
|
130
|
-
resolve(metadata);
|
|
131
|
-
}
|
|
132
|
-
catch (parseError) {
|
|
133
|
-
reject(new Error(`[FFmpeg] Failed to parse ffprobe output: ${parseError instanceof Error ? parseError.message : parseError}`));
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
ffprobe.on("error", (err) => {
|
|
137
|
-
if (err.code === "ENOENT") {
|
|
138
|
-
reject(new Error("[FFmpeg] ffprobe not found. Please install FFmpeg."));
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
reject(err);
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
});
|
|
108
|
+
]);
|
|
109
|
+
const output = parseProbeJson(stdout);
|
|
110
|
+
const audioStream = output.streams.find((s) => s.codec_type === "audio");
|
|
111
|
+
if (!audioStream)
|
|
112
|
+
throw new Error("[FFmpeg] No audio stream found");
|
|
113
|
+
const durationSeconds = output.format.duration ? parseFloat(output.format.duration) : 0;
|
|
114
|
+
return {
|
|
115
|
+
durationSeconds,
|
|
116
|
+
sampleRate: audioStream.sample_rate ? parseInt(audioStream.sample_rate) : 44100,
|
|
117
|
+
channels: audioStream.channels || 2,
|
|
118
|
+
audioCodec: audioStream.codec_name || "unknown",
|
|
119
|
+
bitrate: output.format.bit_rate ? parseInt(output.format.bit_rate) : undefined,
|
|
120
|
+
};
|
|
121
|
+
})();
|
|
145
122
|
audioMetadataCache.set(filePath, probePromise);
|
|
146
123
|
probePromise.catch(() => {
|
|
147
124
|
if (audioMetadataCache.get(filePath) === probePromise) {
|
|
@@ -150,4 +127,65 @@ export async function extractAudioMetadata(filePath) {
|
|
|
150
127
|
});
|
|
151
128
|
return probePromise;
|
|
152
129
|
}
|
|
130
|
+
const keyframeCache = new Map();
|
|
131
|
+
/**
|
|
132
|
+
* Check keyframe intervals in a video file. Intervals > 2s cause seeking
|
|
133
|
+
* issues in the headless renderer and audio/video desync. Videos from
|
|
134
|
+
* yt-dlp --download-sections or screen recordings often have sparse keyframes.
|
|
135
|
+
*/
|
|
136
|
+
export async function analyzeKeyframeIntervals(filePath) {
|
|
137
|
+
const cached = keyframeCache.get(filePath);
|
|
138
|
+
if (cached)
|
|
139
|
+
return cached;
|
|
140
|
+
const promise = analyzeKeyframeIntervalsUncached(filePath);
|
|
141
|
+
keyframeCache.set(filePath, promise);
|
|
142
|
+
promise.catch(() => {
|
|
143
|
+
if (keyframeCache.get(filePath) === promise) {
|
|
144
|
+
keyframeCache.delete(filePath);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return promise;
|
|
148
|
+
}
|
|
149
|
+
async function analyzeKeyframeIntervalsUncached(filePath) {
|
|
150
|
+
const stdout = await runFfprobe([
|
|
151
|
+
"-v",
|
|
152
|
+
"quiet",
|
|
153
|
+
"-select_streams",
|
|
154
|
+
"v:0",
|
|
155
|
+
"-skip_frame",
|
|
156
|
+
"nokey",
|
|
157
|
+
"-show_entries",
|
|
158
|
+
"frame=pts_time",
|
|
159
|
+
"-of",
|
|
160
|
+
"csv=p=0",
|
|
161
|
+
filePath,
|
|
162
|
+
]);
|
|
163
|
+
const timestamps = stdout
|
|
164
|
+
.split("\n")
|
|
165
|
+
.map((line) => parseFloat(line.trim()))
|
|
166
|
+
.filter((t) => Number.isFinite(t));
|
|
167
|
+
if (timestamps.length < 2) {
|
|
168
|
+
return {
|
|
169
|
+
avgIntervalSeconds: 0,
|
|
170
|
+
maxIntervalSeconds: 0,
|
|
171
|
+
keyframeCount: timestamps.length,
|
|
172
|
+
isProblematic: false,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
let maxInterval = 0;
|
|
176
|
+
let totalInterval = 0;
|
|
177
|
+
for (let i = 1; i < timestamps.length; i++) {
|
|
178
|
+
const interval = (timestamps[i] ?? 0) - (timestamps[i - 1] ?? 0);
|
|
179
|
+
totalInterval += interval;
|
|
180
|
+
if (interval > maxInterval)
|
|
181
|
+
maxInterval = interval;
|
|
182
|
+
}
|
|
183
|
+
const avgInterval = totalInterval / (timestamps.length - 1);
|
|
184
|
+
return {
|
|
185
|
+
avgIntervalSeconds: Math.round(avgInterval * 100) / 100,
|
|
186
|
+
maxIntervalSeconds: Math.round(maxInterval * 100) / 100,
|
|
187
|
+
keyframeCount: timestamps.length,
|
|
188
|
+
isProblematic: maxInterval > 2,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
153
191
|
//# sourceMappingURL=ffprobe.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ffprobe.js","sourceRoot":"","sources":["../../src/utils/ffprobe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEtC,
|
|
1
|
+
{"version":3,"file":"ffprobe.js","sourceRoot":"","sources":["../../src/utils/ffprobe.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "Seekable web page to video rendering engine (Puppeteer + FFmpeg)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"linkedom": "^0.18.12",
|
|
20
20
|
"puppeteer": "^24.0.0",
|
|
21
21
|
"puppeteer-core": "^24.39.1",
|
|
22
|
-
"@hyperframes/core": "^0.1.
|
|
22
|
+
"@hyperframes/core": "^0.1.14"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^22.10.1",
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser integration test for fitTextFontSize.
|
|
3
|
+
*
|
|
4
|
+
* Launches headless Chrome, loads the runtime IIFE into a page,
|
|
5
|
+
* and verifies that window.__hyperframes.fitTextFontSize produces
|
|
6
|
+
* correct results with real canvas measureText.
|
|
7
|
+
*
|
|
8
|
+
* Requires: puppeteer (dep of @hyperframes/engine)
|
|
9
|
+
* Run: cd packages/engine && npx tsx scripts/test-fitTextFontSize-browser.ts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { buildHyperframesRuntimeScript } from "../../core/src/inline-scripts/hyperframesRuntime.engine";
|
|
13
|
+
|
|
14
|
+
function assert(condition: unknown, message: string): void {
|
|
15
|
+
if (!condition) {
|
|
16
|
+
throw new Error(`FAIL: ${message}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function main() {
|
|
21
|
+
// Dynamic import — puppeteer is a monorepo dep, not a core dep
|
|
22
|
+
let puppeteer;
|
|
23
|
+
try {
|
|
24
|
+
puppeteer = (await import("puppeteer")).default;
|
|
25
|
+
} catch {
|
|
26
|
+
console.log(
|
|
27
|
+
JSON.stringify({
|
|
28
|
+
event: "fitTextFontSize_browser_test_skipped",
|
|
29
|
+
reason: "puppeteer not available",
|
|
30
|
+
}),
|
|
31
|
+
);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const runtimeSource = buildHyperframesRuntimeScript({ minify: false });
|
|
36
|
+
|
|
37
|
+
const html = `<!DOCTYPE html>
|
|
38
|
+
<html><head>
|
|
39
|
+
<style>
|
|
40
|
+
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@900&display=block');
|
|
41
|
+
</style>
|
|
42
|
+
</head><body>
|
|
43
|
+
<script>${runtimeSource}</script>
|
|
44
|
+
<script>
|
|
45
|
+
window.__testResults = {};
|
|
46
|
+
|
|
47
|
+
// Test 1: Short text should fit at base size
|
|
48
|
+
var r1 = window.__hyperframes.fitTextFontSize("HI");
|
|
49
|
+
window.__testResults.shortText = r1;
|
|
50
|
+
|
|
51
|
+
// Test 2: Wide text should shrink below base size but still fit at 1600px
|
|
52
|
+
var r2 = window.__hyperframes.fitTextFontSize(
|
|
53
|
+
"CONGRATULATIONS TO EVERYBODY IN THE WORLD",
|
|
54
|
+
{ fontFamily: "sans-serif", fontWeight: 900, maxWidth: 1600 }
|
|
55
|
+
);
|
|
56
|
+
window.__testResults.wideText = r2;
|
|
57
|
+
|
|
58
|
+
// Test 3: Extremely wide text that can't fit should return minFontSize
|
|
59
|
+
var r3 = window.__hyperframes.fitTextFontSize(
|
|
60
|
+
"WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW",
|
|
61
|
+
{ fontFamily: "sans-serif", fontWeight: 900, maxWidth: 400 }
|
|
62
|
+
);
|
|
63
|
+
window.__testResults.extremeText = r3;
|
|
64
|
+
|
|
65
|
+
// Test 4: Function exists and is callable
|
|
66
|
+
window.__testResults.exists = typeof window.__hyperframes.fitTextFontSize === "function";
|
|
67
|
+
</script>
|
|
68
|
+
</body></html>`;
|
|
69
|
+
|
|
70
|
+
const browser = await puppeteer.launch({
|
|
71
|
+
headless: true,
|
|
72
|
+
args: ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const page = await browser.newPage();
|
|
77
|
+
await page.setContent(html, { waitUntil: "networkidle0", timeout: 10000 });
|
|
78
|
+
|
|
79
|
+
// Wait for font to potentially load (best effort — sans-serif fallback is fine for testing)
|
|
80
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
81
|
+
|
|
82
|
+
const results = await page.evaluate(() => (window as any).__testResults);
|
|
83
|
+
|
|
84
|
+
// Test 1: Short text fits at base size (78px default)
|
|
85
|
+
assert(results.exists === true, "fitTextFontSize should exist on window.__hyperframes");
|
|
86
|
+
assert(
|
|
87
|
+
results.shortText.fits === true,
|
|
88
|
+
`Short text should fit, got fits=${results.shortText.fits}`,
|
|
89
|
+
);
|
|
90
|
+
assert(
|
|
91
|
+
results.shortText.fontSize === 78,
|
|
92
|
+
`Short text should use base size 78, got ${results.shortText.fontSize}`,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// Test 2: Wide text should shrink below base size
|
|
96
|
+
assert(
|
|
97
|
+
results.wideText.fits === true,
|
|
98
|
+
`Wide text should still fit, got fits=${results.wideText.fits}`,
|
|
99
|
+
);
|
|
100
|
+
assert(
|
|
101
|
+
results.wideText.fontSize < 78,
|
|
102
|
+
`Wide text should shrink below 78, got ${results.wideText.fontSize}`,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Test 3: Extreme text should hit floor
|
|
106
|
+
assert(
|
|
107
|
+
results.extremeText.fontSize === 42,
|
|
108
|
+
`Extreme text should hit minFontSize 42, got ${results.extremeText.fontSize}`,
|
|
109
|
+
);
|
|
110
|
+
assert(
|
|
111
|
+
results.extremeText.fits === false,
|
|
112
|
+
`Extreme text should not fit, got fits=${results.extremeText.fits}`,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
console.log(
|
|
116
|
+
JSON.stringify({
|
|
117
|
+
event: "fitTextFontSize_browser_test_passed",
|
|
118
|
+
shortText: results.shortText,
|
|
119
|
+
wideText: results.wideText,
|
|
120
|
+
extremeText: results.extremeText,
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
123
|
+
} finally {
|
|
124
|
+
await browser.close();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
main().catch((err) => {
|
|
129
|
+
console.error(err.message);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -146,8 +146,10 @@ export { quantizeTimeToFrame, MEDIA_VISUAL_STYLE_PROPERTIES } from "@hyperframes
|
|
|
146
146
|
export {
|
|
147
147
|
extractVideoMetadata,
|
|
148
148
|
extractAudioMetadata,
|
|
149
|
+
analyzeKeyframeIntervals,
|
|
149
150
|
type VideoMetadata,
|
|
150
151
|
type AudioMetadata,
|
|
152
|
+
type KeyframeAnalysis,
|
|
151
153
|
} from "./utils/ffprobe.js";
|
|
152
154
|
|
|
153
155
|
export { downloadToTemp, isHttpUrl } from "./utils/urlDownloader.js";
|
|
@@ -52,7 +52,17 @@ export function parseVideoElements(html: string): VideoElement[] {
|
|
|
52
52
|
const videos: VideoElement[] = [];
|
|
53
53
|
const { document } = parseHTML(html);
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
// Union: original "video[id][src]" (backward compat) + "video[src][data-start]"
|
|
56
|
+
// (sub-composition videos that have timing but no explicit id).
|
|
57
|
+
const videoEls = Array.from(
|
|
58
|
+
new Set([
|
|
59
|
+
...Array.from(document.querySelectorAll("video[id][src]")),
|
|
60
|
+
...Array.from(document.querySelectorAll("video[src][data-start]")),
|
|
61
|
+
]),
|
|
62
|
+
);
|
|
63
|
+
videoEls.forEach((el, i) => {
|
|
64
|
+
if (!el.id) el.id = `hf-video-${i}`;
|
|
65
|
+
});
|
|
56
66
|
for (const el of videoEls) {
|
|
57
67
|
const id = el.getAttribute("id");
|
|
58
68
|
const src = el.getAttribute("src");
|
|
@@ -60,14 +70,27 @@ export function parseVideoElements(html: string): VideoElement[] {
|
|
|
60
70
|
|
|
61
71
|
const startAttr = el.getAttribute("data-start");
|
|
62
72
|
const endAttr = el.getAttribute("data-end");
|
|
73
|
+
const durationAttr = el.getAttribute("data-duration");
|
|
63
74
|
const mediaStartAttr = el.getAttribute("data-media-start");
|
|
64
75
|
const hasAudioAttr = el.getAttribute("data-has-audio");
|
|
65
76
|
|
|
77
|
+
const start = startAttr ? parseFloat(startAttr) : 0;
|
|
78
|
+
// Derive end from data-end → data-start+data-duration → Infinity (natural duration).
|
|
79
|
+
// The caller (htmlCompiler) clamps Infinity to the composition's absoluteEnd.
|
|
80
|
+
let end = 0;
|
|
81
|
+
if (endAttr) {
|
|
82
|
+
end = parseFloat(endAttr);
|
|
83
|
+
} else if (durationAttr) {
|
|
84
|
+
end = start + parseFloat(durationAttr);
|
|
85
|
+
} else {
|
|
86
|
+
end = Infinity; // no explicit bounds — play for the full natural video duration
|
|
87
|
+
}
|
|
88
|
+
|
|
66
89
|
videos.push({
|
|
67
90
|
id,
|
|
68
91
|
src,
|
|
69
|
-
start
|
|
70
|
-
end
|
|
92
|
+
start,
|
|
93
|
+
end,
|
|
71
94
|
mediaStart: mediaStartAttr ? parseFloat(mediaStartAttr) : 0,
|
|
72
95
|
hasAudio: hasAudioAttr === "true",
|
|
73
96
|
});
|
|
@@ -212,8 +235,9 @@ export async function extractAllVideoFrames(
|
|
|
212
235
|
|
|
213
236
|
let videoDuration = video.end - video.start;
|
|
214
237
|
|
|
215
|
-
// Fallback: if no data-duration/data-end was specified
|
|
216
|
-
|
|
238
|
+
// Fallback: if no data-duration/data-end was specified (end is Infinity or 0),
|
|
239
|
+
// probe the actual video file to get its natural duration.
|
|
240
|
+
if (!Number.isFinite(videoDuration) || videoDuration <= 0) {
|
|
217
241
|
const metadata = await extractVideoMetadata(videoPath);
|
|
218
242
|
const sourceDuration = metadata.durationSeconds - video.mediaStart;
|
|
219
243
|
videoDuration = sourceDuration > 0 ? sourceDuration : metadata.durationSeconds;
|
package/src/utils/ffprobe.ts
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
import { spawn } from "child_process";
|
|
2
2
|
|
|
3
|
+
/** Spawn ffprobe with given args, return stdout. Throws on non-zero exit or missing binary. */
|
|
4
|
+
function runFfprobe(args: string[]): Promise<string> {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const proc = spawn("ffprobe", args);
|
|
7
|
+
let stdout = "";
|
|
8
|
+
let stderr = "";
|
|
9
|
+
proc.stdout.on("data", (data) => {
|
|
10
|
+
stdout += data.toString();
|
|
11
|
+
});
|
|
12
|
+
proc.stderr.on("data", (data) => {
|
|
13
|
+
stderr += data.toString();
|
|
14
|
+
});
|
|
15
|
+
proc.on("close", (code) => {
|
|
16
|
+
if (code !== 0) {
|
|
17
|
+
reject(new Error(`[FFmpeg] ffprobe exited with code ${code}: ${stderr}`));
|
|
18
|
+
} else {
|
|
19
|
+
resolve(stdout);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
proc.on("error", (err) => {
|
|
23
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
24
|
+
reject(new Error("[FFmpeg] ffprobe not found. Please install FFmpeg."));
|
|
25
|
+
} else {
|
|
26
|
+
reject(err);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseProbeJson(stdout: string): FFProbeOutput {
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(stdout);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`[FFmpeg] Failed to parse ffprobe output: ${e instanceof Error ? e.message : e}`,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
3
42
|
const videoMetadataCache = new Map<string, Promise<VideoMetadata>>();
|
|
4
43
|
const audioMetadataCache = new Map<string, Promise<AudioMetadata>>();
|
|
5
44
|
|
|
@@ -10,6 +49,8 @@ export interface VideoMetadata {
|
|
|
10
49
|
fps: number;
|
|
11
50
|
videoCodec: string;
|
|
12
51
|
hasAudio: boolean;
|
|
52
|
+
/** True when r_frame_rate and avg_frame_rate differ significantly (>10%), indicating variable frame rate. */
|
|
53
|
+
isVFR: boolean;
|
|
13
54
|
}
|
|
14
55
|
|
|
15
56
|
export interface AudioMetadata {
|
|
@@ -54,12 +95,10 @@ function parseFrameRate(frameRateStr: string | undefined): number {
|
|
|
54
95
|
|
|
55
96
|
export async function extractVideoMetadata(filePath: string): Promise<VideoMetadata> {
|
|
56
97
|
const cached = videoMetadataCache.get(filePath);
|
|
57
|
-
if (cached)
|
|
58
|
-
return cached;
|
|
59
|
-
}
|
|
98
|
+
if (cached) return cached;
|
|
60
99
|
|
|
61
|
-
const probePromise =
|
|
62
|
-
const
|
|
100
|
+
const probePromise = (async (): Promise<VideoMetadata> => {
|
|
101
|
+
const stdout = await runFfprobe([
|
|
63
102
|
"-v",
|
|
64
103
|
"quiet",
|
|
65
104
|
"-print_format",
|
|
@@ -67,64 +106,28 @@ export async function extractVideoMetadata(filePath: string): Promise<VideoMetad
|
|
|
67
106
|
"-show_format",
|
|
68
107
|
"-show_streams",
|
|
69
108
|
filePath,
|
|
70
|
-
];
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
109
|
+
]);
|
|
110
|
+
const output = parseProbeJson(stdout);
|
|
111
|
+
const videoStream = output.streams.find((s) => s.codec_type === "video");
|
|
112
|
+
if (!videoStream) throw new Error("[FFmpeg] No video stream found");
|
|
113
|
+
|
|
114
|
+
const rFps = parseFrameRate(videoStream.r_frame_rate);
|
|
115
|
+
const avgFps = parseFrameRate(videoStream.avg_frame_rate);
|
|
116
|
+
const fps = avgFps || rFps;
|
|
117
|
+
// VFR: r_frame_rate (max/nominal) differs from avg_frame_rate (actual average) by >10%
|
|
118
|
+
const isVFR = rFps > 0 && avgFps > 0 && Math.abs(rFps - avgFps) / Math.max(rFps, avgFps) > 0.1;
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
durationSeconds: output.format.duration ? parseFloat(output.format.duration) : 0,
|
|
122
|
+
width: videoStream.width || 0,
|
|
123
|
+
height: videoStream.height || 0,
|
|
124
|
+
fps,
|
|
125
|
+
videoCodec: videoStream.codec_name || "unknown",
|
|
126
|
+
hasAudio: output.streams.some((s) => s.codec_type === "audio"),
|
|
127
|
+
isVFR,
|
|
128
|
+
};
|
|
129
|
+
})();
|
|
88
130
|
|
|
89
|
-
try {
|
|
90
|
-
const output: FFProbeOutput = JSON.parse(stdout);
|
|
91
|
-
const videoStream = output.streams.find((s) => s.codec_type === "video");
|
|
92
|
-
if (!videoStream) {
|
|
93
|
-
reject(new Error("[FFmpeg] No video stream found"));
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const hasAudio = output.streams.some((s) => s.codec_type === "audio");
|
|
98
|
-
const fps =
|
|
99
|
-
parseFrameRate(videoStream.avg_frame_rate) || parseFrameRate(videoStream.r_frame_rate);
|
|
100
|
-
const durationSeconds = output.format.duration ? parseFloat(output.format.duration) : 0;
|
|
101
|
-
|
|
102
|
-
const metadata: VideoMetadata = {
|
|
103
|
-
durationSeconds,
|
|
104
|
-
width: videoStream.width || 0,
|
|
105
|
-
height: videoStream.height || 0,
|
|
106
|
-
fps,
|
|
107
|
-
videoCodec: videoStream.codec_name || "unknown",
|
|
108
|
-
hasAudio,
|
|
109
|
-
};
|
|
110
|
-
resolve(metadata);
|
|
111
|
-
} catch (parseError: unknown) {
|
|
112
|
-
reject(
|
|
113
|
-
new Error(
|
|
114
|
-
`[FFmpeg] Failed to parse ffprobe output: ${parseError instanceof Error ? parseError.message : parseError}`,
|
|
115
|
-
),
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
ffprobe.on("error", (err) => {
|
|
121
|
-
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
122
|
-
reject(new Error("[FFmpeg] ffprobe not found. Please install FFmpeg."));
|
|
123
|
-
} else {
|
|
124
|
-
reject(err);
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
131
|
videoMetadataCache.set(filePath, probePromise);
|
|
129
132
|
probePromise.catch(() => {
|
|
130
133
|
if (videoMetadataCache.get(filePath) === probePromise) {
|
|
@@ -136,12 +139,10 @@ export async function extractVideoMetadata(filePath: string): Promise<VideoMetad
|
|
|
136
139
|
|
|
137
140
|
export async function extractAudioMetadata(filePath: string): Promise<AudioMetadata> {
|
|
138
141
|
const cached = audioMetadataCache.get(filePath);
|
|
139
|
-
if (cached)
|
|
140
|
-
return cached;
|
|
141
|
-
}
|
|
142
|
+
if (cached) return cached;
|
|
142
143
|
|
|
143
|
-
const probePromise =
|
|
144
|
-
const
|
|
144
|
+
const probePromise = (async (): Promise<AudioMetadata> => {
|
|
145
|
+
const stdout = await runFfprobe([
|
|
145
146
|
"-v",
|
|
146
147
|
"quiet",
|
|
147
148
|
"-print_format",
|
|
@@ -149,60 +150,22 @@ export async function extractAudioMetadata(filePath: string): Promise<AudioMetad
|
|
|
149
150
|
"-show_format",
|
|
150
151
|
"-show_streams",
|
|
151
152
|
filePath,
|
|
152
|
-
];
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
reject(new Error(`[FFmpeg] ffprobe exited with code ${code}: ${stderr}`));
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
try {
|
|
172
|
-
const output: FFProbeOutput = JSON.parse(stdout);
|
|
173
|
-
const audioStream = output.streams.find((s) => s.codec_type === "audio");
|
|
174
|
-
if (!audioStream) {
|
|
175
|
-
reject(new Error("[FFmpeg] No audio stream found"));
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const durationSeconds = output.format.duration ? parseFloat(output.format.duration) : 0;
|
|
180
|
-
|
|
181
|
-
const metadata: AudioMetadata = {
|
|
182
|
-
durationSeconds,
|
|
183
|
-
sampleRate: audioStream.sample_rate ? parseInt(audioStream.sample_rate) : 44100,
|
|
184
|
-
channels: audioStream.channels || 2,
|
|
185
|
-
audioCodec: audioStream.codec_name || "unknown",
|
|
186
|
-
bitrate: output.format.bit_rate ? parseInt(output.format.bit_rate) : undefined,
|
|
187
|
-
};
|
|
188
|
-
resolve(metadata);
|
|
189
|
-
} catch (parseError: unknown) {
|
|
190
|
-
reject(
|
|
191
|
-
new Error(
|
|
192
|
-
`[FFmpeg] Failed to parse ffprobe output: ${parseError instanceof Error ? parseError.message : parseError}`,
|
|
193
|
-
),
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
});
|
|
153
|
+
]);
|
|
154
|
+
const output = parseProbeJson(stdout);
|
|
155
|
+
const audioStream = output.streams.find((s) => s.codec_type === "audio");
|
|
156
|
+
if (!audioStream) throw new Error("[FFmpeg] No audio stream found");
|
|
157
|
+
|
|
158
|
+
const durationSeconds = output.format.duration ? parseFloat(output.format.duration) : 0;
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
durationSeconds,
|
|
162
|
+
sampleRate: audioStream.sample_rate ? parseInt(audioStream.sample_rate) : 44100,
|
|
163
|
+
channels: audioStream.channels || 2,
|
|
164
|
+
audioCodec: audioStream.codec_name || "unknown",
|
|
165
|
+
bitrate: output.format.bit_rate ? parseInt(output.format.bit_rate) : undefined,
|
|
166
|
+
};
|
|
167
|
+
})();
|
|
197
168
|
|
|
198
|
-
ffprobe.on("error", (err) => {
|
|
199
|
-
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
200
|
-
reject(new Error("[FFmpeg] ffprobe not found. Please install FFmpeg."));
|
|
201
|
-
} else {
|
|
202
|
-
reject(err);
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
169
|
audioMetadataCache.set(filePath, probePromise);
|
|
207
170
|
probePromise.catch(() => {
|
|
208
171
|
if (audioMetadataCache.get(filePath) === probePromise) {
|
|
@@ -211,3 +174,77 @@ export async function extractAudioMetadata(filePath: string): Promise<AudioMetad
|
|
|
211
174
|
});
|
|
212
175
|
return probePromise;
|
|
213
176
|
}
|
|
177
|
+
|
|
178
|
+
export interface KeyframeAnalysis {
|
|
179
|
+
avgIntervalSeconds: number;
|
|
180
|
+
maxIntervalSeconds: number;
|
|
181
|
+
keyframeCount: number;
|
|
182
|
+
isProblematic: boolean;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const keyframeCache = new Map<string, Promise<KeyframeAnalysis>>();
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Check keyframe intervals in a video file. Intervals > 2s cause seeking
|
|
189
|
+
* issues in the headless renderer and audio/video desync. Videos from
|
|
190
|
+
* yt-dlp --download-sections or screen recordings often have sparse keyframes.
|
|
191
|
+
*/
|
|
192
|
+
export async function analyzeKeyframeIntervals(filePath: string): Promise<KeyframeAnalysis> {
|
|
193
|
+
const cached = keyframeCache.get(filePath);
|
|
194
|
+
if (cached) return cached;
|
|
195
|
+
|
|
196
|
+
const promise = analyzeKeyframeIntervalsUncached(filePath);
|
|
197
|
+
keyframeCache.set(filePath, promise);
|
|
198
|
+
promise.catch(() => {
|
|
199
|
+
if (keyframeCache.get(filePath) === promise) {
|
|
200
|
+
keyframeCache.delete(filePath);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
return promise;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function analyzeKeyframeIntervalsUncached(filePath: string): Promise<KeyframeAnalysis> {
|
|
207
|
+
const stdout = await runFfprobe([
|
|
208
|
+
"-v",
|
|
209
|
+
"quiet",
|
|
210
|
+
"-select_streams",
|
|
211
|
+
"v:0",
|
|
212
|
+
"-skip_frame",
|
|
213
|
+
"nokey",
|
|
214
|
+
"-show_entries",
|
|
215
|
+
"frame=pts_time",
|
|
216
|
+
"-of",
|
|
217
|
+
"csv=p=0",
|
|
218
|
+
filePath,
|
|
219
|
+
]);
|
|
220
|
+
|
|
221
|
+
const timestamps = stdout
|
|
222
|
+
.split("\n")
|
|
223
|
+
.map((line) => parseFloat(line.trim()))
|
|
224
|
+
.filter((t) => Number.isFinite(t));
|
|
225
|
+
|
|
226
|
+
if (timestamps.length < 2) {
|
|
227
|
+
return {
|
|
228
|
+
avgIntervalSeconds: 0,
|
|
229
|
+
maxIntervalSeconds: 0,
|
|
230
|
+
keyframeCount: timestamps.length,
|
|
231
|
+
isProblematic: false,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let maxInterval = 0;
|
|
236
|
+
let totalInterval = 0;
|
|
237
|
+
for (let i = 1; i < timestamps.length; i++) {
|
|
238
|
+
const interval = (timestamps[i] ?? 0) - (timestamps[i - 1] ?? 0);
|
|
239
|
+
totalInterval += interval;
|
|
240
|
+
if (interval > maxInterval) maxInterval = interval;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const avgInterval = totalInterval / (timestamps.length - 1);
|
|
244
|
+
return {
|
|
245
|
+
avgIntervalSeconds: Math.round(avgInterval * 100) / 100,
|
|
246
|
+
maxIntervalSeconds: Math.round(maxInterval * 100) / 100,
|
|
247
|
+
keyframeCount: timestamps.length,
|
|
248
|
+
isProblematic: maxInterval > 2,
|
|
249
|
+
};
|
|
250
|
+
}
|