comfy-qa 2.2.0 → 2.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "comfy-qa",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "ComfyUI QA automation CLI",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,6 +22,11 @@ function srtTime(ms: number): string {
22
22
  return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")},${String(msr).padStart(3, "0")}`;
23
23
  }
24
24
 
25
+ /** Format ms as WebVTT timestamp HH:MM:SS.mmm */
26
+ function vttTime(ms: number): string {
27
+ return srtTime(ms).replace(",", ".");
28
+ }
29
+
25
30
  /** Generate SRT subtitle file from meta + initial offset */
26
31
  function generateSrt(meta: Meta, offsetMs: number, outPath: string): void {
27
32
  const lines: string[] = [];
@@ -38,6 +43,22 @@ function generateSrt(meta: Meta, offsetMs: number, outPath: string): void {
38
43
  fs.writeFileSync(outPath, lines.join("\n"));
39
44
  }
40
45
 
46
+ /** Generate WebVTT subtitle file from meta + initial offset (browser-native, no libass) */
47
+ export function generateVtt(meta: Meta, offsetMs: number, outPath: string): void {
48
+ const lines: string[] = ["WEBVTT", ""];
49
+ let cursor = offsetMs;
50
+ meta.segments.forEach((seg, i) => {
51
+ const start = cursor;
52
+ const end = cursor + seg.durationMs;
53
+ lines.push(String(i + 1));
54
+ lines.push(`${vttTime(start)} --> ${vttTime(end)}`);
55
+ lines.push(seg.text);
56
+ lines.push("");
57
+ cursor = end;
58
+ });
59
+ fs.writeFileSync(outPath, lines.join("\n"));
60
+ }
61
+
41
62
  /**
42
63
  * Mix audio + subtitles onto video.
43
64
  * @param videoPath path to silent recorded video (webm/mp4)
@@ -63,19 +84,15 @@ export async function postMix(
63
84
  console.log(` [post-mix] Overlaying audio (adelay=${offsetMs}ms)…`);
64
85
  await $`ffmpeg -y -i ${videoPath} -i ${trackPath} -filter_complex ${`[1:a]adelay=${adelay}[aout]`} -map 0:v -map [aout] -c:v libx264 -preset fast -pix_fmt yuv420p -c:a aac -shortest ${audioMixed}`.quiet();
65
86
 
66
- // Step 2: generate SRT
87
+ // Step 2: generate SRT + WebVTT (browser-native, no libass needed)
67
88
  const srtPath = path.join(tmpDir, "narration.srt");
68
89
  generateSrt(meta, offsetMs, srtPath);
69
- console.log(` [post-mix] Subtitles → ${srtPath}`);
70
-
71
- // Step 3: burn subtitles
72
- console.log(` [post-mix] Burning subtitles…`);
73
- // Escape path for ffmpeg subtitle filter
74
- const escSrt = srtPath.replace(/\\/g, "/").replace(/:/g, "\\:");
75
- await $`ffmpeg -y -i ${audioMixed} -vf ${`subtitles=${escSrt}:force_style='FontSize=18,Alignment=2,OutlineColour=&H80000000,BorderStyle=4,MarginV=30'`} -c:a copy ${outPath}`.quiet();
90
+ const vttPath = path.join(tmpDir, "narration.vtt");
91
+ generateVtt(meta, offsetMs, vttPath);
92
+ console.log(` [post-mix] Subtitles ${srtPath} + ${vttPath}`);
76
93
 
77
- // Cleanup intermediate
78
- try { fs.unlinkSync(audioMixed); } catch {}
94
+ // Step 3: rename audio-mixed to final output (subtitles served as sidecar .vtt)
95
+ fs.renameSync(audioMixed, outPath);
79
96
 
80
- console.log(` [post-mix] Final video → ${outPath}`);
97
+ console.log(` [post-mix] Final video → ${outPath} (subtitles: ${vttPath})`);
81
98
  }