@twick/media-utils 0.14.2 → 0.14.3

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/README.md CHANGED
@@ -1,23 +1,95 @@
1
1
  # @twick/media-utils
2
2
 
3
- Media utilities for the Twick platform.
3
+ Core utilities for media handling and manipulation in the Twick platform.
4
+
5
+ ## Overview
6
+
7
+ This package provides essential utilities for working with various media types including video, audio, and images. It offers functions for metadata extraction, dimension handling, caching, and file management.
4
8
 
5
9
  ## Installation
6
10
 
7
11
  ```bash
12
+ npm install @twick/media-utils
13
+ # or
8
14
  pnpm add @twick/media-utils
9
15
  ```
10
16
 
11
- ## Usage
17
+ ## Quick Start
12
18
 
13
19
  ```typescript
14
- import { /* your exports */ } from '@twick/media-utils';
20
+ import {
21
+ getVideoMetadata,
22
+ getImageDimensions,
23
+ getAudioDuration,
24
+ getThumbnail
25
+ } from '@twick/media-utils';
26
+
27
+ // Get video metadata
28
+ const videoMeta = await getVideoMetadata('path/to/video.mp4');
29
+ console.log(videoMeta); // { width, height, duration, etc. }
30
+
31
+ // Get image dimensions
32
+ const dimensions = await getImageDimensions('path/to/image.jpg');
33
+ console.log(dimensions); // { width, height }
34
+
35
+ // Get audio duration
36
+ const duration = await getAudioDuration('path/to/audio.mp3');
37
+ console.log(duration); // duration in seconds
38
+
39
+ // Generate thumbnail
40
+ const thumbnail = await getThumbnail('path/to/video.mp4', 5); // 5 seconds
41
+ console.log(thumbnail); // base64 thumbnail
15
42
  ```
16
43
 
17
- ## API Documentation
44
+ ## Key Features
45
+
46
+ - **Video Metadata**: Extract width, height, duration, and other properties
47
+ - **Image Processing**: Get dimensions and generate thumbnails
48
+ - **Audio Utilities**: Extract duration and audio properties
49
+ - **Caching**: Built-in caching for improved performance
50
+ - **File Management**: URL and file path handling utilities
51
+ - **Dimension Handling**: Responsive dimension calculations
52
+
53
+ ## API Reference
54
+
55
+ ### Core Functions
56
+
57
+ - `getVideoMetadata`: Extract video metadata
58
+ - `getImageDimensions`: Get image dimensions
59
+ - `getAudioDuration`: Extract audio duration
60
+ - `getThumbnail`: Generate video thumbnails
61
+ - `limit`: Apply size and duration limits
62
+ - `urlHelper`: URL manipulation utilities
18
63
 
19
- For detailed API documentation, see the generated docs in the `docs` directory.
64
+ ### Types
65
+
66
+ - `VideoMetadata`: Video metadata interface
67
+ - `ImageDimensions`: Image dimension interface
68
+ - `AudioMetadata`: Audio metadata interface
69
+ - `ThumbnailOptions`: Thumbnail generation options
70
+
71
+ For complete API documentation, refer to the generated documentation.
72
+
73
+ ## Browser Support
74
+
75
+ This package requires a browser environment with support for:
76
+ - HTML5 Video and Audio elements
77
+ - Canvas API
78
+ - File API
79
+ - Modern JavaScript features (ES2020+)
80
+
81
+ ## Documentation
82
+
83
+ For complete documentation, refer to the project documentation site.
20
84
 
21
85
  ## License
22
86
 
