You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, includeAnimeSearch = "yes") {
// Top-level container
const container = document.createElement('div');
container.style.cssText = `
max-width: 900px;
margin: 0 auto;
background: #1e1e2f;
color: #f5f6fa;
padding: 25px;
border-radius: 12px;
font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
box-shadow: 0 8px 30px rgba(0,0,0,0.4);
`;
// Inject CSS for spinner animation into container
const styleId = 'movie-identifier-styles';
if (!document.getElementById(styleId)) {
const style = document.createElement('style');
style.id = styleId;
style.textContent = `
@keyframes movieIdenSpin {
to { transform: rotate(360deg); }
}
.movieIdenSpinner {
display: inline-block;
width: 14px;
height: 14px;
margin-right: 10px;
border: 2px solid rgba(255,255,255,0.3);
border-radius: 50%;
border-top-color: inherit;
animation: movieIdenSpin 1s linear infinite;
vertical-align: middle;
}
`;
container.appendChild(style);
}
// Header structure
container.innerHTML += `
<h2 style="margin-top: 0; margin-bottom: 20px; font-size: 24px; color: #fff; border-bottom: 1px solid #3d3d5c; padding-bottom: 10px;">
Advanced Movie Identifier
</h2>
<div style="display: flex; gap: 25px; flex-wrap: wrap;">
<div style="flex: 1; min-width: 300px;">
<canvas id="img-preview" style="width: 100%; border-radius: 8px; background: #000; box-shadow: 0 4px 12px rgba(0,0,0,0.3);"></canvas>
</div>
<div style="flex: 1; min-width: 300px; display: flex; flex-direction: column; gap: 15px;">
<div id="status-box" style="padding: 15px; background: #2d2d44; border-radius: 8px; border-left: 4px solid #f39c12; color: #f39c12; font-weight: bold; display: flex; align-items: center;">
<div class="movieIdenSpinner" style="border-top-color: #f39c12;"></div>
<span id="status-text">Initializing advanced search...</span>
</div>
<div id="anime-results" style="padding: 15px; background: #2d2d44; border-radius: 8px; display: none;"></div>
<div id="text-results" style="padding: 15px; background: #2d2d44; border-radius: 8px; display: none;"></div>
<div id="reverse-search-results" style="padding: 15px; background: #2d2d44; border-radius: 8px; display: none;"></div>
</div>
</div>
`;
// References to DOM elements
const canvas = container.querySelector('#img-preview');
const ctx = canvas.getContext('2d');
const statusBox = container.querySelector('#status-box');
const statusText = container.querySelector('#status-text');
const animeEl = container.querySelector('#anime-results');
const textEl = container.querySelector('#text-results');
const reverseEl = container.querySelector('#reverse-search-results');
const spinner = container.querySelector('.movieIdenSpinner');
// Draw and downscale image to cap memory/processing limits
const MAX_DIM = 800;
let w = originalImg.width;
let h = originalImg.height;
if (w > MAX_DIM || h > MAX_DIM) {
const ratio = Math.min(MAX_DIM / w, MAX_DIM / h);
w = Math.round(w * ratio);
h = Math.round(h * ratio);
}
canvas.width = w;
canvas.height = h;
ctx.drawImage(originalImg, 0, 0, w, h);
// Helper to update status
const updateStatus = (text, color) => {
statusText.innerText = text;
statusBox.style.borderLeftColor = color;
statusBox.style.color = color;
spinner.style.borderTopColor = color;
if (color === '#2ecc71' || color === '#e74c3c') {
spinner.style.display = 'none'; // Stop spinner on completion or error
}
};
// Run async tasks immediately so function unblocks and returns container
(async () => {
try {
const blob = await new Promise(res => canvas.toBlob(res, 'image/jpeg', 0.8));
// Phase 1: Check Anime Database (trace.moe)
if (includeAnimeSearch.toLowerCase() === "yes") {
updateStatus("Querying anime & animation databases...", "#3498db");
try {
const traceRes = await fetch("https://api.trace.moe/search", {
method: "POST",
body: blob,
headers: { "Content-Type": "image/jpeg" }
});
if (traceRes.ok) {
const traceData = await traceRes.json();
if (traceData && traceData.result && traceData.result.length > 0) {
const bestMatch = traceData.result[0];
if (bestMatch.similarity > 0.85) {
animeEl.style.display = 'block';
animeEl.innerHTML = `
<h3 style="margin-top:0; margin-bottom: 15px; color: #3498db; font-size: 18px;">Anime/Animation Match</h3>
<p style="margin: 5px 0; font-size: 14px;"><strong>Title:</strong> ${bestMatch.filename}</p>
<p style="margin: 5px 0; font-size: 14px;"><strong>Episode:</strong> ${bestMatch.episode || 'N/A'}</p>
<p style="margin: 5px 0; font-size: 14px;"><strong>Confidence:</strong> ${(bestMatch.similarity * 100).toFixed(1)}%</p>
<video src="${bestMatch.video}" autoplay loop muted style="width: 100%; border-radius: 6px; margin-top: 15px; background: #000;"></video>
`;
}
}
}
} catch (e) {
console.warn("Anime search failed", e);
}
}
// Phase 2: Extract text / Subtitles via Tesseract.js OCR
updateStatus("Loading Optical Character Recognition (OCR)...", "#9b59b6");
if (!window.Tesseract) {
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;
document.head.appendChild(script);
});
}
updateStatus("Extracting subtitles and text from frame...", "#9b59b6");
const { data: { text } } = await Tesseract.recognize(canvas, 'eng');
const cleanText = text.replace(/\n+/g, ' ').trim();
textEl.style.display = 'block';
if (cleanText.length >= 4) {
const encodedText = encodeURIComponent(cleanText);
textEl.innerHTML = `
<h3 style="margin-top:0; margin-bottom: 15px; color: #9b59b6; font-size: 18px;">Extracted Subtitles / Text</h3>
<blockquote style="background: rgba(155, 89, 182, 0.1); padding: 12px; border-left: 4px solid #9b59b6; margin: 0 0 15px 0; font-style: italic; font-size: 15px; border-radius: 0 6px 6px 0;">
"${cleanText}"
</blockquote>
<p style="font-size: 13px; margin-bottom: 10px; color: #aaa;">Search this quote to find the movie:</p>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<a href="https://www.imdb.com/find?q=${encodedText}" target="_blank" style="padding: 6px 12px; background: #f5c518; color: #000; text-decoration: none; border-radius: 4px; font-weight: 600; font-size: 13px; display: inline-block;">IMDb Search</a>
<a href="https://www.google.com/search?q=%22${encodedText}%22+movie" target="_blank" style="padding: 6px 12px; background: #4285F4; color: #fff; text-decoration: none; border-radius: 4px; font-weight: 600; font-size: 13px; display: inline-block;">Google Search</a>
<a href="https://www.themoviedb.org/search?query=${encodedText}" target="_blank" style="padding: 6px 12px; background: #01b4e4; color: #fff; text-decoration: none; border-radius: 4px; font-weight: 600; font-size: 13px; display: inline-block;">TMDB Search</a>
</div>
`;
} else {
textEl.innerHTML = `
<h3 style="margin-top:0; margin-bottom: 10px; color: #9b59b6; font-size: 18px;">No Subtitles Detected</h3>
<p style="margin: 0; font-size: 14px; color: #ccc;">We couldn't extract any readable text or subtitles from this image to use as search context.</p>
`;
}
// Phase 3: Setup Reverse Image Search Fallbacks
reverseEl.style.display = 'block';
reverseEl.innerHTML = `
<h3 style="margin-top:0; margin-bottom: 10px; color: #e74c3c; font-size: 18px;">Reverse Image Search</h3>
<p style="font-size: 14px; margin-bottom: 15px; color: #ccc;">To identify live-action movies globally, utilize these visual search engines:</p>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<button id="google-lens-btn" style="cursor: pointer; border: none; padding: 6px 12px; background: #fff; color: #333; border-radius: 4px; font-weight: 600; font-size: 13px;">Google Lens</button>
<a href="https://yandex.com/images/search?rpt=imageview" target="_blank" onclick="alert('Upload your saved image on the Yandex page to find identical movie frames.');" style="padding: 6px 12px; background: #fc3f1d; color: #fff; text-decoration: none; border-radius: 4px; font-weight: 600; font-size: 13px;">Yandex Vision</a>
<a href="https://tineye.com/" target="_blank" onclick="alert('Upload your saved image on TinEye to find where this frame appears online.');" style="padding: 6px 12px; background: #2c9ab7; color: #fff; text-decoration: none; border-radius: 4px; font-weight: 600; font-size: 13px;">TinEye</a>
</div>
`;
// Bind Google Lens explanation helper
container.querySelector('#google-lens-btn').addEventListener('click', () => {
alert("To search with Google Lens:\n1. Right-click (or long-press) the Image Preview on the left.\n2. Select 'Search Image with Google'.");
});
// Mark completion
updateStatus("Analysis Complete", "#2ecc71");
statusBox.style.background = "rgba(46, 204, 113, 0.1)";
} catch (error) {
console.error("Movie Identifier Error:", error);
updateStatus("An error occurred during image intelligence analysis.", "#e74c3c");
}
})();
return container;
}
Apply Changes