Please bookmark this page to avoid losing your image tool!

Movie Poster Image Scanner And Identifier Tool

(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.
function processImage(originalImg, searchCountry = 'US', language = 'eng') {
    // Escape helper to prevent XSS issues when displaying API data or recognized text
    function escapeHTML(str) {
        if (!str) return '';
        return String(str).replace(/[&<>'"]/g, match => {
            return {
                '&': '&amp;',
                '<': '&lt;',
                '>': '&gt;',
                "'": '&#39;',
                '"': '&quot;'
            }[match];
        });
    }

    // Set up the container
    const container = document.createElement('div');
    container.style.fontFamily = 'system-ui, -apple-system, sans-serif';
    container.style.padding = '30px 20px';
    container.style.boxSizing = 'border-box';
    container.style.width = '100%';
    container.style.minHeight = '400px';
    container.style.backgroundColor = '#121212';
    container.style.color = '#fff';
    container.style.borderRadius = '12px';
    container.style.boxShadow = '0 8px 24px rgba(0,0,0,0.5)';
    container.style.overflow = 'hidden';
    container.style.display = 'flex';
    container.style.flexDirection = 'column';
    container.style.alignItems = 'center';
    container.style.justifyContent = 'center';

    // Spinner view
    const spinnerSvg = `
    <svg width="60" height="60" viewBox="0 0 50 50" style="margin-bottom: 20px;">
        <circle cx="25" cy="25" r="20" fill="none" stroke="#007aff" stroke-width="4" stroke-dasharray="31.4 31.4" stroke-dashoffset="0">
            <animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="1s" repeatCount="indefinite" />
        </circle>
    </svg>
    `;
    const loadingText = document.createElement('div');
    loadingText.innerText = "Initializing Scanner...";
    loadingText.style.fontSize = '16px';
    loadingText.style.fontWeight = '500';
    loadingText.style.color = '#ddd';
    
    container.innerHTML = spinnerSvg;
    container.appendChild(loadingText);

    // Run async process cleanly so we don't block the caller from mounting the element
    (async () => {
        try {
            // Set limits to prevent out-of-memory errors and handle transparent images
            let procW = originalImg.width;
            let procH = originalImg.height;
            const MAX_PROC_SIZE = 1200;
            if (procW > MAX_PROC_SIZE || procH > MAX_PROC_SIZE) {
                const ratio = Math.min(MAX_PROC_SIZE / procW, MAX_PROC_SIZE / procH);
                procW = Math.round(procW * ratio);
                procH = Math.round(procH * ratio);
            }
            
            const procCanvas = document.createElement('canvas');
            procCanvas.width = procW;
            procCanvas.height = procH;
            const procCtx = procCanvas.getContext('2d');
            procCtx.fillStyle = '#ffffff'; // Fills white in case of transparent background
            procCtx.fillRect(0, 0, procW, procH);
            procCtx.drawImage(originalImg, 0, 0, procW, procH);
            const thumbDataUrl = procCanvas.toDataURL('image/jpeg', 0.8);

            // Dynamically import Tesseract.js if not available
            if (!window.Tesseract) {
                loadingText.innerText = "Loading OCR resources...";
                await new Promise((resolve, reject) => {
                    const script = document.createElement('script');
                    script.src = 'https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js';
                    script.onload = resolve;
                    script.onerror = () => reject(new Error("Failed to load OCR library. Check network connection."));
                    document.head.appendChild(script);
                });
            }

            // Begin OCR Recognition
            const result = await window.Tesseract.recognize(procCanvas, language, {
                logger: m => {
                    if (m.status === 'recognizing text') {
                        loadingText.innerText = `Scanning Poster... ${Math.round(m.progress * 100)}%`;
                    }
                }
            });

            // Extract valid lines and prioritize the largest bounding boxes
            const lines = result.data.lines;
            let validLines = (lines || []).filter(l => {
                const cleanText = l.text.trim().replace(/[^a-zA-Z0-9]/g, '');
                return cleanText.length >= 1; 
            });

            if (validLines.length === 0) {
                throw new Error("No readable text found on the poster. Cannot identify the movie.");
            }

            validLines.sort((a, b) => (b.bbox.y1 - b.bbox.y0) - (a.bbox.y1 - a.bbox.y0));

            let maxHeight = validLines[0].bbox.y1 - validLines[0].bbox.y0;
            let titleLines = validLines.filter(l => (l.bbox.y1 - l.bbox.y0) >= maxHeight * 0.6);
            titleLines.sort((a, b) => a.bbox.y0 - b.bbox.y0);

            // Cleanly format text candidate
            let titleCandidate = titleLines.map(l => l.text.trim()).join(' ').replace(/\s+/g, ' ').replace(/[^a-zA-Z0-9 ':!.-]/g, '');
            let singleBiggestLineText = validLines[0].text.trim().replace(/\s+/g, ' ').replace(/[^a-zA-Z0-9 ':!.-]/g, '');

            loadingText.innerText = `Searching for movie: "${titleCandidate}"...`;

            // Query free iTunes Search API limits (No API key needed, open endpoint with fast responses)
            let response = await fetch(`https://itunes.apple.com/search?term=${encodeURIComponent(titleCandidate)}&entity=movie&limit=1&country=${searchCountry}`);
            if (!response.ok) throw new Error("Search API error.");
            let data = await response.json();

            let movie = data.results && data.results.length > 0 ? data.results[0] : null;

            // Fallback attempt with just strictly the largest text line
            if (!movie && titleCandidate !== singleBiggestLineText && singleBiggestLineText.length > 0) {
                loadingText.innerText = `Searching fallback: "${singleBiggestLineText}"...`;
                response = await fetch(`https://itunes.apple.com/search?term=${encodeURIComponent(singleBiggestLineText)}&entity=movie&limit=1&country=${searchCountry}`);
                data = await response.json();
                movie = data.results && data.results.length > 0 ? data.results[0] : null;
            }

            const titleCandidateSafe = escapeHTML(titleCandidate);

            // Handle match failure
            if (!movie) {
                container.innerHTML = `
                    <div style="text-align: center; padding: 20px;">
                        <h3 style="color: #ff5555; margin-top: 0;">Movie Not Identified</h3>
                        <p style="color: #ccc; font-size: 15px;">We scanned the poster but couldn't find a matching movie in our database. Ensure the poster title is readable and unobstructed.</p>
                        <div style="margin-top: 20px; display: inline-block; text-align: left; background: #222; padding: 15px; border-radius: 8px; border: 1px solid #333;">
                            <div style="color: #888; font-size: 12px; margin-bottom: 5px; font-weight: bold; letter-spacing: 1px;">TEXT EXTRACTED</div>
                            <div style="color: #fff; font-size: 16px;">"${titleCandidateSafe}"</div>
                        </div>
                        <div style="margin-top: 25px;">
                            <img src="${thumbDataUrl}" style="max-height: 250px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.5);">
                        </div>
                    </div>
                `;
                container.style.justifyContent = 'flex-start';
                return;
            }

            // Secure and structure movie data match
            const titleSafe = escapeHTML(movie.trackName);
            const genreSafe = escapeHTML(movie.primaryGenreName);
            const descSafe = escapeHTML(movie.longDescription || movie.shortDescription || "No plot description available.");
            const directorSafe = escapeHTML(movie.artistName);
            const ratingSafe = escapeHTML(movie.contentAdvisoryRating || 'Unrated');
            // Using a higher resolution URL provided by Apple iTunes backend format
            const posterUrl = movie.artworkUrl100 ? escapeHTML(movie.artworkUrl100.replace('100x100bb', '600x600bb')) : '';
            const trackUrl = escapeHTML(movie.trackViewUrl || '#');

            // Construct final rendering
            container.innerHTML = `
                <div style="display: flex; gap: 25px; text-align: left; flex-wrap: wrap; justify-content: center; width: 100%;">
                    <div style="flex: 1 1 250px; max-width: 320px;">
                        <div style="font-size: 12px; color: #888; margin-bottom: 8px; text-align: center; font-weight: bold; letter-spacing: 1px;">SCANNED POSTER</div>
                        <img src="${thumbDataUrl}" style="width: 100%; border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.6);">
                    </div>
                    <div style="flex: 1 1 300px; max-width: 450px; background: #1e1e1e; padding: 25px; border-radius: 12px; border: 1px solid #2a2a2a; box-shadow: 0 8px 20px rgba(0,0,0,0.4);">
                        <div style="font-size: 12px; color: #4caf50; margin-bottom: 15px; font-weight: bold; text-transform: uppercase; letter-spacing: 1px;">
                            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: text-bottom; margin-right: 4px;"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
                            Match Found
                        </div>
                        
                        <div style="display: flex; gap: 15px; align-items: flex-start; margin-bottom: 15px;">
                            ${posterUrl ? `<img src="${posterUrl}" style="width: 100px; flex-shrink: 0; border-radius: 6px; box-shadow: 0 2px 8px rgba(0,0,0,0.5);">` : ''}
                            <div style="display: flex; flex-direction: column;">
                                <h2 style="margin: 0 0 8px 0; font-size: 24px; color: #fff; line-height: 1.25;">${titleSafe}</h2>
                                <div style="color: #4caf50; font-size: 14px; font-weight: 500;">
                                    ${genreSafe} &nbsp;&bull;&nbsp; ${movie.releaseDate ? new Date(movie.releaseDate).getFullYear() : 'N/A'}
                                </div>
                            </div>
                        </div>

                        <p style="font-size: 14px; line-height: 1.6; color: #ddd; margin: 0 0 20px 0;">${descSafe}</p>
                        
                        <div style="font-size: 13px; color: #999; border-top: 1px solid #333; padding-top: 15px; line-height: 1.6;">
                            <strong style="color: #bbb;">Director & Cast:</strong> ${directorSafe}<br>
                            <strong style="color: #bbb;">Content Rating:</strong> ${ratingSafe}<br>
                            <strong style="color: #bbb;">Matched Text:</strong> "${titleCandidateSafe}"
                        </div>
                        
                        <a href="${trackUrl}" target="_blank" style="display: inline-block; margin-top: 20px; padding: 10px 18px; background: #007aff; color: #fff; text-decoration: none; border-radius: 6px; font-size: 14px; font-weight: 500;">
                            View Details on Apple TV
                        </a>
                    </div>
                </div>
            `;
            container.style.justifyContent = 'flex-start';

        } catch (e) {
            container.innerHTML = `
                <div style="text-align: center; padding: 40px 20px;">
                    <svg width="50" height="50" viewBox="0 0 24 24" fill="none" stroke="#ff5555" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="margin-bottom: 15px;">
                        <circle cx="12" cy="12" r="10"></circle>
                        <line x1="12" y1="8" x2="12" y2="12"></line>
                        <line x1="12" y1="16" x2="12.01" y2="16"></line>
                    </svg>
                    <h3 style="color: #ff5555; margin-top: 0; font-size: 20px;">Tool Error</h3>
                    <p style="color: #ccc; font-size: 15px;">${escapeHTML(e.message)}</p>
                </div>
            `;
            container.style.justifyContent = 'center';
        }
    })();

    return container;
}

Free Image Tool Creator

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

Description

This tool allows users to identify movies by scanning their posters. Using optical character recognition (OCR) technology, the scanner extracts text from an uploaded image and searches for matching movie titles in an extensive database. Once a match is found, the tool provides detailed information such as the movie title, genre, release year, director or cast, a plot description, and content rating. It is a useful resource for film enthusiasts who want to quickly find information about a movie they see on a poster in real life or online.

Leave a Reply

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