23
- Apache-2.0
87
+ This package is licensed under the **Sustainable Use License (SUL) Version 1.0**.
88
+
89
+ - Free for use in commercial and non-commercial apps
90
+ - Can be modified and self-hosted
91
+ - Cannot be sold, rebranded, or distributed as a standalone SDK
92
+
93
+ For commercial licensing inquiries, contact: contact@kifferai.com
94
+
95
+ For full license terms, see the main LICENSE.md file in the project root.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/cache.ts","../src/get-audio-duration.ts","../src/limit.ts","../src/get-image-dimensions.ts","../src/get-video-metadata.ts","../src/get-thumbnail.ts","../src/audio-utils.ts","../src/dimension-handler.ts","../src/file-helper.ts","../src/url-helper.ts"],"sourcesContent":["import { Dimensions, VideoMeta } from \"./types\";\n\nexport const imageDimensionsCache: Record<string, Dimensions> = {};\nexport const videoMetaCache: Record<string, VideoMeta> = {};\nexport const audioDurationCache: Record<string, number> = {};","import { audioDurationCache } from \"./cache\";\n\n/**\n * Retrieves the duration (in seconds) of an audio file from a given source URL.\n * Uses a cache to avoid reloading the same audio multiple times.\n *\n * @param audioSrc - The source URL of the audio file.\n * @returns A Promise that resolves to the duration of the audio in seconds.\n */\nexport const getAudioDuration = (audioSrc: string): Promise<number> => {\n // Return cached duration if available\n if (audioDurationCache[audioSrc]) {\n return Promise.resolve(audioDurationCache[audioSrc]);\n }\n\n return new Promise((resolve, reject) => {\n const audio = document.createElement(\"audio\");\n audio.preload = \"metadata\"; // Only load metadata (e.g., duration)\n // Sanitize the audioSrc to prevent XSS by only allowing safe URLs (http, https, blob, data)\n const isSafeUrl = /^(https?:|blob:|data:audio\\/)/i.test(audioSrc);\n if (!isSafeUrl) {\n throw new Error(\"Unsafe audio source URL\");\n }\n audio.src = audioSrc;\n\n // When metadata is loaded, store duration in cache and resolve\n audio.onloadedmetadata = () => {\n const duration = audio.duration;\n audioDurationCache[audioSrc] = duration;\n resolve(duration);\n };\n\n // Handle loading errors\n audio.onerror = () => {\n reject(new Error(\"Failed to load audio metadata\"));\n };\n });\n};\n","// Maximum number of concurrent promises allowed to run\nconst concurrencyLimit = 5;\n\n// Number of currently active (running) promises\nlet activeCount = 0;\n\n// Queue to hold pending tasks waiting to be run when concurrency slots free up\nconst queue: Array<() => void> = [];\n\n/**\n * Runs the next task from the queue if concurrency limit is not reached.\n */\nfunction runNext() {\n // If no tasks are queued or we're already at the concurrency limit, do nothing\n if (queue.length === 0 || activeCount >= concurrencyLimit) return;\n\n // Dequeue next task\n const next = queue.shift();\n\n if (next) {\n activeCount++; // Mark one more active task\n next(); // Run it\n }\n}\n\n/**\n * Wraps an async function to enforce concurrency limits.\n * If concurrency limit is reached, the function is queued and executed later.\n * \n * @param fn - Async function returning a Promise\n * @returns Promise that resolves/rejects with fn's result\n */\nexport function limit<T>(fn: () => Promise<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n // Task to run the function and handle completion\n const task = () => {\n fn()\n .then(resolve)\n .catch(reject)\n .finally(() => {\n activeCount--; // Mark task as done\n runNext(); // Trigger next queued task, if any\n });\n };\n\n if (activeCount < concurrencyLimit) {\n activeCount++; // Increment active count for immediate run\n task();\n } else {\n // Queue the task if concurrency limit reached\n queue.push(task);\n }\n });\n}\n","import { limit } from \"./limit\";\nimport { Dimensions } from \"./types\";\nimport { imageDimensionsCache } from \"./cache\";\n\n/**\n * Loads an image from the given URL and resolves with its natural dimensions.\n *\n * @param url - The image URL to load.\n * @returns A Promise that resolves with the image's width and height.\n */\nconst loadImageDimensions = (url: string): Promise<Dimensions> => {\n return new Promise((resolve, reject) => {\n if (typeof document === 'undefined') {\n reject(new Error('getImageDimensions() is only available in the browser.'));\n return;\n }\n\n const img = new Image();\n img.onload = () => {\n resolve({ width: img.naturalWidth, height: img.naturalHeight });\n };\n img.onerror = reject;\n img.src = url;\n });\n};\n\n/**\n * Gets the dimensions (width and height) of an image from the given URL.\n * Uses a cache to avoid reloading the image if already fetched.\n * Also uses a concurrency limiter to control resource usage.\n *\n * @param url - The URL of the image.\n * @returns A Promise that resolves to an object containing `width` and `height`.\n */\nexport const getImageDimensions = (url: string): Promise<Dimensions> => {\n // Return cached dimensions if available\n if (imageDimensionsCache[url]) {\n return Promise.resolve(imageDimensionsCache[url]);\n }\n\n // Fetch and cache the dimensions using a concurrency limit\n return limit(() => loadImageDimensions(url)).then((dimensions) => {\n imageDimensionsCache[url] = dimensions;\n return dimensions;\n });\n};\n","import { videoMetaCache } from \"./cache\";\nimport { VideoMeta } from \"./types\";\n\n/**\n * Fetches metadata (width, height, duration) for a given video source.\n * If metadata has already been fetched and cached, it returns the cached data.\n *\n * @param videoSrc - The URL or path to the video file.\n * @returns A Promise that resolves to an object containing video metadata.\n */\nexport const getVideoMeta = (videoSrc: string): Promise<VideoMeta> => {\n // Return cached metadata if available\n if (videoMetaCache[videoSrc]) {\n return Promise.resolve(videoMetaCache[videoSrc]);\n }\n\n return new Promise<VideoMeta>((resolve, reject) => {\n const video: HTMLVideoElement = document.createElement(\"video\");\n video.preload = \"metadata\"; // Only preload metadata to reduce bandwidth\n // Validate the videoSrc to ensure it's a safe URL before assigning it to video.src\n const isSafeUrl = /^(https?:|blob:|data:video\\/)/i.test(videoSrc);\n if (!isSafeUrl) {\n reject(new Error(\"Unsafe video source URL\"));\n return;\n }\n video.src = videoSrc;\n\n // When metadata is loaded, extract and cache it\n video.onloadedmetadata = () => {\n const meta: VideoMeta = {\n width: video.videoWidth,\n height: video.videoHeight,\n duration: video.duration,\n };\n videoMetaCache[videoSrc] = meta;\n resolve(meta);\n };\n\n // Handle video loading errors\n video.onerror = () => reject(new Error(\"Failed to load video metadata\"));\n });\n};\n","/**\n * Extracts a thumbnail from a video at a specific seek time and playback rate.\n *\n * This function creates a hidden `<video>` element in the browser,\n * seeks to the specified time, and captures the frame into a canvas,\n * which is then exported as a JPEG data URL or Blob URL.\n *\n * @param videoUrl - The URL of the video to extract the thumbnail from.\n * @param seekTime - The time in seconds at which to capture the frame. Default is 0.1s.\n * @param playbackRate - Playback speed for the video. Default is 1.\n * @returns A Promise that resolves to a thumbnail image URL (either a base64 data URL or blob URL).\n */\nexport const getThumbnail = async (\n videoUrl: string,\n seekTime = 0.1,\n playbackRate = 1\n ): Promise<string> => {\n return new Promise((resolve, reject) => {\n const video = document.createElement(\"video\");\n video.crossOrigin = \"anonymous\";\n video.muted = true;\n video.playsInline = true;\n video.autoplay = false;\n video.preload = \"auto\";\n video.playbackRate = playbackRate;\n \n // Make video element hidden\n video.style.position = \"absolute\";\n video.style.left = \"-9999px\";\n video.style.top = \"-9999px\";\n video.style.width = \"1px\";\n video.style.height = \"1px\";\n video.style.opacity = \"0\";\n video.style.pointerEvents = \"none\";\n video.style.zIndex = \"-1\";\n \n let timeoutId: number | undefined;\n \n // Cleanup video element and timeout\n const cleanup = () => {\n if (video.parentNode) video.remove();\n if (timeoutId) clearTimeout(timeoutId);\n };\n \n // Handle errors during video loading\n const handleError = () => {\n cleanup();\n reject(new Error(`Failed to load video: ${video.error?.message || \"Unknown error\"}`));\n };\n \n // Once seeked to target frame, capture the image\n const handleSeeked = () => {\n try {\n video.pause();\n \n const canvas = document.createElement(\"canvas\");\n const width = video.videoWidth || 640;\n const height = video.videoHeight || 360;\n canvas.width = width;\n canvas.height = height;\n \n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n cleanup();\n reject(new Error(\"Failed to get canvas context\"));\n return;\n }\n \n // Draw current video frame onto canvas\n ctx.drawImage(video, 0, 0, width, height);\n \n // Attempt to export canvas to base64 image URL\n try {\n const dataUrl = canvas.toDataURL(\"image/jpeg\", 0.8);\n cleanup();\n resolve(dataUrl);\n } catch {\n // Fallback: convert canvas to Blob\n canvas.toBlob((blob) => {\n if (!blob) {\n cleanup();\n reject(new Error(\"Failed to create Blob\"));\n return;\n }\n const blobUrl = URL.createObjectURL(blob);\n cleanup();\n resolve(blobUrl);\n }, \"image/jpeg\", 0.8);\n }\n } catch (err) {\n cleanup();\n reject(new Error(`Error creating thumbnail: ${err}`));\n }\n };\n \n video.addEventListener(\"error\", handleError, { once: true });\n video.addEventListener(\"seeked\", handleSeeked, { once: true });\n \n // After metadata is loaded, seek to the desired frame\n video.addEventListener(\"loadedmetadata\", () => {\n const playPromise = video.play();\n if (playPromise !== undefined) {\n playPromise\n .then(() => {\n video.currentTime = seekTime;\n })\n .catch(() => {\n video.currentTime = seekTime;\n });\n } else {\n video.currentTime = seekTime;\n }\n }, { once: true });\n \n // Timeout protection in case video loading hangs\n timeoutId = window.setTimeout(() => {\n cleanup();\n reject(new Error(\"Video loading timed out\"));\n }, 5000);\n \n // Assign video source and add it to the DOM (helps Safari/iOS behavior)\n video.src = videoUrl;\n document.body.appendChild(video);\n });\n };","/**\n * Audio segment interface for stitching\n */\nexport interface AudioSegment {\n src: string;\n s: number; // start time in seconds\n e: number; // end time in seconds\n volume?: number; // volume level (0-1), defaults to 1, 0 = muted\n}\n\n/**\n * Extracts an audio segment from a media source (e.g., video) between start and end times,\n * rendered at the specified playback rate, and returns a Blob URL to an MP3 file.\n *\n * The function fetches the source, decodes the audio track using Web Audio API,\n * renders the segment offline for speed and determinism, encodes it as MP3 using lamejs,\n * and returns an object URL. Callers should revoke the URL when done.\n *\n * Example:\n * const url = await extractAudio({ src, start: 3, end: 8, playbackRate: 1.25 });\n * const audio = new Audio(url);\n * audio.play();\n * // later: URL.revokeObjectURL(url);\n */\nexport const extractAudio = async ({\n src,\n playbackRate = 1,\n start = 0,\n end,\n}: {\n src: string;\n playbackRate?: number;\n start?: number;\n end?: number;\n}): Promise<string> => {\n if (!src) throw new Error(\"src is required\");\n if (playbackRate <= 0) throw new Error(\"playbackRate must be > 0\");\n\n // Basic URL safety check\n const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);\n if (!isSafeUrl) throw new Error(\"Unsafe media source URL\");\n\n // Fetch and decode audio\n const audioBuffer = await fetchAndDecodeAudio(src);\n\n // Normalize time range\n const clampedStart = Math.max(0, start || 0);\n const fullDuration = audioBuffer.duration;\n const clampedEnd = Math.min(\n typeof end === \"number\" ? end : fullDuration,\n fullDuration\n );\n if (clampedEnd <= clampedStart)\n throw new Error(\"Invalid range: end must be greater than start\");\n\n // Render segment with playback rate\n const renderedBuffer = await renderAudioSegment(\n audioBuffer,\n clampedStart,\n clampedEnd,\n playbackRate\n );\n\n // Convert to MP3 and return URL\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\n\n/**\n * Stitches multiple audio segments into a single MP3 file.\n * Creates a timeline where each segment plays at its specified time,\n * with silence filling gaps between segments.\n * \n * @param segments - Array of audio segments with source, start, and end times\n * @param totalDuration - Total duration of the output audio (optional, auto-calculated if not provided)\n * @returns Promise<string> - Blob URL to the stitched MP3 file\n * \n * Example:\n * const segments = [\n * { src: \"audio1.mp3\", s: 0, e: 2, volume: 1.0 },\n * { src: \"audio2.mp3\", s: 1, e: 4, volume: 0.5 }, // overlaps with audio1\n * { src: \"audio3.mp3\", s: 4, e: 7, volume: 0 } // muted, won't be included\n * ];\n * const url = await stitchAudio(segments, 7); // 7 second output with overlapping audio\n */\nexport const stitchAudio = async (\n segments: AudioSegment[],\n totalDuration?: number\n): Promise<string> => {\n if (!segments || segments.length === 0) {\n throw new Error(\"At least one audio segment is required\");\n }\n\n // Calculate total duration if not provided\n const duration = totalDuration || Math.max(...segments.map(s => s.e));\n\n // Create timeline and render segments\n const renderedBuffer = await createAudioTimeline(segments, duration);\n\n // Convert to MP3 and return URL\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\n\n// ===== SHARED UTILITIES =====\n\n/**\n * Fetches and decodes audio from a URL\n */\nconst fetchAndDecodeAudio = async (src: string): Promise<AudioBuffer> => {\n const response = await fetch(src);\n if (!response.ok) throw new Error(`Failed to fetch source: ${response.status}`);\n \n const arrayBuffer = await response.arrayBuffer();\n return decodeAudioData(arrayBuffer);\n};\n\n/**\n * Decodes audio data using Web Audio API\n */\nconst decodeAudioData = async (arrayBuffer: ArrayBuffer): Promise<AudioBuffer> => {\n const AudioContextCtor: typeof AudioContext =\n (window as any).AudioContext || (window as any).webkitAudioContext;\n if (!AudioContextCtor) throw new Error(\"Web Audio API not supported\");\n \n const audioContext = new AudioContextCtor();\n try {\n return await new Promise<AudioBuffer>((resolve, reject) => {\n audioContext.decodeAudioData(\n arrayBuffer.slice(0),\n (buf) => resolve(buf),\n (err) => reject(err || new Error(\"Failed to decode audio\"))\n );\n });\n } finally {\n audioContext.close();\n }\n};\n\n/**\n * Renders an audio segment with playback rate\n */\nconst renderAudioSegment = async (\n audioBuffer: AudioBuffer,\n start: number,\n end: number,\n playbackRate: number\n): Promise<AudioBuffer> => {\n const OfflineAudioContextCtor: typeof OfflineAudioContext =\n (window as any).OfflineAudioContext || (window as any).webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n\n const sampleRate = audioBuffer.sampleRate;\n const numChannels = audioBuffer.numberOfChannels;\n const sourceDuration = end - start;\n const renderedFrames = Math.max(\n 1,\n Math.ceil((sourceDuration / playbackRate) * sampleRate)\n );\n\n const offline = new OfflineAudioContextCtor(numChannels, renderedFrames, sampleRate);\n const sourceNode = offline.createBufferSource();\n sourceNode.buffer = audioBuffer;\n sourceNode.playbackRate.value = playbackRate;\n sourceNode.connect(offline.destination);\n sourceNode.start(0, start, sourceDuration);\n\n return await offline.startRendering();\n};\n\n/**\n * Creates an audio timeline with multiple segments\n */\nconst createAudioTimeline = async (\n segments: AudioSegment[],\n duration: number\n): Promise<AudioBuffer> => {\n const OfflineAudioContextCtor: typeof OfflineAudioContext =\n (window as any).OfflineAudioContext || (window as any).webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n\n const sampleRate = 44100; // Standard sample rate\n const totalFrames = Math.ceil(duration * sampleRate);\n const offline = new OfflineAudioContextCtor(2, totalFrames, sampleRate); // Stereo output\n\n // Process each segment\n for (const segment of segments) {\n if (segment.s >= segment.e) {\n console.warn(`Invalid segment: start (${segment.s}) >= end (${segment.e})`);\n continue;\n }\n\n // Skip segments with volume 0 (muted)\n const volume = segment.volume ?? 1;\n if (volume <= 0) {\n console.warn(`Skipping muted segment: ${segment.src}`);\n continue;\n }\n\n try {\n const audioBuffer = await fetchAndDecodeAudio(segment.src);\n const segmentDuration = segment.e - segment.s;\n const sourceDuration = Math.min(segmentDuration, audioBuffer.duration);\n\n const source = offline.createBufferSource();\n source.buffer = audioBuffer;\n \n // Apply volume control if not 1.0\n if (volume !== 1) {\n const gainNode = offline.createGain();\n gainNode.gain.value = volume;\n source.connect(gainNode);\n gainNode.connect(offline.destination);\n } else {\n source.connect(offline.destination);\n }\n \n source.start(segment.s, 0, sourceDuration);\n } catch (error) {\n console.warn(`Failed to process segment: ${segment.src}`, error);\n }\n }\n\n return await offline.startRendering();\n};\n\n/**\n * Converts an AudioBuffer to an MP3 Blob using lamejs\n */\nconst audioBufferToMp3 = async (buffer: AudioBuffer): Promise<Blob> => {\n try {\n // Convert AudioBuffer to WAV ArrayBuffer\n const wavArrayBuffer = audioBufferToWavArrayBuffer(buffer);\n \n // Decode WAV back to PCM using AudioContext\n const pcmBuffer = await decodeAudioData(wavArrayBuffer);\n \n // Encode PCM to MP3 using lamejs\n return await encodePcmToMp3(pcmBuffer);\n } catch (error) {\n // Fallback to WAV if MP3 encoding fails\n return audioBufferToWavBlob(buffer);\n }\n};\n\n/**\n * Converts AudioBuffer to WAV ArrayBuffer\n */\nconst audioBufferToWavArrayBuffer = (buffer: AudioBuffer): ArrayBuffer => {\n const numChannels = buffer.numberOfChannels;\n const sampleRate = buffer.sampleRate;\n const numFrames = buffer.length;\n\n // Interleave channels\n const interleaved = interleave(buffer, numChannels, numFrames);\n\n // Create WAV ArrayBuffer\n const bytesPerSample = 2; // 16-bit\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n const dataSize = interleaved.length * bytesPerSample;\n const bufferSize = 44 + dataSize;\n const arrayBuffer = new ArrayBuffer(bufferSize);\n const view = new DataView(arrayBuffer);\n\n // RIFF header\n writeString(view, 0, \"RIFF\");\n view.setUint32(4, 36 + dataSize, true);\n writeString(view, 8, \"WAVE\");\n\n // fmt chunk\n writeString(view, 12, \"fmt \");\n view.setUint32(16, 16, true); // PCM\n view.setUint16(20, 1, true); // audio format = 1 (PCM)\n view.setUint16(22, numChannels, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, blockAlign, true);\n view.setUint16(34, 16, true); // bits per sample\n\n // data chunk\n writeString(view, 36, \"data\");\n view.setUint32(40, dataSize, true);\n\n // PCM samples\n floatTo16BitPCM(view, 44, interleaved);\n\n return arrayBuffer;\n};\n\n/**\n * Encodes PCM AudioBuffer to MP3 using lamejs\n */\nconst encodePcmToMp3 = async (buffer: AudioBuffer): Promise<Blob> => {\n const lamejs = await import(\"lamejs\");\n\n const channels = buffer.numberOfChannels >= 2 ? 2 : 1;\n // Downsample to 22050 Hz for smaller file size (good for voice/speech)\n const targetSampleRate = 22050;\n const downsampledBuffer = downsampleAudioBuffer(buffer, targetSampleRate);\n const kbps = 48; // Reduced bitrate for smaller file size\n\n const mp3encoder = new lamejs.default.Mp3Encoder(channels, targetSampleRate, kbps);\n const samplesPerFrame = 1152;\n\n // Prepare PCM Int16 arrays\n const leftFloat = downsampledBuffer.getChannelData(0);\n const left = floatTo16(leftFloat);\n let right: Int16Array | undefined;\n if (channels === 2) {\n const rightFloat = downsampledBuffer.getChannelData(1);\n right = floatTo16(rightFloat);\n }\n\n const mp3Chunks: Uint8Array[] = [];\n for (let i = 0; i < left.length; i += samplesPerFrame) {\n const leftChunk = left.subarray(i, Math.min(i + samplesPerFrame, left.length));\n let mp3buf: Uint8Array;\n if (channels === 2 && right) {\n const rightChunk = right.subarray(i, Math.min(i + samplesPerFrame, right.length));\n mp3buf = mp3encoder.encodeBuffer(leftChunk, rightChunk);\n } else {\n mp3buf = mp3encoder.encodeBuffer(leftChunk);\n }\n if (mp3buf.length > 0) mp3Chunks.push(mp3buf);\n }\n\n const end = mp3encoder.flush();\n if (end.length > 0) mp3Chunks.push(end);\n\n return new Blob(mp3Chunks, { type: \"audio/mpeg\" });\n};\n\n/**\n * Converts an AudioBuffer to a WAV Blob (fallback)\n */\nconst audioBufferToWavBlob = (buffer: AudioBuffer): Blob => {\n const arrayBuffer = audioBufferToWavArrayBuffer(buffer);\n return new Blob([arrayBuffer], { type: \"audio/wav\" });\n};\n\n/**\n * Downsamples an AudioBuffer to a lower sample rate for smaller file size\n */\nconst downsampleAudioBuffer = (buffer: AudioBuffer, targetSampleRate: number): AudioBuffer => {\n if (buffer.sampleRate === targetSampleRate) {\n return buffer;\n }\n\n const ratio = buffer.sampleRate / targetSampleRate;\n const newLength = Math.round(buffer.length / ratio);\n const newBuffer = new AudioContext().createBuffer(\n buffer.numberOfChannels,\n newLength,\n targetSampleRate\n );\n\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const oldData = buffer.getChannelData(channel);\n const newData = newBuffer.getChannelData(channel);\n \n for (let i = 0; i < newLength; i++) {\n const oldIndex = Math.floor(i * ratio);\n newData[i] = oldData[oldIndex];\n }\n }\n\n return newBuffer;\n};\n\n/**\n * Interleaves audio channels\n */\nconst interleave = (buffer: AudioBuffer, numChannels: number, numFrames: number): Float32Array => {\n if (numChannels === 1) {\n return buffer.getChannelData(0).slice(0, numFrames);\n }\n const result = new Float32Array(numFrames * numChannels);\n const channelData: Float32Array[] = [];\n for (let ch = 0; ch < numChannels; ch++) {\n channelData[ch] = buffer.getChannelData(ch);\n }\n let writeIndex = 0;\n for (let i = 0; i < numFrames; i++) {\n for (let ch = 0; ch < numChannels; ch++) {\n result[writeIndex++] = channelData[ch][i];\n }\n }\n return result;\n};\n\n/**\n * Converts float32 audio data to 16-bit PCM\n */\nconst floatTo16BitPCM = (view: DataView, offset: number, input: Float32Array): void => {\n let pos = offset;\n for (let i = 0; i < input.length; i++, pos += 2) {\n let s = Math.max(-1, Math.min(1, input[i]));\n view.setInt16(pos, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n }\n};\n\n/**\n * Converts float32 array to int16 array\n */\nconst floatTo16 = (input: Float32Array): Int16Array => {\n const output = new Int16Array(input.length);\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output[i] = s < 0 ? s * 0x8000 : s * 0x7fff;\n }\n return output;\n};\n\n/**\n * Writes string to DataView\n */\nconst writeString = (view: DataView, offset: number, str: string): void => {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n};\n","import { Dimensions } from \"./types\";\n\n/**\n * Calculates the scaled dimensions of an element to fit inside a container\n * based on the specified max dimensions.\n *\n * @param width - The original width of the element.\n * @param height - The original height of the element.\n * @param maxWidth - The maximum width of the container.\n * @param maxHeight - The maximum height of the container.\n * @returns An object containing the calculated width and height for the element.\n */\nexport const getScaledDimensions = (\n width: number, \n height: number,\n maxWidth: number,\n maxHeight: number\n ): Dimensions => {\n // If the original dimensions are smaller than or equal to the max values, return the original dimensions\n if (width <= maxWidth && height <= maxHeight) {\n // Ensure the width and height are even numbers\n return {\n width: width % 2 === 0 ? width : width - 1,\n height: height % 2 === 0 ? height : height - 1,\n };\n }\n \n // Calculate scaling factor based on the maximum width and height\n const widthRatio = maxWidth / width;\n const heightRatio = maxHeight / height;\n \n // Use the smaller of the two ratios to maintain the aspect ratio\n const scale = Math.min(widthRatio, heightRatio);\n \n // Calculate the scaled dimensions\n let scaledWidth = Math.round(width * scale);\n let scaledHeight = Math.round(height * scale);\n \n // Ensure the width and height are even numbers\n if (scaledWidth % 2 !== 0) {\n scaledWidth -= 1; // Make width even if it's odd\n }\n if (scaledHeight % 2 !== 0) {\n scaledHeight -= 1; // Make height even if it's odd\n }\n \n // Ensure the scaled width and height fit within the max dimensions\n return {\n width: Math.min(scaledWidth, maxWidth),\n height: Math.min(scaledHeight, maxHeight),\n };\n };\n\n/**\n * Calculates the resized dimensions of an element to fit inside a container\n * based on the specified object-fit strategy (\"contain\", \"cover\", \"fill\", or default).\n *\n * @param objectFit - The object-fit behavior ('contain', 'cover', 'fill', or default/fallback).\n * @param elementSize - The original size of the element (width and height).\n * @param containerSize - The size of the container (width and height).\n * @returns An object containing the calculated width and height for the element.\n */\nexport const getObjectFitSize = (\n objectFit: string,\n elementSize: Dimensions,\n containerSize: Dimensions\n): Dimensions => {\n const elementAspectRatio = elementSize.width / elementSize.height;\n const containerAspectRatio = containerSize.width / containerSize.height;\n\n switch (objectFit) {\n case \"contain\":\n // Fit entire element inside container without cropping, maintaining aspect ratio\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio,\n };\n } else {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height,\n };\n }\n\n case \"cover\":\n // Fill container while maintaining aspect ratio, possibly cropping the element\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height,\n };\n } else {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio,\n };\n }\n\n case \"fill\":\n // Stretch element to completely fill the container, ignoring aspect ratio\n return {\n width: containerSize.width,\n height: containerSize.height,\n };\n\n default:\n // Default behavior: return original size of the element\n return {\n width: elementSize.width,\n height: elementSize.height,\n };\n }\n};\n\n ","/**\n * Converts a Blob URL to a File object.\n *\n * @param blobUrl - The Blob URL to convert.\n * @param fileName - The name to assign to the resulting File.\n * @returns A Promise that resolves to a File object.\n */\nexport const blobUrlToFile = async (blobUrl: string, fileName: string): Promise<File> => {\n const response = await fetch(blobUrl);\n const blob = await response.blob();\n return new File([blob], fileName, { type: blob.type });\n };\n \n /**\n * Triggers a download of a file from a string or Blob.\n *\n * @param content - The content to save, either a string or a Blob.\n * @param type - The MIME type of the content.\n * @param name - The name of the file to be saved.\n */\n export const saveAsFile = (content: string | Blob, type: string, name: string): void => {\n const blob = typeof content === \"string\" ? new Blob([content], { type }) : content;\n const url = URL.createObjectURL(blob);\n \n const a = document.createElement(\"a\");\n a.href = url;\n a.download = name;\n a.click();\n \n // Clean up the URL object after download\n URL.revokeObjectURL(url);\n };\n \n /**\n * Downloads a file from a given URL and triggers a browser download.\n *\n * @param url - The URL of the file to download.\n * @param filename - The name of the file to be saved.\n * @returns A Promise that resolves when the download is initiated or rejects if there is an error.\n */\n export const downloadFile = async (url: string, filename: string): Promise<void> => {\n try {\n const response = await fetch(url);\n const blob = await response.blob();\n const downloadUrl = window.URL.createObjectURL(blob);\n \n const link = document.createElement(\"a\");\n link.href = downloadUrl;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n \n // Clean up\n document.body.removeChild(link);\n window.URL.revokeObjectURL(downloadUrl);\n } catch (error) {\n console.error(\"Error downloading file:\", error);\n throw error;\n }\n };\n ","/**\n * Detects the media type (image, video, or audio) of a given URL by sending a HEAD request.\n *\n * @param url - The URL of the media file.\n * @returns A promise that resolves to 'image', 'video', or 'audio' based on the Content-Type header,\n * or `null` if the type couldn't be determined or the request fails.\n */\nexport const detectMediaTypeFromUrl = async (url: string): Promise<'image' | 'video' | 'audio' | null> => {\n try {\n // Use a HEAD request to fetch only the headers, avoiding download of the full file\n const response = await fetch(url, { method: 'HEAD' });\n \n // Extract the 'Content-Type' header from the response\n const contentType = response.headers.get('Content-Type');\n \n if (!contentType) return null;\n \n // Determine the media type from the content type\n if (contentType.startsWith('image/')) return 'image';\n if (contentType.startsWith('video/')) return 'video';\n if (contentType.startsWith('audio/')) return 'audio';\n \n // Return null if not a recognized media type\n return null;\n } catch (error) {\n console.error('Fetch failed:', error);\n return null;\n }\n };\n "],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/cache.ts","../src/get-audio-duration.ts","../src/limit.ts","../src/get-image-dimensions.ts","../src/get-video-metadata.ts","../src/get-thumbnail.ts","../src/audio-utils.ts","../src/dimension-handler.ts","../src/file-helper.ts","../src/url-helper.ts"],"sourcesContent":["import { Dimensions, VideoMeta } from \"./types\";\n\nexport const imageDimensionsCache: Record<string, Dimensions> = {};\nexport const videoMetaCache: Record<string, VideoMeta> = {};\nexport const audioDurationCache: Record<string, number> = {};","import { audioDurationCache } from \"./cache\";\n\n/**\n * Retrieves the duration (in seconds) of an audio file from a given source URL.\n * Uses a cache to avoid reloading the same audio multiple times for better performance.\n * The function creates a temporary audio element, loads only metadata, and extracts\n * the duration without downloading the entire audio file.\n *\n * @param audioSrc - The source URL of the audio file\n * @returns Promise resolving to the duration of the audio in seconds\n * \n * @example\n * ```js\n * // Get duration of an MP3 file\n * const duration = await getAudioDuration(\"https://example.com/audio.mp3\");\n * // duration = 180.5 (3 minutes and 0.5 seconds)\n * \n * // Get duration of a local blob URL\n * const duration = await getAudioDuration(\"blob:http://localhost:3000/abc123\");\n * // duration = 45.2\n * ```\n */\nexport const getAudioDuration = (audioSrc: string): Promise<number> => {\n // Return cached duration if available\n if (audioDurationCache[audioSrc]) {\n return Promise.resolve(audioDurationCache[audioSrc]);\n }\n\n return new Promise((resolve, reject) => {\n const audio = document.createElement(\"audio\");\n audio.preload = \"metadata\"; // Only load metadata (e.g., duration)\n // Sanitize the audioSrc to prevent XSS by only allowing safe URLs (http, https, blob, data)\n const isSafeUrl = /^(https?:|blob:|data:audio\\/)/i.test(audioSrc);\n if (!isSafeUrl) {\n throw new Error(\"Unsafe audio source URL\");\n }\n audio.src = audioSrc;\n\n // When metadata is loaded, store duration in cache and resolve\n audio.onloadedmetadata = () => {\n const duration = audio.duration;\n audioDurationCache[audioSrc] = duration;\n resolve(duration);\n };\n\n // Handle loading errors\n audio.onerror = () => {\n reject(new Error(\"Failed to load audio metadata\"));\n };\n });\n};\n","// Maximum number of concurrent promises allowed to run\nconst concurrencyLimit = 5;\n\n// Number of currently active (running) promises\nlet activeCount = 0;\n\n// Queue to hold pending tasks waiting to be run when concurrency slots free up\nconst queue: Array<() => void> = [];\n\n/**\n * Runs the next task from the queue if concurrency limit is not reached.\n * Internal helper function that manages the execution of queued tasks.\n * Dequeues and executes the next task when a concurrency slot becomes available.\n */\nfunction runNext() {\n // If no tasks are queued or we're already at the concurrency limit, do nothing\n if (queue.length === 0 || activeCount >= concurrencyLimit) return;\n\n // Dequeue next task\n const next = queue.shift();\n\n if (next) {\n activeCount++; // Mark one more active task\n next(); // Run it\n }\n}\n\n/**\n * Wraps an async function to enforce concurrency limits.\n * If the concurrency limit is reached, the function is queued and executed later\n * when a slot becomes available. This prevents overwhelming the system with too\n * many concurrent operations, which is useful for resource-intensive tasks like\n * media processing or API calls.\n * \n * @param fn - Async function returning a Promise that should be executed with concurrency control\n * @returns Promise resolving with the result of the wrapped function\n * \n * @example\n * ```js\n * // Limit concurrent image processing operations\n * const processImage = async (imageUrl) => {\n * // Expensive image processing operation\n * return await someImageProcessing(imageUrl);\n * };\n * \n * // Process multiple images with concurrency limit\n * const results = await Promise.all([\n * limit(() => processImage(\"image1.jpg\")),\n * limit(() => processImage(\"image2.jpg\")),\n * limit(() => processImage(\"image3.jpg\")),\n * limit(() => processImage(\"image4.jpg\")),\n * limit(() => processImage(\"image5.jpg\")),\n * limit(() => processImage(\"image6.jpg\")), // This will be queued until a slot opens\n * ]);\n * ```\n */\nexport function limit<T>(fn: () => Promise<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n // Task to run the function and handle completion\n const task = () => {\n fn()\n .then(resolve)\n .catch(reject)\n .finally(() => {\n activeCount--; // Mark task as done\n runNext(); // Trigger next queued task, if any\n });\n };\n\n if (activeCount < concurrencyLimit) {\n activeCount++; // Increment active count for immediate run\n task();\n } else {\n // Queue the task if concurrency limit reached\n queue.push(task);\n }\n });\n}\n","import { limit } from \"./limit\";\nimport { Dimensions } from \"./types\";\nimport { imageDimensionsCache } from \"./cache\";\n\n/**\n * Loads an image from the given URL and resolves with its natural dimensions.\n * Internal helper function that creates a temporary Image element to extract\n * the natural width and height of an image without displaying it.\n *\n * @param url - The image URL to load\n * @returns Promise resolving with the image's natural width and height\n */\nconst loadImageDimensions = (url: string): Promise<Dimensions> => {\n return new Promise((resolve, reject) => {\n if (typeof document === 'undefined') {\n reject(new Error('getImageDimensions() is only available in the browser.'));\n return;\n }\n\n const img = new Image();\n img.onload = () => {\n resolve({ width: img.naturalWidth, height: img.naturalHeight });\n };\n img.onerror = reject;\n img.src = url;\n });\n};\n\n/**\n * Gets the dimensions (width and height) of an image from the given URL.\n * Uses a cache to avoid reloading the image if already fetched, and employs\n * a concurrency limiter to control resource usage and prevent overwhelming\n * the browser with too many simultaneous image loads.\n *\n * @param url - The URL of the image to analyze\n * @returns Promise resolving to an object containing width and height\n * \n * @example\n * ```js\n * // Get dimensions of a remote image\n * const dimensions = await getImageDimensions(\"https://example.com/image.jpg\");\n * // dimensions = { width: 1920, height: 1080 }\n * \n * // Get dimensions of a local blob URL\n * const dimensions = await getImageDimensions(\"blob:http://localhost:3000/abc123\");\n * // dimensions = { width: 800, height: 600 }\n * \n * // Subsequent calls for the same URL will use cache\n * const cachedDimensions = await getImageDimensions(\"https://example.com/image.jpg\");\n * // Returns immediately from cache without reloading\n * ```\n */\nexport const getImageDimensions = (url: string): Promise<Dimensions> => {\n // Return cached dimensions if available\n if (imageDimensionsCache[url]) {\n return Promise.resolve(imageDimensionsCache[url]);\n }\n\n // Fetch and cache the dimensions using a concurrency limit\n return limit(() => loadImageDimensions(url)).then((dimensions) => {\n imageDimensionsCache[url] = dimensions;\n return dimensions;\n });\n};\n","import { videoMetaCache } from \"./cache\";\nimport { VideoMeta } from \"./types\";\n\n/**\n * Fetches metadata (width, height, duration) for a given video source.\n * Uses a cache to avoid reloading the same video multiple times for better performance.\n * The function creates a temporary video element, loads only metadata, and extracts\n * the video properties without downloading the entire file.\n *\n * @param videoSrc - The URL or path to the video file\n * @returns Promise resolving to an object containing video metadata\n * \n * @example\n * ```js\n * // Get metadata for a video\n * const metadata = await getVideoMeta(\"https://example.com/video.mp4\");\n * // metadata = { width: 1920, height: 1080, duration: 120.5 }\n * \n * // Get metadata for a local blob URL\n * const metadata = await getVideoMeta(\"blob:http://localhost:3000/abc123\");\n * // metadata = { width: 1280, height: 720, duration: 30.0 }\n * ```\n */\nexport const getVideoMeta = (videoSrc: string): Promise<VideoMeta> => {\n // Return cached metadata if available\n if (videoMetaCache[videoSrc]) {\n return Promise.resolve(videoMetaCache[videoSrc]);\n }\n\n return new Promise<VideoMeta>((resolve, reject) => {\n const video: HTMLVideoElement = document.createElement(\"video\");\n video.preload = \"metadata\"; // Only preload metadata to reduce bandwidth\n // Validate the videoSrc to ensure it's a safe URL before assigning it to video.src\n const isSafeUrl = /^(https?:|blob:|data:video\\/)/i.test(videoSrc);\n if (!isSafeUrl) {\n reject(new Error(\"Unsafe video source URL\"));\n return;\n }\n video.src = videoSrc;\n\n // When metadata is loaded, extract and cache it\n video.onloadedmetadata = () => {\n const meta: VideoMeta = {\n width: video.videoWidth,\n height: video.videoHeight,\n duration: video.duration,\n };\n videoMetaCache[videoSrc] = meta;\n resolve(meta);\n };\n\n // Handle video loading errors\n video.onerror = () => reject(new Error(\"Failed to load video metadata\"));\n });\n};\n","/**\n * Extracts a thumbnail from a video at a specific seek time and playback rate.\n * Creates a hidden video element in the browser, seeks to the specified time,\n * and captures the frame into a canvas, which is then exported as a JPEG data URL or Blob URL.\n * The function handles video loading, seeking, frame capture, and cleanup automatically.\n *\n * @param videoUrl - The URL of the video to extract the thumbnail from\n * @param seekTime - The time in seconds at which to capture the frame\n * @param playbackRate - Playback speed for the video\n * @returns Promise resolving to a thumbnail image URL\n * \n * @example\n * ```js\n * // Extract thumbnail at 5 seconds\n * const thumbnail = await getThumbnail(\"https://example.com/video.mp4\", 5);\n * // thumbnail is a data URL like \"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...\"\n * \n * // Extract thumbnail with custom playback rate\n * const thumbnail = await getThumbnail(\"https://example.com/video.mp4\", 2.5, 1.5);\n * ```\n */\nexport const getThumbnail = async (\n videoUrl: string,\n seekTime = 0.1,\n playbackRate = 1\n ): Promise<string> => {\n return new Promise((resolve, reject) => {\n const video = document.createElement(\"video\");\n video.crossOrigin = \"anonymous\";\n video.muted = true;\n video.playsInline = true;\n video.autoplay = false;\n video.preload = \"auto\";\n video.playbackRate = playbackRate;\n \n // Make video element hidden\n video.style.position = \"absolute\";\n video.style.left = \"-9999px\";\n video.style.top = \"-9999px\";\n video.style.width = \"1px\";\n video.style.height = \"1px\";\n video.style.opacity = \"0\";\n video.style.pointerEvents = \"none\";\n video.style.zIndex = \"-1\";\n \n let timeoutId: number | undefined;\n \n // Cleanup video element and timeout\n const cleanup = () => {\n if (video.parentNode) video.remove();\n if (timeoutId) clearTimeout(timeoutId);\n };\n \n // Handle errors during video loading\n const handleError = () => {\n cleanup();\n reject(new Error(`Failed to load video: ${video.error?.message || \"Unknown error\"}`));\n };\n \n // Once seeked to target frame, capture the image\n const handleSeeked = () => {\n try {\n video.pause();\n \n const canvas = document.createElement(\"canvas\");\n const width = video.videoWidth || 640;\n const height = video.videoHeight || 360;\n canvas.width = width;\n canvas.height = height;\n \n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n cleanup();\n reject(new Error(\"Failed to get canvas context\"));\n return;\n }\n \n // Draw current video frame onto canvas\n ctx.drawImage(video, 0, 0, width, height);\n \n // Attempt to export canvas to base64 image URL\n try {\n const dataUrl = canvas.toDataURL(\"image/jpeg\", 0.8);\n cleanup();\n resolve(dataUrl);\n } catch {\n // Fallback: convert canvas to Blob\n canvas.toBlob((blob) => {\n if (!blob) {\n cleanup();\n reject(new Error(\"Failed to create Blob\"));\n return;\n }\n const blobUrl = URL.createObjectURL(blob);\n cleanup();\n resolve(blobUrl);\n }, \"image/jpeg\", 0.8);\n }\n } catch (err) {\n cleanup();\n reject(new Error(`Error creating thumbnail: ${err}`));\n }\n };\n \n video.addEventListener(\"error\", handleError, { once: true });\n video.addEventListener(\"seeked\", handleSeeked, { once: true });\n \n // After metadata is loaded, seek to the desired frame\n video.addEventListener(\"loadedmetadata\", () => {\n const playPromise = video.play();\n if (playPromise !== undefined) {\n playPromise\n .then(() => {\n video.currentTime = seekTime;\n })\n .catch(() => {\n video.currentTime = seekTime;\n });\n } else {\n video.currentTime = seekTime;\n }\n }, { once: true });\n \n // Timeout protection in case video loading hangs\n timeoutId = window.setTimeout(() => {\n cleanup();\n reject(new Error(\"Video loading timed out\"));\n }, 5000);\n \n // Assign video source and add it to the DOM (helps Safari/iOS behavior)\n video.src = videoUrl;\n document.body.appendChild(video);\n });\n };","/**\n * Audio segment interface for stitching\n */\nexport interface AudioSegment {\n src: string;\n s: number; // start time in seconds\n e: number; // end time in seconds\n volume?: number; // volume level (0-1), defaults to 1, 0 = muted\n}\n\n/**\n * Extracts an audio segment from a media source between start and end times,\n * rendered at the specified playback rate, and returns a Blob URL to an MP3 file.\n * The function fetches the source, decodes the audio track using Web Audio API,\n * renders the segment offline for speed and determinism, encodes it as MP3 using lamejs,\n * and returns an object URL. Callers should revoke the URL when done.\n *\n * @param src - The source URL of the media file\n * @param playbackRate - The playback rate for the extracted segment\n * @param start - The start time in seconds\n * @param end - The end time in seconds\n * @returns Promise resolving to a Blob URL to the extracted MP3 file\n * \n * @example\n * ```js\n * const url = await extractAudio({ src, start: 3, end: 8, playbackRate: 1.25 });\n * const audio = new Audio(url);\n * audio.play();\n * // later: URL.revokeObjectURL(url);\n * ```\n */\nexport const extractAudio = async ({\n src,\n playbackRate = 1,\n start = 0,\n end,\n}: {\n src: string;\n playbackRate?: number;\n start?: number;\n end?: number;\n}): Promise<string> => {\n if (!src) throw new Error(\"src is required\");\n if (playbackRate <= 0) throw new Error(\"playbackRate must be > 0\");\n\n // Basic URL safety check\n const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);\n if (!isSafeUrl) throw new Error(\"Unsafe media source URL\");\n\n // Fetch and decode audio\n const audioBuffer = await fetchAndDecodeAudio(src);\n\n // Normalize time range\n const clampedStart = Math.max(0, start || 0);\n const fullDuration = audioBuffer.duration;\n const clampedEnd = Math.min(\n typeof end === \"number\" ? end : fullDuration,\n fullDuration\n );\n if (clampedEnd <= clampedStart)\n throw new Error(\"Invalid range: end must be greater than start\");\n\n // Render segment with playback rate\n const renderedBuffer = await renderAudioSegment(\n audioBuffer,\n clampedStart,\n clampedEnd,\n playbackRate\n );\n\n // Convert to MP3 and return URL\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\n\n/**\n * Stitches multiple audio segments into a single MP3 file.\n * Creates a timeline where each segment plays at its specified time,\n * with silence filling gaps between segments.\n * \n * @param segments - Array of audio segments with source, start, and end times\n * @param totalDuration - Total duration of the output audio\n * @returns Promise resolving to a Blob URL to the stitched MP3 file\n * \n * @example\n * ```js\n * const segments = [\n * { src: \"audio1.mp3\", s: 0, e: 5, volume: 1.0 },\n * { src: \"audio2.mp3\", s: 10, e: 15, volume: 0.8 }\n * ];\n * const url = await stitchAudio(segments, 15);\n * // Creates a 15-second audio file with segments at specified times\n * ```\n */\nexport const stitchAudio = async (\n segments: AudioSegment[],\n totalDuration?: number\n): Promise<string> => {\n if (!segments || segments.length === 0) {\n throw new Error(\"At least one audio segment is required\");\n }\n\n // Calculate total duration if not provided\n const duration = totalDuration || Math.max(...segments.map(s => s.e));\n\n // Create timeline and render segments\n const renderedBuffer = await createAudioTimeline(segments, duration);\n\n // Convert to MP3 and return URL\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\n\n// ===== SHARED UTILITIES =====\n\n/**\n * Fetches and decodes audio from a URL.\n * \n * @param src - The URL of the audio file to fetch and decode\n * @returns Promise<AudioBuffer> - The decoded audio buffer\n */\nconst fetchAndDecodeAudio = async (src: string): Promise<AudioBuffer> => {\n const response = await fetch(src);\n if (!response.ok) throw new Error(`Failed to fetch source: ${response.status}`);\n \n const arrayBuffer = await response.arrayBuffer();\n return decodeAudioData(arrayBuffer);\n};\n\n/**\n * Decodes audio data using Web Audio API\n */\nconst decodeAudioData = async (arrayBuffer: ArrayBuffer): Promise<AudioBuffer> => {\n const AudioContextCtor: typeof AudioContext =\n (window as any).AudioContext || (window as any).webkitAudioContext;\n if (!AudioContextCtor) throw new Error(\"Web Audio API not supported\");\n \n const audioContext = new AudioContextCtor();\n try {\n return await new Promise<AudioBuffer>((resolve, reject) => {\n audioContext.decodeAudioData(\n arrayBuffer.slice(0),\n (buf) => resolve(buf),\n (err) => reject(err || new Error(\"Failed to decode audio\"))\n );\n });\n } finally {\n audioContext.close();\n }\n};\n\n/**\n * Renders an audio segment with playback rate\n */\nconst renderAudioSegment = async (\n audioBuffer: AudioBuffer,\n start: number,\n end: number,\n playbackRate: number\n): Promise<AudioBuffer> => {\n const OfflineAudioContextCtor: typeof OfflineAudioContext =\n (window as any).OfflineAudioContext || (window as any).webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n\n const sampleRate = audioBuffer.sampleRate;\n const numChannels = audioBuffer.numberOfChannels;\n const sourceDuration = end - start;\n const renderedFrames = Math.max(\n 1,\n Math.ceil((sourceDuration / playbackRate) * sampleRate)\n );\n\n const offline = new OfflineAudioContextCtor(numChannels, renderedFrames, sampleRate);\n const sourceNode = offline.createBufferSource();\n sourceNode.buffer = audioBuffer;\n sourceNode.playbackRate.value = playbackRate;\n sourceNode.connect(offline.destination);\n sourceNode.start(0, start, sourceDuration);\n\n return await offline.startRendering();\n};\n\n/**\n * Creates an audio timeline with multiple segments\n */\nconst createAudioTimeline = async (\n segments: AudioSegment[],\n duration: number\n): Promise<AudioBuffer> => {\n const OfflineAudioContextCtor: typeof OfflineAudioContext =\n (window as any).OfflineAudioContext || (window as any).webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n\n const sampleRate = 44100; // Standard sample rate\n const totalFrames = Math.ceil(duration * sampleRate);\n const offline = new OfflineAudioContextCtor(2, totalFrames, sampleRate); // Stereo output\n\n // Process each segment\n for (const segment of segments) {\n if (segment.s >= segment.e) {\n console.warn(`Invalid segment: start (${segment.s}) >= end (${segment.e})`);\n continue;\n }\n\n // Skip segments with volume 0 (muted)\n const volume = segment.volume ?? 1;\n if (volume <= 0) {\n console.warn(`Skipping muted segment: ${segment.src}`);\n continue;\n }\n\n try {\n const audioBuffer = await fetchAndDecodeAudio(segment.src);\n const segmentDuration = segment.e - segment.s;\n const sourceDuration = Math.min(segmentDuration, audioBuffer.duration);\n\n const source = offline.createBufferSource();\n source.buffer = audioBuffer;\n \n // Apply volume control if not 1.0\n if (volume !== 1) {\n const gainNode = offline.createGain();\n gainNode.gain.value = volume;\n source.connect(gainNode);\n gainNode.connect(offline.destination);\n } else {\n source.connect(offline.destination);\n }\n \n source.start(segment.s, 0, sourceDuration);\n } catch (error) {\n console.warn(`Failed to process segment: ${segment.src}`, error);\n }\n }\n\n return await offline.startRendering();\n};\n\n/**\n * Converts an AudioBuffer to an MP3 Blob using lamejs\n */\nconst audioBufferToMp3 = async (buffer: AudioBuffer): Promise<Blob> => {\n try {\n // Convert AudioBuffer to WAV ArrayBuffer\n const wavArrayBuffer = audioBufferToWavArrayBuffer(buffer);\n \n // Decode WAV back to PCM using AudioContext\n const pcmBuffer = await decodeAudioData(wavArrayBuffer);\n \n // Encode PCM to MP3 using lamejs\n return await encodePcmToMp3(pcmBuffer);\n } catch (error) {\n // Fallback to WAV if MP3 encoding fails\n return audioBufferToWavBlob(buffer);\n }\n};\n\n/**\n * Converts AudioBuffer to WAV ArrayBuffer\n */\nconst audioBufferToWavArrayBuffer = (buffer: AudioBuffer): ArrayBuffer => {\n const numChannels = buffer.numberOfChannels;\n const sampleRate = buffer.sampleRate;\n const numFrames = buffer.length;\n\n // Interleave channels\n const interleaved = interleave(buffer, numChannels, numFrames);\n\n // Create WAV ArrayBuffer\n const bytesPerSample = 2; // 16-bit\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n const dataSize = interleaved.length * bytesPerSample;\n const bufferSize = 44 + dataSize;\n const arrayBuffer = new ArrayBuffer(bufferSize);\n const view = new DataView(arrayBuffer);\n\n // RIFF header\n writeString(view, 0, \"RIFF\");\n view.setUint32(4, 36 + dataSize, true);\n writeString(view, 8, \"WAVE\");\n\n // fmt chunk\n writeString(view, 12, \"fmt \");\n view.setUint32(16, 16, true); // PCM\n view.setUint16(20, 1, true); // audio format = 1 (PCM)\n view.setUint16(22, numChannels, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, blockAlign, true);\n view.setUint16(34, 16, true); // bits per sample\n\n // data chunk\n writeString(view, 36, \"data\");\n view.setUint32(40, dataSize, true);\n\n // PCM samples\n floatTo16BitPCM(view, 44, interleaved);\n\n return arrayBuffer;\n};\n\n/**\n * Encodes PCM AudioBuffer to MP3 using lamejs\n */\nconst encodePcmToMp3 = async (buffer: AudioBuffer): Promise<Blob> => {\n const lamejs = await import(\"lamejs\");\n\n const channels = buffer.numberOfChannels >= 2 ? 2 : 1;\n // Downsample to 22050 Hz for smaller file size (good for voice/speech)\n const targetSampleRate = 22050;\n const downsampledBuffer = downsampleAudioBuffer(buffer, targetSampleRate);\n const kbps = 48; // Reduced bitrate for smaller file size\n\n const mp3encoder = new lamejs.default.Mp3Encoder(channels, targetSampleRate, kbps);\n const samplesPerFrame = 1152;\n\n // Prepare PCM Int16 arrays\n const leftFloat = downsampledBuffer.getChannelData(0);\n const left = floatTo16(leftFloat);\n let right: Int16Array | undefined;\n if (channels === 2) {\n const rightFloat = downsampledBuffer.getChannelData(1);\n right = floatTo16(rightFloat);\n }\n\n const mp3Chunks: Uint8Array[] = [];\n for (let i = 0; i < left.length; i += samplesPerFrame) {\n const leftChunk = left.subarray(i, Math.min(i + samplesPerFrame, left.length));\n let mp3buf: Uint8Array;\n if (channels === 2 && right) {\n const rightChunk = right.subarray(i, Math.min(i + samplesPerFrame, right.length));\n mp3buf = mp3encoder.encodeBuffer(leftChunk, rightChunk);\n } else {\n mp3buf = mp3encoder.encodeBuffer(leftChunk);\n }\n if (mp3buf.length > 0) mp3Chunks.push(mp3buf);\n }\n\n const end = mp3encoder.flush();\n if (end.length > 0) mp3Chunks.push(end);\n\n return new Blob(mp3Chunks, { type: \"audio/mpeg\" });\n};\n\n/**\n * Converts an AudioBuffer to a WAV Blob (fallback)\n */\nconst audioBufferToWavBlob = (buffer: AudioBuffer): Blob => {\n const arrayBuffer = audioBufferToWavArrayBuffer(buffer);\n return new Blob([arrayBuffer], { type: \"audio/wav\" });\n};\n\n/**\n * Downsamples an AudioBuffer to a lower sample rate for smaller file size\n */\nconst downsampleAudioBuffer = (buffer: AudioBuffer, targetSampleRate: number): AudioBuffer => {\n if (buffer.sampleRate === targetSampleRate) {\n return buffer;\n }\n\n const ratio = buffer.sampleRate / targetSampleRate;\n const newLength = Math.round(buffer.length / ratio);\n const newBuffer = new AudioContext().createBuffer(\n buffer.numberOfChannels,\n newLength,\n targetSampleRate\n );\n\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const oldData = buffer.getChannelData(channel);\n const newData = newBuffer.getChannelData(channel);\n \n for (let i = 0; i < newLength; i++) {\n const oldIndex = Math.floor(i * ratio);\n newData[i] = oldData[oldIndex];\n }\n }\n\n return newBuffer;\n};\n\n/**\n * Interleaves audio channels\n */\nconst interleave = (buffer: AudioBuffer, numChannels: number, numFrames: number): Float32Array => {\n if (numChannels === 1) {\n return buffer.getChannelData(0).slice(0, numFrames);\n }\n const result = new Float32Array(numFrames * numChannels);\n const channelData: Float32Array[] = [];\n for (let ch = 0; ch < numChannels; ch++) {\n channelData[ch] = buffer.getChannelData(ch);\n }\n let writeIndex = 0;\n for (let i = 0; i < numFrames; i++) {\n for (let ch = 0; ch < numChannels; ch++) {\n result[writeIndex++] = channelData[ch][i];\n }\n }\n return result;\n};\n\n/**\n * Converts float32 audio data to 16-bit PCM\n */\nconst floatTo16BitPCM = (view: DataView, offset: number, input: Float32Array): void => {\n let pos = offset;\n for (let i = 0; i < input.length; i++, pos += 2) {\n let s = Math.max(-1, Math.min(1, input[i]));\n view.setInt16(pos, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n }\n};\n\n/**\n * Converts float32 array to int16 array\n */\nconst floatTo16 = (input: Float32Array): Int16Array => {\n const output = new Int16Array(input.length);\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output[i] = s < 0 ? s * 0x8000 : s * 0x7fff;\n }\n return output;\n};\n\n/**\n * Writes string to DataView\n */\nconst writeString = (view: DataView, offset: number, str: string): void => {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n};\n","import { Dimensions } from \"./types\";\n\n/**\n * Calculates the scaled dimensions of an element to fit inside a container\n * based on the specified max dimensions while maintaining aspect ratio.\n * Ensures the resulting dimensions are even numbers and fit within the specified bounds.\n * If the original dimensions are already smaller than the max values, returns the original dimensions.\n *\n * @param width - The original width of the element in pixels\n * @param height - The original height of the element in pixels\n * @param maxWidth - The maximum allowed width of the container in pixels\n * @param maxHeight - The maximum allowed height of the container in pixels\n * @returns Object containing the calculated width and height\n * \n * @example\n * ```js\n * // Scale down a large image to fit in a smaller container\n * const scaled = getScaledDimensions(1920, 1080, 800, 600);\n * // scaled = { width: 800, height: 450 }\n * \n * // Small image that doesn't need scaling\n * const scaled = getScaledDimensions(400, 300, 800, 600);\n * // scaled = { width: 400, height: 300 }\n * \n * // Ensure even dimensions for video encoding\n * const scaled = getScaledDimensions(1001, 1001, 1000, 1000);\n * // scaled = { width: 1000, height: 1000 }\n * ```\n */\nexport const getScaledDimensions = (\n width: number, \n height: number,\n maxWidth: number,\n maxHeight: number\n ): Dimensions => {\n // If the original dimensions are smaller than or equal to the max values, return the original dimensions\n if (width <= maxWidth && height <= maxHeight) {\n // Ensure the width and height are even numbers\n return {\n width: width % 2 === 0 ? width : width - 1,\n height: height % 2 === 0 ? height : height - 1,\n };\n }\n \n // Calculate scaling factor based on the maximum width and height\n const widthRatio = maxWidth / width;\n const heightRatio = maxHeight / height;\n \n // Use the smaller of the two ratios to maintain the aspect ratio\n const scale = Math.min(widthRatio, heightRatio);\n \n // Calculate the scaled dimensions\n let scaledWidth = Math.round(width * scale);\n let scaledHeight = Math.round(height * scale);\n \n // Ensure the width and height are even numbers\n if (scaledWidth % 2 !== 0) {\n scaledWidth -= 1; // Make width even if it's odd\n }\n if (scaledHeight % 2 !== 0) {\n scaledHeight -= 1; // Make height even if it's odd\n }\n \n // Ensure the scaled width and height fit within the max dimensions\n return {\n width: Math.min(scaledWidth, maxWidth),\n height: Math.min(scaledHeight, maxHeight),\n };\n };\n\n/**\n * Calculates the resized dimensions of an element to fit inside a container\n * based on the specified object-fit strategy (\"contain\", \"cover\", \"fill\", or default).\n * Implements CSS object-fit behavior for programmatic dimension calculations.\n * Useful for responsive design and media scaling applications.\n *\n * @param objectFit - The object-fit behavior\n * @param elementSize - The original size of the element\n * @param containerSize - The size of the container\n * @returns Object containing the calculated width and height\n * \n * @example\n * ```js\n * // Contain: fit entire element inside container\n * const contained = getObjectFitSize(\"contain\", {width: 1000, height: 500}, {width: 400, height: 300});\n * // contained = { width: 400, height: 200 }\n * \n * // Cover: fill container while maintaining aspect ratio\n * const covered = getObjectFitSize(\"cover\", {width: 1000, height: 500}, {width: 400, height: 300});\n * // covered = { width: 600, height: 300 }\n * \n * // Fill: stretch to completely fill container\n * const filled = getObjectFitSize(\"fill\", {width: 1000, height: 500}, {width: 400, height: 300});\n * // filled = { width: 400, height: 300 }\n * ```\n */\nexport const getObjectFitSize = (\n objectFit: string,\n elementSize: Dimensions,\n containerSize: Dimensions\n): Dimensions => {\n const elementAspectRatio = elementSize.width / elementSize.height;\n const containerAspectRatio = containerSize.width / containerSize.height;\n\n switch (objectFit) {\n case \"contain\":\n // Fit entire element inside container without cropping, maintaining aspect ratio\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio,\n };\n } else {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height,\n };\n }\n\n case \"cover\":\n // Fill container while maintaining aspect ratio, possibly cropping the element\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height,\n };\n } else {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio,\n };\n }\n\n case \"fill\":\n // Stretch element to completely fill the container, ignoring aspect ratio\n return {\n width: containerSize.width,\n height: containerSize.height,\n };\n\n default:\n // Default behavior: return original size of the element\n return {\n width: elementSize.width,\n height: elementSize.height,\n };\n }\n};\n\n ","/**\n * Converts a Blob URL to a File object.\n * Fetches the blob data from the URL and creates a new File object with the specified name.\n * Useful for converting blob URLs back to File objects for upload or processing.\n *\n * @param blobUrl - The Blob URL to convert\n * @param fileName - The name to assign to the resulting File object\n * @returns Promise resolving to a File object with the blob data\n * \n * @example\n * ```js\n * const file = await blobUrlToFile(\"blob:http://localhost:3000/abc123\", \"image.jpg\");\n * // file is now a File object that can be uploaded or processed\n * ```\n */\nexport const blobUrlToFile = async (blobUrl: string, fileName: string): Promise<File> => {\n const response = await fetch(blobUrl);\n const blob = await response.blob();\n return new File([blob], fileName, { type: blob.type });\n };\n \n /**\n * Triggers a download of a file from a string or Blob.\n * Creates a temporary download link and automatically clicks it to initiate the download.\n * The function handles both string content and Blob objects, and automatically cleans up\n * the created object URL after the download is initiated.\n *\n * @param content - The content to save, either a string or a Blob object\n * @param type - The MIME type of the content\n * @param name - The name of the file to be saved\n * \n * @example\n * ```js\n * // Download text content\n * saveAsFile(\"Hello World\", \"text/plain\", \"hello.txt\");\n * \n * // Download JSON data\n * saveAsFile(JSON.stringify({data: \"value\"}), \"application/json\", \"data.json\");\n * \n * // Download blob content\n * saveAsFile(imageBlob, \"image/png\", \"screenshot.png\");\n * ```\n */\n export const saveAsFile = (content: string | Blob, type: string, name: string): void => {\n const blob = typeof content === \"string\" ? new Blob([content], { type }) : content;\n const url = URL.createObjectURL(blob);\n \n const a = document.createElement(\"a\");\n a.href = url;\n a.download = name;\n a.click();\n \n // Clean up the URL object after download\n URL.revokeObjectURL(url);\n };\n \n /**\n * Downloads a file from a given URL and triggers a browser download.\n * Fetches the file content from the provided URL and creates a download link\n * to save it locally. The function handles the entire download process including\n * fetching, blob creation, and cleanup of temporary resources.\n *\n * @param url - The URL of the file to download\n * @param filename - The name of the file to be saved locally\n * @returns Promise resolving when the download is initiated\n * \n * @example\n * ```js\n * await downloadFile(\"https://example.com/image.jpg\", \"downloaded-image.jpg\");\n * // Browser will automatically download the file with the specified name\n * ```\n */\n export const downloadFile = async (url: string, filename: string): Promise<void> => {\n try {\n const response = await fetch(url);\n const blob = await response.blob();\n const downloadUrl = window.URL.createObjectURL(blob);\n \n const link = document.createElement(\"a\");\n link.href = downloadUrl;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n \n // Clean up\n document.body.removeChild(link);\n window.URL.revokeObjectURL(downloadUrl);\n } catch (error) {\n console.error(\"Error downloading file:\", error);\n throw error;\n }\n };\n ","/**\n * Detects the media type (image, video, or audio) of a given URL by sending a HEAD request.\n * Uses a lightweight HEAD request to fetch only the headers, avoiding download of the full file.\n * The function analyzes the Content-Type header to determine the media type category.\n *\n * @param url - The URL of the media file to analyze\n * @returns Promise resolving to the detected media type or null\n * \n * @example\n * ```js\n * // Detect image type\n * const type = await detectMediaTypeFromUrl(\"https://example.com/image.jpg\");\n * // type = \"image\"\n * \n * // Detect video type\n * const type = await detectMediaTypeFromUrl(\"https://example.com/video.mp4\");\n * // type = \"video\"\n * \n * // Detect audio type\n * const type = await detectMediaTypeFromUrl(\"https://example.com/audio.mp3\");\n * // type = \"audio\"\n * \n * // Invalid or inaccessible URL\n * const type = await detectMediaTypeFromUrl(\"https://example.com/invalid\");\n * // type = null\n * ```\n */\nexport const detectMediaTypeFromUrl = async (url: string): Promise<'image' | 'video' | 'audio' | null> => {\n try {\n // Use a HEAD request to fetch only the headers, avoiding download of the full file\n const response = await fetch(url, { method: 'HEAD' });\n \n // Extract the 'Content-Type' header from the response\n const contentType = response.headers.get('Content-Type');\n \n if (!contentType) return null;\n \n // Determine the media type from the content type\n if (contentType.startsWith('image/')) return 'image';\n if (contentType.startsWith('video/')) return 'video';\n if (contentType.startsWith('audio/')) return 'audio';\n \n // Return null if not a recognized media type\n return null;\n } catch (error) {\n console.error('Fetch failed:', error);\n return null;\n }\n };\n "],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/cache.ts","../src/get-audio-duration.ts","../src/limit.ts","../src/get-image-dimensions.ts","../src/get-video-metadata.ts","../src/get-thumbnail.ts","../src/audio-utils.ts","../src/dimension-handler.ts","../src/file-helper.ts","../src/url-helper.ts"],"sourcesContent":["import { Dimensions, VideoMeta } from \"./types\";\n\nexport const imageDimensionsCache: Record<string, Dimensions> = {};\nexport const videoMetaCache: Record<string, VideoMeta> = {};\nexport const audioDurationCache: Record<string, number> = {};","import { audioDurationCache } from \"./cache\";\n\n/**\n * Retrieves the duration (in seconds) of an audio file from a given source URL.\n * Uses a cache to avoid reloading the same audio multiple times.\n *\n * @param audioSrc - The source URL of the audio file.\n * @returns A Promise that resolves to the duration of the audio in seconds.\n */\nexport const getAudioDuration = (audioSrc: string): Promise<number> => {\n // Return cached duration if available\n if (audioDurationCache[audioSrc]) {\n return Promise.resolve(audioDurationCache[audioSrc]);\n }\n\n return new Promise((resolve, reject) => {\n const audio = document.createElement(\"audio\");\n audio.preload = \"metadata\"; // Only load metadata (e.g., duration)\n // Sanitize the audioSrc to prevent XSS by only allowing safe URLs (http, https, blob, data)\n const isSafeUrl = /^(https?:|blob:|data:audio\\/)/i.test(audioSrc);\n if (!isSafeUrl) {\n throw new Error(\"Unsafe audio source URL\");\n }\n audio.src = audioSrc;\n\n // When metadata is loaded, store duration in cache and resolve\n audio.onloadedmetadata = () => {\n const duration = audio.duration;\n audioDurationCache[audioSrc] = duration;\n resolve(duration);\n };\n\n // Handle loading errors\n audio.onerror = () => {\n reject(new Error(\"Failed to load audio metadata\"));\n };\n });\n};\n","// Maximum number of concurrent promises allowed to run\nconst concurrencyLimit = 5;\n\n// Number of currently active (running) promises\nlet activeCount = 0;\n\n// Queue to hold pending tasks waiting to be run when concurrency slots free up\nconst queue: Array<() => void> = [];\n\n/**\n * Runs the next task from the queue if concurrency limit is not reached.\n */\nfunction runNext() {\n // If no tasks are queued or we're already at the concurrency limit, do nothing\n if (queue.length === 0 || activeCount >= concurrencyLimit) return;\n\n // Dequeue next task\n const next = queue.shift();\n\n if (next) {\n activeCount++; // Mark one more active task\n next(); // Run it\n }\n}\n\n/**\n * Wraps an async function to enforce concurrency limits.\n * If concurrency limit is reached, the function is queued and executed later.\n * \n * @param fn - Async function returning a Promise\n * @returns Promise that resolves/rejects with fn's result\n */\nexport function limit<T>(fn: () => Promise<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n // Task to run the function and handle completion\n const task = () => {\n fn()\n .then(resolve)\n .catch(reject)\n .finally(() => {\n activeCount--; // Mark task as done\n runNext(); // Trigger next queued task, if any\n });\n };\n\n if (activeCount < concurrencyLimit) {\n activeCount++; // Increment active count for immediate run\n task();\n } else {\n // Queue the task if concurrency limit reached\n queue.push(task);\n }\n });\n}\n","import { limit } from \"./limit\";\nimport { Dimensions } from \"./types\";\nimport { imageDimensionsCache } from \"./cache\";\n\n/**\n * Loads an image from the given URL and resolves with its natural dimensions.\n *\n * @param url - The image URL to load.\n * @returns A Promise that resolves with the image's width and height.\n */\nconst loadImageDimensions = (url: string): Promise<Dimensions> => {\n return new Promise((resolve, reject) => {\n if (typeof document === 'undefined') {\n reject(new Error('getImageDimensions() is only available in the browser.'));\n return;\n }\n\n const img = new Image();\n img.onload = () => {\n resolve({ width: img.naturalWidth, height: img.naturalHeight });\n };\n img.onerror = reject;\n img.src = url;\n });\n};\n\n/**\n * Gets the dimensions (width and height) of an image from the given URL.\n * Uses a cache to avoid reloading the image if already fetched.\n * Also uses a concurrency limiter to control resource usage.\n *\n * @param url - The URL of the image.\n * @returns A Promise that resolves to an object containing `width` and `height`.\n */\nexport const getImageDimensions = (url: string): Promise<Dimensions> => {\n // Return cached dimensions if available\n if (imageDimensionsCache[url]) {\n return Promise.resolve(imageDimensionsCache[url]);\n }\n\n // Fetch and cache the dimensions using a concurrency limit\n return limit(() => loadImageDimensions(url)).then((dimensions) => {\n imageDimensionsCache[url] = dimensions;\n return dimensions;\n });\n};\n","import { videoMetaCache } from \"./cache\";\nimport { VideoMeta } from \"./types\";\n\n/**\n * Fetches metadata (width, height, duration) for a given video source.\n * If metadata has already been fetched and cached, it returns the cached data.\n *\n * @param videoSrc - The URL or path to the video file.\n * @returns A Promise that resolves to an object containing video metadata.\n */\nexport const getVideoMeta = (videoSrc: string): Promise<VideoMeta> => {\n // Return cached metadata if available\n if (videoMetaCache[videoSrc]) {\n return Promise.resolve(videoMetaCache[videoSrc]);\n }\n\n return new Promise<VideoMeta>((resolve, reject) => {\n const video: HTMLVideoElement = document.createElement(\"video\");\n video.preload = \"metadata\"; // Only preload metadata to reduce bandwidth\n // Validate the videoSrc to ensure it's a safe URL before assigning it to video.src\n const isSafeUrl = /^(https?:|blob:|data:video\\/)/i.test(videoSrc);\n if (!isSafeUrl) {\n reject(new Error(\"Unsafe video source URL\"));\n return;\n }\n video.src = videoSrc;\n\n // When metadata is loaded, extract and cache it\n video.onloadedmetadata = () => {\n const meta: VideoMeta = {\n width: video.videoWidth,\n height: video.videoHeight,\n duration: video.duration,\n };\n videoMetaCache[videoSrc] = meta;\n resolve(meta);\n };\n\n // Handle video loading errors\n video.onerror = () => reject(new Error(\"Failed to load video metadata\"));\n });\n};\n","/**\n * Extracts a thumbnail from a video at a specific seek time and playback rate.\n *\n * This function creates a hidden `<video>` element in the browser,\n * seeks to the specified time, and captures the frame into a canvas,\n * which is then exported as a JPEG data URL or Blob URL.\n *\n * @param videoUrl - The URL of the video to extract the thumbnail from.\n * @param seekTime - The time in seconds at which to capture the frame. Default is 0.1s.\n * @param playbackRate - Playback speed for the video. Default is 1.\n * @returns A Promise that resolves to a thumbnail image URL (either a base64 data URL or blob URL).\n */\nexport const getThumbnail = async (\n videoUrl: string,\n seekTime = 0.1,\n playbackRate = 1\n ): Promise<string> => {\n return new Promise((resolve, reject) => {\n const video = document.createElement(\"video\");\n video.crossOrigin = \"anonymous\";\n video.muted = true;\n video.playsInline = true;\n video.autoplay = false;\n video.preload = \"auto\";\n video.playbackRate = playbackRate;\n \n // Make video element hidden\n video.style.position = \"absolute\";\n video.style.left = \"-9999px\";\n video.style.top = \"-9999px\";\n video.style.width = \"1px\";\n video.style.height = \"1px\";\n video.style.opacity = \"0\";\n video.style.pointerEvents = \"none\";\n video.style.zIndex = \"-1\";\n \n let timeoutId: number | undefined;\n \n // Cleanup video element and timeout\n const cleanup = () => {\n if (video.parentNode) video.remove();\n if (timeoutId) clearTimeout(timeoutId);\n };\n \n // Handle errors during video loading\n const handleError = () => {\n cleanup();\n reject(new Error(`Failed to load video: ${video.error?.message || \"Unknown error\"}`));\n };\n \n // Once seeked to target frame, capture the image\n const handleSeeked = () => {\n try {\n video.pause();\n \n const canvas = document.createElement(\"canvas\");\n const width = video.videoWidth || 640;\n const height = video.videoHeight || 360;\n canvas.width = width;\n canvas.height = height;\n \n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n cleanup();\n reject(new Error(\"Failed to get canvas context\"));\n return;\n }\n \n // Draw current video frame onto canvas\n ctx.drawImage(video, 0, 0, width, height);\n \n // Attempt to export canvas to base64 image URL\n try {\n const dataUrl = canvas.toDataURL(\"image/jpeg\", 0.8);\n cleanup();\n resolve(dataUrl);\n } catch {\n // Fallback: convert canvas to Blob\n canvas.toBlob((blob) => {\n if (!blob) {\n cleanup();\n reject(new Error(\"Failed to create Blob\"));\n return;\n }\n const blobUrl = URL.createObjectURL(blob);\n cleanup();\n resolve(blobUrl);\n }, \"image/jpeg\", 0.8);\n }\n } catch (err) {\n cleanup();\n reject(new Error(`Error creating thumbnail: ${err}`));\n }\n };\n \n video.addEventListener(\"error\", handleError, { once: true });\n video.addEventListener(\"seeked\", handleSeeked, { once: true });\n \n // After metadata is loaded, seek to the desired frame\n video.addEventListener(\"loadedmetadata\", () => {\n const playPromise = video.play();\n if (playPromise !== undefined) {\n playPromise\n .then(() => {\n video.currentTime = seekTime;\n })\n .catch(() => {\n video.currentTime = seekTime;\n });\n } else {\n video.currentTime = seekTime;\n }\n }, { once: true });\n \n // Timeout protection in case video loading hangs\n timeoutId = window.setTimeout(() => {\n cleanup();\n reject(new Error(\"Video loading timed out\"));\n }, 5000);\n \n // Assign video source and add it to the DOM (helps Safari/iOS behavior)\n video.src = videoUrl;\n document.body.appendChild(video);\n });\n };","/**\n * Audio segment interface for stitching\n */\nexport interface AudioSegment {\n src: string;\n s: number; // start time in seconds\n e: number; // end time in seconds\n volume?: number; // volume level (0-1), defaults to 1, 0 = muted\n}\n\n/**\n * Extracts an audio segment from a media source (e.g., video) between start and end times,\n * rendered at the specified playback rate, and returns a Blob URL to an MP3 file.\n *\n * The function fetches the source, decodes the audio track using Web Audio API,\n * renders the segment offline for speed and determinism, encodes it as MP3 using lamejs,\n * and returns an object URL. Callers should revoke the URL when done.\n *\n * Example:\n * const url = await extractAudio({ src, start: 3, end: 8, playbackRate: 1.25 });\n * const audio = new Audio(url);\n * audio.play();\n * // later: URL.revokeObjectURL(url);\n */\nexport const extractAudio = async ({\n src,\n playbackRate = 1,\n start = 0,\n end,\n}: {\n src: string;\n playbackRate?: number;\n start?: number;\n end?: number;\n}): Promise<string> => {\n if (!src) throw new Error(\"src is required\");\n if (playbackRate <= 0) throw new Error(\"playbackRate must be > 0\");\n\n // Basic URL safety check\n const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);\n if (!isSafeUrl) throw new Error(\"Unsafe media source URL\");\n\n // Fetch and decode audio\n const audioBuffer = await fetchAndDecodeAudio(src);\n\n // Normalize time range\n const clampedStart = Math.max(0, start || 0);\n const fullDuration = audioBuffer.duration;\n const clampedEnd = Math.min(\n typeof end === \"number\" ? end : fullDuration,\n fullDuration\n );\n if (clampedEnd <= clampedStart)\n throw new Error(\"Invalid range: end must be greater than start\");\n\n // Render segment with playback rate\n const renderedBuffer = await renderAudioSegment(\n audioBuffer,\n clampedStart,\n clampedEnd,\n playbackRate\n );\n\n // Convert to MP3 and return URL\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\n\n/**\n * Stitches multiple audio segments into a single MP3 file.\n * Creates a timeline where each segment plays at its specified time,\n * with silence filling gaps between segments.\n * \n * @param segments - Array of audio segments with source, start, and end times\n * @param totalDuration - Total duration of the output audio (optional, auto-calculated if not provided)\n * @returns Promise<string> - Blob URL to the stitched MP3 file\n * \n * Example:\n * const segments = [\n * { src: \"audio1.mp3\", s: 0, e: 2, volume: 1.0 },\n * { src: \"audio2.mp3\", s: 1, e: 4, volume: 0.5 }, // overlaps with audio1\n * { src: \"audio3.mp3\", s: 4, e: 7, volume: 0 } // muted, won't be included\n * ];\n * const url = await stitchAudio(segments, 7); // 7 second output with overlapping audio\n */\nexport const stitchAudio = async (\n segments: AudioSegment[],\n totalDuration?: number\n): Promise<string> => {\n if (!segments || segments.length === 0) {\n throw new Error(\"At least one audio segment is required\");\n }\n\n // Calculate total duration if not provided\n const duration = totalDuration || Math.max(...segments.map(s => s.e));\n\n // Create timeline and render segments\n const renderedBuffer = await createAudioTimeline(segments, duration);\n\n // Convert to MP3 and return URL\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\n\n// ===== SHARED UTILITIES =====\n\n/**\n * Fetches and decodes audio from a URL\n */\nconst fetchAndDecodeAudio = async (src: string): Promise<AudioBuffer> => {\n const response = await fetch(src);\n if (!response.ok) throw new Error(`Failed to fetch source: ${response.status}`);\n \n const arrayBuffer = await response.arrayBuffer();\n return decodeAudioData(arrayBuffer);\n};\n\n/**\n * Decodes audio data using Web Audio API\n */\nconst decodeAudioData = async (arrayBuffer: ArrayBuffer): Promise<AudioBuffer> => {\n const AudioContextCtor: typeof AudioContext =\n (window as any).AudioContext || (window as any).webkitAudioContext;\n if (!AudioContextCtor) throw new Error(\"Web Audio API not supported\");\n \n const audioContext = new AudioContextCtor();\n try {\n return await new Promise<AudioBuffer>((resolve, reject) => {\n audioContext.decodeAudioData(\n arrayBuffer.slice(0),\n (buf) => resolve(buf),\n (err) => reject(err || new Error(\"Failed to decode audio\"))\n );\n });\n } finally {\n audioContext.close();\n }\n};\n\n/**\n * Renders an audio segment with playback rate\n */\nconst renderAudioSegment = async (\n audioBuffer: AudioBuffer,\n start: number,\n end: number,\n playbackRate: number\n): Promise<AudioBuffer> => {\n const OfflineAudioContextCtor: typeof OfflineAudioContext =\n (window as any).OfflineAudioContext || (window as any).webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n\n const sampleRate = audioBuffer.sampleRate;\n const numChannels = audioBuffer.numberOfChannels;\n const sourceDuration = end - start;\n const renderedFrames = Math.max(\n 1,\n Math.ceil((sourceDuration / playbackRate) * sampleRate)\n );\n\n const offline = new OfflineAudioContextCtor(numChannels, renderedFrames, sampleRate);\n const sourceNode = offline.createBufferSource();\n sourceNode.buffer = audioBuffer;\n sourceNode.playbackRate.value = playbackRate;\n sourceNode.connect(offline.destination);\n sourceNode.start(0, start, sourceDuration);\n\n return await offline.startRendering();\n};\n\n/**\n * Creates an audio timeline with multiple segments\n */\nconst createAudioTimeline = async (\n segments: AudioSegment[],\n duration: number\n): Promise<AudioBuffer> => {\n const OfflineAudioContextCtor: typeof OfflineAudioContext =\n (window as any).OfflineAudioContext || (window as any).webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n\n const sampleRate = 44100; // Standard sample rate\n const totalFrames = Math.ceil(duration * sampleRate);\n const offline = new OfflineAudioContextCtor(2, totalFrames, sampleRate); // Stereo output\n\n // Process each segment\n for (const segment of segments) {\n if (segment.s >= segment.e) {\n console.warn(`Invalid segment: start (${segment.s}) >= end (${segment.e})`);\n continue;\n }\n\n // Skip segments with volume 0 (muted)\n const volume = segment.volume ?? 1;\n if (volume <= 0) {\n console.warn(`Skipping muted segment: ${segment.src}`);\n continue;\n }\n\n try {\n const audioBuffer = await fetchAndDecodeAudio(segment.src);\n const segmentDuration = segment.e - segment.s;\n const sourceDuration = Math.min(segmentDuration, audioBuffer.duration);\n\n const source = offline.createBufferSource();\n source.buffer = audioBuffer;\n \n // Apply volume control if not 1.0\n if (volume !== 1) {\n const gainNode = offline.createGain();\n gainNode.gain.value = volume;\n source.connect(gainNode);\n gainNode.connect(offline.destination);\n } else {\n source.connect(offline.destination);\n }\n \n source.start(segment.s, 0, sourceDuration);\n } catch (error) {\n console.warn(`Failed to process segment: ${segment.src}`, error);\n }\n }\n\n return await offline.startRendering();\n};\n\n/**\n * Converts an AudioBuffer to an MP3 Blob using lamejs\n */\nconst audioBufferToMp3 = async (buffer: AudioBuffer): Promise<Blob> => {\n try {\n // Convert AudioBuffer to WAV ArrayBuffer\n const wavArrayBuffer = audioBufferToWavArrayBuffer(buffer);\n \n // Decode WAV back to PCM using AudioContext\n const pcmBuffer = await decodeAudioData(wavArrayBuffer);\n \n // Encode PCM to MP3 using lamejs\n return await encodePcmToMp3(pcmBuffer);\n } catch (error) {\n // Fallback to WAV if MP3 encoding fails\n return audioBufferToWavBlob(buffer);\n }\n};\n\n/**\n * Converts AudioBuffer to WAV ArrayBuffer\n */\nconst audioBufferToWavArrayBuffer = (buffer: AudioBuffer): ArrayBuffer => {\n const numChannels = buffer.numberOfChannels;\n const sampleRate = buffer.sampleRate;\n const numFrames = buffer.length;\n\n // Interleave channels\n const interleaved = interleave(buffer, numChannels, numFrames);\n\n // Create WAV ArrayBuffer\n const bytesPerSample = 2; // 16-bit\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n const dataSize = interleaved.length * bytesPerSample;\n const bufferSize = 44 + dataSize;\n const arrayBuffer = new ArrayBuffer(bufferSize);\n const view = new DataView(arrayBuffer);\n\n // RIFF header\n writeString(view, 0, \"RIFF\");\n view.setUint32(4, 36 + dataSize, true);\n writeString(view, 8, \"WAVE\");\n\n // fmt chunk\n writeString(view, 12, \"fmt \");\n view.setUint32(16, 16, true); // PCM\n view.setUint16(20, 1, true); // audio format = 1 (PCM)\n view.setUint16(22, numChannels, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, blockAlign, true);\n view.setUint16(34, 16, true); // bits per sample\n\n // data chunk\n writeString(view, 36, \"data\");\n view.setUint32(40, dataSize, true);\n\n // PCM samples\n floatTo16BitPCM(view, 44, interleaved);\n\n return arrayBuffer;\n};\n\n/**\n * Encodes PCM AudioBuffer to MP3 using lamejs\n */\nconst encodePcmToMp3 = async (buffer: AudioBuffer): Promise<Blob> => {\n const lamejs = await import(\"lamejs\");\n\n const channels = buffer.numberOfChannels >= 2 ? 2 : 1;\n // Downsample to 22050 Hz for smaller file size (good for voice/speech)\n const targetSampleRate = 22050;\n const downsampledBuffer = downsampleAudioBuffer(buffer, targetSampleRate);\n const kbps = 48; // Reduced bitrate for smaller file size\n\n const mp3encoder = new lamejs.default.Mp3Encoder(channels, targetSampleRate, kbps);\n const samplesPerFrame = 1152;\n\n // Prepare PCM Int16 arrays\n const leftFloat = downsampledBuffer.getChannelData(0);\n const left = floatTo16(leftFloat);\n let right: Int16Array | undefined;\n if (channels === 2) {\n const rightFloat = downsampledBuffer.getChannelData(1);\n right = floatTo16(rightFloat);\n }\n\n const mp3Chunks: Uint8Array[] = [];\n for (let i = 0; i < left.length; i += samplesPerFrame) {\n const leftChunk = left.subarray(i, Math.min(i + samplesPerFrame, left.length));\n let mp3buf: Uint8Array;\n if (channels === 2 && right) {\n const rightChunk = right.subarray(i, Math.min(i + samplesPerFrame, right.length));\n mp3buf = mp3encoder.encodeBuffer(leftChunk, rightChunk);\n } else {\n mp3buf = mp3encoder.encodeBuffer(leftChunk);\n }\n if (mp3buf.length > 0) mp3Chunks.push(mp3buf);\n }\n\n const end = mp3encoder.flush();\n if (end.length > 0) mp3Chunks.push(end);\n\n return new Blob(mp3Chunks, { type: \"audio/mpeg\" });\n};\n\n/**\n * Converts an AudioBuffer to a WAV Blob (fallback)\n */\nconst audioBufferToWavBlob = (buffer: AudioBuffer): Blob => {\n const arrayBuffer = audioBufferToWavArrayBuffer(buffer);\n return new Blob([arrayBuffer], { type: \"audio/wav\" });\n};\n\n/**\n * Downsamples an AudioBuffer to a lower sample rate for smaller file size\n */\nconst downsampleAudioBuffer = (buffer: AudioBuffer, targetSampleRate: number): AudioBuffer => {\n if (buffer.sampleRate === targetSampleRate) {\n return buffer;\n }\n\n const ratio = buffer.sampleRate / targetSampleRate;\n const newLength = Math.round(buffer.length / ratio);\n const newBuffer = new AudioContext().createBuffer(\n buffer.numberOfChannels,\n newLength,\n targetSampleRate\n );\n\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const oldData = buffer.getChannelData(channel);\n const newData = newBuffer.getChannelData(channel);\n \n for (let i = 0; i < newLength; i++) {\n const oldIndex = Math.floor(i * ratio);\n newData[i] = oldData[oldIndex];\n }\n }\n\n return newBuffer;\n};\n\n/**\n * Interleaves audio channels\n */\nconst interleave = (buffer: AudioBuffer, numChannels: number, numFrames: number): Float32Array => {\n if (numChannels === 1) {\n return buffer.getChannelData(0).slice(0, numFrames);\n }\n const result = new Float32Array(numFrames * numChannels);\n const channelData: Float32Array[] = [];\n for (let ch = 0; ch < numChannels; ch++) {\n channelData[ch] = buffer.getChannelData(ch);\n }\n let writeIndex = 0;\n for (let i = 0; i < numFrames; i++) {\n for (let ch = 0; ch < numChannels; ch++) {\n result[writeIndex++] = channelData[ch][i];\n }\n }\n return result;\n};\n\n/**\n * Converts float32 audio data to 16-bit PCM\n */\nconst floatTo16BitPCM = (view: DataView, offset: number, input: Float32Array): void => {\n let pos = offset;\n for (let i = 0; i < input.length; i++, pos += 2) {\n let s = Math.max(-1, Math.min(1, input[i]));\n view.setInt16(pos, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n }\n};\n\n/**\n * Converts float32 array to int16 array\n */\nconst floatTo16 = (input: Float32Array): Int16Array => {\n const output = new Int16Array(input.length);\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output[i] = s < 0 ? s * 0x8000 : s * 0x7fff;\n }\n return output;\n};\n\n/**\n * Writes string to DataView\n */\nconst writeString = (view: DataView, offset: number, str: string): void => {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n};\n","import { Dimensions } from \"./types\";\n\n/**\n * Calculates the scaled dimensions of an element to fit inside a container\n * based on the specified max dimensions.\n *\n * @param width - The original width of the element.\n * @param height - The original height of the element.\n * @param maxWidth - The maximum width of the container.\n * @param maxHeight - The maximum height of the container.\n * @returns An object containing the calculated width and height for the element.\n */\nexport const getScaledDimensions = (\n width: number, \n height: number,\n maxWidth: number,\n maxHeight: number\n ): Dimensions => {\n // If the original dimensions are smaller than or equal to the max values, return the original dimensions\n if (width <= maxWidth && height <= maxHeight) {\n // Ensure the width and height are even numbers\n return {\n width: width % 2 === 0 ? width : width - 1,\n height: height % 2 === 0 ? height : height - 1,\n };\n }\n \n // Calculate scaling factor based on the maximum width and height\n const widthRatio = maxWidth / width;\n const heightRatio = maxHeight / height;\n \n // Use the smaller of the two ratios to maintain the aspect ratio\n const scale = Math.min(widthRatio, heightRatio);\n \n // Calculate the scaled dimensions\n let scaledWidth = Math.round(width * scale);\n let scaledHeight = Math.round(height * scale);\n \n // Ensure the width and height are even numbers\n if (scaledWidth % 2 !== 0) {\n scaledWidth -= 1; // Make width even if it's odd\n }\n if (scaledHeight % 2 !== 0) {\n scaledHeight -= 1; // Make height even if it's odd\n }\n \n // Ensure the scaled width and height fit within the max dimensions\n return {\n width: Math.min(scaledWidth, maxWidth),\n height: Math.min(scaledHeight, maxHeight),\n };\n };\n\n/**\n * Calculates the resized dimensions of an element to fit inside a container\n * based on the specified object-fit strategy (\"contain\", \"cover\", \"fill\", or default).\n *\n * @param objectFit - The object-fit behavior ('contain', 'cover', 'fill', or default/fallback).\n * @param elementSize - The original size of the element (width and height).\n * @param containerSize - The size of the container (width and height).\n * @returns An object containing the calculated width and height for the element.\n */\nexport const getObjectFitSize = (\n objectFit: string,\n elementSize: Dimensions,\n containerSize: Dimensions\n): Dimensions => {\n const elementAspectRatio = elementSize.width / elementSize.height;\n const containerAspectRatio = containerSize.width / containerSize.height;\n\n switch (objectFit) {\n case \"contain\":\n // Fit entire element inside container without cropping, maintaining aspect ratio\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio,\n };\n } else {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height,\n };\n }\n\n case \"cover\":\n // Fill container while maintaining aspect ratio, possibly cropping the element\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height,\n };\n } else {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio,\n };\n }\n\n case \"fill\":\n // Stretch element to completely fill the container, ignoring aspect ratio\n return {\n width: containerSize.width,\n height: containerSize.height,\n };\n\n default:\n // Default behavior: return original size of the element\n return {\n width: elementSize.width,\n height: elementSize.height,\n };\n }\n};\n\n ","/**\n * Converts a Blob URL to a File object.\n *\n * @param blobUrl - The Blob URL to convert.\n * @param fileName - The name to assign to the resulting File.\n * @returns A Promise that resolves to a File object.\n */\nexport const blobUrlToFile = async (blobUrl: string, fileName: string): Promise<File> => {\n const response = await fetch(blobUrl);\n const blob = await response.blob();\n return new File([blob], fileName, { type: blob.type });\n };\n \n /**\n * Triggers a download of a file from a string or Blob.\n *\n * @param content - The content to save, either a string or a Blob.\n * @param type - The MIME type of the content.\n * @param name - The name of the file to be saved.\n */\n export const saveAsFile = (content: string | Blob, type: string, name: string): void => {\n const blob = typeof content === \"string\" ? new Blob([content], { type }) : content;\n const url = URL.createObjectURL(blob);\n \n const a = document.createElement(\"a\");\n a.href = url;\n a.download = name;\n a.click();\n \n // Clean up the URL object after download\n URL.revokeObjectURL(url);\n };\n \n /**\n * Downloads a file from a given URL and triggers a browser download.\n *\n * @param url - The URL of the file to download.\n * @param filename - The name of the file to be saved.\n * @returns A Promise that resolves when the download is initiated or rejects if there is an error.\n */\n export const downloadFile = async (url: string, filename: string): Promise<void> => {\n try {\n const response = await fetch(url);\n const blob = await response.blob();\n const downloadUrl = window.URL.createObjectURL(blob);\n \n const link = document.createElement(\"a\");\n link.href = downloadUrl;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n \n // Clean up\n document.body.removeChild(link);\n window.URL.revokeObjectURL(downloadUrl);\n } catch (error) {\n console.error(\"Error downloading file:\", error);\n throw error;\n }\n };\n ","/**\n * Detects the media type (image, video, or audio) of a given URL by sending a HEAD request.\n *\n * @param url - The URL of the media file.\n * @returns A promise that resolves to 'image', 'video', or 'audio' based on the Content-Type header,\n * or `null` if the type couldn't be determined or the request fails.\n */\nexport const detectMediaTypeFromUrl = async (url: string): Promise<'image' | 'video' | 'audio' | null> => {\n try {\n // Use a HEAD request to fetch only the headers, avoiding download of the full file\n const response = await fetch(url, { method: 'HEAD' });\n \n // Extract the 'Content-Type' header from the response\n const contentType = response.headers.get('Content-Type');\n \n if (!contentType) return null;\n \n // Determine the media type from the content type\n if (contentType.startsWith('image/')) return 'image';\n if (contentType.startsWith('video/')) return 'video';\n if (contentType.startsWith('audio/')) return 'audio';\n \n // Return null if not a recognized media type\n return null;\n } catch (error) {\n console.error('Fetch failed:', error);\n return null;\n }\n };\n "],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/cache.ts","../src/get-audio-duration.ts","../src/limit.ts","../src/get-image-dimensions.ts","../src/get-video-metadata.ts","../src/get-thumbnail.ts","../src/audio-utils.ts","../src/dimension-handler.ts","../src/file-helper.ts","../src/url-helper.ts"],"sourcesContent":["import { Dimensions, VideoMeta } from \"./types\";\n\nexport const imageDimensionsCache: Record<string, Dimensions> = {};\nexport const videoMetaCache: Record<string, VideoMeta> = {};\nexport const audioDurationCache: Record<string, number> = {};","import { audioDurationCache } from \"./cache\";\n\n/**\n * Retrieves the duration (in seconds) of an audio file from a given source URL.\n * Uses a cache to avoid reloading the same audio multiple times for better performance.\n * The function creates a temporary audio element, loads only metadata, and extracts\n * the duration without downloading the entire audio file.\n *\n * @param audioSrc - The source URL of the audio file\n * @returns Promise resolving to the duration of the audio in seconds\n * \n * @example\n * ```js\n * // Get duration of an MP3 file\n * const duration = await getAudioDuration(\"https://example.com/audio.mp3\");\n * // duration = 180.5 (3 minutes and 0.5 seconds)\n * \n * // Get duration of a local blob URL\n * const duration = await getAudioDuration(\"blob:http://localhost:3000/abc123\");\n * // duration = 45.2\n * ```\n */\nexport const getAudioDuration = (audioSrc: string): Promise<number> => {\n // Return cached duration if available\n if (audioDurationCache[audioSrc]) {\n return Promise.resolve(audioDurationCache[audioSrc]);\n }\n\n return new Promise((resolve, reject) => {\n const audio = document.createElement(\"audio\");\n audio.preload = \"metadata\"; // Only load metadata (e.g., duration)\n // Sanitize the audioSrc to prevent XSS by only allowing safe URLs (http, https, blob, data)\n const isSafeUrl = /^(https?:|blob:|data:audio\\/)/i.test(audioSrc);\n if (!isSafeUrl) {\n throw new Error(\"Unsafe audio source URL\");\n }\n audio.src = audioSrc;\n\n // When metadata is loaded, store duration in cache and resolve\n audio.onloadedmetadata = () => {\n const duration = audio.duration;\n audioDurationCache[audioSrc] = duration;\n resolve(duration);\n };\n\n // Handle loading errors\n audio.onerror = () => {\n reject(new Error(\"Failed to load audio metadata\"));\n };\n });\n};\n","// Maximum number of concurrent promises allowed to run\nconst concurrencyLimit = 5;\n\n// Number of currently active (running) promises\nlet activeCount = 0;\n\n// Queue to hold pending tasks waiting to be run when concurrency slots free up\nconst queue: Array<() => void> = [];\n\n/**\n * Runs the next task from the queue if concurrency limit is not reached.\n * Internal helper function that manages the execution of queued tasks.\n * Dequeues and executes the next task when a concurrency slot becomes available.\n */\nfunction runNext() {\n // If no tasks are queued or we're already at the concurrency limit, do nothing\n if (queue.length === 0 || activeCount >= concurrencyLimit) return;\n\n // Dequeue next task\n const next = queue.shift();\n\n if (next) {\n activeCount++; // Mark one more active task\n next(); // Run it\n }\n}\n\n/**\n * Wraps an async function to enforce concurrency limits.\n * If the concurrency limit is reached, the function is queued and executed later\n * when a slot becomes available. This prevents overwhelming the system with too\n * many concurrent operations, which is useful for resource-intensive tasks like\n * media processing or API calls.\n * \n * @param fn - Async function returning a Promise that should be executed with concurrency control\n * @returns Promise resolving with the result of the wrapped function\n * \n * @example\n * ```js\n * // Limit concurrent image processing operations\n * const processImage = async (imageUrl) => {\n * // Expensive image processing operation\n * return await someImageProcessing(imageUrl);\n * };\n * \n * // Process multiple images with concurrency limit\n * const results = await Promise.all([\n * limit(() => processImage(\"image1.jpg\")),\n * limit(() => processImage(\"image2.jpg\")),\n * limit(() => processImage(\"image3.jpg\")),\n * limit(() => processImage(\"image4.jpg\")),\n * limit(() => processImage(\"image5.jpg\")),\n * limit(() => processImage(\"image6.jpg\")), // This will be queued until a slot opens\n * ]);\n * ```\n */\nexport function limit<T>(fn: () => Promise<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n // Task to run the function and handle completion\n const task = () => {\n fn()\n .then(resolve)\n .catch(reject)\n .finally(() => {\n activeCount--; // Mark task as done\n runNext(); // Trigger next queued task, if any\n });\n };\n\n if (activeCount < concurrencyLimit) {\n activeCount++; // Increment active count for immediate run\n task();\n } else {\n // Queue the task if concurrency limit reached\n queue.push(task);\n }\n });\n}\n","import { limit } from \"./limit\";\nimport { Dimensions } from \"./types\";\nimport { imageDimensionsCache } from \"./cache\";\n\n/**\n * Loads an image from the given URL and resolves with its natural dimensions.\n * Internal helper function that creates a temporary Image element to extract\n * the natural width and height of an image without displaying it.\n *\n * @param url - The image URL to load\n * @returns Promise resolving with the image's natural width and height\n */\nconst loadImageDimensions = (url: string): Promise<Dimensions> => {\n return new Promise((resolve, reject) => {\n if (typeof document === 'undefined') {\n reject(new Error('getImageDimensions() is only available in the browser.'));\n return;\n }\n\n const img = new Image();\n img.onload = () => {\n resolve({ width: img.naturalWidth, height: img.naturalHeight });\n };\n img.onerror = reject;\n img.src = url;\n });\n};\n\n/**\n * Gets the dimensions (width and height) of an image from the given URL.\n * Uses a cache to avoid reloading the image if already fetched, and employs\n * a concurrency limiter to control resource usage and prevent overwhelming\n * the browser with too many simultaneous image loads.\n *\n * @param url - The URL of the image to analyze\n * @returns Promise resolving to an object containing width and height\n * \n * @example\n * ```js\n * // Get dimensions of a remote image\n * const dimensions = await getImageDimensions(\"https://example.com/image.jpg\");\n * // dimensions = { width: 1920, height: 1080 }\n * \n * // Get dimensions of a local blob URL\n * const dimensions = await getImageDimensions(\"blob:http://localhost:3000/abc123\");\n * // dimensions = { width: 800, height: 600 }\n * \n * // Subsequent calls for the same URL will use cache\n * const cachedDimensions = await getImageDimensions(\"https://example.com/image.jpg\");\n * // Returns immediately from cache without reloading\n * ```\n */\nexport const getImageDimensions = (url: string): Promise<Dimensions> => {\n // Return cached dimensions if available\n if (imageDimensionsCache[url]) {\n return Promise.resolve(imageDimensionsCache[url]);\n }\n\n // Fetch and cache the dimensions using a concurrency limit\n return limit(() => loadImageDimensions(url)).then((dimensions) => {\n imageDimensionsCache[url] = dimensions;\n return dimensions;\n });\n};\n","import { videoMetaCache } from \"./cache\";\nimport { VideoMeta } from \"./types\";\n\n/**\n * Fetches metadata (width, height, duration) for a given video source.\n * Uses a cache to avoid reloading the same video multiple times for better performance.\n * The function creates a temporary video element, loads only metadata, and extracts\n * the video properties without downloading the entire file.\n *\n * @param videoSrc - The URL or path to the video file\n * @returns Promise resolving to an object containing video metadata\n * \n * @example\n * ```js\n * // Get metadata for a video\n * const metadata = await getVideoMeta(\"https://example.com/video.mp4\");\n * // metadata = { width: 1920, height: 1080, duration: 120.5 }\n * \n * // Get metadata for a local blob URL\n * const metadata = await getVideoMeta(\"blob:http://localhost:3000/abc123\");\n * // metadata = { width: 1280, height: 720, duration: 30.0 }\n * ```\n */\nexport const getVideoMeta = (videoSrc: string): Promise<VideoMeta> => {\n // Return cached metadata if available\n if (videoMetaCache[videoSrc]) {\n return Promise.resolve(videoMetaCache[videoSrc]);\n }\n\n return new Promise<VideoMeta>((resolve, reject) => {\n const video: HTMLVideoElement = document.createElement(\"video\");\n video.preload = \"metadata\"; // Only preload metadata to reduce bandwidth\n // Validate the videoSrc to ensure it's a safe URL before assigning it to video.src\n const isSafeUrl = /^(https?:|blob:|data:video\\/)/i.test(videoSrc);\n if (!isSafeUrl) {\n reject(new Error(\"Unsafe video source URL\"));\n return;\n }\n video.src = videoSrc;\n\n // When metadata is loaded, extract and cache it\n video.onloadedmetadata = () => {\n const meta: VideoMeta = {\n width: video.videoWidth,\n height: video.videoHeight,\n duration: video.duration,\n };\n videoMetaCache[videoSrc] = meta;\n resolve(meta);\n };\n\n // Handle video loading errors\n video.onerror = () => reject(new Error(\"Failed to load video metadata\"));\n });\n};\n","/**\n * Extracts a thumbnail from a video at a specific seek time and playback rate.\n * Creates a hidden video element in the browser, seeks to the specified time,\n * and captures the frame into a canvas, which is then exported as a JPEG data URL or Blob URL.\n * The function handles video loading, seeking, frame capture, and cleanup automatically.\n *\n * @param videoUrl - The URL of the video to extract the thumbnail from\n * @param seekTime - The time in seconds at which to capture the frame\n * @param playbackRate - Playback speed for the video\n * @returns Promise resolving to a thumbnail image URL\n * \n * @example\n * ```js\n * // Extract thumbnail at 5 seconds\n * const thumbnail = await getThumbnail(\"https://example.com/video.mp4\", 5);\n * // thumbnail is a data URL like \"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...\"\n * \n * // Extract thumbnail with custom playback rate\n * const thumbnail = await getThumbnail(\"https://example.com/video.mp4\", 2.5, 1.5);\n * ```\n */\nexport const getThumbnail = async (\n videoUrl: string,\n seekTime = 0.1,\n playbackRate = 1\n ): Promise<string> => {\n return new Promise((resolve, reject) => {\n const video = document.createElement(\"video\");\n video.crossOrigin = \"anonymous\";\n video.muted = true;\n video.playsInline = true;\n video.autoplay = false;\n video.preload = \"auto\";\n video.playbackRate = playbackRate;\n \n // Make video element hidden\n video.style.position = \"absolute\";\n video.style.left = \"-9999px\";\n video.style.top = \"-9999px\";\n video.style.width = \"1px\";\n video.style.height = \"1px\";\n video.style.opacity = \"0\";\n video.style.pointerEvents = \"none\";\n video.style.zIndex = \"-1\";\n \n let timeoutId: number | undefined;\n \n // Cleanup video element and timeout\n const cleanup = () => {\n if (video.parentNode) video.remove();\n if (timeoutId) clearTimeout(timeoutId);\n };\n \n // Handle errors during video loading\n const handleError = () => {\n cleanup();\n reject(new Error(`Failed to load video: ${video.error?.message || \"Unknown error\"}`));\n };\n \n // Once seeked to target frame, capture the image\n const handleSeeked = () => {\n try {\n video.pause();\n \n const canvas = document.createElement(\"canvas\");\n const width = video.videoWidth || 640;\n const height = video.videoHeight || 360;\n canvas.width = width;\n canvas.height = height;\n \n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n cleanup();\n reject(new Error(\"Failed to get canvas context\"));\n return;\n }\n \n // Draw current video frame onto canvas\n ctx.drawImage(video, 0, 0, width, height);\n \n // Attempt to export canvas to base64 image URL\n try {\n const dataUrl = canvas.toDataURL(\"image/jpeg\", 0.8);\n cleanup();\n resolve(dataUrl);\n } catch {\n // Fallback: convert canvas to Blob\n canvas.toBlob((blob) => {\n if (!blob) {\n cleanup();\n reject(new Error(\"Failed to create Blob\"));\n return;\n }\n const blobUrl = URL.createObjectURL(blob);\n cleanup();\n resolve(blobUrl);\n }, \"image/jpeg\", 0.8);\n }\n } catch (err) {\n cleanup();\n reject(new Error(`Error creating thumbnail: ${err}`));\n }\n };\n \n video.addEventListener(\"error\", handleError, { once: true });\n video.addEventListener(\"seeked\", handleSeeked, { once: true });\n \n // After metadata is loaded, seek to the desired frame\n video.addEventListener(\"loadedmetadata\", () => {\n const playPromise = video.play();\n if (playPromise !== undefined) {\n playPromise\n .then(() => {\n video.currentTime = seekTime;\n })\n .catch(() => {\n video.currentTime = seekTime;\n });\n } else {\n video.currentTime = seekTime;\n }\n }, { once: true });\n \n // Timeout protection in case video loading hangs\n timeoutId = window.setTimeout(() => {\n cleanup();\n reject(new Error(\"Video loading timed out\"));\n }, 5000);\n \n // Assign video source and add it to the DOM (helps Safari/iOS behavior)\n video.src = videoUrl;\n document.body.appendChild(video);\n });\n };","/**\n * Audio segment interface for stitching\n */\nexport interface AudioSegment {\n src: string;\n s: number; // start time in seconds\n e: number; // end time in seconds\n volume?: number; // volume level (0-1), defaults to 1, 0 = muted\n}\n\n/**\n * Extracts an audio segment from a media source between start and end times,\n * rendered at the specified playback rate, and returns a Blob URL to an MP3 file.\n * The function fetches the source, decodes the audio track using Web Audio API,\n * renders the segment offline for speed and determinism, encodes it as MP3 using lamejs,\n * and returns an object URL. Callers should revoke the URL when done.\n *\n * @param src - The source URL of the media file\n * @param playbackRate - The playback rate for the extracted segment\n * @param start - The start time in seconds\n * @param end - The end time in seconds\n * @returns Promise resolving to a Blob URL to the extracted MP3 file\n * \n * @example\n * ```js\n * const url = await extractAudio({ src, start: 3, end: 8, playbackRate: 1.25 });\n * const audio = new Audio(url);\n * audio.play();\n * // later: URL.revokeObjectURL(url);\n * ```\n */\nexport const extractAudio = async ({\n src,\n playbackRate = 1,\n start = 0,\n end,\n}: {\n src: string;\n playbackRate?: number;\n start?: number;\n end?: number;\n}): Promise<string> => {\n if (!src) throw new Error(\"src is required\");\n if (playbackRate <= 0) throw new Error(\"playbackRate must be > 0\");\n\n // Basic URL safety check\n const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);\n if (!isSafeUrl) throw new Error(\"Unsafe media source URL\");\n\n // Fetch and decode audio\n const audioBuffer = await fetchAndDecodeAudio(src);\n\n // Normalize time range\n const clampedStart = Math.max(0, start || 0);\n const fullDuration = audioBuffer.duration;\n const clampedEnd = Math.min(\n typeof end === \"number\" ? end : fullDuration,\n fullDuration\n );\n if (clampedEnd <= clampedStart)\n throw new Error(\"Invalid range: end must be greater than start\");\n\n // Render segment with playback rate\n const renderedBuffer = await renderAudioSegment(\n audioBuffer,\n clampedStart,\n clampedEnd,\n playbackRate\n );\n\n // Convert to MP3 and return URL\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\n\n/**\n * Stitches multiple audio segments into a single MP3 file.\n * Creates a timeline where each segment plays at its specified time,\n * with silence filling gaps between segments.\n * \n * @param segments - Array of audio segments with source, start, and end times\n * @param totalDuration - Total duration of the output audio\n * @returns Promise resolving to a Blob URL to the stitched MP3 file\n * \n * @example\n * ```js\n * const segments = [\n * { src: \"audio1.mp3\", s: 0, e: 5, volume: 1.0 },\n * { src: \"audio2.mp3\", s: 10, e: 15, volume: 0.8 }\n * ];\n * const url = await stitchAudio(segments, 15);\n * // Creates a 15-second audio file with segments at specified times\n * ```\n */\nexport const stitchAudio = async (\n segments: AudioSegment[],\n totalDuration?: number\n): Promise<string> => {\n if (!segments || segments.length === 0) {\n throw new Error(\"At least one audio segment is required\");\n }\n\n // Calculate total duration if not provided\n const duration = totalDuration || Math.max(...segments.map(s => s.e));\n\n // Create timeline and render segments\n const renderedBuffer = await createAudioTimeline(segments, duration);\n\n // Convert to MP3 and return URL\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\n\n// ===== SHARED UTILITIES =====\n\n/**\n * Fetches and decodes audio from a URL.\n * \n * @param src - The URL of the audio file to fetch and decode\n * @returns Promise<AudioBuffer> - The decoded audio buffer\n */\nconst fetchAndDecodeAudio = async (src: string): Promise<AudioBuffer> => {\n const response = await fetch(src);\n if (!response.ok) throw new Error(`Failed to fetch source: ${response.status}`);\n \n const arrayBuffer = await response.arrayBuffer();\n return decodeAudioData(arrayBuffer);\n};\n\n/**\n * Decodes audio data using Web Audio API\n */\nconst decodeAudioData = async (arrayBuffer: ArrayBuffer): Promise<AudioBuffer> => {\n const AudioContextCtor: typeof AudioContext =\n (window as any).AudioContext || (window as any).webkitAudioContext;\n if (!AudioContextCtor) throw new Error(\"Web Audio API not supported\");\n \n const audioContext = new AudioContextCtor();\n try {\n return await new Promise<AudioBuffer>((resolve, reject) => {\n audioContext.decodeAudioData(\n arrayBuffer.slice(0),\n (buf) => resolve(buf),\n (err) => reject(err || new Error(\"Failed to decode audio\"))\n );\n });\n } finally {\n audioContext.close();\n }\n};\n\n/**\n * Renders an audio segment with playback rate\n */\nconst renderAudioSegment = async (\n audioBuffer: AudioBuffer,\n start: number,\n end: number,\n playbackRate: number\n): Promise<AudioBuffer> => {\n const OfflineAudioContextCtor: typeof OfflineAudioContext =\n (window as any).OfflineAudioContext || (window as any).webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n\n const sampleRate = audioBuffer.sampleRate;\n const numChannels = audioBuffer.numberOfChannels;\n const sourceDuration = end - start;\n const renderedFrames = Math.max(\n 1,\n Math.ceil((sourceDuration / playbackRate) * sampleRate)\n );\n\n const offline = new OfflineAudioContextCtor(numChannels, renderedFrames, sampleRate);\n const sourceNode = offline.createBufferSource();\n sourceNode.buffer = audioBuffer;\n sourceNode.playbackRate.value = playbackRate;\n sourceNode.connect(offline.destination);\n sourceNode.start(0, start, sourceDuration);\n\n return await offline.startRendering();\n};\n\n/**\n * Creates an audio timeline with multiple segments\n */\nconst createAudioTimeline = async (\n segments: AudioSegment[],\n duration: number\n): Promise<AudioBuffer> => {\n const OfflineAudioContextCtor: typeof OfflineAudioContext =\n (window as any).OfflineAudioContext || (window as any).webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n\n const sampleRate = 44100; // Standard sample rate\n const totalFrames = Math.ceil(duration * sampleRate);\n const offline = new OfflineAudioContextCtor(2, totalFrames, sampleRate); // Stereo output\n\n // Process each segment\n for (const segment of segments) {\n if (segment.s >= segment.e) {\n console.warn(`Invalid segment: start (${segment.s}) >= end (${segment.e})`);\n continue;\n }\n\n // Skip segments with volume 0 (muted)\n const volume = segment.volume ?? 1;\n if (volume <= 0) {\n console.warn(`Skipping muted segment: ${segment.src}`);\n continue;\n }\n\n try {\n const audioBuffer = await fetchAndDecodeAudio(segment.src);\n const segmentDuration = segment.e - segment.s;\n const sourceDuration = Math.min(segmentDuration, audioBuffer.duration);\n\n const source = offline.createBufferSource();\n source.buffer = audioBuffer;\n \n // Apply volume control if not 1.0\n if (volume !== 1) {\n const gainNode = offline.createGain();\n gainNode.gain.value = volume;\n source.connect(gainNode);\n gainNode.connect(offline.destination);\n } else {\n source.connect(offline.destination);\n }\n \n source.start(segment.s, 0, sourceDuration);\n } catch (error) {\n console.warn(`Failed to process segment: ${segment.src}`, error);\n }\n }\n\n return await offline.startRendering();\n};\n\n/**\n * Converts an AudioBuffer to an MP3 Blob using lamejs\n */\nconst audioBufferToMp3 = async (buffer: AudioBuffer): Promise<Blob> => {\n try {\n // Convert AudioBuffer to WAV ArrayBuffer\n const wavArrayBuffer = audioBufferToWavArrayBuffer(buffer);\n \n // Decode WAV back to PCM using AudioContext\n const pcmBuffer = await decodeAudioData(wavArrayBuffer);\n \n // Encode PCM to MP3 using lamejs\n return await encodePcmToMp3(pcmBuffer);\n } catch (error) {\n // Fallback to WAV if MP3 encoding fails\n return audioBufferToWavBlob(buffer);\n }\n};\n\n/**\n * Converts AudioBuffer to WAV ArrayBuffer\n */\nconst audioBufferToWavArrayBuffer = (buffer: AudioBuffer): ArrayBuffer => {\n const numChannels = buffer.numberOfChannels;\n const sampleRate = buffer.sampleRate;\n const numFrames = buffer.length;\n\n // Interleave channels\n const interleaved = interleave(buffer, numChannels, numFrames);\n\n // Create WAV ArrayBuffer\n const bytesPerSample = 2; // 16-bit\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n const dataSize = interleaved.length * bytesPerSample;\n const bufferSize = 44 + dataSize;\n const arrayBuffer = new ArrayBuffer(bufferSize);\n const view = new DataView(arrayBuffer);\n\n // RIFF header\n writeString(view, 0, \"RIFF\");\n view.setUint32(4, 36 + dataSize, true);\n writeString(view, 8, \"WAVE\");\n\n // fmt chunk\n writeString(view, 12, \"fmt \");\n view.setUint32(16, 16, true); // PCM\n view.setUint16(20, 1, true); // audio format = 1 (PCM)\n view.setUint16(22, numChannels, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, blockAlign, true);\n view.setUint16(34, 16, true); // bits per sample\n\n // data chunk\n writeString(view, 36, \"data\");\n view.setUint32(40, dataSize, true);\n\n // PCM samples\n floatTo16BitPCM(view, 44, interleaved);\n\n return arrayBuffer;\n};\n\n/**\n * Encodes PCM AudioBuffer to MP3 using lamejs\n */\nconst encodePcmToMp3 = async (buffer: AudioBuffer): Promise<Blob> => {\n const lamejs = await import(\"lamejs\");\n\n const channels = buffer.numberOfChannels >= 2 ? 2 : 1;\n // Downsample to 22050 Hz for smaller file size (good for voice/speech)\n const targetSampleRate = 22050;\n const downsampledBuffer = downsampleAudioBuffer(buffer, targetSampleRate);\n const kbps = 48; // Reduced bitrate for smaller file size\n\n const mp3encoder = new lamejs.default.Mp3Encoder(channels, targetSampleRate, kbps);\n const samplesPerFrame = 1152;\n\n // Prepare PCM Int16 arrays\n const leftFloat = downsampledBuffer.getChannelData(0);\n const left = floatTo16(leftFloat);\n let right: Int16Array | undefined;\n if (channels === 2) {\n const rightFloat = downsampledBuffer.getChannelData(1);\n right = floatTo16(rightFloat);\n }\n\n const mp3Chunks: Uint8Array[] = [];\n for (let i = 0; i < left.length; i += samplesPerFrame) {\n const leftChunk = left.subarray(i, Math.min(i + samplesPerFrame, left.length));\n let mp3buf: Uint8Array;\n if (channels === 2 && right) {\n const rightChunk = right.subarray(i, Math.min(i + samplesPerFrame, right.length));\n mp3buf = mp3encoder.encodeBuffer(leftChunk, rightChunk);\n } else {\n mp3buf = mp3encoder.encodeBuffer(leftChunk);\n }\n if (mp3buf.length > 0) mp3Chunks.push(mp3buf);\n }\n\n const end = mp3encoder.flush();\n if (end.length > 0) mp3Chunks.push(end);\n\n return new Blob(mp3Chunks, { type: \"audio/mpeg\" });\n};\n\n/**\n * Converts an AudioBuffer to a WAV Blob (fallback)\n */\nconst audioBufferToWavBlob = (buffer: AudioBuffer): Blob => {\n const arrayBuffer = audioBufferToWavArrayBuffer(buffer);\n return new Blob([arrayBuffer], { type: \"audio/wav\" });\n};\n\n/**\n * Downsamples an AudioBuffer to a lower sample rate for smaller file size\n */\nconst downsampleAudioBuffer = (buffer: AudioBuffer, targetSampleRate: number): AudioBuffer => {\n if (buffer.sampleRate === targetSampleRate) {\n return buffer;\n }\n\n const ratio = buffer.sampleRate / targetSampleRate;\n const newLength = Math.round(buffer.length / ratio);\n const newBuffer = new AudioContext().createBuffer(\n buffer.numberOfChannels,\n newLength,\n targetSampleRate\n );\n\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const oldData = buffer.getChannelData(channel);\n const newData = newBuffer.getChannelData(channel);\n \n for (let i = 0; i < newLength; i++) {\n const oldIndex = Math.floor(i * ratio);\n newData[i] = oldData[oldIndex];\n }\n }\n\n return newBuffer;\n};\n\n/**\n * Interleaves audio channels\n */\nconst interleave = (buffer: AudioBuffer, numChannels: number, numFrames: number): Float32Array => {\n if (numChannels === 1) {\n return buffer.getChannelData(0).slice(0, numFrames);\n }\n const result = new Float32Array(numFrames * numChannels);\n const channelData: Float32Array[] = [];\n for (let ch = 0; ch < numChannels; ch++) {\n channelData[ch] = buffer.getChannelData(ch);\n }\n let writeIndex = 0;\n for (let i = 0; i < numFrames; i++) {\n for (let ch = 0; ch < numChannels; ch++) {\n result[writeIndex++] = channelData[ch][i];\n }\n }\n return result;\n};\n\n/**\n * Converts float32 audio data to 16-bit PCM\n */\nconst floatTo16BitPCM = (view: DataView, offset: number, input: Float32Array): void => {\n let pos = offset;\n for (let i = 0; i < input.length; i++, pos += 2) {\n let s = Math.max(-1, Math.min(1, input[i]));\n view.setInt16(pos, s < 0 ? s * 0x8000 : s * 0x7fff, true);\n }\n};\n\n/**\n * Converts float32 array to int16 array\n */\nconst floatTo16 = (input: Float32Array): Int16Array => {\n const output = new Int16Array(input.length);\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output[i] = s < 0 ? s * 0x8000 : s * 0x7fff;\n }\n return output;\n};\n\n/**\n * Writes string to DataView\n */\nconst writeString = (view: DataView, offset: number, str: string): void => {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n};\n","import { Dimensions } from \"./types\";\n\n/**\n * Calculates the scaled dimensions of an element to fit inside a container\n * based on the specified max dimensions while maintaining aspect ratio.\n * Ensures the resulting dimensions are even numbers and fit within the specified bounds.\n * If the original dimensions are already smaller than the max values, returns the original dimensions.\n *\n * @param width - The original width of the element in pixels\n * @param height - The original height of the element in pixels\n * @param maxWidth - The maximum allowed width of the container in pixels\n * @param maxHeight - The maximum allowed height of the container in pixels\n * @returns Object containing the calculated width and height\n * \n * @example\n * ```js\n * // Scale down a large image to fit in a smaller container\n * const scaled = getScaledDimensions(1920, 1080, 800, 600);\n * // scaled = { width: 800, height: 450 }\n * \n * // Small image that doesn't need scaling\n * const scaled = getScaledDimensions(400, 300, 800, 600);\n * // scaled = { width: 400, height: 300 }\n * \n * // Ensure even dimensions for video encoding\n * const scaled = getScaledDimensions(1001, 1001, 1000, 1000);\n * // scaled = { width: 1000, height: 1000 }\n * ```\n */\nexport const getScaledDimensions = (\n width: number, \n height: number,\n maxWidth: number,\n maxHeight: number\n ): Dimensions => {\n // If the original dimensions are smaller than or equal to the max values, return the original dimensions\n if (width <= maxWidth && height <= maxHeight) {\n // Ensure the width and height are even numbers\n return {\n width: width % 2 === 0 ? width : width - 1,\n height: height % 2 === 0 ? height : height - 1,\n };\n }\n \n // Calculate scaling factor based on the maximum width and height\n const widthRatio = maxWidth / width;\n const heightRatio = maxHeight / height;\n \n // Use the smaller of the two ratios to maintain the aspect ratio\n const scale = Math.min(widthRatio, heightRatio);\n \n // Calculate the scaled dimensions\n let scaledWidth = Math.round(width * scale);\n let scaledHeight = Math.round(height * scale);\n \n // Ensure the width and height are even numbers\n if (scaledWidth % 2 !== 0) {\n scaledWidth -= 1; // Make width even if it's odd\n }\n if (scaledHeight % 2 !== 0) {\n scaledHeight -= 1; // Make height even if it's odd\n }\n \n // Ensure the scaled width and height fit within the max dimensions\n return {\n width: Math.min(scaledWidth, maxWidth),\n height: Math.min(scaledHeight, maxHeight),\n };\n };\n\n/**\n * Calculates the resized dimensions of an element to fit inside a container\n * based on the specified object-fit strategy (\"contain\", \"cover\", \"fill\", or default).\n * Implements CSS object-fit behavior for programmatic dimension calculations.\n * Useful for responsive design and media scaling applications.\n *\n * @param objectFit - The object-fit behavior\n * @param elementSize - The original size of the element\n * @param containerSize - The size of the container\n * @returns Object containing the calculated width and height\n * \n * @example\n * ```js\n * // Contain: fit entire element inside container\n * const contained = getObjectFitSize(\"contain\", {width: 1000, height: 500}, {width: 400, height: 300});\n * // contained = { width: 400, height: 200 }\n * \n * // Cover: fill container while maintaining aspect ratio\n * const covered = getObjectFitSize(\"cover\", {width: 1000, height: 500}, {width: 400, height: 300});\n * // covered = { width: 600, height: 300 }\n * \n * // Fill: stretch to completely fill container\n * const filled = getObjectFitSize(\"fill\", {width: 1000, height: 500}, {width: 400, height: 300});\n * // filled = { width: 400, height: 300 }\n * ```\n */\nexport const getObjectFitSize = (\n objectFit: string,\n elementSize: Dimensions,\n containerSize: Dimensions\n): Dimensions => {\n const elementAspectRatio = elementSize.width / elementSize.height;\n const containerAspectRatio = containerSize.width / containerSize.height;\n\n switch (objectFit) {\n case \"contain\":\n // Fit entire element inside container without cropping, maintaining aspect ratio\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio,\n };\n } else {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height,\n };\n }\n\n case \"cover\":\n // Fill container while maintaining aspect ratio, possibly cropping the element\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height,\n };\n } else {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio,\n };\n }\n\n case \"fill\":\n // Stretch element to completely fill the container, ignoring aspect ratio\n return {\n width: containerSize.width,\n height: containerSize.height,\n };\n\n default:\n // Default behavior: return original size of the element\n return {\n width: elementSize.width,\n height: elementSize.height,\n };\n }\n};\n\n ","/**\n * Converts a Blob URL to a File object.\n * Fetches the blob data from the URL and creates a new File object with the specified name.\n * Useful for converting blob URLs back to File objects for upload or processing.\n *\n * @param blobUrl - The Blob URL to convert\n * @param fileName - The name to assign to the resulting File object\n * @returns Promise resolving to a File object with the blob data\n * \n * @example\n * ```js\n * const file = await blobUrlToFile(\"blob:http://localhost:3000/abc123\", \"image.jpg\");\n * // file is now a File object that can be uploaded or processed\n * ```\n */\nexport const blobUrlToFile = async (blobUrl: string, fileName: string): Promise<File> => {\n const response = await fetch(blobUrl);\n const blob = await response.blob();\n return new File([blob], fileName, { type: blob.type });\n };\n \n /**\n * Triggers a download of a file from a string or Blob.\n * Creates a temporary download link and automatically clicks it to initiate the download.\n * The function handles both string content and Blob objects, and automatically cleans up\n * the created object URL after the download is initiated.\n *\n * @param content - The content to save, either a string or a Blob object\n * @param type - The MIME type of the content\n * @param name - The name of the file to be saved\n * \n * @example\n * ```js\n * // Download text content\n * saveAsFile(\"Hello World\", \"text/plain\", \"hello.txt\");\n * \n * // Download JSON data\n * saveAsFile(JSON.stringify({data: \"value\"}), \"application/json\", \"data.json\");\n * \n * // Download blob content\n * saveAsFile(imageBlob, \"image/png\", \"screenshot.png\");\n * ```\n */\n export const saveAsFile = (content: string | Blob, type: string, name: string): void => {\n const blob = typeof content === \"string\" ? new Blob([content], { type }) : content;\n const url = URL.createObjectURL(blob);\n \n const a = document.createElement(\"a\");\n a.href = url;\n a.download = name;\n a.click();\n \n // Clean up the URL object after download\n URL.revokeObjectURL(url);\n };\n \n /**\n * Downloads a file from a given URL and triggers a browser download.\n * Fetches the file content from the provided URL and creates a download link\n * to save it locally. The function handles the entire download process including\n * fetching, blob creation, and cleanup of temporary resources.\n *\n * @param url - The URL of the file to download\n * @param filename - The name of the file to be saved locally\n * @returns Promise resolving when the download is initiated\n * \n * @example\n * ```js\n * await downloadFile(\"https://example.com/image.jpg\", \"downloaded-image.jpg\");\n * // Browser will automatically download the file with the specified name\n * ```\n */\n export const downloadFile = async (url: string, filename: string): Promise<void> => {\n try {\n const response = await fetch(url);\n const blob = await response.blob();\n const downloadUrl = window.URL.createObjectURL(blob);\n \n const link = document.createElement(\"a\");\n link.href = downloadUrl;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n \n // Clean up\n document.body.removeChild(link);\n window.URL.revokeObjectURL(downloadUrl);\n } catch (error) {\n console.error(\"Error downloading file:\", error);\n throw error;\n }\n };\n ","/**\n * Detects the media type (image, video, or audio) of a given URL by sending a HEAD request.\n * Uses a lightweight HEAD request to fetch only the headers, avoiding download of the full file.\n * The function analyzes the Content-Type header to determine the media type category.\n *\n * @param url - The URL of the media file to analyze\n * @returns Promise resolving to the detected media type or null\n * \n * @example\n * ```js\n * // Detect image type\n * const type = await detectMediaTypeFromUrl(\"https://example.com/image.jpg\");\n * // type = \"image\"\n * \n * // Detect video type\n * const type = await detectMediaTypeFromUrl(\"https://example.com/video.mp4\");\n * // type = \"video\"\n * \n * // Detect audio type\n * const type = await detectMediaTypeFromUrl(\"https://example.com/audio.mp3\");\n * // type = \"audio\"\n * \n * // Invalid or inaccessible URL\n * const type = await detectMediaTypeFromUrl(\"https://example.com/invalid\");\n * // type = null\n * ```\n */\nexport const detectMediaTypeFromUrl = async (url: string): Promise<'image' | 'video' | 'audio' | null> => {\n try {\n // Use a HEAD request to fetch only the headers, avoiding download of the full file\n const response = await fetch(url, { method: 'HEAD' });\n \n // Extract the 'Content-Type' header from the response\n const contentType = response.headers.get('Content-Type');\n \n if (!contentType) return null;\n \n // Determine the media type from the content type\n if (contentType.startsWith('image/')) return 'image';\n if (contentType.startsWith('video/')) return 'video';\n if (contentType.startsWith('audio/')) return 'audio';\n \n // Return null if not a recognized media type\n return null;\n } catch (error) {\n console.error('Fetch failed:', error);\n return null;\n }\n };\n "],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@twick/media-utils",
3
- "version": "0.14.2",
3
+ "version": "0.14.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
8
8
  "sideEffects": false,
9
- "license": "Apache-2.0",
9
+ "license": "SEE LICENSE IN LICENSE.md",
10
10
  "files": [
11
11
  "dist"
12
12
  ],