@mux/ai 0.13.1 → 0.15.1

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.
@@ -592,6 +592,26 @@ async function fetchHotspots(identifierType, id, options) {
592
592
  return transformHotspotResponse(response);
593
593
  }
594
594
 
595
+ // src/lib/mux-ai-error.ts
596
+ var MuxAiError = class extends Error {
597
+ constructor(message, opts) {
598
+ super(message);
599
+ /** Structural brand for duck-type identification. */
600
+ this.__robots_error = true;
601
+ this.name = "MuxAiError";
602
+ this.publicType = opts?.type ?? "processing_error";
603
+ this.publicMessage = message;
604
+ this.retryable = opts?.retryable ?? false;
605
+ }
606
+ };
607
+ function wrapError(error, message) {
608
+ if (error instanceof MuxAiError) {
609
+ throw error;
610
+ }
611
+ const detail = error instanceof Error ? error.message : "Unknown error";
612
+ throw new Error(`${message}: ${detail}`);
613
+ }
614
+
595
615
  // src/primitives/shots.ts
596
616
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
597
617
  var MIN_POLL_INTERVAL_MS = 1e3;
@@ -717,14 +737,15 @@ async function waitForShotsForAsset(assetId, options = {}) {
717
737
  return result;
718
738
  }
719
739
  if (result.status === "errored") {
720
- throw new Error(`Shots generation errored for asset '${assetId}'`);
740
+ throw new MuxAiError(`Shot generation failed for asset ${assetId}.`);
721
741
  }
722
742
  if (attempt < normalizedMaxAttempts - 1) {
723
743
  await sleep(normalizedPollIntervalMs);
724
744
  }
725
745
  }
726
- throw new Error(
727
- `Timed out waiting for shots for asset '${assetId}' after ${normalizedMaxAttempts} attempts. Last status: ${lastStatus ?? "unknown"}`
746
+ throw new MuxAiError(
747
+ `Timed out waiting for shots for asset ${assetId}. Last status: ${lastStatus ?? "unknown"}.`,
748
+ { type: "timeout_error", retryable: true }
728
749
  );
729
750
  }
730
751
 
@@ -1087,6 +1108,13 @@ async function getThumbnailUrls(playbackId, duration, options = {}) {
1087
1108
  return Promise.all(urlPromises);
1088
1109
  }
1089
1110
 
1111
+ // src/lib/mux-assets.ts
1112
+ function isAudioOnlyAsset(asset) {
1113
+ const hasAudioTrack = asset.tracks?.some((track) => track.type === "audio") ?? false;
1114
+ const hasVideoTrack = asset.tracks?.some((track) => track.type === "video") ?? false;
1115
+ return hasAudioTrack && !hasVideoTrack;
1116
+ }
1117
+
1090
1118
  // src/primitives/transcripts.ts
1091
1119
  function getReadyTextTracks(asset) {
1092
1120
  return (asset.tracks || []).filter(
@@ -1098,11 +1126,20 @@ function findCaptionTrack(asset, languageCode) {
1098
1126
  if (!tracks.length)
1099
1127
  return void 0;
1100
1128
  if (!languageCode) {
1101
- return tracks[0];
1129
+ const englishTrack = tracks.find(
1130
+ (track) => track.text_type === "subtitles" && track.language_code === "en"
1131
+ );
1132
+ return englishTrack ?? tracks[0];
1102
1133
  }
1103
- return tracks.find(
1134
+ const languageMatch = tracks.find(
1104
1135
  (track) => track.text_type === "subtitles" && track.language_code === languageCode
1105
1136
  );
1137
+ if (languageMatch)
1138
+ return languageMatch;
1139
+ if (isAudioOnlyAsset(asset) && tracks.length === 1) {
1140
+ return tracks[0];
1141
+ }
1142
+ return void 0;
1106
1143
  }
1107
1144
  function normalizeLineEndings(value) {
1108
1145
  return value.replace(/\r\n/g, "\n");
@@ -1390,15 +1427,16 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
1390
1427
  if (!track) {
1391
1428
  if (required) {
1392
1429
  const availableLanguages = getReadyTextTracks(asset).map((t) => t.language_code).filter(Boolean).join(", ");
1393
- throw new Error(
1394
- `No transcript track found${languageCode ? ` for language '${languageCode}'` : ""}. Available languages: ${availableLanguages || "none"}`
1430
+ throw new MuxAiError(
1431
+ `No caption track found${languageCode ? ` for language ${languageCode}` : ""}. Available languages: ${availableLanguages || "none"}.`,
1432
+ { type: "validation_error" }
1395
1433
  );
1396
1434
  }
1397
1435
  return { transcriptText: "" };
1398
1436
  }
1399
1437
  if (!track.id) {
1400
1438
  if (required) {
1401
- throw new Error("Transcript track is missing an id");
1439
+ throw new MuxAiError("Transcript track is missing an id.", { type: "validation_error" });
1402
1440
  }
1403
1441
  return { transcriptText: "", track };
1404
1442
  }
@@ -1414,14 +1452,12 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
1414
1452
  const rawVtt = await response.text();
1415
1453
  const transcriptText = cleanTranscript ? extractTextFromVTT(rawVtt) : rawVtt;
1416
1454
  if (required && !transcriptText.trim()) {
1417
- throw new Error("Transcript is empty");
1455
+ throw new MuxAiError("Transcript is empty.", { type: "validation_error" });
1418
1456
  }
1419
1457
  return { transcriptText, transcriptUrl, track };
1420
1458
  } catch (error) {
1421
1459
  if (required) {
1422
- throw new Error(
1423
- `Failed to fetch transcript: ${error instanceof Error ? error.message : "Unknown error"}`
1424
- );
1460
+ wrapError(error, "Failed to fetch transcript");
1425
1461
  }
1426
1462
  console.warn("Failed to fetch transcript:", error);
1427
1463
  return { transcriptText: "", transcriptUrl, track };