Please bookmark this page to avoid losing your image tool!

Image Extractor From Video And Audio Sources

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
/**
 * Extracts a representative image from various sources like video files, YouTube URLs, or audio file cover art.
 * The operation is asynchronous and returns a Promise that resolves to an HTMLCanvasElement.
 *
 * @param {Image} originalImg - A JavaScript Image object. This parameter is required by the prompt's structure but is not used in this function's logic.
 * @param {string} sourceUrl - The URL of the source media. This can be a direct link to a video/audio file or a YouTube page URL.
 * @param {string} sourceType - The type of the source. Can be 'youtube', 'video', 'audio', or 'auto'. If 'auto', the function will try to guess the type from the URL. Defaults to 'auto'.
 * @param {number} timeInSeconds - For video sources, the timestamp (in seconds) from which to capture the frame. Defaults to 0 (the beginning).
 * @returns {Promise<HTMLCanvasElement>} A promise that resolves with a canvas element containing the extracted image, or an error message if extraction fails.
 */
async function processImage(originalImg, sourceUrl = '', sourceType = 'auto', timeInSeconds = 0) {

    /**
     * Creates a canvas with an error message.
     * @param {string} message - The error message to display.
     * @returns {HTMLCanvasElement} A canvas element with the message drawn on it.
     */
    const createErrorCanvas = (message) => {
        const canvas = document.createElement('canvas');
        canvas.width = 480;
        canvas.height = 270;
        const ctx = canvas.getContext('2d');
        ctx.fillStyle = '#2d2d2d';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = '#ffffff';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.font = '16px "Arial", sans-serif'; // Web-safe font
        
        // Simple text wrapping
        const words = message.split(' ');
        let line = '';
        let y = canvas.height / 2 - (words.length > 5 ? 10 : 0);
        for (let n = 0; n < words.length; n++) {
            const testLine = line + words[n] + ' ';
            const metrics = ctx.measureText(testLine);
            const testWidth = metrics.width;
            if (testWidth > canvas.width - 40 && n > 0) {
                ctx.fillText(line, canvas.width / 2, y);
                line = words[n] + ' ';
                y += 20;
            } else {
                line = testLine;
            }
        }
        ctx.fillText(line, canvas.width / 2, y);
        
        return canvas;
    };

    /**
     * Extracts a video ID from a YouTube URL.
     * @param {string} url - The YouTube URL.
     * @returns {string|null} The video ID or null if not found.
     */
    const getYouTubeId = (url) => {
        const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/;
        const match = url.match(regExp);
        return (match && match[2].length === 11) ? match[2] : null;
    };
    
    /**
     * Extracts a thumbnail image from a YouTube URL.
     * @param {string} url - The YouTube URL.
     * @returns {Promise<HTMLCanvasElement>} A canvas with the thumbnail.
     */
    const extractFromYouTube = (url) => {
        return new Promise((resolve, reject) => {
            const videoId = getYouTubeId(url);
            if (!videoId) {
                return reject(new Error('Could not extract YouTube video ID.'));
            }

            const thumbnailUrl = `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`;
            const fallbackUrl = `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`;

            const img = new Image();
            img.crossOrigin = 'Anonymous';

            const tryUrl = (src) => {
                img.onload = () => {
                    const canvas = document.createElement('canvas');
                    canvas.width = img.naturalWidth;
                    canvas.height = img.naturalHeight;
                    const ctx = canvas.getContext('2d');
                    ctx.drawImage(img, 0, 0);
                    resolve(canvas);
                };
                img.onerror = () => {
                    if (src === thumbnailUrl) {
                        tryUrl(fallbackUrl); // Try fallback quality
                    } else {
                        reject(new Error('Failed to load YouTube thumbnail. It may be private or deleted.'));
                    }
                };
                img.src = src;
            };

            tryUrl(thumbnailUrl);
        });
    };

    /**
     * Extracts a frame from a video URL at a specific time.
     * @param {string} url - The URL to the video file.
     * @param {number} time - The time in seconds to capture the frame.
     * @returns {Promise<HTMLCanvasElement>} A canvas with the video frame.
     */
    const extractFromVideo = (url, time) => {
        return new Promise((resolve, reject) => {
            const video = document.createElement('video');
            video.crossOrigin = 'Anonymous';

            const onSeeked = () => {
                const canvas = document.createElement('canvas');
                canvas.width = video.videoWidth;
                canvas.height = video.videoHeight;
                if(canvas.width === 0 || canvas.height === 0){
                    return reject(new Error('Invalid video dimensions.'));
                }
                const ctx = canvas.getContext('2d');
                ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
                cleanup();
                resolve(canvas);
            };

            const onLoadedMetadata = () => {
                video.currentTime = Math.min(Math.max(0, time), video.duration);
            };
            
            const onError = () => {
                 reject(new Error('Failed to load video. Check URL and CORS policy.'));
                 cleanup();
            };
            
            const cleanup = () => {
                video.removeEventListener('seeked', onSeeked);
                video.removeEventListener('loadedmetadata', onLoadedMetadata);
                video.removeEventListener('error', onError);
                video.remove();
            };

            video.addEventListener('loadedmetadata', onLoadedMetadata);
            video.addEventListener('seeked', onSeeked);
            video.addEventListener('error', onError);

            video.src = url;
            video.load();
        });
    };

    /**
     * Extracts cover art from an audio file URL.
     * @param {string} url - The URL to the audio file.
     * @returns {Promise<HTMLCanvasElement>} A canvas with the cover art.
     */
    const extractFromAudio = async (url) => {
        // Dynamically import jsmediatags library if not already present
        if (typeof window.jsmediatags === 'undefined') {
            const script = document.createElement('script');
            script.src = 'https://cdn.jsdelivr.net/npm/jsmediatags@3.9.8/dist/jsmediatags.min.js';
            document.head.appendChild(script);
            await new Promise((resolve, reject) => {
                script.onload = resolve;
                script.onerror = () => reject(new Error('Failed to load required audio metadata library.'));
            });
        }
        
        return new Promise((resolve, reject) => {
            window.jsmediatags.read(url, {
                onSuccess: (tag) => {
                    const { picture } = tag.tags;
                    if (picture) {
                        const blob = new Blob([new Uint8Array(picture.data)], { type: picture.format });
                        const objectUrl = URL.createObjectURL(blob);
                        const img = new Image();
                        img.onload = () => {
                            const canvas = document.createElement('canvas');
                            canvas.width = img.naturalWidth;
                            canvas.height = img.naturalHeight;
                            const ctx = canvas.getContext('2d');
                            ctx.drawImage(img, 0, 0);
                            URL.revokeObjectURL(objectUrl);
                            resolve(canvas);
                        };
                        img.onerror = () => {
                           URL.revokeObjectURL(objectUrl);
                           reject(new Error('Failed to parse image from audio metadata.'));
                        }
                        img.src = objectUrl;
                    } else {
                        resolve(createErrorCanvas('No cover art found in the audio file.'));
                    }
                },
                onError: (error) => {
                    reject(new Error(`Failed to read audio file: ${error.info}`));
                }
            });
        });
    };
    
    /**
     * Detects the source type from a URL.
     * @param {string} url - The source URL.
     * @returns {string} The detected source type ('youtube', 'video', 'audio', 'unknown').
     */
    const detectSourceType = (url) => {
        try {
            const urlObj = new URL(url);
            const hostname = urlObj.hostname.toLowerCase();
            const pathname = urlObj.pathname.toLowerCase();

            if (hostname.includes('youtube.com') || hostname.includes('youtu.be')) {
                return 'youtube';
            }
            if (/\.(mp4|webm|ogv|mov)$/i.test(pathname)) {
                return 'video';
            }
            if (/\.(mp3|flac|m4a|ogg|wav|aac)$/i.test(pathname)) {
                return 'audio';
            }
        } catch(e) { /* Invalid URL, return unknown */ }
        return 'unknown';
    };

    // --- Main Function Logic ---
    if (!sourceUrl) {
        return createErrorCanvas('No source URL provided.');
    }
    
    let type = sourceType === 'auto' ? detectSourceType(sourceUrl) : sourceType;

    try {
        switch (type) {
            case 'youtube':
                return await extractFromYouTube(sourceUrl);
            case 'video':
                return await extractFromVideo(sourceUrl, timeInSeconds);
            case 'audio':
                return await extractFromAudio(sourceUrl);
            default:
                 return createErrorCanvas(`Unsupported or unknown source type for URL.`);
        }
    } catch (error) {
        console.error("Image Extractor Error:", error);
        return createErrorCanvas(error.message || 'An unexpected error occurred.');
    }
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Extractor from Video and Audio Sources tool allows users to extract representative images from various media formats, including video files, YouTube URLs, and audio cover art. This tool is particularly useful for content creators, marketers, and anyone needing to capture visual content from multimedia sources, as it can provide thumbnails or snapshot images for use in presentations, social media, or other visual projects. The extraction process is quick and can be done by specifying the media source URL and an optional timestamp for videos.

Leave a Reply

Your email address will not be published. Required fields are marked *