@mux/ai 0.52.0 → 0.53.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-sqJ9PpHw.d.ts → index-BbxJ0X25.d.ts} +154 -17
- package/dist/index-cbaPe-aE.d.ts +225 -0
- package/dist/index.d.ts +8 -5
- package/dist/index.js +441 -175
- package/dist/index.js.map +1 -1
- package/dist/primitives/index.d.ts +2 -2
- package/dist/primitives/index.js +47 -33
- package/dist/primitives/index.js.map +1 -1
- package/dist/{shots-3wmvZnUV.d.ts → transcripts-BCtzC8hZ.d.ts} +200 -1
- package/dist/workflows/index.d.ts +2 -2
- package/dist/workflows/index.js +439 -174
- package/dist/workflows/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/index-nu1UOAMW.d.ts +0 -387
package/dist/workflows/index.js
CHANGED
|
@@ -531,6 +531,9 @@ var MuxAiError = class extends Error {
|
|
|
531
531
|
super(message);
|
|
532
532
|
/** Structural brand for duck-type identification. */
|
|
533
533
|
this.__robots_error = true;
|
|
534
|
+
if (opts?.cause !== void 0) {
|
|
535
|
+
Object.defineProperty(this, "cause", { value: opts.cause, writable: true, configurable: true });
|
|
536
|
+
}
|
|
534
537
|
this.name = "FatalError";
|
|
535
538
|
this.publicType = opts?.type ?? "processing_error";
|
|
536
539
|
this.publicMessage = message;
|
|
@@ -2601,9 +2604,10 @@ function extractTimestampedTranscript(vttContent) {
|
|
|
2601
2604
|
segments.map((segment) => `[${Math.floor(segment.time)}s] ${segment.text}`).join("\n")
|
|
2602
2605
|
);
|
|
2603
2606
|
}
|
|
2604
|
-
function parseVTTCues(vttContent) {
|
|
2607
|
+
function parseVTTCues(vttContent, options = {}) {
|
|
2605
2608
|
if (!vttContent.trim())
|
|
2606
2609
|
return [];
|
|
2610
|
+
const { capCueText = true } = options;
|
|
2607
2611
|
const lines = vttContent.split("\n");
|
|
2608
2612
|
const cues = [];
|
|
2609
2613
|
let previousCueIdentifier = null;
|
|
@@ -2635,15 +2639,16 @@ function parseVTTCues(vttContent) {
|
|
|
2635
2639
|
}
|
|
2636
2640
|
const textLines = rawTextLines.map((textLine) => textLine.replace(/<[^>]*>/g, "")).filter(Boolean);
|
|
2637
2641
|
if (textLines.length > 0) {
|
|
2642
|
+
const sanitized = sanitizeUntrustedText(textLines.join(" "));
|
|
2638
2643
|
cues.push({
|
|
2639
2644
|
startTime,
|
|
2640
2645
|
endTime,
|
|
2641
2646
|
// Sanitize at the cue boundary so every downstream consumer
|
|
2642
2647
|
// (engagement-insights, translate-captions chunked path, etc.)
|
|
2643
2648
|
// receives text free of invisible / bidi obfuscation. Length-
|
|
2644
|
-
// cap the result so an adversarially oversized cue
|
|
2645
|
-
// smuggle a large injection payload into
|
|
2646
|
-
text: capCueTextLength(
|
|
2649
|
+
// cap the result by default so an adversarially oversized cue
|
|
2650
|
+
// cannot smuggle a large injection payload into a prompt.
|
|
2651
|
+
text: capCueText ? capCueTextLength(sanitized) : sanitized
|
|
2647
2652
|
});
|
|
2648
2653
|
}
|
|
2649
2654
|
previousCueIdentifier = currentCueIdentifier;
|
|
@@ -2767,16 +2772,32 @@ async function buildTranscriptUrl(playbackId, trackId, shouldSign = false, crede
|
|
|
2767
2772
|
}
|
|
2768
2773
|
return baseUrl;
|
|
2769
2774
|
}
|
|
2770
|
-
async function
|
|
2775
|
+
async function fetchTranscriptDataForUrl(transcriptUrl, options = {}) {
|
|
2771
2776
|
"use step";
|
|
2772
|
-
const {
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2777
|
+
const { cleanTranscript = true, scope } = options;
|
|
2778
|
+
try {
|
|
2779
|
+
const response = await fetch(transcriptUrl);
|
|
2780
|
+
if (!response.ok) {
|
|
2781
|
+
throw new Error(`Failed to fetch transcript (HTTP ${response.status})`);
|
|
2782
|
+
}
|
|
2783
|
+
const rawVtt = await response.text();
|
|
2784
|
+
const effectiveScope = hasWorkflowScopeBoundaries(scope) ? scope : void 0;
|
|
2785
|
+
const scopedVtt = filterVttByScope(rawVtt, effectiveScope);
|
|
2786
|
+
const transcriptText = cleanTranscript ? extractTextFromVTT(scopedVtt) : scopedVtt;
|
|
2787
|
+
const hasScopedCues = !effectiveScope || parseVTTCues(scopedVtt).length > 0;
|
|
2788
|
+
if (!transcriptText.trim() || !hasScopedCues) {
|
|
2789
|
+
return {
|
|
2790
|
+
transcriptText,
|
|
2791
|
+
emptyReason: hasScopedCues ? "empty_body" : "no_scoped_cues"
|
|
2792
|
+
};
|
|
2793
|
+
}
|
|
2794
|
+
return { transcriptText };
|
|
2795
|
+
} catch (error) {
|
|
2796
|
+
wrapError(error, "Failed to fetch transcript");
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
2800
|
+
const { required = false, languageCode, cleanTranscript, scope, shouldSign, credentials } = options;
|
|
2780
2801
|
const track = findCaptionTrack(asset, languageCode);
|
|
2781
2802
|
if (!track) {
|
|
2782
2803
|
if (required) {
|
|
@@ -2795,33 +2816,25 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
|
2795
2816
|
return { transcriptText: "", track };
|
|
2796
2817
|
}
|
|
2797
2818
|
const transcriptUrl = await buildTranscriptUrl(playbackId, track.id, shouldSign, credentials);
|
|
2819
|
+
let result;
|
|
2798
2820
|
try {
|
|
2799
|
-
|
|
2800
|
-
if (!response.ok) {
|
|
2801
|
-
if (required) {
|
|
2802
|
-
throw new Error(`Failed to fetch transcript (HTTP ${response.status})`);
|
|
2803
|
-
}
|
|
2804
|
-
return { transcriptText: "", transcriptUrl, track };
|
|
2805
|
-
}
|
|
2806
|
-
const rawVtt = await response.text();
|
|
2807
|
-
const effectiveScope = hasWorkflowScopeBoundaries(scope) ? scope : void 0;
|
|
2808
|
-
const scopedVtt = filterVttByScope(rawVtt, effectiveScope);
|
|
2809
|
-
const transcriptText = cleanTranscript ? extractTextFromVTT(scopedVtt) : scopedVtt;
|
|
2810
|
-
const hasScopedCues = !effectiveScope || parseVTTCues(scopedVtt).length > 0;
|
|
2811
|
-
if (required && (!transcriptText.trim() || !hasScopedCues)) {
|
|
2812
|
-
throw new MuxAiError(
|
|
2813
|
-
effectiveScope ? "Transcript has no cues in the requested scope." : "Transcript is empty.",
|
|
2814
|
-
{ type: "validation_error" }
|
|
2815
|
-
);
|
|
2816
|
-
}
|
|
2817
|
-
return { transcriptText, transcriptUrl, track };
|
|
2821
|
+
result = await fetchTranscriptDataForUrl(transcriptUrl, { cleanTranscript, scope });
|
|
2818
2822
|
} catch (error) {
|
|
2819
2823
|
if (required) {
|
|
2820
|
-
|
|
2824
|
+
throw error;
|
|
2821
2825
|
}
|
|
2822
2826
|
console.warn("Failed to fetch transcript:", error);
|
|
2823
2827
|
return { transcriptText: "", transcriptUrl, track };
|
|
2824
2828
|
}
|
|
2829
|
+
if (!required || !result.emptyReason) {
|
|
2830
|
+
return { transcriptText: result.transcriptText, transcriptUrl, track };
|
|
2831
|
+
}
|
|
2832
|
+
switch (result.emptyReason) {
|
|
2833
|
+
case "no_scoped_cues":
|
|
2834
|
+
throw new MuxAiError("Transcript has no cues in the requested scope.", { type: "validation_error" });
|
|
2835
|
+
case "empty_body":
|
|
2836
|
+
throw new MuxAiError("Transcript is empty.", { type: "validation_error" });
|
|
2837
|
+
}
|
|
2825
2838
|
}
|
|
2826
2839
|
|
|
2827
2840
|
// src/workflows/ask-questions.ts
|
|
@@ -6839,10 +6852,27 @@ ${renderSection({ tag: "revision_request", content: "The previous draft was empt
|
|
|
6839
6852
|
usages.push(...repaired.attempts.flatMap((item) => item.call.usage ? [item.call.usage] : []));
|
|
6840
6853
|
if (repaired.status !== "valid") {
|
|
6841
6854
|
const cause = repaired.attempts.at(-1)?.call.errorCause;
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
})
|
|
6855
|
+
let failure;
|
|
6856
|
+
if (isIncompleteGenerationError(cause)) {
|
|
6857
|
+
failure = new IncompleteGenerationError(cause.finishReason, cause.rawFinishReason);
|
|
6858
|
+
} else if (MuxAiError.is(cause)) {
|
|
6859
|
+
failure = new MuxAiError(cause.publicMessage, { type: cause.publicType, retryable: cause.retryable });
|
|
6860
|
+
} else {
|
|
6861
|
+
const apiError = APICallError3.isInstance(cause) ? cause : void 0;
|
|
6862
|
+
console.warn("[@mux/ai] Bounded text repair failed.", {
|
|
6863
|
+
provider: args.provider,
|
|
6864
|
+
modelId: args.modelId,
|
|
6865
|
+
reason: repaired.reason,
|
|
6866
|
+
errorName: cause instanceof Error ? cause.name : void 0,
|
|
6867
|
+
statusCode: apiError?.statusCode,
|
|
6868
|
+
requestId: apiError?.responseHeaders?.["x-request-id"] ?? apiError?.responseHeaders?.["request-id"]
|
|
6869
|
+
});
|
|
6870
|
+
failure = new MuxAiError("Unable to complete text generation.", {
|
|
6871
|
+
type: "processing_error",
|
|
6872
|
+
retryable: apiError?.isRetryable ?? true,
|
|
6873
|
+
cause
|
|
6874
|
+
});
|
|
6875
|
+
}
|
|
6846
6876
|
rethrowWithTokenUsage(failure, usages);
|
|
6847
6877
|
}
|
|
6848
6878
|
return { content: repaired.content, usages, unexpectedKeys };
|
|
@@ -7020,6 +7050,9 @@ async function generateTextInternal(assetId, rawOptions, collectedUsage) {
|
|
|
7020
7050
|
};
|
|
7021
7051
|
}
|
|
7022
7052
|
|
|
7053
|
+
// src/workflows/moderation.ts
|
|
7054
|
+
import { z as z9 } from "zod";
|
|
7055
|
+
|
|
7023
7056
|
// src/lib/sampling-plan.ts
|
|
7024
7057
|
var DEFAULT_FPS = 30;
|
|
7025
7058
|
function roundToNearestFrameMs(tsMs, fps = DEFAULT_FPS) {
|
|
@@ -7133,6 +7166,15 @@ var OPENAI_MODERATION_BASE_DELAY_MS = 750;
|
|
|
7133
7166
|
var OPENAI_MODERATION_MAX_DELAY_MS = 3e3;
|
|
7134
7167
|
var MIN_SAMPLE_COVERAGE_FOR_CONFIDENT_THRESHOLDING = 0.5;
|
|
7135
7168
|
var MIN_SUCCESSFUL_THUMBNAILS_FOR_CONFIDENT_THRESHOLDING = 3;
|
|
7169
|
+
var DEFAULT_TRANSCRIPT_WINDOWING = {
|
|
7170
|
+
targetWindowCount: 40,
|
|
7171
|
+
minWindowSeconds: 20,
|
|
7172
|
+
maxWindowSeconds: 120,
|
|
7173
|
+
overlapFraction: 0.15,
|
|
7174
|
+
minOverlapSeconds: 5
|
|
7175
|
+
};
|
|
7176
|
+
var TRANSCRIPT_BATCH_MAX_UTF16_CODE_UNITS = 1e5;
|
|
7177
|
+
var TRANSCRIPT_BATCH_MAX_ITEMS = 100;
|
|
7136
7178
|
var GOOGLE_VISION_ENDPOINT = "https://vision.googleapis.com/v1/images:annotate";
|
|
7137
7179
|
var GOOGLE_VISION_LIKELIHOOD_TO_SCORE = {
|
|
7138
7180
|
UNKNOWN: 0,
|
|
@@ -7200,6 +7242,14 @@ function parseOpenAIModerationResponse(bodyText, status, statusText) {
|
|
|
7200
7242
|
);
|
|
7201
7243
|
}
|
|
7202
7244
|
}
|
|
7245
|
+
var OpenAIModerationResponseSchema = z9.object({
|
|
7246
|
+
results: z9.array(z9.object({
|
|
7247
|
+
category_scores: z9.object({
|
|
7248
|
+
sexual: z9.number().finite().nullish(),
|
|
7249
|
+
violence: z9.number().finite().nullish()
|
|
7250
|
+
})
|
|
7251
|
+
}))
|
|
7252
|
+
});
|
|
7203
7253
|
async function callOpenAIModerationApi({
|
|
7204
7254
|
model,
|
|
7205
7255
|
input,
|
|
@@ -7239,7 +7289,21 @@ async function callOpenAIModerationApi({
|
|
|
7239
7289
|
}
|
|
7240
7290
|
);
|
|
7241
7291
|
}
|
|
7242
|
-
|
|
7292
|
+
const parsed = OpenAIModerationResponseSchema.safeParse(json);
|
|
7293
|
+
if (!parsed.success) {
|
|
7294
|
+
throw new OpenAIModerationRequestError(
|
|
7295
|
+
`OpenAI moderation returned an unexpected response shape: ${compactBodySnippet(bodyText)}`,
|
|
7296
|
+
{ status: res.status, retriable: false }
|
|
7297
|
+
);
|
|
7298
|
+
}
|
|
7299
|
+
const expectedResultCount = Array.isArray(input) ? input.length : 1;
|
|
7300
|
+
if (parsed.data.results.length !== expectedResultCount) {
|
|
7301
|
+
throw new OpenAIModerationRequestError(
|
|
7302
|
+
`OpenAI moderation returned ${parsed.data.results.length} result(s) for ${expectedResultCount} input(s).`,
|
|
7303
|
+
{ status: res.status, retriable: false }
|
|
7304
|
+
);
|
|
7305
|
+
}
|
|
7306
|
+
return parsed.data;
|
|
7243
7307
|
},
|
|
7244
7308
|
{
|
|
7245
7309
|
maxRetries: OPENAI_MODERATION_MAX_RETRIES,
|
|
@@ -7263,7 +7327,7 @@ async function processConcurrently(items, processor, maxConcurrent = 5) {
|
|
|
7263
7327
|
async function moderateImageWithOpenAI(entry) {
|
|
7264
7328
|
"use step";
|
|
7265
7329
|
try {
|
|
7266
|
-
const
|
|
7330
|
+
const { results } = await callOpenAIModerationApi({
|
|
7267
7331
|
model: entry.model,
|
|
7268
7332
|
input: [
|
|
7269
7333
|
{
|
|
@@ -7275,12 +7339,12 @@ async function moderateImageWithOpenAI(entry) {
|
|
|
7275
7339
|
],
|
|
7276
7340
|
credentials: entry.credentials
|
|
7277
7341
|
});
|
|
7278
|
-
const
|
|
7342
|
+
const [result] = results;
|
|
7279
7343
|
return {
|
|
7280
7344
|
url: entry.url,
|
|
7281
7345
|
time: entry.time,
|
|
7282
|
-
sexual:
|
|
7283
|
-
violence:
|
|
7346
|
+
sexual: result.category_scores.sexual ?? 0,
|
|
7347
|
+
violence: result.category_scores.violence ?? 0,
|
|
7284
7348
|
error: false
|
|
7285
7349
|
};
|
|
7286
7350
|
} catch (error) {
|
|
@@ -7304,65 +7368,187 @@ async function requestOpenAIModeration(images, model, maxConcurrent = 5, submiss
|
|
|
7304
7368
|
) : images.map((img) => ({ url: img.url, time: img.time, image: img.url, model, credentials }));
|
|
7305
7369
|
return processConcurrently(targetUrls, moderateImageWithOpenAI, maxConcurrent);
|
|
7306
7370
|
}
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7371
|
+
var TRANSCRIPT_WINDOW_MAX_UTF16_CODE_UNITS = 1e4;
|
|
7372
|
+
function describeSurfaceFailure(itemLabel, scores) {
|
|
7373
|
+
const messages = Array.from(new Set(scores.map((s) => s.errorMessage || "Unknown error")));
|
|
7374
|
+
return `All ${scores.length} ${itemLabel}(s) failed to moderate: ${messages.join("; ")}`;
|
|
7375
|
+
}
|
|
7376
|
+
function clamp(value, min, max) {
|
|
7377
|
+
return Math.min(Math.max(value, min), max);
|
|
7378
|
+
}
|
|
7379
|
+
function splitCuesUnderCharCeiling(cues, maxUnits = TRANSCRIPT_WINDOW_MAX_UTF16_CODE_UNITS) {
|
|
7380
|
+
const windows = [];
|
|
7381
|
+
let current = [];
|
|
7382
|
+
let currentLength = 0;
|
|
7383
|
+
const flush = () => {
|
|
7384
|
+
if (current.length === 0) {
|
|
7385
|
+
return;
|
|
7386
|
+
}
|
|
7387
|
+
windows.push({
|
|
7388
|
+
startTime: Math.min(...current.map((cue) => cue.startTime)),
|
|
7389
|
+
endTime: Math.max(...current.map((cue) => cue.endTime)),
|
|
7390
|
+
text: current.map((cue) => cue.text).join(" ")
|
|
7314
7391
|
});
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
sexual: 0,
|
|
7327
|
-
violence: 0,
|
|
7328
|
-
error: true,
|
|
7329
|
-
errorMessage: error instanceof Error ? error.message : String(error)
|
|
7330
|
-
};
|
|
7392
|
+
current = [];
|
|
7393
|
+
currentLength = 0;
|
|
7394
|
+
};
|
|
7395
|
+
for (const cue of cues) {
|
|
7396
|
+
const cueText = cue.text;
|
|
7397
|
+
const addedLength = currentLength === 0 ? cueText.length : currentLength + 1 + cueText.length;
|
|
7398
|
+
if (current.length > 0 && addedLength > maxUnits) {
|
|
7399
|
+
flush();
|
|
7400
|
+
}
|
|
7401
|
+
current.push(cue);
|
|
7402
|
+
currentLength = current.length === 1 ? cueText.length : currentLength + 1 + cueText.length;
|
|
7331
7403
|
}
|
|
7404
|
+
flush();
|
|
7405
|
+
return windows;
|
|
7332
7406
|
}
|
|
7333
|
-
function
|
|
7334
|
-
|
|
7407
|
+
function buildTranscriptWindows(cues, duration) {
|
|
7408
|
+
const usableCues = cues.filter((cue) => cue.text.trim().length > 0).slice().sort((a, b) => a.startTime - b.startTime);
|
|
7409
|
+
if (usableCues.length === 0) {
|
|
7335
7410
|
return [];
|
|
7336
7411
|
}
|
|
7337
|
-
|
|
7338
|
-
|
|
7412
|
+
const lastCueEnd = Math.max(...usableCues.map((cue) => cue.endTime));
|
|
7413
|
+
const effectiveDuration = duration && duration > 0 ? duration : lastCueEnd;
|
|
7414
|
+
const { targetWindowCount, minWindowSeconds, maxWindowSeconds, overlapFraction, minOverlapSeconds } = DEFAULT_TRANSCRIPT_WINDOWING;
|
|
7415
|
+
const windowSeconds = clamp(
|
|
7416
|
+
effectiveDuration / targetWindowCount,
|
|
7417
|
+
minWindowSeconds,
|
|
7418
|
+
maxWindowSeconds
|
|
7419
|
+
);
|
|
7420
|
+
const overlapSeconds = Math.max(
|
|
7421
|
+
minOverlapSeconds,
|
|
7422
|
+
windowSeconds * overlapFraction
|
|
7423
|
+
);
|
|
7424
|
+
const stride = Math.max(windowSeconds - overlapSeconds, 1);
|
|
7425
|
+
const rawWindows = [];
|
|
7426
|
+
for (let k = 0; ; k++) {
|
|
7427
|
+
const windowStart = k * stride;
|
|
7428
|
+
if (windowStart > lastCueEnd) {
|
|
7429
|
+
break;
|
|
7430
|
+
}
|
|
7431
|
+
const windowEnd = windowStart + windowSeconds;
|
|
7432
|
+
const windowCues = usableCues.filter(
|
|
7433
|
+
(cue) => cue.startTime < windowEnd && cue.endTime > windowStart
|
|
7434
|
+
);
|
|
7435
|
+
if (windowCues.length > 0) {
|
|
7436
|
+
rawWindows.push(windowCues);
|
|
7437
|
+
}
|
|
7438
|
+
if (stride <= 0) {
|
|
7439
|
+
break;
|
|
7440
|
+
}
|
|
7339
7441
|
}
|
|
7340
|
-
const
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7442
|
+
const dedupedCueGroups = [];
|
|
7443
|
+
const cueGroupKey = (group) => group.map((cue) => `${cue.startTime}:${cue.endTime}`).join("|");
|
|
7444
|
+
let previousKey;
|
|
7445
|
+
for (const group of rawWindows) {
|
|
7446
|
+
const key = cueGroupKey(group);
|
|
7447
|
+
if (key === previousKey) {
|
|
7448
|
+
continue;
|
|
7345
7449
|
}
|
|
7450
|
+
dedupedCueGroups.push(group);
|
|
7451
|
+
previousKey = key;
|
|
7346
7452
|
}
|
|
7347
|
-
|
|
7453
|
+
const windows = [];
|
|
7454
|
+
for (const group of dedupedCueGroups) {
|
|
7455
|
+
const joinedLength = group.reduce(
|
|
7456
|
+
(sum, cue, index) => sum + cue.text.length + (index === 0 ? 0 : 1),
|
|
7457
|
+
0
|
|
7458
|
+
);
|
|
7459
|
+
if (joinedLength > TRANSCRIPT_WINDOW_MAX_UTF16_CODE_UNITS) {
|
|
7460
|
+
windows.push(...splitCuesUnderCharCeiling(group));
|
|
7461
|
+
} else {
|
|
7462
|
+
windows.push({
|
|
7463
|
+
startTime: Math.min(...group.map((cue) => cue.startTime)),
|
|
7464
|
+
endTime: Math.max(...group.map((cue) => cue.endTime)),
|
|
7465
|
+
text: group.map((cue) => cue.text).join(" ")
|
|
7466
|
+
});
|
|
7467
|
+
}
|
|
7468
|
+
}
|
|
7469
|
+
return windows;
|
|
7470
|
+
}
|
|
7471
|
+
function packWindowsIntoBatches(windows) {
|
|
7472
|
+
const batches = [];
|
|
7473
|
+
let current = [];
|
|
7474
|
+
let currentLength = 0;
|
|
7475
|
+
for (const window of windows) {
|
|
7476
|
+
const wouldExceedChars = current.length > 0 && currentLength + window.text.length > TRANSCRIPT_BATCH_MAX_UTF16_CODE_UNITS;
|
|
7477
|
+
const wouldExceedItems = current.length >= TRANSCRIPT_BATCH_MAX_ITEMS;
|
|
7478
|
+
if (wouldExceedChars || wouldExceedItems) {
|
|
7479
|
+
batches.push(current);
|
|
7480
|
+
current = [];
|
|
7481
|
+
currentLength = 0;
|
|
7482
|
+
}
|
|
7483
|
+
current.push(window);
|
|
7484
|
+
currentLength += window.text.length;
|
|
7485
|
+
}
|
|
7486
|
+
if (current.length > 0) {
|
|
7487
|
+
batches.push(current);
|
|
7488
|
+
}
|
|
7489
|
+
return batches;
|
|
7490
|
+
}
|
|
7491
|
+
function transcriptErrorScore(window, error) {
|
|
7492
|
+
return {
|
|
7493
|
+
startTime: window.startTime,
|
|
7494
|
+
endTime: window.endTime,
|
|
7495
|
+
sexual: 0,
|
|
7496
|
+
violence: 0,
|
|
7497
|
+
error: true,
|
|
7498
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
7499
|
+
};
|
|
7348
7500
|
}
|
|
7349
|
-
async function
|
|
7501
|
+
async function moderateTranscriptBatchWithOpenAI(batch) {
|
|
7350
7502
|
"use step";
|
|
7351
|
-
const
|
|
7352
|
-
if (
|
|
7353
|
-
return [
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7503
|
+
const { windows, model, credentials } = batch;
|
|
7504
|
+
if (windows.length === 0) {
|
|
7505
|
+
return [];
|
|
7506
|
+
}
|
|
7507
|
+
try {
|
|
7508
|
+
const { results } = await callOpenAIModerationApi({
|
|
7509
|
+
model,
|
|
7510
|
+
input: windows.map((window) => window.text),
|
|
7511
|
+
credentials
|
|
7512
|
+
});
|
|
7513
|
+
return windows.map((window, index) => {
|
|
7514
|
+
const result = results[index];
|
|
7515
|
+
return {
|
|
7516
|
+
startTime: window.startTime,
|
|
7517
|
+
endTime: window.endTime,
|
|
7518
|
+
sexual: result.category_scores.sexual ?? 0,
|
|
7519
|
+
violence: result.category_scores.violence ?? 0,
|
|
7520
|
+
error: false
|
|
7521
|
+
};
|
|
7522
|
+
});
|
|
7523
|
+
} catch (error) {
|
|
7524
|
+
const status = error instanceof OpenAIModerationRequestError ? error.status : void 0;
|
|
7525
|
+
if (status === 400 && windows.length > 1) {
|
|
7526
|
+
const mid = Math.ceil(windows.length / 2);
|
|
7527
|
+
const [left, right] = await Promise.all([
|
|
7528
|
+
moderateTranscriptBatchWithOpenAI({ windows: windows.slice(0, mid), model, credentials }),
|
|
7529
|
+
moderateTranscriptBatchWithOpenAI({ windows: windows.slice(mid), model, credentials })
|
|
7530
|
+
]);
|
|
7531
|
+
return [...left, ...right];
|
|
7532
|
+
}
|
|
7533
|
+
console.error("OpenAI transcript moderation failed:", error);
|
|
7534
|
+
return windows.map((window) => transcriptErrorScore(window, error));
|
|
7535
|
+
}
|
|
7536
|
+
}
|
|
7537
|
+
async function requestOpenAITranscriptModeration(cues, duration, model, maxConcurrent = 5, credentials) {
|
|
7538
|
+
"use step";
|
|
7539
|
+
const windows = buildTranscriptWindows(cues, duration);
|
|
7540
|
+
if (!windows.length) {
|
|
7541
|
+
return [];
|
|
7542
|
+
}
|
|
7543
|
+
const batches = packWindowsIntoBatches(windows).map(
|
|
7544
|
+
(windowBatch) => ({ windows: windowBatch, model, credentials })
|
|
7545
|
+
);
|
|
7546
|
+
const batchResults = await processConcurrently(
|
|
7547
|
+
batches,
|
|
7548
|
+
moderateTranscriptBatchWithOpenAI,
|
|
7364
7549
|
maxConcurrent
|
|
7365
7550
|
);
|
|
7551
|
+
return batchResults.flat();
|
|
7366
7552
|
}
|
|
7367
7553
|
function getHiveCategoryScores(classes, categoryNames) {
|
|
7368
7554
|
const scoreMap = Object.fromEntries(
|
|
@@ -7585,6 +7771,28 @@ async function getThumbnailUrlsFromTimestamps(playbackId, timestampsMs, options)
|
|
|
7585
7771
|
});
|
|
7586
7772
|
return Promise.all(urlPromises);
|
|
7587
7773
|
}
|
|
7774
|
+
async function requestThumbnailModeration(provider, thumbnailUrls, options) {
|
|
7775
|
+
const { model, maxConcurrent, imageSubmissionMode, imageDownloadOptions, credentials } = options;
|
|
7776
|
+
switch (provider) {
|
|
7777
|
+
case "openai":
|
|
7778
|
+
return requestOpenAIModeration(
|
|
7779
|
+
thumbnailUrls,
|
|
7780
|
+
model || "omni-moderation-latest",
|
|
7781
|
+
maxConcurrent,
|
|
7782
|
+
imageSubmissionMode,
|
|
7783
|
+
imageDownloadOptions,
|
|
7784
|
+
credentials
|
|
7785
|
+
);
|
|
7786
|
+
case "hive":
|
|
7787
|
+
return requestHiveModeration(thumbnailUrls, maxConcurrent, imageSubmissionMode, imageDownloadOptions, credentials);
|
|
7788
|
+
case "google-vision-api":
|
|
7789
|
+
return requestGoogleVisionModeration(thumbnailUrls, maxConcurrent, imageSubmissionMode, imageDownloadOptions, credentials);
|
|
7790
|
+
default: {
|
|
7791
|
+
const exhaustiveCheck = provider;
|
|
7792
|
+
throw new Error(`Unsupported moderation provider: ${exhaustiveCheck}`);
|
|
7793
|
+
}
|
|
7794
|
+
}
|
|
7795
|
+
}
|
|
7588
7796
|
async function getModerationScores(assetId, options = {}) {
|
|
7589
7797
|
"use workflow";
|
|
7590
7798
|
const {
|
|
@@ -7617,47 +7825,31 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
7617
7825
|
startTime: Math.min(resolvedScope.startTime, duration),
|
|
7618
7826
|
endTime: Math.min(resolvedScope.endTime, duration)
|
|
7619
7827
|
} : void 0;
|
|
7620
|
-
if (!isAudioOnly && renderableScope && renderableScope.startTime >= renderableScope.endTime) {
|
|
7621
|
-
throw new Error("The requested scope does not include any renderable video.");
|
|
7622
|
-
}
|
|
7623
7828
|
const signingContext = await resolveMuxSigningContext(credentials);
|
|
7624
7829
|
if (policy === "signed" && !signingContext) {
|
|
7625
7830
|
throw new Error(
|
|
7626
7831
|
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
7627
7832
|
);
|
|
7628
7833
|
}
|
|
7629
|
-
let
|
|
7630
|
-
let
|
|
7631
|
-
let
|
|
7834
|
+
let thumbnailModeration = { status: "completed" };
|
|
7835
|
+
let thumbnailUrls = [];
|
|
7836
|
+
let shouldRunThumbnails = false;
|
|
7632
7837
|
if (isAudioOnly) {
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
transcriptResult.transcriptText,
|
|
7645
|
-
model || "omni-moderation-latest",
|
|
7646
|
-
maxConcurrent,
|
|
7647
|
-
credentials
|
|
7648
|
-
);
|
|
7649
|
-
} else if (provider === "hive") {
|
|
7650
|
-
throw new Error("Hive does not support transcript moderation in this workflow. Use provider: 'openai' for audio-only assets.");
|
|
7651
|
-
} else if (provider === "google-vision-api") {
|
|
7652
|
-
throw new Error("google-vision-api is image-only and does not support transcript moderation. Use provider: 'openai' for audio-only assets.");
|
|
7653
|
-
} else {
|
|
7654
|
-
const exhaustiveCheck = provider;
|
|
7655
|
-
throw new Error(`Unsupported moderation provider: ${exhaustiveCheck}`);
|
|
7656
|
-
}
|
|
7838
|
+
thumbnailModeration = {
|
|
7839
|
+
status: "skipped",
|
|
7840
|
+
skipReason: "no_video_track",
|
|
7841
|
+
skipMessage: "Asset has no video track, so there are no thumbnails to moderate."
|
|
7842
|
+
};
|
|
7843
|
+
} else if (renderableScope && renderableScope.startTime >= renderableScope.endTime) {
|
|
7844
|
+
thumbnailModeration = {
|
|
7845
|
+
status: "skipped",
|
|
7846
|
+
skipReason: "no_video_in_scope",
|
|
7847
|
+
skipMessage: "The requested scope does not include any renderable video, so there are no thumbnails to moderate."
|
|
7848
|
+
};
|
|
7657
7849
|
} else {
|
|
7658
7850
|
const scopedDuration = renderableScope ? renderableScope.endTime - renderableScope.startTime : duration;
|
|
7659
7851
|
const estimatedIntervalCount = scopedDuration <= 50 ? 5 : Math.ceil(scopedDuration / thumbnailInterval);
|
|
7660
|
-
|
|
7852
|
+
thumbnailUrls = maxSamples !== void 0 && estimatedIntervalCount > maxSamples ? await getThumbnailUrlsFromTimestamps(
|
|
7661
7853
|
playbackId,
|
|
7662
7854
|
filterTimestampsWithinExclusiveScope(
|
|
7663
7855
|
planSamplingTimestamps({
|
|
@@ -7682,70 +7874,141 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
7682
7874
|
credentials,
|
|
7683
7875
|
scope: renderableScope
|
|
7684
7876
|
});
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7877
|
+
shouldRunThumbnails = true;
|
|
7878
|
+
}
|
|
7879
|
+
let transcriptModeration = { status: "completed" };
|
|
7880
|
+
let transcriptCues = [];
|
|
7881
|
+
let shouldRunTranscript = false;
|
|
7882
|
+
const captionTrack = provider === "openai" ? findCaptionTrack(asset, languageCode) : void 0;
|
|
7883
|
+
if (provider !== "openai") {
|
|
7884
|
+
transcriptModeration = {
|
|
7885
|
+
status: "skipped",
|
|
7886
|
+
skipReason: "unsupported_provider",
|
|
7887
|
+
skipMessage: `Provider '${provider}' is image-only and cannot moderate transcript text; use provider 'openai'.`
|
|
7888
|
+
};
|
|
7889
|
+
} else if (!captionTrack) {
|
|
7890
|
+
transcriptModeration = {
|
|
7891
|
+
status: "skipped",
|
|
7892
|
+
skipReason: "no_ready_text_track",
|
|
7893
|
+
skipMessage: languageCode ? `No ready caption/subtitle track found for language '${languageCode}'.` : "No ready caption/subtitle track found for this asset."
|
|
7894
|
+
};
|
|
7895
|
+
} else {
|
|
7896
|
+
if (!captionTrack.id) {
|
|
7897
|
+
throw new MuxAiError("Transcript track is missing an id.", { type: "validation_error" });
|
|
7898
|
+
}
|
|
7899
|
+
const transcriptUrl = await buildTranscriptUrl(playbackId, captionTrack.id, policy === "signed", credentials);
|
|
7900
|
+
const { transcriptText, emptyReason } = await fetchTranscriptDataForUrl(transcriptUrl, {
|
|
7901
|
+
cleanTranscript: false,
|
|
7902
|
+
scope: effectiveScope
|
|
7903
|
+
});
|
|
7904
|
+
if (emptyReason) {
|
|
7905
|
+
transcriptModeration = {
|
|
7906
|
+
status: "skipped",
|
|
7907
|
+
skipReason: "no_cues",
|
|
7908
|
+
skipMessage: emptyReason === "no_scoped_cues" ? "Transcript has no cues in the requested scope." : "Transcript is empty."
|
|
7909
|
+
};
|
|
7711
7910
|
} else {
|
|
7712
|
-
|
|
7713
|
-
|
|
7911
|
+
transcriptCues = parseVTTCues(transcriptText, { capCueText: false });
|
|
7912
|
+
const windows = transcriptCues.length === 0 ? [] : buildTranscriptWindows(transcriptCues, duration);
|
|
7913
|
+
if (windows.length === 0) {
|
|
7914
|
+
transcriptModeration = {
|
|
7915
|
+
status: "skipped",
|
|
7916
|
+
skipReason: "no_cues",
|
|
7917
|
+
skipMessage: "Caption track had no parseable cues."
|
|
7918
|
+
};
|
|
7919
|
+
} else {
|
|
7920
|
+
shouldRunTranscript = true;
|
|
7921
|
+
}
|
|
7714
7922
|
}
|
|
7715
7923
|
}
|
|
7716
|
-
|
|
7717
|
-
|
|
7924
|
+
if (!shouldRunThumbnails && !shouldRunTranscript) {
|
|
7925
|
+
throw new MuxAiError(
|
|
7926
|
+
`Nothing to moderate. Thumbnails skipped: ${thumbnailModeration.skipMessage} Transcript skipped: ${transcriptModeration.skipMessage}`,
|
|
7927
|
+
{ type: "validation_error" }
|
|
7928
|
+
);
|
|
7929
|
+
}
|
|
7930
|
+
const [thumbnailScores, transcriptScores] = await Promise.all([
|
|
7931
|
+
shouldRunThumbnails ? requestThumbnailModeration(provider, thumbnailUrls, {
|
|
7932
|
+
model,
|
|
7933
|
+
maxConcurrent,
|
|
7934
|
+
imageSubmissionMode,
|
|
7935
|
+
imageDownloadOptions,
|
|
7936
|
+
credentials
|
|
7937
|
+
}) : Promise.resolve([]),
|
|
7938
|
+
shouldRunTranscript ? requestOpenAITranscriptModeration(
|
|
7939
|
+
transcriptCues,
|
|
7940
|
+
duration,
|
|
7941
|
+
model || "omni-moderation-latest",
|
|
7942
|
+
maxConcurrent,
|
|
7943
|
+
credentials
|
|
7944
|
+
) : Promise.resolve([])
|
|
7945
|
+
]);
|
|
7946
|
+
const mode = shouldRunThumbnails && shouldRunTranscript ? "combined" : shouldRunTranscript ? "transcript" : "thumbnails";
|
|
7947
|
+
const thumbnailCount = shouldRunThumbnails ? thumbnailUrls.length : void 0;
|
|
7948
|
+
const allScores = [
|
|
7949
|
+
...thumbnailScores.map((s) => ({ ...s, label: s.url })),
|
|
7950
|
+
...transcriptScores.map((s) => ({ ...s, error: s.error ?? false, label: `transcript window ${s.startTime}-${s.endTime}s` }))
|
|
7951
|
+
];
|
|
7952
|
+
const failed = allScores.filter((s) => s.error);
|
|
7953
|
+
const successful = allScores.filter((s) => !s.error);
|
|
7718
7954
|
if (successful.length === 0) {
|
|
7719
|
-
const details = failed.map((s) => `${s.
|
|
7955
|
+
const details = failed.map((s) => `${s.label}: ${s.errorMessage || "Unknown error"}`).join("; ");
|
|
7720
7956
|
throw new Error(
|
|
7721
|
-
`Moderation failed for all ${
|
|
7957
|
+
`Moderation failed for all ${allScores.length} sample(s): ${details}`
|
|
7722
7958
|
);
|
|
7723
7959
|
}
|
|
7724
7960
|
if (failed.length > 0) {
|
|
7725
7961
|
console.warn(
|
|
7726
|
-
`Moderation had partial failures (${failed.length}/${
|
|
7962
|
+
`Moderation had partial failures (${failed.length}/${allScores.length}); continuing with successful samples.`
|
|
7727
7963
|
);
|
|
7728
7964
|
}
|
|
7965
|
+
const thumbnailsFailed = shouldRunThumbnails && thumbnailScores.length > 0 && thumbnailScores.every((s) => s.error);
|
|
7966
|
+
const transcriptFailed = shouldRunTranscript && transcriptScores.length > 0 && transcriptScores.every((s) => s.error);
|
|
7967
|
+
if (thumbnailsFailed) {
|
|
7968
|
+
thumbnailModeration = {
|
|
7969
|
+
status: "failed",
|
|
7970
|
+
errorMessage: describeSurfaceFailure("thumbnail", thumbnailScores)
|
|
7971
|
+
};
|
|
7972
|
+
}
|
|
7973
|
+
if (transcriptFailed) {
|
|
7974
|
+
transcriptModeration = {
|
|
7975
|
+
status: "failed",
|
|
7976
|
+
errorMessage: describeSurfaceFailure("transcript window", transcriptScores)
|
|
7977
|
+
};
|
|
7978
|
+
}
|
|
7729
7979
|
const maxSexual = Math.max(...successful.map((s) => s.sexual));
|
|
7730
7980
|
const maxViolence = Math.max(...successful.map((s) => s.violence));
|
|
7731
7981
|
const finalThresholds = { ...DEFAULT_THRESHOLDS, ...thresholds };
|
|
7732
|
-
const
|
|
7733
|
-
const
|
|
7734
|
-
const
|
|
7735
|
-
const
|
|
7736
|
-
const
|
|
7737
|
-
const
|
|
7982
|
+
const coverageScores = thumbnailScores;
|
|
7983
|
+
const coverageFailed = coverageScores.filter((s) => s.error);
|
|
7984
|
+
const coverageSuccessful = coverageScores.filter((s) => !s.error);
|
|
7985
|
+
const requestedSampleCount = coverageScores.length;
|
|
7986
|
+
const successfulSampleCount = coverageSuccessful.length;
|
|
7987
|
+
const failedSampleCount = coverageFailed.length;
|
|
7988
|
+
const sampleCoverage = requestedSampleCount > 0 ? successfulSampleCount / requestedSampleCount : 0;
|
|
7989
|
+
const hasThumbnails = requestedSampleCount > 0;
|
|
7990
|
+
const hasSuccessfulTranscript = transcriptScores.some((s) => !s.error);
|
|
7991
|
+
let isLowConfidence;
|
|
7992
|
+
if (!hasThumbnails) {
|
|
7993
|
+
isLowConfidence = !hasSuccessfulTranscript;
|
|
7994
|
+
} else {
|
|
7995
|
+
const hasEnoughSuccessfulSamples = successfulSampleCount >= MIN_SUCCESSFUL_THUMBNAILS_FOR_CONFIDENT_THRESHOLDING;
|
|
7996
|
+
isLowConfidence = sampleCoverage < MIN_SAMPLE_COVERAGE_FOR_CONFIDENT_THRESHOLDING || !hasEnoughSuccessfulSamples || transcriptFailed;
|
|
7997
|
+
}
|
|
7738
7998
|
return {
|
|
7739
7999
|
assetId,
|
|
7740
8000
|
mode,
|
|
7741
8001
|
isAudioOnly,
|
|
7742
8002
|
thumbnailScores,
|
|
8003
|
+
transcriptScores,
|
|
8004
|
+
thumbnailModeration,
|
|
8005
|
+
transcriptModeration,
|
|
7743
8006
|
coverage: {
|
|
7744
8007
|
requestedSampleCount,
|
|
7745
8008
|
successfulSampleCount,
|
|
7746
8009
|
failedSampleCount,
|
|
7747
8010
|
sampleCoverage,
|
|
7748
|
-
isPartial:
|
|
8011
|
+
isPartial: failed.length > 0,
|
|
7749
8012
|
isLowConfidence
|
|
7750
8013
|
},
|
|
7751
8014
|
usage: {
|
|
@@ -7765,7 +8028,7 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
7765
8028
|
|
|
7766
8029
|
// src/workflows/summarization.ts
|
|
7767
8030
|
import { generateText as generateText8, Output as Output8 } from "ai";
|
|
7768
|
-
import { z as
|
|
8031
|
+
import { z as z10 } from "zod";
|
|
7769
8032
|
|
|
7770
8033
|
// src/lib/markdown-normalization.ts
|
|
7771
8034
|
var ESCAPED_LINE_BREAK_REGEX = /\\r\\n|\\n|\\r/g;
|
|
@@ -7910,17 +8173,17 @@ function createSummarizationGuidance({
|
|
|
7910
8173
|
}
|
|
7911
8174
|
|
|
7912
8175
|
// src/workflows/summarization.ts
|
|
7913
|
-
var summarySchema =
|
|
7914
|
-
keywords:
|
|
7915
|
-
title:
|
|
7916
|
-
description:
|
|
8176
|
+
var summarySchema = z10.object({
|
|
8177
|
+
keywords: z10.array(z10.string().max(200)),
|
|
8178
|
+
title: z10.string().max(500),
|
|
8179
|
+
description: z10.string().max(1e4)
|
|
7917
8180
|
});
|
|
7918
8181
|
function buildSummarySchema(descriptionLength) {
|
|
7919
8182
|
const descriptionMaxChars = Math.max(2e3, descriptionLength * 15);
|
|
7920
|
-
return
|
|
7921
|
-
keywords:
|
|
7922
|
-
title:
|
|
7923
|
-
description:
|
|
8183
|
+
return z10.object({
|
|
8184
|
+
keywords: z10.array(z10.string().max(200)),
|
|
8185
|
+
title: z10.string().max(500),
|
|
8186
|
+
description: z10.string().max(descriptionMaxChars)
|
|
7924
8187
|
});
|
|
7925
8188
|
}
|
|
7926
8189
|
var VALID_TONES = ["neutral", "playful", "professional"];
|
|
@@ -9091,9 +9354,9 @@ import {
|
|
|
9091
9354
|
RetryError as RetryError2,
|
|
9092
9355
|
TypeValidationError
|
|
9093
9356
|
} from "ai";
|
|
9094
|
-
import { z as
|
|
9095
|
-
var translationSchema =
|
|
9096
|
-
translation:
|
|
9357
|
+
import { z as z11 } from "zod";
|
|
9358
|
+
var translationSchema = z11.object({
|
|
9359
|
+
translation: z11.string()
|
|
9097
9360
|
});
|
|
9098
9361
|
var SYSTEM_PROMPT5 = promptDedent`
|
|
9099
9362
|
You are a subtitle translation expert. Translate VTT subtitle files to the target language specified by the user.
|
|
@@ -9472,8 +9735,8 @@ async function translateCueChunkWithAI({
|
|
|
9472
9735
|
}) {
|
|
9473
9736
|
"use step";
|
|
9474
9737
|
const model = await createLanguageModelFromConfig(provider, modelId, credentials);
|
|
9475
|
-
const schema =
|
|
9476
|
-
translations:
|
|
9738
|
+
const schema = z11.object({
|
|
9739
|
+
translations: z11.array(z11.string().min(1).max(2e3)).length(cues.length)
|
|
9477
9740
|
});
|
|
9478
9741
|
const cuePayload = cues.map((cue, index) => ({
|
|
9479
9742
|
index,
|
|
@@ -9931,6 +10194,7 @@ export {
|
|
|
9931
10194
|
DEFAULT_DESCRIPTION_LENGTH,
|
|
9932
10195
|
DEFAULT_SUMMARY_KEYWORD_LIMIT,
|
|
9933
10196
|
DEFAULT_TITLE_LENGTH,
|
|
10197
|
+
DEFAULT_TRANSCRIPT_WINDOWING,
|
|
9934
10198
|
GENERATE_TEXT_CALLS_TO_ACTION,
|
|
9935
10199
|
GENERATE_TEXT_CHANNELS,
|
|
9936
10200
|
GENERATE_TEXT_CHANNEL_CEILINGS,
|
|
@@ -9956,6 +10220,7 @@ export {
|
|
|
9956
10220
|
applyTextRepair,
|
|
9957
10221
|
askQuestions,
|
|
9958
10222
|
buildReplacementRegex,
|
|
10223
|
+
buildTranscriptWindows,
|
|
9959
10224
|
burnedInCaptionsSchema,
|
|
9960
10225
|
censorVttContent,
|
|
9961
10226
|
chapterSchema,
|