agentreel 0.2.5 → 0.2.7

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.
Files changed (2) hide show
  1. package/bin/agentreel.mjs +87 -23
  2. package/package.json +1 -1
package/bin/agentreel.mjs CHANGED
@@ -154,6 +154,89 @@ function extractBrowserHighlights(videoPath, task) {
154
154
  return outFile;
155
155
  }
156
156
 
157
+ // ── Browser Highlight Builder ───────────────────────────────
158
+
159
+ function buildBrowserHighlights(clicks, videoPath, task) {
160
+ const CLIP_DUR = 7;
161
+ const labels = ["Overview", "Interact", "Navigate", "Result"];
162
+ const overlays = ["**First look**", "**Key action**", "**Exploring**", "**The result**"];
163
+
164
+ // If we have clicks, cluster them into highlights
165
+ if (clicks.length >= 2) {
166
+ // Group clicks that are within 3s of each other
167
+ const clusters = [];
168
+ let cluster = [clicks[0]];
169
+
170
+ for (let i = 1; i < clicks.length; i++) {
171
+ if (clicks[i].timeSec - cluster[cluster.length - 1].timeSec < 3) {
172
+ cluster.push(clicks[i]);
173
+ } else {
174
+ clusters.push(cluster);
175
+ cluster = [clicks[i]];
176
+ }
177
+ }
178
+ clusters.push(cluster);
179
+
180
+ // Take up to 4 clusters, pick the ones with most clicks
181
+ const ranked = clusters
182
+ .map((c, i) => ({ cluster: c, idx: i }))
183
+ .sort((a, b) => b.cluster.length - a.cluster.length)
184
+ .slice(0, 4)
185
+ .sort((a, b) => a.cluster[0].timeSec - b.cluster[0].timeSec);
186
+
187
+ const highlights = ranked.map((r, i) => {
188
+ const first = r.cluster[0];
189
+ const last = r.cluster[r.cluster.length - 1];
190
+ const center = (first.timeSec + last.timeSec) / 2;
191
+ const startSec = Math.max(0, center - CLIP_DUR / 2);
192
+ const endSec = startSec + CLIP_DUR;
193
+
194
+ const hlClicks = r.cluster.map(c => ({
195
+ x: Math.max(0, Math.min(1280, c.x)),
196
+ y: Math.max(0, Math.min(800, c.y)),
197
+ timeSec: c.timeSec - startSec,
198
+ }));
199
+
200
+ const focusX = hlClicks.reduce((s, c) => s + c.x, 0) / hlClicks.length / 1280;
201
+ const focusY = hlClicks.reduce((s, c) => s + c.y, 0) / hlClicks.length / 800;
202
+
203
+ return {
204
+ label: labels[i % labels.length],
205
+ overlay: overlays[i % overlays.length],
206
+ videoSrc: "browser-demo.mp4",
207
+ videoStartSec: Math.round(startSec * 10) / 10,
208
+ videoEndSec: Math.round(endSec * 10) / 10,
209
+ focusX,
210
+ focusY,
211
+ clicks: hlClicks,
212
+ };
213
+ });
214
+
215
+ console.error(` ${highlights.length} highlights from ${clicks.length} clicks`);
216
+ return highlights;
217
+ }
218
+
219
+ // Fallback: try Claude extraction, or use evenly-spaced defaults
220
+ try {
221
+ const highlightsPath = extractBrowserHighlights(videoPath, task);
222
+ const highlights = JSON.parse(readFileSync(highlightsPath, "utf-8"));
223
+ if (highlights.length > 0) {
224
+ console.error(` ${highlights.length} highlights from Claude`);
225
+ return highlights;
226
+ }
227
+ } catch {
228
+ // Claude failed, use defaults
229
+ }
230
+
231
+ // Last resort: evenly-spaced clips
232
+ console.error(" Using default highlights (no clicks, no Claude)");
233
+ return [
234
+ { label: "Overview", overlay: "**Quick look**", videoSrc: "browser-demo.mp4", videoStartSec: 1, videoEndSec: 8 },
235
+ { label: "Features", overlay: "**Key features**", videoSrc: "browser-demo.mp4", videoStartSec: 8, videoEndSec: 15 },
236
+ { label: "Result", overlay: "**See it work**", videoSrc: "browser-demo.mp4", videoStartSec: 15, videoEndSec: 22 },
237
+ ];
238
+ }
239
+
157
240
  // ── Render ──────────────────────────────────────────────────
158
241
 
159
242
  async function renderVideo(props, output, musicPath) {
@@ -185,7 +268,7 @@ async function renderVideo(props, output, musicPath) {
185
268
  inputProps: props,
186
269
  onBrowserDownload: () => {
187
270
  console.error(" Downloading renderer (one-time, ~90MB)...");
188
- return () => {}; // suppress progress logs
271
+ return { onProgress: () => {} };
189
272
  },
190
273
  });
191
274
 
@@ -312,12 +395,9 @@ async function main() {
312
395
  if (!existsSync(publicDir)) mkdirSync(publicDir, { recursive: true });
313
396
  copyFileSync(videoPath, join(publicDir, "browser-demo.mp4"));
314
397
 
315
- console.error("Step 2/3: Extracting highlights...");
316
- const highlightsPath = extractBrowserHighlights(videoPath, task);
317
- const highlights = JSON.parse(readFileSync(highlightsPath, "utf-8"));
318
- console.error(` ${highlights.length} highlights extracted`);
398
+ console.error("Step 2/3: Building highlights...");
319
399
 
320
- // Merge click data into highlights
400
+ // Read click data this is the primary signal for highlights
321
401
  const clicksPath = videoPath.replace(".mp4", "-clicks.json");
322
402
  let allClicks = [];
323
403
  if (existsSync(clicksPath)) {
@@ -325,23 +405,7 @@ async function main() {
325
405
  console.error(` ${allClicks.length} clicks captured`);
326
406
  }
327
407
 
328
- for (const h of highlights) {
329
- const startSec = h.videoStartSec || 0;
330
- const endSec = h.videoEndSec || (startSec + 7);
331
-
332
- h.clicks = allClicks
333
- .filter(c => c.timeSec >= startSec && c.timeSec <= endSec)
334
- .map(c => ({
335
- x: Math.max(0, Math.min(1280, c.x)),
336
- y: Math.max(0, Math.min(800, c.y)),
337
- timeSec: c.timeSec - startSec,
338
- }));
339
-
340
- if (h.clicks.length > 0) {
341
- h.focusX = h.clicks.reduce((s, c) => s + c.x, 0) / h.clicks.length / 1280;
342
- h.focusY = h.clicks.reduce((s, c) => s + c.y, 0) / h.clicks.length / 800;
343
- }
344
- }
408
+ const highlights = buildBrowserHighlights(allClicks, videoPath, task);
345
409
 
346
410
  console.error("Step 3/3: Rendering video...");
347
411
  await renderVideo({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentreel",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Turn your web apps and CLIs into viral clips",
5
5
  "bin": {
6
6
  "agentreel": "./bin/agentreel.mjs"