Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 27 additions & 22 deletions src/primitives/thumbnails.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { signUrl } from "@mux/ai/lib/url-signing";
import type { WorkflowCredentialsInput } from "@mux/ai/types";

const MIN_DURATION = 50;
const MIN_SAMPLES = 2;

export interface ThumbnailOptions {
/** Interval between thumbnails in seconds (default: 10) */
interval?: number;
Expand Down Expand Up @@ -30,37 +33,39 @@ export async function getThumbnailUrls(
): Promise<string[]> {
"use step";
const { interval = 10, width = 640, shouldSign = false, maxSamples, credentials } = options;
let timestamps: number[] = [];
const timestamps: number[] = [];

if (duration <= 50) {
const spacing = duration / 6;
for (let i = 1; i <= 5; i++) {
timestamps.push(Math.round(i * spacing));
}
// Calculate expected number of timestamps before generating
let expectedCount: number;
if (duration <= MIN_DURATION) {
expectedCount = 5;
} else {
for (let time = 0; time < duration; time += interval) {
timestamps.push(time);
}
expectedCount = Math.floor(duration / interval) + 1;
}

// Apply maxSamples cap if specified and we have more timestamps than the limit
if (maxSamples !== undefined && timestamps.length > maxSamples) {
const newTimestamps: number[] = [];
// If maxSamples is set and would be exceeded, generate evenly-spaced samples directly
if (maxSamples !== undefined && expectedCount > maxSamples) {
timestamps.push(0); // Always include first frame

// Always include first frame
newTimestamps.push(0);

// If maxSamples >= 2, add evenly distributed middle frames and last frame
if (maxSamples >= 2) {
if (maxSamples >= MIN_SAMPLES) {
const spacing = duration / (maxSamples - 1);
for (let i = 1; i < maxSamples - 1; i++) {
newTimestamps.push(spacing * i);
timestamps.push(spacing * i);
}
timestamps.push(duration); // Always include last frame
}
} else {
// Generate timestamps based on duration and interval
if (duration <= MIN_DURATION) {
const spacing = duration / 6;
for (let i = 1; i <= 5; i++) {
timestamps.push(Math.round(i * spacing));
}
} else {
for (let time = 0; time < duration; time += interval) {
timestamps.push(time);
}
// Always include last frame
newTimestamps.push(duration);
}

timestamps = newTimestamps;
}

const baseUrl = `https://image.mux.com/${playbackId}/thumbnail.png`;
Expand Down