@mux/ai 0.28.0 → 0.29.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.
@@ -1,3 +1,3 @@
1
- export { D as DEFAULT_STORYBOARD_WIDTH, H as HeatmapOptions, a as HeatmapResponse, L as LOW_CONFIDENCE_THRESHOLD, M as MAX_CUE_TEXT_CHARS, T as ThumbnailOptions, b as TranscriptFetchOptions, c as TranscriptResult, V as VTTCue, d as VTTCueBudgetChunkingOptions, e as VTTDurationChunk, f as VTTDurationChunkingOptions, g as buildTranscriptUrl, h as buildVttFromCueBlocks, j as buildVttFromTranslatedCueBlocks, k as chunkByTokens, l as chunkText, m as chunkVTTCues, n as chunkVTTCuesByBudget, o as chunkVTTCuesByDuration, p as concatenateVttSegments, q as estimateTokenCount, r as extractTextFromVTT, s as extractTimestampedTranscript, t as fetchTranscriptForAsset, u as findCaptionTrack, v as getHeatmapForAsset, w as getHeatmapForPlaybackId, x as getHeatmapForVideo, y as getReadyTextTracks, z as getReliableLanguageCode, A as getStoryboardUrl, B as getThumbnailUrls, C as parseVTTCues, E as replaceCueText, F as sanitizeUntrustedText, G as secondsToTimestamp, I as splitVttPreambleAndCueBlocks, J as stripVttMetadataBlocks, K as vttTimestampToSeconds } from '../index-bjQi1ZCj.js';
2
- export { o as CompletedShotsResult, p as ErroredShotsResult, H as Hotspot, q as HotspotOptions, r as HotspotResponse, s as PendingShotsResult, t as Shot, u as ShotRequestOptions, v as ShotsResult, w as WaitForShotsOptions, x as getHotspotsForAsset, y as getHotspotsForPlaybackId, z as getHotspotsForVideo, B as getShotsForAsset, D as requestShotsForAsset, F as waitForShotsForAsset } from '../shots-mGJfYGiO.js';
1
+ export { D as DEFAULT_STORYBOARD_WIDTH, H as HeatmapOptions, a as HeatmapResponse, L as LOW_CONFIDENCE_THRESHOLD, M as MAX_CUE_TEXT_CHARS, T as ThumbnailOptions, b as TranscriptFetchOptions, c as TranscriptResult, V as VTTCue, d as VTTCueBudgetChunkingOptions, e as VTTDurationChunk, f as VTTDurationChunkingOptions, g as buildTranscriptUrl, h as buildVttFromCueBlocks, j as buildVttFromTranslatedCueBlocks, k as chunkByTokens, l as chunkText, m as chunkVTTCues, n as chunkVTTCuesByBudget, o as chunkVTTCuesByDuration, p as concatenateVttSegments, q as estimateTokenCount, r as extractTextFromVTT, s as extractTimestampedTranscript, t as fetchTranscriptForAsset, u as filterVttByScope, v as findCaptionTrack, w as getHeatmapForAsset, x as getHeatmapForPlaybackId, y as getHeatmapForVideo, z as getReadyTextTracks, A as getReliableLanguageCode, B as getStoryboardUrl, C as getThumbnailUrls, E as parseVTTCues, F as replaceCueText, G as sanitizeUntrustedText, I as secondsToTimestamp, J as splitVttPreambleAndCueBlocks, K as stripVttMetadataBlocks, N as vttTimestampToSeconds } from '../index-CXwsu679.js';
2
+ export { q as CompletedShotsResult, r as ErroredShotsResult, H as Hotspot, s as HotspotOptions, t as HotspotResponse, u as PendingShotsResult, v as Shot, w as ShotRequestOptions, x as ShotsResult, y as WaitForShotsOptions, z as getHotspotsForAsset, B as getHotspotsForPlaybackId, D as getHotspotsForVideo, F as getShotsForAsset, G as requestShotsForAsset, J as waitForShotsForAsset } from '../shots-BKd7TMFN.js';
3
3
  import '@mux/mux-node';
