@mux/ai 0.43.0 → 0.45.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-BCyjRRuI.d.ts → index-BD4acrwt.d.ts} +3 -6
- package/dist/{index-C2Pk7bbF.d.ts → index-D6VGaoPm.d.ts} +1 -1
- package/dist/index.d.ts +14 -7
- package/dist/index.js +244 -185
- package/dist/index.js.map +1 -1
- package/dist/primitives/index.d.ts +2 -2
- package/dist/primitives/index.js +64 -23
- package/dist/primitives/index.js.map +1 -1
- package/dist/prompts/index.d.ts +77 -305
- package/dist/prompts/index.js +218 -2
- package/dist/prompts/index.js.map +1 -1
- package/dist/{shots-CUI09Ohe.d.ts → shots-B7G0IIF1.d.ts} +2 -0
- package/dist/summarization-BFmhXeAu.d.ts +351 -0
- package/dist/workflows/index.d.ts +3 -3
- package/dist/workflows/index.js +210 -155
- package/dist/workflows/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// package.json
|
|
8
|
-
var version = "0.
|
|
8
|
+
var version = "0.45.0";
|
|
9
9
|
|
|
10
10
|
// src/lib/language-codes.ts
|
|
11
11
|
var ISO639_1_TO_3 = {
|
|
@@ -204,12 +204,16 @@ var EnvSchema = z.object({
|
|
|
204
204
|
),
|
|
205
205
|
MUX_SIGNING_KEY: optionalString("Mux signing key ID for signed playback URLs.", "Used to sign playback URLs"),
|
|
206
206
|
MUX_PRIVATE_KEY: optionalString("Mux signing private key for signed playback URLs.", "Used to sign playback URLs"),
|
|
207
|
+
MUX_CUSTOM_DOMAIN: optionalString(
|
|
208
|
+
"Mux domain for image.* and stream.* playback URLs, e.g. staging.mux.com or a custom domain (defaults to mux.com).",
|
|
209
|
+
"Mux custom domain"
|
|
210
|
+
),
|
|
207
211
|
MUX_IMAGE_URL_OVERRIDE: optionalString(
|
|
208
|
-
"Override for Mux image base URL (defaults to https://image.mux.com).",
|
|
212
|
+
"Deprecated: use MUX_CUSTOM_DOMAIN. Override for Mux image base URL (defaults to https://image.mux.com).",
|
|
209
213
|
"Mux image URL override"
|
|
210
214
|
),
|
|
211
215
|
MUX_STREAM_URL_OVERRIDE: optionalString(
|
|
212
|
-
"Override for Mux stream base URL (defaults to https://stream.mux.com).",
|
|
216
|
+
"Deprecated: use MUX_CUSTOM_DOMAIN. Override for Mux stream base URL (defaults to https://stream.mux.com).",
|
|
213
217
|
"Mux stream URL override"
|
|
214
218
|
),
|
|
215
219
|
MUX_BASE_URL: optionalString(
|
|
@@ -328,7 +332,18 @@ function parseEnv() {
|
|
|
328
332
|
}
|
|
329
333
|
return parsedEnv.data;
|
|
330
334
|
}
|
|
335
|
+
var hasWarnedAboutDeprecatedUrlOverrides = false;
|
|
336
|
+
function warnOnDeprecatedUrlOverrides(parsed) {
|
|
337
|
+
if (hasWarnedAboutDeprecatedUrlOverrides || !parsed.MUX_IMAGE_URL_OVERRIDE && !parsed.MUX_STREAM_URL_OVERRIDE) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
hasWarnedAboutDeprecatedUrlOverrides = true;
|
|
341
|
+
console.warn(
|
|
342
|
+
"MUX_IMAGE_URL_OVERRIDE and MUX_STREAM_URL_OVERRIDE are deprecated and will be removed in a future release. Set MUX_CUSTOM_DOMAIN instead (e.g. MUX_CUSTOM_DOMAIN=staging.mux.com for image.staging.mux.com and stream.staging.mux.com)."
|
|
343
|
+
);
|
|
344
|
+
}
|
|
331
345
|
var env = parseEnv();
|
|
346
|
+
warnOnDeprecatedUrlOverrides(env);
|
|
332
347
|
var env_default = env;
|
|
333
348
|
|
|
334
349
|
// src/lib/prompt-fragments.ts
|
|
@@ -1354,6 +1369,82 @@ async function replaceAndCreateTextTrack(input) {
|
|
|
1354
1369
|
return replaceAndCreateTrack({ ...input, target: { ...input.target, type: "text" } });
|
|
1355
1370
|
}
|
|
1356
1371
|
|
|
1372
|
+
// src/lib/mux-url.ts
|
|
1373
|
+
var DEFAULT_MUX_DOMAIN = "mux.com";
|
|
1374
|
+
function normalizeOrigin(value, envVarName) {
|
|
1375
|
+
const trimmed = value.trim();
|
|
1376
|
+
const candidate = trimmed.includes("://") ? trimmed : `https://${trimmed}`;
|
|
1377
|
+
const exampleHostname = envVarName === "MUX_IMAGE_URL_OVERRIDE" ? "image.example.mux.com" : "stream.example.mux.com";
|
|
1378
|
+
let parsed;
|
|
1379
|
+
try {
|
|
1380
|
+
parsed = new URL(candidate);
|
|
1381
|
+
} catch {
|
|
1382
|
+
throw new Error(
|
|
1383
|
+
`Invalid ${envVarName}. Provide a hostname (e.g. "${exampleHostname}") or a URL origin (e.g. "https://${exampleHostname}").`
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
if (parsed.username || parsed.password || parsed.search || parsed.hash || parsed.pathname && parsed.pathname !== "/") {
|
|
1387
|
+
throw new Error(
|
|
1388
|
+
`Invalid ${envVarName}. Only a hostname/origin is allowed (no credentials, query params, hash fragments, or path).`
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1391
|
+
return parsed.origin;
|
|
1392
|
+
}
|
|
1393
|
+
function buildPlaybackOrigin(service, customDomain, source) {
|
|
1394
|
+
const invalidDomainError = new Error(
|
|
1395
|
+
`Invalid ${source}. Provide a bare hostname (e.g. "media.example.com") with no scheme, port, credentials, query params, hash fragments, or path.`
|
|
1396
|
+
);
|
|
1397
|
+
const trimmed = customDomain.trim();
|
|
1398
|
+
if (!trimmed) {
|
|
1399
|
+
throw invalidDomainError;
|
|
1400
|
+
}
|
|
1401
|
+
let parsed;
|
|
1402
|
+
try {
|
|
1403
|
+
parsed = new URL(`https://${service}.${trimmed}`);
|
|
1404
|
+
} catch {
|
|
1405
|
+
throw invalidDomainError;
|
|
1406
|
+
}
|
|
1407
|
+
if (parsed.port || parsed.username || parsed.password || parsed.search || parsed.hash || parsed.pathname !== "/") {
|
|
1408
|
+
throw invalidDomainError;
|
|
1409
|
+
}
|
|
1410
|
+
return parsed.origin;
|
|
1411
|
+
}
|
|
1412
|
+
function getMuxPlaybackOrigin(service, customDomain = DEFAULT_MUX_DOMAIN) {
|
|
1413
|
+
return buildPlaybackOrigin(service, customDomain, "custom domain");
|
|
1414
|
+
}
|
|
1415
|
+
async function resolveMuxPlaybackOrigin(service, credentials) {
|
|
1416
|
+
"use step";
|
|
1417
|
+
const { muxCustomDomain } = await resolveWorkflowCredentials(credentials);
|
|
1418
|
+
if (muxCustomDomain) {
|
|
1419
|
+
return buildPlaybackOrigin(service, muxCustomDomain, "muxCustomDomain");
|
|
1420
|
+
}
|
|
1421
|
+
const legacyOverrideEnvVarName = service === "image" ? "MUX_IMAGE_URL_OVERRIDE" : "MUX_STREAM_URL_OVERRIDE";
|
|
1422
|
+
const legacyOverride = env_default[legacyOverrideEnvVarName];
|
|
1423
|
+
if (legacyOverride) {
|
|
1424
|
+
return normalizeOrigin(legacyOverride, legacyOverrideEnvVarName);
|
|
1425
|
+
}
|
|
1426
|
+
if (env_default.MUX_CUSTOM_DOMAIN) {
|
|
1427
|
+
return buildPlaybackOrigin(service, env_default.MUX_CUSTOM_DOMAIN, "MUX_CUSTOM_DOMAIN");
|
|
1428
|
+
}
|
|
1429
|
+
return buildPlaybackOrigin(service, DEFAULT_MUX_DOMAIN, "custom domain");
|
|
1430
|
+
}
|
|
1431
|
+
async function getMuxImageOrigin(credentials) {
|
|
1432
|
+
return resolveMuxPlaybackOrigin("image", credentials);
|
|
1433
|
+
}
|
|
1434
|
+
async function getMuxStreamOrigin(credentials) {
|
|
1435
|
+
return resolveMuxPlaybackOrigin("stream", credentials);
|
|
1436
|
+
}
|
|
1437
|
+
async function getMuxImageBaseUrl(playbackId, assetType, credentials) {
|
|
1438
|
+
const origin = await getMuxImageOrigin(credentials);
|
|
1439
|
+
return `${origin}/${playbackId}/${assetType}.png`;
|
|
1440
|
+
}
|
|
1441
|
+
async function getMuxStoryboardBaseUrl(playbackId, credentials) {
|
|
1442
|
+
return getMuxImageBaseUrl(playbackId, "storyboard", credentials);
|
|
1443
|
+
}
|
|
1444
|
+
async function getMuxThumbnailBaseUrl(playbackId, credentials) {
|
|
1445
|
+
return getMuxImageBaseUrl(playbackId, "thumbnail", credentials);
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1357
1448
|
// node_modules/@workflow/serde/dist/index.js
|
|
1358
1449
|
var WORKFLOW_SERIALIZE = /* @__PURE__ */ Symbol.for("workflow-serialize");
|
|
1359
1450
|
var WORKFLOW_DESERIALIZE = /* @__PURE__ */ Symbol.for("workflow-deserialize");
|
|
@@ -2309,53 +2400,6 @@ async function waitForShotsForAsset(assetId, options = {}) {
|
|
|
2309
2400
|
);
|
|
2310
2401
|
}
|
|
2311
2402
|
|
|
2312
|
-
// src/lib/mux-url.ts
|
|
2313
|
-
var DEFAULT_MUX_IMAGE_ORIGIN = "https://image.mux.com";
|
|
2314
|
-
var DEFAULT_MUX_STREAM_ORIGIN = "https://stream.mux.com";
|
|
2315
|
-
function normalizeOrigin(value, envVarName) {
|
|
2316
|
-
const trimmed = value.trim();
|
|
2317
|
-
const candidate = trimmed.includes("://") ? trimmed : `https://${trimmed}`;
|
|
2318
|
-
const exampleHostname = envVarName === "MUX_IMAGE_URL_OVERRIDE" ? "image.example.mux.com" : "stream.example.mux.com";
|
|
2319
|
-
let parsed;
|
|
2320
|
-
try {
|
|
2321
|
-
parsed = new URL(candidate);
|
|
2322
|
-
} catch {
|
|
2323
|
-
throw new Error(
|
|
2324
|
-
`Invalid ${envVarName}. Provide a hostname (e.g. "${exampleHostname}") or a URL origin (e.g. "https://${exampleHostname}").`
|
|
2325
|
-
);
|
|
2326
|
-
}
|
|
2327
|
-
if (parsed.username || parsed.password || parsed.search || parsed.hash || parsed.pathname && parsed.pathname !== "/") {
|
|
2328
|
-
throw new Error(
|
|
2329
|
-
`Invalid ${envVarName}. Only a hostname/origin is allowed (no credentials, query params, hash fragments, or path).`
|
|
2330
|
-
);
|
|
2331
|
-
}
|
|
2332
|
-
return parsed.origin;
|
|
2333
|
-
}
|
|
2334
|
-
function getMuxImageOrigin() {
|
|
2335
|
-
const override = env_default.MUX_IMAGE_URL_OVERRIDE;
|
|
2336
|
-
if (!override) {
|
|
2337
|
-
return DEFAULT_MUX_IMAGE_ORIGIN;
|
|
2338
|
-
}
|
|
2339
|
-
return normalizeOrigin(override, "MUX_IMAGE_URL_OVERRIDE");
|
|
2340
|
-
}
|
|
2341
|
-
function getMuxStreamOrigin() {
|
|
2342
|
-
const override = env_default.MUX_STREAM_URL_OVERRIDE;
|
|
2343
|
-
if (!override) {
|
|
2344
|
-
return DEFAULT_MUX_STREAM_ORIGIN;
|
|
2345
|
-
}
|
|
2346
|
-
return normalizeOrigin(override, "MUX_STREAM_URL_OVERRIDE");
|
|
2347
|
-
}
|
|
2348
|
-
function getMuxImageBaseUrl(playbackId, assetType) {
|
|
2349
|
-
const origin = getMuxImageOrigin();
|
|
2350
|
-
return `${origin}/${playbackId}/${assetType}.png`;
|
|
2351
|
-
}
|
|
2352
|
-
function getMuxStoryboardBaseUrl(playbackId) {
|
|
2353
|
-
return getMuxImageBaseUrl(playbackId, "storyboard");
|
|
2354
|
-
}
|
|
2355
|
-
function getMuxThumbnailBaseUrl(playbackId) {
|
|
2356
|
-
return getMuxImageBaseUrl(playbackId, "thumbnail");
|
|
2357
|
-
}
|
|
2358
|
-
|
|
2359
2403
|
// src/lib/url-signing.ts
|
|
2360
2404
|
async function createSigningClient(context) {
|
|
2361
2405
|
const { default: MuxClient } = await import("@mux/mux-node");
|
|
@@ -2397,7 +2441,7 @@ async function signUrl(url, playbackId, type = "video", params, credentials) {
|
|
|
2397
2441
|
var DEFAULT_STORYBOARD_WIDTH = 640;
|
|
2398
2442
|
async function getStoryboardUrl(playbackId, width = DEFAULT_STORYBOARD_WIDTH, shouldSign = false, credentials, scope) {
|
|
2399
2443
|
"use step";
|
|
2400
|
-
const baseUrl = getMuxStoryboardBaseUrl(playbackId);
|
|
2444
|
+
const baseUrl = await getMuxStoryboardBaseUrl(playbackId, credentials);
|
|
2401
2445
|
const params = { width };
|
|
2402
2446
|
if (scope?.startTime !== void 0) {
|
|
2403
2447
|
params.asset_start_time = scope.startTime;
|
|
@@ -2746,7 +2790,7 @@ async function getThumbnailUrls(playbackId, duration, options = {}) {
|
|
|
2746
2790
|
}
|
|
2747
2791
|
timestamps = newTimestamps;
|
|
2748
2792
|
}
|
|
2749
|
-
const baseUrl = getMuxThumbnailBaseUrl(playbackId);
|
|
2793
|
+
const baseUrl = await getMuxThumbnailBaseUrl(playbackId, credentials);
|
|
2750
2794
|
const urlPromises = timestamps.map(async (time) => {
|
|
2751
2795
|
const url = shouldSign ? await signUrl(baseUrl, playbackId, "thumbnail", { time, width }, credentials) : `${baseUrl}?time=${time}&width=${width}`;
|
|
2752
2796
|
return { url, time };
|
|
@@ -3189,7 +3233,7 @@ function concatenateVttSegments(segments, preamble = "WEBVTT") {
|
|
|
3189
3233
|
}
|
|
3190
3234
|
async function buildTranscriptUrl(playbackId, trackId, shouldSign = false, credentials) {
|
|
3191
3235
|
"use step";
|
|
3192
|
-
const baseUrl = `${getMuxStreamOrigin()}/${playbackId}/text/${trackId}.vtt`;
|
|
3236
|
+
const baseUrl = `${await getMuxStreamOrigin(credentials)}/${playbackId}/text/${trackId}.vtt`;
|
|
3193
3237
|
if (shouldSign) {
|
|
3194
3238
|
return signUrl(baseUrl, playbackId, "video", void 0, credentials);
|
|
3195
3239
|
}
|
|
@@ -4699,8 +4743,32 @@ async function hasBurnedInCaptionsInternal(assetId, options, collectedUsage) {
|
|
|
4699
4743
|
|
|
4700
4744
|
// src/workflows/chapters.ts
|
|
4701
4745
|
import { generateText as generateText3, Output as Output3 } from "ai";
|
|
4702
|
-
import
|
|
4746
|
+
import dedent5 from "dedent";
|
|
4703
4747
|
import { z as z5 } from "zod";
|
|
4748
|
+
|
|
4749
|
+
// src/prompts/chapters.ts
|
|
4750
|
+
import dedent4 from "dedent";
|
|
4751
|
+
function createChapterGuidance({
|
|
4752
|
+
terminologySource = "transcript"
|
|
4753
|
+
} = {}) {
|
|
4754
|
+
return {
|
|
4755
|
+
qualityGuidelines: {
|
|
4756
|
+
tag: "quality_guidelines",
|
|
4757
|
+
content: dedent4`
|
|
4758
|
+
- Create chapters at topic shifts or clear transitions
|
|
4759
|
+
- Keep chapter titles concise and descriptive`
|
|
4760
|
+
},
|
|
4761
|
+
titleGuidelines: {
|
|
4762
|
+
tag: "title_guidelines",
|
|
4763
|
+
content: dedent4`
|
|
4764
|
+
- Keep titles concise and descriptive
|
|
4765
|
+
- Avoid filler or generic labels like "Chapter 1"
|
|
4766
|
+
${terminologySource === "transcript" ? "- Use the transcript's terminology" : "- Use terminology from the provided evidence"}`
|
|
4767
|
+
}
|
|
4768
|
+
};
|
|
4769
|
+
}
|
|
4770
|
+
|
|
4771
|
+
// src/workflows/chapters.ts
|
|
4704
4772
|
var chapterSchema = z5.object({
|
|
4705
4773
|
startTime: z5.number(),
|
|
4706
4774
|
title: z5.string().max(300)
|
|
@@ -4758,6 +4826,7 @@ async function generateChaptersWithAI({
|
|
|
4758
4826
|
unexpectedChapterKeys
|
|
4759
4827
|
};
|
|
4760
4828
|
}
|
|
4829
|
+
var chapterGuidance = createChapterGuidance();
|
|
4761
4830
|
var chapterSystemPromptBuilder = createPromptBuilder({
|
|
4762
4831
|
template: {
|
|
4763
4832
|
role: {
|
|
@@ -4766,13 +4835,13 @@ var chapterSystemPromptBuilder = createPromptBuilder({
|
|
|
4766
4835
|
},
|
|
4767
4836
|
context: {
|
|
4768
4837
|
tag: "context",
|
|
4769
|
-
content:
|
|
4838
|
+
content: dedent5`
|
|
4770
4839
|
You receive a timestamped transcript with lines in the form "[12s] Caption text".
|
|
4771
4840
|
Use those timestamps as anchors to determine chapter start times in seconds.`
|
|
4772
4841
|
},
|
|
4773
4842
|
security: {
|
|
4774
4843
|
tag: "security",
|
|
4775
|
-
content:
|
|
4844
|
+
content: dedent5`
|
|
4776
4845
|
${NON_DISCLOSURE_CONSTRAINT}
|
|
4777
4846
|
|
|
4778
4847
|
${UNTRUSTED_USER_INPUT_NOTICE}
|
|
@@ -4781,34 +4850,32 @@ var chapterSystemPromptBuilder = createPromptBuilder({
|
|
|
4781
4850
|
},
|
|
4782
4851
|
constraints: {
|
|
4783
4852
|
tag: "constraints",
|
|
4784
|
-
content:
|
|
4853
|
+
content: dedent5`
|
|
4785
4854
|
- Only use information present in the transcript
|
|
4786
4855
|
- Return structured data that matches the requested JSON schema
|
|
4787
4856
|
- Do not add commentary or extra text outside the JSON
|
|
4788
4857
|
- When a <language> section is provided, all chapter titles MUST be written in that language`
|
|
4789
4858
|
},
|
|
4790
4859
|
qualityGuidelines: {
|
|
4791
|
-
|
|
4792
|
-
content:
|
|
4793
|
-
|
|
4794
|
-
- Keep chapter titles concise and descriptive
|
|
4795
|
-
- Ensure the first chapter starts at 0 seconds`
|
|
4860
|
+
...chapterGuidance.qualityGuidelines,
|
|
4861
|
+
content: `${chapterGuidance.qualityGuidelines.content}
|
|
4862
|
+
- Ensure the first chapter starts at 0 seconds`
|
|
4796
4863
|
}
|
|
4797
4864
|
},
|
|
4798
4865
|
sectionOrder: ["role", "context", "security", "constraints", "qualityGuidelines"]
|
|
4799
4866
|
});
|
|
4800
4867
|
var AUDIO_ONLY_SYSTEM_PROMPT_OVERRIDES = {
|
|
4801
4868
|
role: "You are an audio editor and transcript analyst specializing in segmenting content into logical chapters.",
|
|
4802
|
-
context:
|
|
4869
|
+
context: dedent5`
|
|
4803
4870
|
You receive a timestamped transcript from audio-only content with lines in the form "[12s] Transcript text".
|
|
4804
4871
|
Use those timestamps as anchors to determine chapter start times in seconds.`
|
|
4805
4872
|
};
|
|
4806
4873
|
var SCENE_CONTEXT_SYSTEM_PROMPT_OVERRIDES = {
|
|
4807
|
-
context:
|
|
4874
|
+
context: dedent5`
|
|
4808
4875
|
You receive a timestamped transcript with lines in the form "[12s] Caption text" and an ordered scene context.
|
|
4809
4876
|
Transcript timestamps are in seconds. Scene start and end timestamps are in milliseconds.
|
|
4810
4877
|
Use the scene progression to choose logical chapter groupings and scene starts as chapter boundaries.`,
|
|
4811
|
-
constraints:
|
|
4878
|
+
constraints: dedent5`
|
|
4812
4879
|
- Only use information present in the transcript and scene context
|
|
4813
4880
|
- Return structured data that matches the requested JSON schema
|
|
4814
4881
|
- Do not add commentary or extra text outside the JSON
|
|
@@ -4822,7 +4889,7 @@ var chaptersPromptBuilder = createPromptBuilder({
|
|
|
4822
4889
|
},
|
|
4823
4890
|
outputFormat: {
|
|
4824
4891
|
tag: "output_format",
|
|
4825
|
-
content:
|
|
4892
|
+
content: dedent5`
|
|
4826
4893
|
Return valid JSON in this exact shape:
|
|
4827
4894
|
{
|
|
4828
4895
|
"chapters": [
|
|
@@ -4839,13 +4906,7 @@ var chaptersPromptBuilder = createPromptBuilder({
|
|
|
4839
4906
|
content: ""
|
|
4840
4907
|
// Placeholder - always overridden with dynamic content
|
|
4841
4908
|
},
|
|
4842
|
-
titleGuidelines:
|
|
4843
|
-
tag: "title_guidelines",
|
|
4844
|
-
content: dedent4`
|
|
4845
|
-
- Keep titles concise and descriptive
|
|
4846
|
-
- Avoid filler or generic labels like "Chapter 1"
|
|
4847
|
-
- Use the transcript's terminology`
|
|
4848
|
-
}
|
|
4909
|
+
titleGuidelines: chapterGuidance.titleGuidelines
|
|
4849
4910
|
},
|
|
4850
4911
|
sectionOrder: ["task", "outputFormat", "chapterGuidelines", "titleGuidelines"]
|
|
4851
4912
|
});
|
|
@@ -4884,7 +4945,7 @@ function buildUserPrompt3({
|
|
|
4884
4945
|
} else if (scope) {
|
|
4885
4946
|
firstChapterGuideline = `- The first chapter must start at ${scope.startTime}s`;
|
|
4886
4947
|
}
|
|
4887
|
-
const dynamicChapterGuidelines =
|
|
4948
|
+
const dynamicChapterGuidelines = dedent5`
|
|
4888
4949
|
- Create at least ${minChaptersPerHour} and at most ${maxChaptersPerHour} chapters per hour of content
|
|
4889
4950
|
- Use start times in seconds (not HH:MM:SS)
|
|
4890
4951
|
${scope ? `- Analyze only the range from ${scope.startTime}s (inclusive) to ${scope.endTime}s (exclusive)` : ""}
|
|
@@ -5862,7 +5923,7 @@ async function generateVideoEmbeddings(assetId, options = {}) {
|
|
|
5862
5923
|
|
|
5863
5924
|
// src/workflows/engagement-insights.ts
|
|
5864
5925
|
import { generateText as generateText5, Output as Output5 } from "ai";
|
|
5865
|
-
import
|
|
5926
|
+
import dedent6 from "dedent";
|
|
5866
5927
|
import { z as z7 } from "zod";
|
|
5867
5928
|
var aiMomentInsightSchema = z7.object({
|
|
5868
5929
|
hotspotIndex: z7.number(),
|
|
@@ -5985,7 +6046,7 @@ var AUDIO_ONLY_SYSTEM_PROMPT2 = promptDedent`
|
|
|
5985
6046
|
- Connect engagement data to content features
|
|
5986
6047
|
</quality_guidelines>
|
|
5987
6048
|
`;
|
|
5988
|
-
var INSIGHT_GUIDELINES =
|
|
6049
|
+
var INSIGHT_GUIDELINES = dedent6`
|
|
5989
6050
|
Provide explanatory insights that describe WHY moments are engaging.
|
|
5990
6051
|
Focus on observable patterns and viewer behavior correlation.
|
|
5991
6052
|
Example: "The cooking demonstration at 2:15 has 3x average engagement
|
|
@@ -6003,7 +6064,7 @@ var engagementInsightsPromptBuilder = createPromptBuilder({
|
|
|
6003
6064
|
},
|
|
6004
6065
|
outputFormat: {
|
|
6005
6066
|
tag: "output_format",
|
|
6006
|
-
content:
|
|
6067
|
+
content: dedent6`
|
|
6007
6068
|
Return valid JSON matching the provided schema.
|
|
6008
6069
|
Include insights for each hotspot and overall engagement trends.
|
|
6009
6070
|
Each momentInsight must include the hotspotIndex matching the input.
|
|
@@ -6011,7 +6072,7 @@ var engagementInsightsPromptBuilder = createPromptBuilder({
|
|
|
6011
6072
|
},
|
|
6012
6073
|
visualContext: {
|
|
6013
6074
|
tag: "visual_context",
|
|
6014
|
-
content:
|
|
6075
|
+
content: dedent6`
|
|
6015
6076
|
Images are provided for each hotspot timestamp.
|
|
6016
6077
|
A storyboard overview image may also be included showing the full video timeline.
|
|
6017
6078
|
Use visual evidence from these images to support your engagement analysis.
|
|
@@ -6025,14 +6086,14 @@ function buildUserPrompt4(hotspots, transcriptSegments, heatmap, isAudioOnly) {
|
|
|
6025
6086
|
const transcript = transcriptSegments[idx]?.text || "(no transcript)";
|
|
6026
6087
|
const startTimestamp = secondsToTimestamp(h.startMs / 1e3);
|
|
6027
6088
|
const endTimestamp = secondsToTimestamp(h.endMs / 1e3);
|
|
6028
|
-
return
|
|
6089
|
+
return dedent6`
|
|
6029
6090
|
Hotspot ${idx} (hotspotIndex: ${idx}):
|
|
6030
6091
|
- Time Range: ${startTimestamp} - ${endTimestamp}
|
|
6031
6092
|
- Engagement Score: ${h.score.toFixed(2)} (0=low, 1=high)
|
|
6032
6093
|
- Transcript: ${JSON.stringify(transcript)}
|
|
6033
6094
|
`;
|
|
6034
6095
|
}).join("\n\n");
|
|
6035
|
-
const heatmapData =
|
|
6096
|
+
const heatmapData = dedent6`
|
|
6036
6097
|
Engagement Heatmap (${heatmap.length} values, each representing 1/${heatmap.length}th of the video, higher = more re-watches):
|
|
6037
6098
|
[${heatmap.map((v) => v.toFixed(2)).join(", ")}]
|
|
6038
6099
|
`;
|
|
@@ -6098,7 +6159,7 @@ function mapShotsToHotspots(shots, hotspots) {
|
|
|
6098
6159
|
}
|
|
6099
6160
|
async function getThumbnailUrlsForHotspots(playbackId, hotspots, options = {}) {
|
|
6100
6161
|
const { width = 640, shouldSign = false, credentials } = options;
|
|
6101
|
-
const baseUrl = getMuxThumbnailBaseUrl(playbackId);
|
|
6162
|
+
const baseUrl = await getMuxThumbnailBaseUrl(playbackId, credentials);
|
|
6102
6163
|
const urlPromises = hotspots.map(async (h) => {
|
|
6103
6164
|
const time = Math.floor(h.startMs / 1e3);
|
|
6104
6165
|
if (shouldSign) {
|
|
@@ -7622,7 +7683,7 @@ function filterTimestampsWithinExclusiveScope(timestampsMs, scope) {
|
|
|
7622
7683
|
async function getThumbnailUrlsFromTimestamps(playbackId, timestampsMs, options) {
|
|
7623
7684
|
"use step";
|
|
7624
7685
|
const { width, shouldSign, credentials } = options;
|
|
7625
|
-
const baseUrl = getMuxThumbnailBaseUrl(playbackId);
|
|
7686
|
+
const baseUrl = await getMuxThumbnailBaseUrl(playbackId, credentials);
|
|
7626
7687
|
const times = [...new Set(timestampsMs.map(toThumbnailTimeSeconds))];
|
|
7627
7688
|
const urlPromises = times.map(async (time) => {
|
|
7628
7689
|
const url = shouldSign ? await signUrl(baseUrl, playbackId, "thumbnail", { time, width }, credentials) : `${baseUrl}?time=${time}&width=${width}`;
|
|
@@ -7810,7 +7871,6 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
7810
7871
|
|
|
7811
7872
|
// src/workflows/summarization.ts
|
|
7812
7873
|
import { generateText as generateText7, Output as Output7 } from "ai";
|
|
7813
|
-
import dedent6 from "dedent";
|
|
7814
7874
|
import { z as z9 } from "zod";
|
|
7815
7875
|
|
|
7816
7876
|
// src/lib/markdown-normalization.ts
|
|
@@ -7820,124 +7880,128 @@ function normalizeMarkdownDescription(description) {
|
|
|
7820
7880
|
return description.replace(ESCAPED_LINE_BREAK_REGEX, "\n").replace(/([^\n]) {2,}([*-]) (?=\S)/g, "$1\n$2 ").replace(/([.!?:;]) ([*-]) (?=\S)/g, "$1\n$2 ").replace(MULTIPLE_BLANK_LINES_REGEX, "\n\n");
|
|
7821
7881
|
}
|
|
7822
7882
|
|
|
7823
|
-
// src/
|
|
7883
|
+
// src/prompts/summarization.ts
|
|
7884
|
+
import dedent7 from "dedent";
|
|
7824
7885
|
var DEFAULT_SUMMARY_KEYWORD_LIMIT = 10;
|
|
7825
7886
|
var DEFAULT_TITLE_LENGTH = 10;
|
|
7826
7887
|
var DEFAULT_DESCRIPTION_LENGTH = 50;
|
|
7827
|
-
var summarySchema = z9.object({
|
|
7828
|
-
keywords: z9.array(z9.string().max(200)),
|
|
7829
|
-
title: z9.string().max(500),
|
|
7830
|
-
description: z9.string().max(1e4)
|
|
7831
|
-
});
|
|
7832
|
-
function buildSummarySchema(descriptionLength) {
|
|
7833
|
-
const descriptionMaxChars = Math.max(2e3, descriptionLength * 15);
|
|
7834
|
-
return z9.object({
|
|
7835
|
-
keywords: z9.array(z9.string().max(200)),
|
|
7836
|
-
title: z9.string().max(500),
|
|
7837
|
-
description: z9.string().max(descriptionMaxChars)
|
|
7838
|
-
});
|
|
7839
|
-
}
|
|
7840
|
-
var VALID_TONES = ["neutral", "playful", "professional"];
|
|
7841
|
-
var TONE_INSTRUCTIONS = {
|
|
7842
|
-
neutral: "Provide a clear, straightforward analysis.",
|
|
7843
|
-
playful: "Channel your inner diva! Answer with maximum sass, wit, and playful attitude. Don't hold back - be cheeky, clever, and delightfully snarky. Make it pop!",
|
|
7844
|
-
professional: "Provide a professional, executive-level analysis suitable for business reporting."
|
|
7845
|
-
};
|
|
7846
7888
|
var DESCRIPTION_LENGTH_THRESHOLD_SMALL = 25;
|
|
7847
7889
|
var DESCRIPTION_LENGTH_THRESHOLD_LARGE = 100;
|
|
7848
7890
|
function buildDescriptionGuidance(wordCount, contentType) {
|
|
7849
7891
|
if (wordCount < DESCRIPTION_LENGTH_THRESHOLD_SMALL) {
|
|
7850
7892
|
if (contentType === "video") {
|
|
7851
|
-
return
|
|
7893
|
+
return dedent7`A brief summary of the video in no more than ${wordCount} words. Shorter is fine.
|
|
7852
7894
|
Focus on the single most important subject or action.
|
|
7853
7895
|
Write in present tense.`;
|
|
7854
7896
|
}
|
|
7855
|
-
return
|
|
7897
|
+
return dedent7`A brief summary of the audio content in no more than ${wordCount} words. Shorter is fine.
|
|
7856
7898
|
Focus on the single most important topic or theme.
|
|
7857
7899
|
Write in present tense.`;
|
|
7858
7900
|
}
|
|
7859
7901
|
if (wordCount > DESCRIPTION_LENGTH_THRESHOLD_LARGE) {
|
|
7860
7902
|
if (contentType === "video") {
|
|
7861
|
-
return
|
|
7903
|
+
return dedent7`A detailed summary that describes what happens across the video.
|
|
7862
7904
|
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
7863
7905
|
Be thorough: cover subjects, actions, setting, progression, and any notable details visible across frames.
|
|
7864
7906
|
Write in present tense. Be specific about observable details rather than making assumptions.
|
|
7865
7907
|
If the transcript provides dialogue or narration, incorporate key points but prioritize visual content.`;
|
|
7866
7908
|
}
|
|
7867
|
-
return
|
|
7909
|
+
return dedent7`A detailed summary that describes the audio content.
|
|
7868
7910
|
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
7869
7911
|
Be thorough: cover topics, speakers, themes, progression, and any notable insights.
|
|
7870
7912
|
Write in present tense. Be specific about what is discussed or presented rather than making assumptions.
|
|
7871
7913
|
Focus on the spoken content and any key insights, dialogue, or narrative elements.`;
|
|
7872
7914
|
}
|
|
7873
7915
|
if (contentType === "video") {
|
|
7874
|
-
return
|
|
7916
|
+
return dedent7`A summary that describes what happens across the video.
|
|
7875
7917
|
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
7876
7918
|
Cover the main subjects, actions, setting, and any notable progression visible across frames.
|
|
7877
7919
|
Write in present tense. Be specific about observable details rather than making assumptions.
|
|
7878
7920
|
If the transcript provides dialogue or narration, incorporate key points but prioritize visual content.`;
|
|
7879
7921
|
}
|
|
7880
|
-
return
|
|
7922
|
+
return dedent7`A summary that describes the audio content.
|
|
7881
7923
|
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
7882
7924
|
Cover the main topics, speakers, themes, and any notable progression in the discussion or narration.
|
|
7883
7925
|
Write in present tense. Be specific about what is discussed or presented rather than making assumptions.
|
|
7884
7926
|
Focus on the spoken content and any key insights, dialogue, or narrative elements.`;
|
|
7885
7927
|
}
|
|
7886
|
-
function buildSceneAwareDescriptionGuidance(wordCount) {
|
|
7928
|
+
function buildSceneAwareDescriptionGuidance(wordCount, hasStoryboard) {
|
|
7929
|
+
const storyboardGuidance = hasStoryboard ? "Use the storyboard as direct visual evidence and the scene context to preserve progression." : "Use the ordered scene context as grounded evidence of the content and its progression.";
|
|
7887
7930
|
if (wordCount < DESCRIPTION_LENGTH_THRESHOLD_SMALL) {
|
|
7888
|
-
return
|
|
7931
|
+
return dedent7`A brief summary of the video in no more than ${wordCount} words. Shorter is fine.
|
|
7889
7932
|
Focus on the single most important subject or action across the ordered scenes.
|
|
7890
|
-
|
|
7933
|
+
${storyboardGuidance}
|
|
7891
7934
|
Write in present tense.`;
|
|
7892
7935
|
}
|
|
7893
7936
|
if (wordCount > DESCRIPTION_LENGTH_THRESHOLD_LARGE) {
|
|
7894
|
-
return
|
|
7937
|
+
return dedent7`A detailed summary that describes what happens across the ordered scenes.
|
|
7895
7938
|
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
7896
7939
|
Be thorough: cover subjects, actions, setting, progression, and notable details supported across scenes.
|
|
7897
|
-
|
|
7940
|
+
${storyboardGuidance}
|
|
7898
7941
|
Write in present tense. If the transcript provides dialogue or narration, incorporate key points while preserving scene progression.`;
|
|
7899
7942
|
}
|
|
7900
|
-
return
|
|
7943
|
+
return dedent7`A summary that describes what happens across the ordered scenes.
|
|
7901
7944
|
Never exceed ${wordCount} words, but shorter is perfectly fine. You may use multiple sentences.
|
|
7902
7945
|
Cover the main subjects, actions, setting, and notable progression supported across scenes.
|
|
7903
|
-
|
|
7946
|
+
${storyboardGuidance}
|
|
7904
7947
|
Write in present tense. If the transcript provides dialogue or narration, incorporate key points while preserving scene progression.`;
|
|
7905
7948
|
}
|
|
7906
|
-
function
|
|
7949
|
+
function createSummarizationGuidance({
|
|
7950
|
+
mediaType = "video",
|
|
7951
|
+
titleLength,
|
|
7952
|
+
descriptionLength,
|
|
7953
|
+
tagCount,
|
|
7954
|
+
hasSceneContext = false,
|
|
7955
|
+
hasStoryboard = true
|
|
7956
|
+
} = {}) {
|
|
7907
7957
|
const titleLimit = titleLength ?? DEFAULT_TITLE_LENGTH;
|
|
7958
|
+
const descriptionLimit = descriptionLength ?? DEFAULT_DESCRIPTION_LENGTH;
|
|
7908
7959
|
const keywordLimit = tagCount ?? DEFAULT_SUMMARY_KEYWORD_LIMIT;
|
|
7909
|
-
const
|
|
7910
|
-
const
|
|
7911
|
-
|
|
7912
|
-
-
|
|
7913
|
-
-
|
|
7914
|
-
-
|
|
7915
|
-
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7960
|
+
const isAudioOnly = mediaType === "audio";
|
|
7961
|
+
const sceneAware = !isAudioOnly && hasSceneContext;
|
|
7962
|
+
const qualityGuidelines = isAudioOnly ? dedent7`
|
|
7963
|
+
- Analyze the full transcript to understand context and themes
|
|
7964
|
+
- Be precise: use specific terminology when mentioned
|
|
7965
|
+
- Capture the narrative: what is introduced, discussed, and concluded
|
|
7966
|
+
- Balance brevity with informativeness` : sceneAware ? dedent7`
|
|
7967
|
+
- Follow scene_index order to understand what begins, develops, and concludes
|
|
7968
|
+
- Use scene narratives and concepts as grounded evidence, preserving consistent terminology across the output
|
|
7969
|
+
${hasStoryboard ? "- Use the storyboard as direct visual evidence for whole-asset details" : "- Ground whole-asset details in the provided scene context and transcript when available"}
|
|
7970
|
+
- Balance brevity with informativeness` : dedent7`
|
|
7971
|
+
- Examine all frames to understand the full context and progression
|
|
7972
|
+
- Be precise: "golden retriever" is better than "dog" when identifiable
|
|
7973
|
+
- Capture the narrative: what begins, develops, and concludes
|
|
7974
|
+
- Balance brevity with informativeness`;
|
|
7975
|
+
return {
|
|
7976
|
+
title: {
|
|
7977
|
+
tag: "title_requirements",
|
|
7978
|
+
content: isAudioOnly ? dedent7`
|
|
7979
|
+
A concise, label-style title — not a sentence or description.
|
|
7980
|
+
Never exceed ${titleLimit} words, but shorter is better.
|
|
7981
|
+
Think of how a podcast episode title or playlist entry would read — e.g. "Weekly News Roundup" or "Interview with Dr. Smith".
|
|
7982
|
+
Start with the primary subject or topic. Never begin with "An audio of" or similar phrasing.
|
|
7983
|
+
Use specific nouns over lengthy descriptions. Avoid clauses, conjunctions, or narrative structure.` : dedent7`
|
|
7928
7984
|
A concise, label-style title — not a sentence or description.
|
|
7929
7985
|
Never exceed ${titleLimit} words, but shorter is better.
|
|
7930
7986
|
Think of how a video card title, playlist entry, or file name would read — e.g. "Predator: Badlands Trailer" or "Chef Prepares Holiday Feast".
|
|
7931
7987
|
Start with the primary subject or topic. Never begin with "A video of" or similar phrasing.
|
|
7932
7988
|
Use specific nouns over lengthy descriptions. Avoid clauses, conjunctions, or narrative structure.`
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7989
|
+
},
|
|
7990
|
+
description: {
|
|
7991
|
+
tag: "description_requirements",
|
|
7992
|
+
content: sceneAware ? buildSceneAwareDescriptionGuidance(descriptionLimit, hasStoryboard) : buildDescriptionGuidance(descriptionLimit, mediaType)
|
|
7993
|
+
},
|
|
7994
|
+
keywords: {
|
|
7995
|
+
tag: "keywords_requirements",
|
|
7996
|
+
content: isAudioOnly ? dedent7`
|
|
7997
|
+
Specific, searchable terms (up to ${keywordLimit}) that capture:
|
|
7998
|
+
- Primary topics and themes
|
|
7999
|
+
- Speakers or presenters (if named)
|
|
8000
|
+
- Key concepts and terminology
|
|
8001
|
+
- Content type (interview, lecture, music, etc.)
|
|
8002
|
+
- Genre or style (if applicable)
|
|
8003
|
+
Prefer concrete nouns and relevant terms over abstract concepts.
|
|
8004
|
+
Use lowercase. Avoid redundant or overly generic terms like "audio" or "content".` : dedent7`
|
|
7941
8005
|
Specific, searchable terms (up to ${keywordLimit}) that capture:
|
|
7942
8006
|
- Primary subjects (people, animals, objects)
|
|
7943
8007
|
- Actions and activities being performed
|
|
@@ -7946,57 +8010,51 @@ function createSummarizationBuilder({ titleLength, descriptionLength, tagCount }
|
|
|
7946
8010
|
- Style or genre (if applicable)
|
|
7947
8011
|
Prefer concrete nouns and action verbs over abstract concepts.
|
|
7948
8012
|
Use lowercase. Avoid redundant or overly generic terms like "video" or "content".`
|
|
8013
|
+
},
|
|
8014
|
+
qualityGuidelines: { tag: "quality_guidelines", content: qualityGuidelines }
|
|
8015
|
+
};
|
|
8016
|
+
}
|
|
8017
|
+
|
|
8018
|
+
// src/workflows/summarization.ts
|
|
8019
|
+
var summarySchema = z9.object({
|
|
8020
|
+
keywords: z9.array(z9.string().max(200)),
|
|
8021
|
+
title: z9.string().max(500),
|
|
8022
|
+
description: z9.string().max(1e4)
|
|
8023
|
+
});
|
|
8024
|
+
function buildSummarySchema(descriptionLength) {
|
|
8025
|
+
const descriptionMaxChars = Math.max(2e3, descriptionLength * 15);
|
|
8026
|
+
return z9.object({
|
|
8027
|
+
keywords: z9.array(z9.string().max(200)),
|
|
8028
|
+
title: z9.string().max(500),
|
|
8029
|
+
description: z9.string().max(descriptionMaxChars)
|
|
8030
|
+
});
|
|
8031
|
+
}
|
|
8032
|
+
var VALID_TONES = ["neutral", "playful", "professional"];
|
|
8033
|
+
var TONE_INSTRUCTIONS = {
|
|
8034
|
+
neutral: "Provide a clear, straightforward analysis.",
|
|
8035
|
+
playful: "Channel your inner diva! Answer with maximum sass, wit, and playful attitude. Don't hold back - be cheeky, clever, and delightfully snarky. Make it pop!",
|
|
8036
|
+
professional: "Provide a professional, executive-level analysis suitable for business reporting."
|
|
8037
|
+
};
|
|
8038
|
+
function createSummarizationBuilder(constraints = {}, { hasSceneContext = false } = {}) {
|
|
8039
|
+
return createPromptBuilder({
|
|
8040
|
+
template: {
|
|
8041
|
+
task: {
|
|
8042
|
+
tag: "task",
|
|
8043
|
+
content: hasSceneContext ? "Analyze the storyboard frames and ordered scene context together, then generate metadata that captures the full progression of the video content." : "Analyze the storyboard frames and generate metadata that captures the essence of the video content."
|
|
7949
8044
|
},
|
|
7950
|
-
|
|
7951
|
-
tag: "quality_guidelines",
|
|
7952
|
-
content: qualityGuidelines
|
|
7953
|
-
}
|
|
8045
|
+
...createSummarizationGuidance({ ...constraints, hasSceneContext })
|
|
7954
8046
|
},
|
|
7955
8047
|
sectionOrder: ["task", "title", "description", "keywords", "qualityGuidelines"]
|
|
7956
8048
|
});
|
|
7957
8049
|
}
|
|
7958
|
-
function createAudioOnlyBuilder(
|
|
7959
|
-
const titleLimit = titleLength ?? DEFAULT_TITLE_LENGTH;
|
|
7960
|
-
const keywordLimit = tagCount ?? DEFAULT_SUMMARY_KEYWORD_LIMIT;
|
|
8050
|
+
function createAudioOnlyBuilder(constraints = {}) {
|
|
7961
8051
|
return createPromptBuilder({
|
|
7962
8052
|
template: {
|
|
7963
8053
|
task: {
|
|
7964
8054
|
tag: "task",
|
|
7965
8055
|
content: "Analyze the transcript and generate metadata that captures the essence of the audio content."
|
|
7966
8056
|
},
|
|
7967
|
-
|
|
7968
|
-
tag: "title_requirements",
|
|
7969
|
-
content: dedent6`
|
|
7970
|
-
A concise, label-style title — not a sentence or description.
|
|
7971
|
-
Never exceed ${titleLimit} words, but shorter is better.
|
|
7972
|
-
Think of how a podcast episode title or playlist entry would read — e.g. "Weekly News Roundup" or "Interview with Dr. Smith".
|
|
7973
|
-
Start with the primary subject or topic. Never begin with "An audio of" or similar phrasing.
|
|
7974
|
-
Use specific nouns over lengthy descriptions. Avoid clauses, conjunctions, or narrative structure.`
|
|
7975
|
-
},
|
|
7976
|
-
description: {
|
|
7977
|
-
tag: "description_requirements",
|
|
7978
|
-
content: buildDescriptionGuidance(descriptionLength ?? DEFAULT_DESCRIPTION_LENGTH, "audio")
|
|
7979
|
-
},
|
|
7980
|
-
keywords: {
|
|
7981
|
-
tag: "keywords_requirements",
|
|
7982
|
-
content: dedent6`
|
|
7983
|
-
Specific, searchable terms (up to ${keywordLimit}) that capture:
|
|
7984
|
-
- Primary topics and themes
|
|
7985
|
-
- Speakers or presenters (if named)
|
|
7986
|
-
- Key concepts and terminology
|
|
7987
|
-
- Content type (interview, lecture, music, etc.)
|
|
7988
|
-
- Genre or style (if applicable)
|
|
7989
|
-
Prefer concrete nouns and relevant terms over abstract concepts.
|
|
7990
|
-
Use lowercase. Avoid redundant or overly generic terms like "audio" or "content".`
|
|
7991
|
-
},
|
|
7992
|
-
qualityGuidelines: {
|
|
7993
|
-
tag: "quality_guidelines",
|
|
7994
|
-
content: dedent6`
|
|
7995
|
-
- Analyze the full transcript to understand context and themes
|
|
7996
|
-
- Be precise: use specific terminology when mentioned
|
|
7997
|
-
- Capture the narrative: what is introduced, discussed, and concluded
|
|
7998
|
-
- Balance brevity with informativeness`
|
|
7999
|
-
}
|
|
8057
|
+
...createSummarizationGuidance({ ...constraints, mediaType: "audio" })
|
|
8000
8058
|
},
|
|
8001
8059
|
sectionOrder: ["task", "title", "description", "keywords", "qualityGuidelines"]
|
|
8002
8060
|
});
|
|
@@ -8809,7 +8867,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
8809
8867
|
{ type: "validation_error" }
|
|
8810
8868
|
);
|
|
8811
8869
|
}
|
|
8812
|
-
let audioUrl = `${getMuxStreamOrigin()}/${playbackId}/audio.m4a`;
|
|
8870
|
+
let audioUrl = `${await getMuxStreamOrigin(credentials)}/${playbackId}/audio.m4a`;
|
|
8813
8871
|
if (policy === "signed") {
|
|
8814
8872
|
audioUrl = await signUrl(audioUrl, playbackId, "video", void 0, credentials);
|
|
8815
8873
|
}
|
|
@@ -9834,6 +9892,7 @@ export {
|
|
|
9834
9892
|
findSameLanguageTextTracks,
|
|
9835
9893
|
getLanguageCodePair,
|
|
9836
9894
|
getLanguageName,
|
|
9895
|
+
getMuxPlaybackOrigin,
|
|
9837
9896
|
isDuplicateTrackNameError,
|
|
9838
9897
|
isMuxGeneratedTextTrack,
|
|
9839
9898
|
isUndeterminedLanguageCode,
|