@mux/ai 0.9.0 → 0.10.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/dist/{index-Nxf6BaBO.d.ts → index-C8-E3VR9.d.ts} +59 -4
- package/dist/{index-CkJStzYO.d.ts → index-CA7bG50u.d.ts} +29 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +711 -31
- package/dist/index.js.map +1 -1
- package/dist/primitives/index.d.ts +1 -1
- package/dist/primitives/index.js +336 -14
- package/dist/primitives/index.js.map +1 -1
- package/dist/workflows/index.d.ts +1 -1
- package/dist/workflows/index.js +703 -30
- package/dist/workflows/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { D as DEFAULT_STORYBOARD_WIDTH, H as HeatmapOptions, a as HeatmapResponse, b as Hotspot, c as HotspotOptions, d as HotspotResponse, T as ThumbnailOptions, e as TranscriptFetchOptions, f as TranscriptResult, V as VTTCue, g as
|
|
1
|
+
export { D as DEFAULT_STORYBOARD_WIDTH, H as HeatmapOptions, a as HeatmapResponse, b as Hotspot, c as HotspotOptions, d as HotspotResponse, T as ThumbnailOptions, e as TranscriptFetchOptions, f as TranscriptResult, V as VTTCue, g as VTTCueBudgetChunkingOptions, h as VTTDurationChunk, j as VTTDurationChunkingOptions, k as buildTranscriptUrl, l as buildVttFromCueBlocks, m as buildVttFromTranslatedCueBlocks, n as chunkByTokens, o as chunkText, p as chunkVTTCues, q as chunkVTTCuesByBudget, r as chunkVTTCuesByDuration, s as concatenateVttSegments, t as estimateTokenCount, u as extractTextFromVTT, v as extractTimestampedTranscript, w as fetchTranscriptForAsset, x as findCaptionTrack, y as getHeatmapForAsset, z as getHeatmapForPlaybackId, A as getHeatmapForVideo, B as getHotspotsForAsset, C as getHotspotsForPlaybackId, E as getHotspotsForVideo, F as getReadyTextTracks, G as getStoryboardUrl, I as getThumbnailUrls, J as parseVTTCues, K as replaceCueText, L as secondsToTimestamp, M as splitVttPreambleAndCueBlocks, N as vttTimestampToSeconds } from '../index-C8-E3VR9.js';
|
|
2
2
|
import '../types-BRbaGW3t.js';
|
|
3
3
|
import '@mux/mux-node';
|
package/dist/primitives/index.js
CHANGED
|
@@ -679,6 +679,14 @@ async function getStoryboardUrl(playbackId, width = DEFAULT_STORYBOARD_WIDTH, sh
|
|
|
679
679
|
}
|
|
680
680
|
|
|
681
681
|
// src/primitives/text-chunking.ts
|
|
682
|
+
var DEFAULT_MIN_CHUNK_DURATION_RATIO = 2 / 3;
|
|
683
|
+
var DEFAULT_BOUNDARY_LOOKAHEAD_CUES = 12;
|
|
684
|
+
var DEFAULT_BOUNDARY_PAUSE_SECONDS = 1.25;
|
|
685
|
+
var STRONG_BOUNDARY_SCORE = 4;
|
|
686
|
+
var PREFERRED_BOUNDARY_WINDOW_SECONDS = 5 * 60;
|
|
687
|
+
var SENTENCE_BOUNDARY_REGEX = /[.!?]["')\]]*$/;
|
|
688
|
+
var CLAUSE_BOUNDARY_REGEX = /[,;:]["')\]]*$/;
|
|
689
|
+
var NEXT_SENTENCE_START_REGEX = /^[A-Z0-9"'([{]/;
|
|
682
690
|
function estimateTokenCount(text) {
|
|
683
691
|
const words = text.trim().split(/\s+/).length;
|
|
684
692
|
return Math.ceil(words / 0.75);
|
|
@@ -751,6 +759,151 @@ function chunkVTTCues(cues, maxTokens, overlapCues = 2) {
|
|
|
751
759
|
}
|
|
752
760
|
return chunks;
|
|
753
761
|
}
|
|
762
|
+
function scoreCueBoundary(cues, index, boundaryPauseSeconds) {
|
|
763
|
+
const cue = cues[index];
|
|
764
|
+
const nextCue = cues[index + 1];
|
|
765
|
+
if (!nextCue) {
|
|
766
|
+
return Number.POSITIVE_INFINITY;
|
|
767
|
+
}
|
|
768
|
+
const trimmedText = cue.text.trim();
|
|
769
|
+
let score = 0;
|
|
770
|
+
if (SENTENCE_BOUNDARY_REGEX.test(trimmedText)) {
|
|
771
|
+
score += 4;
|
|
772
|
+
} else if (CLAUSE_BOUNDARY_REGEX.test(trimmedText)) {
|
|
773
|
+
score += 2;
|
|
774
|
+
}
|
|
775
|
+
if (nextCue.startTime - cue.endTime >= boundaryPauseSeconds) {
|
|
776
|
+
score += 2;
|
|
777
|
+
}
|
|
778
|
+
if (NEXT_SENTENCE_START_REGEX.test(nextCue.text.trim())) {
|
|
779
|
+
score += 1;
|
|
780
|
+
}
|
|
781
|
+
return score;
|
|
782
|
+
}
|
|
783
|
+
function chunkVTTCuesByBudget(cues, options) {
|
|
784
|
+
if (cues.length === 0) {
|
|
785
|
+
return [];
|
|
786
|
+
}
|
|
787
|
+
const maxCuesPerChunk = Math.max(1, options.maxCuesPerChunk);
|
|
788
|
+
let maxTextTokensPerChunk = Number.POSITIVE_INFINITY;
|
|
789
|
+
if (options.maxTextTokensPerChunk) {
|
|
790
|
+
maxTextTokensPerChunk = Math.max(1, options.maxTextTokensPerChunk);
|
|
791
|
+
}
|
|
792
|
+
const chunks = [];
|
|
793
|
+
let chunkIndex = 0;
|
|
794
|
+
let cueStartIndex = 0;
|
|
795
|
+
let currentTokenCount = 0;
|
|
796
|
+
for (let cueIndex = 0; cueIndex < cues.length; cueIndex++) {
|
|
797
|
+
const cue = cues[cueIndex];
|
|
798
|
+
const cueTokenCount = estimateTokenCount(cue.text);
|
|
799
|
+
const currentCueCount = cueIndex - cueStartIndex;
|
|
800
|
+
const wouldExceedCueCount = currentCueCount >= maxCuesPerChunk;
|
|
801
|
+
const wouldExceedTokenCount = currentCueCount > 0 && currentTokenCount + cueTokenCount > maxTextTokensPerChunk;
|
|
802
|
+
if (wouldExceedCueCount || wouldExceedTokenCount) {
|
|
803
|
+
chunks.push({
|
|
804
|
+
id: `chunk-${chunkIndex}`,
|
|
805
|
+
cueStartIndex,
|
|
806
|
+
cueEndIndex: cueIndex - 1,
|
|
807
|
+
cueCount: cueIndex - cueStartIndex,
|
|
808
|
+
startTime: cues[cueStartIndex].startTime,
|
|
809
|
+
endTime: cues[cueIndex - 1].endTime
|
|
810
|
+
});
|
|
811
|
+
cueStartIndex = cueIndex;
|
|
812
|
+
currentTokenCount = 0;
|
|
813
|
+
chunkIndex++;
|
|
814
|
+
}
|
|
815
|
+
currentTokenCount += cueTokenCount;
|
|
816
|
+
}
|
|
817
|
+
chunks.push({
|
|
818
|
+
id: `chunk-${chunkIndex}`,
|
|
819
|
+
cueStartIndex,
|
|
820
|
+
cueEndIndex: cues.length - 1,
|
|
821
|
+
cueCount: cues.length - cueStartIndex,
|
|
822
|
+
startTime: cues[cueStartIndex].startTime,
|
|
823
|
+
endTime: cues[cues.length - 1].endTime
|
|
824
|
+
});
|
|
825
|
+
return chunks;
|
|
826
|
+
}
|
|
827
|
+
function chunkVTTCuesByDuration(cues, options) {
|
|
828
|
+
if (cues.length === 0) {
|
|
829
|
+
return [];
|
|
830
|
+
}
|
|
831
|
+
const targetChunkDurationSeconds = Math.max(1, options.targetChunkDurationSeconds);
|
|
832
|
+
const maxChunkDurationSeconds = Math.max(targetChunkDurationSeconds, options.maxChunkDurationSeconds);
|
|
833
|
+
const minChunkDurationSeconds = Math.min(
|
|
834
|
+
targetChunkDurationSeconds,
|
|
835
|
+
Math.max(
|
|
836
|
+
1,
|
|
837
|
+
options.minChunkDurationSeconds ?? Math.floor(targetChunkDurationSeconds * DEFAULT_MIN_CHUNK_DURATION_RATIO)
|
|
838
|
+
)
|
|
839
|
+
);
|
|
840
|
+
const boundaryLookaheadCues = Math.max(1, options.boundaryLookaheadCues ?? DEFAULT_BOUNDARY_LOOKAHEAD_CUES);
|
|
841
|
+
const boundaryPauseSeconds = options.boundaryPauseSeconds ?? DEFAULT_BOUNDARY_PAUSE_SECONDS;
|
|
842
|
+
const preferredBoundaryStartSeconds = Math.max(
|
|
843
|
+
minChunkDurationSeconds,
|
|
844
|
+
targetChunkDurationSeconds - Math.min(PREFERRED_BOUNDARY_WINDOW_SECONDS, targetChunkDurationSeconds / 6)
|
|
845
|
+
);
|
|
846
|
+
const chunks = [];
|
|
847
|
+
let chunkIndex = 0;
|
|
848
|
+
let cueStartIndex = 0;
|
|
849
|
+
while (cueStartIndex < cues.length) {
|
|
850
|
+
const chunkStartTime = cues[cueStartIndex].startTime;
|
|
851
|
+
let cueEndIndex = cueStartIndex;
|
|
852
|
+
let bestBoundaryIndex = -1;
|
|
853
|
+
let bestBoundaryScore = -1;
|
|
854
|
+
let bestPreferredBoundaryIndex = -1;
|
|
855
|
+
let bestPreferredBoundaryScore = -1;
|
|
856
|
+
while (cueEndIndex < cues.length) {
|
|
857
|
+
const cue = cues[cueEndIndex];
|
|
858
|
+
const currentDuration = cue.endTime - chunkStartTime;
|
|
859
|
+
if (currentDuration >= minChunkDurationSeconds) {
|
|
860
|
+
const boundaryScore = scoreCueBoundary(cues, cueEndIndex, boundaryPauseSeconds);
|
|
861
|
+
if (boundaryScore >= bestBoundaryScore) {
|
|
862
|
+
bestBoundaryIndex = cueEndIndex;
|
|
863
|
+
bestBoundaryScore = boundaryScore;
|
|
864
|
+
}
|
|
865
|
+
if (currentDuration >= preferredBoundaryStartSeconds && boundaryScore >= bestPreferredBoundaryScore) {
|
|
866
|
+
bestPreferredBoundaryIndex = cueEndIndex;
|
|
867
|
+
bestPreferredBoundaryScore = boundaryScore;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
const nextCue = cues[cueEndIndex + 1];
|
|
871
|
+
if (!nextCue) {
|
|
872
|
+
break;
|
|
873
|
+
}
|
|
874
|
+
const nextDuration = nextCue.endTime - chunkStartTime;
|
|
875
|
+
const lookaheadExceeded = cueEndIndex - cueStartIndex >= boundaryLookaheadCues;
|
|
876
|
+
const preferredBoundaryIndex = bestPreferredBoundaryIndex >= cueStartIndex ? bestPreferredBoundaryIndex : bestBoundaryIndex;
|
|
877
|
+
const preferredBoundaryScore = bestPreferredBoundaryIndex >= cueStartIndex ? bestPreferredBoundaryScore : bestBoundaryScore;
|
|
878
|
+
if (currentDuration >= targetChunkDurationSeconds) {
|
|
879
|
+
if (preferredBoundaryIndex >= cueStartIndex && preferredBoundaryScore >= STRONG_BOUNDARY_SCORE) {
|
|
880
|
+
cueEndIndex = preferredBoundaryIndex;
|
|
881
|
+
break;
|
|
882
|
+
}
|
|
883
|
+
if (nextDuration > maxChunkDurationSeconds || lookaheadExceeded) {
|
|
884
|
+
cueEndIndex = preferredBoundaryIndex >= cueStartIndex ? preferredBoundaryIndex : cueEndIndex;
|
|
885
|
+
break;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if (nextDuration > maxChunkDurationSeconds) {
|
|
889
|
+
cueEndIndex = preferredBoundaryIndex >= cueStartIndex ? preferredBoundaryIndex : cueEndIndex;
|
|
890
|
+
break;
|
|
891
|
+
}
|
|
892
|
+
cueEndIndex++;
|
|
893
|
+
}
|
|
894
|
+
chunks.push({
|
|
895
|
+
id: `chunk-${chunkIndex}`,
|
|
896
|
+
cueStartIndex,
|
|
897
|
+
cueEndIndex,
|
|
898
|
+
cueCount: cueEndIndex - cueStartIndex + 1,
|
|
899
|
+
startTime: cues[cueStartIndex].startTime,
|
|
900
|
+
endTime: cues[cueEndIndex].endTime
|
|
901
|
+
});
|
|
902
|
+
cueStartIndex = cueEndIndex + 1;
|
|
903
|
+
chunkIndex++;
|
|
904
|
+
}
|
|
905
|
+
return chunks;
|
|
906
|
+
}
|
|
754
907
|
function chunkText(text, strategy) {
|
|
755
908
|
switch (strategy.type) {
|
|
756
909
|
case "token": {
|
|
@@ -792,10 +945,8 @@ async function getThumbnailUrls(playbackId, duration, options = {}) {
|
|
|
792
945
|
}
|
|
793
946
|
const baseUrl = getMuxThumbnailBaseUrl(playbackId);
|
|
794
947
|
const urlPromises = timestamps.map(async (time) => {
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
}
|
|
798
|
-
return `${baseUrl}?time=${time}&width=${width}`;
|
|
948
|
+
const url = shouldSign ? await signUrl(baseUrl, playbackId, "thumbnail", { time, width }, credentials) : `${baseUrl}?time=${time}&width=${width}`;
|
|
949
|
+
return { url, time };
|
|
799
950
|
});
|
|
800
951
|
return Promise.all(urlPromises);
|
|
801
952
|
}
|
|
@@ -817,24 +968,82 @@ function findCaptionTrack(asset, languageCode) {
|
|
|
817
968
|
(track) => track.text_type === "subtitles" && track.language_code === languageCode
|
|
818
969
|
);
|
|
819
970
|
}
|
|
971
|
+
function normalizeLineEndings(value) {
|
|
972
|
+
return value.replace(/\r\n/g, "\n");
|
|
973
|
+
}
|
|
974
|
+
function isTimingLine(line) {
|
|
975
|
+
return line.includes("-->");
|
|
976
|
+
}
|
|
977
|
+
function parseNumericCueIdentifier(line) {
|
|
978
|
+
if (!/^\d+$/.test(line)) {
|
|
979
|
+
return null;
|
|
980
|
+
}
|
|
981
|
+
return Number.parseInt(line, 10);
|
|
982
|
+
}
|
|
983
|
+
function isLikelyTitledCueIdentifier(line) {
|
|
984
|
+
return /^\d+\s+-\s+\S.*$/.test(line);
|
|
985
|
+
}
|
|
986
|
+
function isLikelyCueIdentifier({
|
|
987
|
+
line,
|
|
988
|
+
nextLine,
|
|
989
|
+
previousCueIdentifier
|
|
990
|
+
}) {
|
|
991
|
+
if (!line || !nextLine || !isTimingLine(nextLine)) {
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
994
|
+
const numericIdentifier = parseNumericCueIdentifier(line);
|
|
995
|
+
if (numericIdentifier !== null) {
|
|
996
|
+
if (previousCueIdentifier === null || previousCueIdentifier === void 0) {
|
|
997
|
+
return numericIdentifier === 1;
|
|
998
|
+
}
|
|
999
|
+
return numericIdentifier === previousCueIdentifier + 1;
|
|
1000
|
+
}
|
|
1001
|
+
return isLikelyTitledCueIdentifier(line);
|
|
1002
|
+
}
|
|
1003
|
+
function getCueIdentifierLineIndex(lines, timingLineIndex, previousCueIdentifier) {
|
|
1004
|
+
const identifierIndex = timingLineIndex - 1;
|
|
1005
|
+
if (identifierIndex < 0) {
|
|
1006
|
+
return -1;
|
|
1007
|
+
}
|
|
1008
|
+
const candidate = lines[identifierIndex].trim();
|
|
1009
|
+
if (!candidate || isTimingLine(candidate)) {
|
|
1010
|
+
return -1;
|
|
1011
|
+
}
|
|
1012
|
+
return isLikelyCueIdentifier({
|
|
1013
|
+
line: candidate,
|
|
1014
|
+
nextLine: lines[timingLineIndex]?.trim(),
|
|
1015
|
+
previousCueIdentifier
|
|
1016
|
+
}) ? identifierIndex : -1;
|
|
1017
|
+
}
|
|
820
1018
|
function extractTextFromVTT(vttContent) {
|
|
821
1019
|
if (!vttContent.trim()) {
|
|
822
1020
|
return "";
|
|
823
1021
|
}
|
|
824
1022
|
const lines = vttContent.split("\n");
|
|
825
1023
|
const textLines = [];
|
|
1024
|
+
let previousCueIdentifier = null;
|
|
1025
|
+
let isInsideNoteBlock = false;
|
|
826
1026
|
for (let i = 0; i < lines.length; i++) {
|
|
827
1027
|
const line = lines[i].trim();
|
|
828
|
-
|
|
1028
|
+
const nextLine = lines[i + 1]?.trim();
|
|
1029
|
+
if (!line) {
|
|
1030
|
+
isInsideNoteBlock = false;
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
if (isInsideNoteBlock)
|
|
829
1034
|
continue;
|
|
830
1035
|
if (line === "WEBVTT")
|
|
831
1036
|
continue;
|
|
832
|
-
if (line.startsWith("NOTE "))
|
|
1037
|
+
if (line === "NOTE" || line.startsWith("NOTE ")) {
|
|
1038
|
+
isInsideNoteBlock = true;
|
|
833
1039
|
continue;
|
|
834
|
-
|
|
1040
|
+
}
|
|
1041
|
+
if (isTimingLine(line))
|
|
835
1042
|
continue;
|
|
836
|
-
if (
|
|
1043
|
+
if (isLikelyCueIdentifier({ line, nextLine, previousCueIdentifier })) {
|
|
1044
|
+
previousCueIdentifier = parseNumericCueIdentifier(line);
|
|
837
1045
|
continue;
|
|
1046
|
+
}
|
|
838
1047
|
if (line.startsWith("STYLE") || line.startsWith("REGION"))
|
|
839
1048
|
continue;
|
|
840
1049
|
const cleanLine = line.replace(/<[^>]*>/g, "").trim();
|
|
@@ -893,20 +1102,34 @@ function parseVTTCues(vttContent) {
|
|
|
893
1102
|
return [];
|
|
894
1103
|
const lines = vttContent.split("\n");
|
|
895
1104
|
const cues = [];
|
|
1105
|
+
let previousCueIdentifier = null;
|
|
896
1106
|
for (let i = 0; i < lines.length; i++) {
|
|
897
1107
|
const line = lines[i].trim();
|
|
898
|
-
if (line
|
|
1108
|
+
if (isTimingLine(line)) {
|
|
899
1109
|
const [startStr, endStr] = line.split(" --> ").map((s) => s.trim());
|
|
900
1110
|
const startTime = vttTimestampToSeconds(startStr);
|
|
901
1111
|
const endTime = vttTimestampToSeconds(endStr.split(" ")[0]);
|
|
902
|
-
const
|
|
1112
|
+
const currentCueIdentifierLine = lines[i - 1]?.trim() ?? "";
|
|
1113
|
+
const currentCueIdentifier = isLikelyCueIdentifier({
|
|
1114
|
+
line: currentCueIdentifierLine,
|
|
1115
|
+
nextLine: line,
|
|
1116
|
+
previousCueIdentifier
|
|
1117
|
+
}) ? parseNumericCueIdentifier(currentCueIdentifierLine) : null;
|
|
1118
|
+
const rawTextLines = [];
|
|
903
1119
|
let j = i + 1;
|
|
904
|
-
while (j < lines.length && lines[j].trim() && !lines[j].
|
|
905
|
-
|
|
906
|
-
if (cleanLine)
|
|
907
|
-
textLines.push(cleanLine);
|
|
1120
|
+
while (j < lines.length && lines[j].trim() && !isTimingLine(lines[j].trim())) {
|
|
1121
|
+
rawTextLines.push(lines[j].trim());
|
|
908
1122
|
j++;
|
|
909
1123
|
}
|
|
1124
|
+
const trailingNumericLine = parseNumericCueIdentifier(rawTextLines.at(-1) ?? "");
|
|
1125
|
+
if (trailingNumericLine !== null && isLikelyCueIdentifier({
|
|
1126
|
+
line: rawTextLines.at(-1) ?? "",
|
|
1127
|
+
nextLine: lines[j]?.trim(),
|
|
1128
|
+
previousCueIdentifier: currentCueIdentifier
|
|
1129
|
+
}) && rawTextLines.length > 1) {
|
|
1130
|
+
rawTextLines.pop();
|
|
1131
|
+
}
|
|
1132
|
+
const textLines = rawTextLines.map((textLine) => textLine.replace(/<[^>]*>/g, "")).filter(Boolean);
|
|
910
1133
|
if (textLines.length > 0) {
|
|
911
1134
|
cues.push({
|
|
912
1135
|
startTime,
|
|
@@ -914,10 +1137,102 @@ function parseVTTCues(vttContent) {
|
|
|
914
1137
|
text: textLines.join(" ")
|
|
915
1138
|
});
|
|
916
1139
|
}
|
|
1140
|
+
previousCueIdentifier = currentCueIdentifier;
|
|
917
1141
|
}
|
|
918
1142
|
}
|
|
919
1143
|
return cues;
|
|
920
1144
|
}
|
|
1145
|
+
function splitVttPreambleAndCueBlocks(vttContent) {
|
|
1146
|
+
const normalizedContent = normalizeLineEndings(vttContent).trim();
|
|
1147
|
+
if (!normalizedContent) {
|
|
1148
|
+
return {
|
|
1149
|
+
preamble: "WEBVTT",
|
|
1150
|
+
cueBlocks: []
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
const rawBlocks = normalizedContent.split(/\n{2,}/).map((block) => block.trim()).filter(Boolean);
|
|
1154
|
+
const cueBlockStartIndex = rawBlocks.findIndex((block) => block.includes("-->"));
|
|
1155
|
+
if (cueBlockStartIndex === -1) {
|
|
1156
|
+
return {
|
|
1157
|
+
preamble: normalizedContent.startsWith("WEBVTT") ? normalizedContent : `WEBVTT
|
|
1158
|
+
|
|
1159
|
+
${normalizedContent}`,
|
|
1160
|
+
cueBlocks: []
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
const hasMergedCueBlocks = rawBlocks.slice(cueBlockStartIndex).some((block) => (block.match(/-->/g) ?? []).length > 1);
|
|
1164
|
+
if (hasMergedCueBlocks) {
|
|
1165
|
+
const lines = normalizedContent.split("\n");
|
|
1166
|
+
const timingLineIndices = lines.map((line, index) => isTimingLine(line.trim()) ? index : -1).filter((index) => index >= 0);
|
|
1167
|
+
let previousCueIdentifier = null;
|
|
1168
|
+
const firstCueStartIndex = getCueIdentifierLineIndex(lines, timingLineIndices[0], previousCueIdentifier);
|
|
1169
|
+
const preambleEndIndex = firstCueStartIndex >= 0 ? firstCueStartIndex : timingLineIndices[0];
|
|
1170
|
+
const preamble2 = lines.slice(0, preambleEndIndex).join("\n").trim() || "WEBVTT";
|
|
1171
|
+
const cueBlocks2 = timingLineIndices.map((timingLineIndex, index) => {
|
|
1172
|
+
const cueIdentifierLineIndex = getCueIdentifierLineIndex(lines, timingLineIndex, previousCueIdentifier);
|
|
1173
|
+
const cueStartIndex = cueIdentifierLineIndex >= 0 ? cueIdentifierLineIndex : timingLineIndex;
|
|
1174
|
+
const currentCueIdentifier = cueIdentifierLineIndex >= 0 ? parseNumericCueIdentifier(lines[cueIdentifierLineIndex].trim()) : null;
|
|
1175
|
+
const nextTimingLineIndex = timingLineIndices[index + 1] ?? lines.length;
|
|
1176
|
+
let cueEndIndex = nextTimingLineIndex - 1;
|
|
1177
|
+
while (cueEndIndex > timingLineIndex && !lines[cueEndIndex].trim()) {
|
|
1178
|
+
cueEndIndex--;
|
|
1179
|
+
}
|
|
1180
|
+
const nextCueIdentifierLineIndex = index < timingLineIndices.length - 1 ? getCueIdentifierLineIndex(lines, nextTimingLineIndex, currentCueIdentifier) : -1;
|
|
1181
|
+
if (nextCueIdentifierLineIndex === cueEndIndex) {
|
|
1182
|
+
cueEndIndex--;
|
|
1183
|
+
}
|
|
1184
|
+
while (cueEndIndex > timingLineIndex && !lines[cueEndIndex].trim()) {
|
|
1185
|
+
cueEndIndex--;
|
|
1186
|
+
}
|
|
1187
|
+
previousCueIdentifier = currentCueIdentifier;
|
|
1188
|
+
return lines.slice(cueStartIndex, cueEndIndex + 1).join("\n").trim();
|
|
1189
|
+
});
|
|
1190
|
+
return {
|
|
1191
|
+
preamble: preamble2,
|
|
1192
|
+
cueBlocks: cueBlocks2
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
const preambleBlocks = rawBlocks.slice(0, cueBlockStartIndex);
|
|
1196
|
+
const cueBlocks = rawBlocks.slice(cueBlockStartIndex);
|
|
1197
|
+
const preamble = preambleBlocks.length > 0 ? preambleBlocks.join("\n\n") : "WEBVTT";
|
|
1198
|
+
return {
|
|
1199
|
+
preamble,
|
|
1200
|
+
cueBlocks
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
function buildVttFromCueBlocks(cueBlocks, preamble = "WEBVTT") {
|
|
1204
|
+
if (cueBlocks.length === 0) {
|
|
1205
|
+
return `${preamble.trim()}
|
|
1206
|
+
`;
|
|
1207
|
+
}
|
|
1208
|
+
return `${preamble.trim()}
|
|
1209
|
+
|
|
1210
|
+
${cueBlocks.map((block) => block.trim()).join("\n\n")}
|
|
1211
|
+
`;
|
|
1212
|
+
}
|
|
1213
|
+
function replaceCueText(cueBlock, translatedText) {
|
|
1214
|
+
const lines = normalizeLineEndings(cueBlock).split("\n").map((line) => line.trim()).filter(Boolean);
|
|
1215
|
+
const timingLineIndex = lines.findIndex((line) => line.includes("-->"));
|
|
1216
|
+
if (timingLineIndex === -1) {
|
|
1217
|
+
throw new Error("Cue block is missing a timestamp line");
|
|
1218
|
+
}
|
|
1219
|
+
const headerLines = lines.slice(0, timingLineIndex + 1);
|
|
1220
|
+
const translatedLines = normalizeLineEndings(translatedText).split("\n").map((line) => line.trim()).filter(Boolean);
|
|
1221
|
+
return [...headerLines, ...translatedLines].join("\n");
|
|
1222
|
+
}
|
|
1223
|
+
function buildVttFromTranslatedCueBlocks(cueBlocks, translatedTexts, preamble = "WEBVTT") {
|
|
1224
|
+
if (cueBlocks.length !== translatedTexts.length) {
|
|
1225
|
+
throw new Error(`Expected ${cueBlocks.length} translated cues, received ${translatedTexts.length}`);
|
|
1226
|
+
}
|
|
1227
|
+
return buildVttFromCueBlocks(
|
|
1228
|
+
cueBlocks.map((cueBlock, index) => replaceCueText(cueBlock, translatedTexts[index])),
|
|
1229
|
+
preamble
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
function concatenateVttSegments(segments, preamble = "WEBVTT") {
|
|
1233
|
+
const cueBlocks = segments.flatMap((segment) => splitVttPreambleAndCueBlocks(segment).cueBlocks);
|
|
1234
|
+
return buildVttFromCueBlocks(cueBlocks, preamble);
|
|
1235
|
+
}
|
|
921
1236
|
async function buildTranscriptUrl(playbackId, trackId, shouldSign = false, credentials) {
|
|
922
1237
|
"use step";
|
|
923
1238
|
const baseUrl = `https://stream.mux.com/${playbackId}/text/${trackId}.vtt`;
|
|
@@ -979,9 +1294,14 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
|
979
1294
|
export {
|
|
980
1295
|
DEFAULT_STORYBOARD_WIDTH,
|
|
981
1296
|
buildTranscriptUrl,
|
|
1297
|
+
buildVttFromCueBlocks,
|
|
1298
|
+
buildVttFromTranslatedCueBlocks,
|
|
982
1299
|
chunkByTokens,
|
|
983
1300
|
chunkText,
|
|
984
1301
|
chunkVTTCues,
|
|
1302
|
+
chunkVTTCuesByBudget,
|
|
1303
|
+
chunkVTTCuesByDuration,
|
|
1304
|
+
concatenateVttSegments,
|
|
985
1305
|
estimateTokenCount,
|
|
986
1306
|
extractTextFromVTT,
|
|
987
1307
|
extractTimestampedTranscript,
|
|
@@ -997,7 +1317,9 @@ export {
|
|
|
997
1317
|
getStoryboardUrl,
|
|
998
1318
|
getThumbnailUrls,
|
|
999
1319
|
parseVTTCues,
|
|
1320
|
+
replaceCueText,
|
|
1000
1321
|
secondsToTimestamp,
|
|
1322
|
+
splitVttPreambleAndCueBlocks,
|
|
1001
1323
|
vttTimestampToSeconds
|
|
1002
1324
|
};
|
|
1003
1325
|
//# sourceMappingURL=index.js.map
|