@lightcone-ai/daemon 0.15.70 → 0.15.71

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": "@lightcone-ai/daemon",
3
- "version": "0.15.70",
3
+ "version": "0.15.71",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -292,7 +292,14 @@ export async function composeVideoV2({
292
292
  finalClip = await silentClip({ videoPath: visualClip.path, duration: visualClip.duration, tmpDir });
293
293
  }
294
294
 
295
- const subtitleText = typeof seg.subtitle_text === 'string' ? seg.subtitle_text.trim() : '';
295
+ // Accept `text` as an alias for `subtitle_text`: plan_video_segments takes
296
+ // segment narration as `text` on input, compose_video_v2's canonical name is
297
+ // `subtitle_text`. Either reaches the burn pass so subtitles aren't silently dropped.
298
+ const subtitleText = (
299
+ typeof seg.subtitle_text === 'string' ? seg.subtitle_text
300
+ : typeof seg.text === 'string' ? seg.text
301
+ : ''
302
+ ).trim();
296
303
  readyClips.push({ path: finalClip, duration: visualClip.duration, transition, subtitleText });
297
304
  }
298
305
 
@@ -59,13 +59,30 @@ export async function runComposeVideoV2Tool({ segments, outro_paths, format, res
59
59
  );
60
60
  }
61
61
  }
62
+ const warnings = [];
62
63
  // Heuristic warning: a multi-segment image video that reuses one single image
63
64
  // will look near-static — usually a sign the source page didn't render and the
64
65
  // agent fell back to one blank screenshot.
65
- let warning = null;
66
66
  if (imagePaths.length >= 2 && new Set(imagePaths).size === 1) {
67
- warning = `WARNING: all ${imagePaths.length} image segments reuse the same file (${imagePaths[0]}). `
68
- + 'The output will be near-static — verify the source page actually rendered before submitting this video.';
67
+ warnings.push(
68
+ `WARNING: all ${imagePaths.length} image segments reuse the same file (${imagePaths[0]}). `
69
+ + 'The output will be near-static — verify the source page actually rendered before submitting this video.'
70
+ );
71
+ }
72
+ // Warn when narration is present but no subtitle text is — compose_video_v2 burns
73
+ // subtitles only from `subtitle_text` (or its `text` alias); without it the video
74
+ // ships with no captions. Simplest fix: pass plan_video_segments' output verbatim.
75
+ {
76
+ const hasSubText = s => (typeof s?.subtitle_text === 'string' && s.subtitle_text.trim())
77
+ || (typeof s?.text === 'string' && s.text.trim());
78
+ const narratedNoSub = segments.filter(s =>
79
+ (typeof s?.audio_path === 'string' && s.audio_path.trim()) && !hasSubText(s)).length;
80
+ if (narratedNoSub > 0) {
81
+ warnings.push(
82
+ `WARNING: ${narratedNoSub} segment(s) have narration audio but no subtitle text — the output will have NO subtitles. `
83
+ + 'If subtitles are wanted, set subtitle_text per segment (or pass the plan_video_segments output array verbatim).'
84
+ );
85
+ }
69
86
  }
70
87
 
71
88
  const outDir = workspaceDir
@@ -89,7 +106,7 @@ export async function runComposeVideoV2Tool({ segments, outro_paths, format, res
89
106
  `segments=${segments.length}`,
90
107
  `outro_clips=${(outro_paths ?? []).length}`,
91
108
  ];
92
- if (warning) lines.push(warning);
109
+ for (const w of warnings) lines.push(w);
93
110
  return toolText(lines.join('\n'));
94
111
  } catch (error) {
95
112
  return toolError(`compose_video_v2 failed: ${error.message}`);