/** * Extract the final frame from a video element as a Blob * This is used to create continuity between video segments * Similar to the approach in sora-extend, but using browser Canvas API */ export async function extractFinalFrame( videoElement: HTMLVideoElement ): Promise { return new Promise((resolve, reject) => { try { // Create a canvas with the video's dimensions const canvas = document.createElement('canvas'); canvas.width = videoElement.videoWidth; canvas.height = videoElement.videoHeight; const ctx = canvas.getContext('2d'); if (!ctx) { reject(new Error('Could not get canvas context')); return; } // Draw the current frame onto the canvas ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height); // Convert canvas to blob canvas.toBlob( (blob) => { if (blob) { resolve(blob); } else { reject(new Error('Failed to create blob from canvas')); } }, 'image/jpeg', 0.95 // High quality for better continuity ); } catch (error) { reject(error); } }); } /** * Seek to the last frame of a video and extract it * Useful for extracting the final frame before the video ends naturally */ export async function seekAndExtractFinalFrame( videoElement: HTMLVideoElement ): Promise { return new Promise((resolve, reject) => { const handleSeeked = async () => { try { const blob = await extractFinalFrame(videoElement); videoElement.removeEventListener('seeked', handleSeeked); resolve(blob); } catch (error) { videoElement.removeEventListener('seeked', handleSeeked); reject(error); } }; videoElement.addEventListener('seeked', handleSeeked); // Seek to 50ms before the end to ensure we get a valid frame const targetTime = Math.max(0, videoElement.duration - 0.05); videoElement.currentTime = targetTime; }); } /** * Create a preview URL for a blob (useful for debugging) */ export function createBlobPreviewUrl(blob: Blob): string { return URL.createObjectURL(blob); } /** * Revoke a blob URL to free memory */ export function revokeBlobUrl(url: string): void { URL.revokeObjectURL(url); } /** * Download a blob as a file (useful for debugging/testing) */ export function downloadBlob(blob: Blob, filename: string): void { const url = createBlobPreviewUrl(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); revokeBlobUrl(url); }