@@ -1111,13 +1111,22 @@ async function signUrl(url, playbackId, type = "video", params, credentials) {
1111
1111
 
1112
1112
  // src/primitives/storyboards.ts
1113
1113
  var DEFAULT_STORYBOARD_WIDTH = 640;
1114
- async function getStoryboardUrl(playbackId, width = DEFAULT_STORYBOARD_WIDTH, shouldSign = false, credentials) {
1114
+ async function getStoryboardUrl(playbackId, width = DEFAULT_STORYBOARD_WIDTH, shouldSign = false, credentials, scope) {
1115
1115
  "use step";
1116
1116
  const baseUrl = getMuxStoryboardBaseUrl(playbackId);
1117
+ const params = { width };
1118
+ if (scope?.startTime !== void 0) {
1119
+ params.asset_start_time = scope.startTime;
1120
+ }
1121
+ if (scope?.endTime !== void 0) {
1122
+ params.asset_end_time = scope.endTime;
1123
+ }
1117
1124
  if (shouldSign) {
1118
- return signUrl(baseUrl, playbackId, "storyboard", { width }, credentials);
1125
+ return signUrl(baseUrl, playbackId, "storyboard", params, credentials);
1119
1126
  }
1120
- return `${baseUrl}?width=${width}`;
1127
+ return `${baseUrl}?${new URLSearchParams(
1128
+ Object.entries(params).map(([key, value]) => [key, String(value)])
1129
+ ).toString()}`;
1121
1130
  }
1122
1131
 
1123
1132
  // src/primitives/text-chunking.ts
@@ -1358,30 +1367,77 @@ function chunkText(text, strategy) {
1358
1367
  }
1359
1368
  }
1360
1369
 
1370
+ // src/lib/workflow-scope.ts
1371
+ function hasWorkflowScopeBoundaries(scope) {
1372
+ return scope?.startTime !== void 0 || scope?.endTime !== void 0;
1373
+ }
1374
+ function resolveWorkflowScope(scope, assetDurationSeconds) {
1375
+ if (assetDurationSeconds === void 0 || !Number.isFinite(assetDurationSeconds) || assetDurationSeconds <= 0) {
1376
+ throw new MuxAiError("Asset has no valid duration.", { type: "validation_error" });
1377
+ }
1378
+ const startTime = scope?.startTime ?? 0;
1379
+ const endTime = scope?.endTime ?? assetDurationSeconds;
1380
+ if (!Number.isFinite(startTime) || startTime < 0) {
1381
+ throw new MuxAiError(
1382
+ `scope.startTime must be a finite, non-negative number (received ${startTime}).`,
1383
+ { type: "validation_error" }
1384
+ );
1385
+ }
1386
+ if (!Number.isFinite(endTime) || endTime < 0) {
1387
+ throw new MuxAiError(
1388
+ `scope.endTime must be a finite, non-negative number (received ${endTime}).`,
1389
+ { type: "validation_error" }
1390
+ );
1391
+ }
1392
+ if (endTime > assetDurationSeconds) {
1393
+ throw new MuxAiError(
1394
+ `scope.endTime (${endTime}) cannot exceed the asset duration (${assetDurationSeconds}).`,
1395
+ { type: "validation_error" }
1396
+ );
1397
+ }
1398
+ if (startTime >= endTime) {
1399
+ throw new MuxAiError(
1400
+ `scope.startTime (${startTime}) must be less than scope.endTime (${endTime}).`,
1401
+ { type: "validation_error" }
1402
+ );
1403
+ }
1404
+ return { startTime, endTime };
1405
+ }
1406
+ function timeRangesOverlap(firstStart, firstEnd, second) {
1407
+ const secondStart = second.startTime ?? 0;
1408
+ const secondEnd = second.endTime ?? Number.POSITIVE_INFINITY;
1409
+ return firstStart < secondEnd && firstEnd > secondStart;
1410
+ }
1411
+
1361
1412
  // src/primitives/thumbnails.ts
1362
1413
  async function getThumbnailUrls(playbackId, duration, options = {}) {
1363
1414
  "use step";
1364
- const { interval = 10, width = 640, shouldSign = false, maxSamples, credentials } = options;
1415
+ const { interval = 10, width = 640, shouldSign = false, maxSamples, credentials, scope } = options;
1416
+ const effectiveScope = hasWorkflowScopeBoundaries(scope) ? scope : void 0;
1417
+ const resolvedScope = effectiveScope ? resolveWorkflowScope(effectiveScope, duration) : { startTime: 0, endTime: duration };
1418
+ const rangeDuration = resolvedScope.endTime - resolvedScope.startTime;
1365
1419
  let timestamps = [];
1366
- if (duration <= 50) {
1367
- const spacing = duration / 6;
1420
+ if (rangeDuration <= 50) {
1421
+ const spacing = rangeDuration / 6;
1368
1422
  for (let i = 1; i <= 5; i++) {
1369
- timestamps.push(Math.round(i * spacing));
1423
+ const time = resolvedScope.startTime + i * spacing;
1424
+ timestamps.push(effectiveScope ? Number(time.toFixed(3)) : Math.round(time));
1370
1425
  }
1371
1426
  } else {
1372
- for (let time = 0; time < duration; time += interval) {
1427
+ for (let time = resolvedScope.startTime; time < resolvedScope.endTime; time += interval) {
1373
1428
  timestamps.push(time);
1374
1429
  }
1375
1430
  }
1376
1431
  if (maxSamples !== void 0 && timestamps.length > maxSamples) {
1377
1432
  const newTimestamps = [];
1378
- newTimestamps.push(0);
1433
+ newTimestamps.push(resolvedScope.startTime);
1379
1434
  if (maxSamples >= 2) {
1380
- const spacing = duration / (maxSamples - 1);
1435
+ const lastTime = effectiveScope ? Math.max(resolvedScope.startTime, resolvedScope.endTime - 1e-3) : resolvedScope.endTime;
1436
+ const spacing = (lastTime - resolvedScope.startTime) / (maxSamples - 1);
1381
1437
  for (let i = 1; i < maxSamples - 1; i++) {
1382
- newTimestamps.push(spacing * i);
1438
+ newTimestamps.push(resolvedScope.startTime + spacing * i);
1383
1439
  }
1384
- newTimestamps.push(duration);
1440
+ newTimestamps.push(lastTime);
1385
1441
  }
1386
1442
  timestamps = newTimestamps;
1387
1443
  }
@@ -1792,6 +1848,23 @@ function parseVTTCues(vttContent) {
1792
1848
  }
1793
1849
  return cues;
1794
1850
  }
1851
+ function filterVttByScope(vttContent, scope) {
1852
+ if (!scope || scope.startTime === void 0 && scope.endTime === void 0) {
1853
+ return vttContent;
1854
+ }
1855
+ const { preamble, cueBlocks } = splitVttPreambleAndCueBlocks(vttContent);
1856
+ const scopedCueBlocks = cueBlocks.filter((cueBlock) => {
1857
+ const cue = parseVTTCues(cueBlock)[0];
1858
+ return cue ? timeRangesOverlap(cue.startTime, cue.endTime, scope) : false;
1859
+ }).map((cueBlock, index) => {
1860
+ const lines = cueBlock.split("\n");
1861
+ if (/^\d+$/.test(lines[0]?.trim()) && lines[1]?.includes("-->")) {
1862
+ lines[0] = String(index + 1);
1863
+ }
1864
+ return lines.join("\n");
1865
+ });
1866
+ return buildVttFromCueBlocks(scopedCueBlocks, preamble);
1867
+ }
1795
1868
  function splitVttPreambleAndCueBlocks(vttContent) {
1796
1869
  const normalizedContent = normalizeLineEndings(vttContent).trim();
1797
1870
  if (!normalizedContent) {
@@ -1896,6 +1969,7 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
1896
1969
  const {
1897
1970
  languageCode,
1898
1971
  cleanTranscript = true,
1972
+ scope,
1899
1973
  shouldSign,
1900
1974
  credentials,
1901
1975
  required = false
@@ -1927,9 +2001,15 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
1927
2001
  return { transcriptText: "", transcriptUrl, track };
1928
2002
  }
1929
2003
  const rawVtt = await response.text();
1930
- const transcriptText = cleanTranscript ? extractTextFromVTT(rawVtt) : rawVtt;
1931
- if (required && !transcriptText.trim()) {
1932
- throw new MuxAiError("Transcript is empty.", { type: "validation_error" });
2004
+ const effectiveScope = hasWorkflowScopeBoundaries(scope) ? scope : void 0;
2005
+ const scopedVtt = filterVttByScope(rawVtt, effectiveScope);
2006
+ const transcriptText = cleanTranscript ? extractTextFromVTT(scopedVtt) : scopedVtt;
2007
+ const hasScopedCues = !effectiveScope || parseVTTCues(scopedVtt).length > 0;
2008
+ if (required && (!transcriptText.trim() || !hasScopedCues)) {
2009
+ throw new MuxAiError(
2010
+ effectiveScope ? "Transcript has no cues in the requested scope." : "Transcript is empty.",
2011
+ { type: "validation_error" }
2012
+ );
1933
2013
  }
1934
2014
  return { transcriptText, transcriptUrl, track };
1935
2015
  } catch (error) {
@@ -1957,6 +2037,7 @@ export {
1957
2037
  extractTextFromVTT,
1958
2038
  extractTimestampedTranscript,
1959
2039
  fetchTranscriptForAsset,
2040
+ filterVttByScope,
1960
2041
  findCaptionTrack,
1961
2042
  getHeatmapForAsset,
1962
2043
  getHeatmapForPlaybackId,