From 88162e08dbb345b99c8555ef6f908c76bc2c91be Mon Sep 17 00:00:00 2001 From: soruly Date: Sun, 22 May 2022 06:48:31 +0000 Subject: [PATCH] Rewrite for Manifest v3 --- background.html | 3 - bg.js | 230 +++++++++++++++++------------------------------- content.js | 131 +++++++++++---------------- manifest.json | 52 +++++------ trace.moe.js | 14 +-- 5 files changed, 162 insertions(+), 268 deletions(-) delete mode 100644 background.html diff --git a/background.html b/background.html deleted file mode 100644 index a733cff..0000000 --- a/background.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/bg.js b/bg.js index 2942877..f4e82ea 100644 --- a/bg.js +++ b/bg.js @@ -1,151 +1,79 @@ -if (!self.browser && self.chrome) { - browser = chrome; -} - -browser.contextMenus.create({ - id: "search-on-trace.moe", - title: "Search on trace.moe", - contexts: ["image", "video"], -}); - -var imageDataURL; - -var handleMessage = function (request, sender, sendResponse) { - if (request.type === "getImageDataURL" && imageDataURL) { - sendResponse({ - imageDataURL: imageDataURL, - }); - imageDataURL = null; - } - return true; -}; - -browser.runtime.onMessage.addListener(handleMessage); - -var search = function (dataURL) { - imageDataURL = dataURL; - browser.tabs.create({ - url: "https://trace.moe", - }); -}; - -var toDataURL = function (source) { - try { - var canvas = document.createElement("canvas"); - canvas.crossOrigin = "anonymous"; - canvas.height = 720; - if (source.nodeName === "VIDEO") { - canvas.width = (source.videoWidth / source.videoHeight) * canvas.height; - } else { - canvas.width = (source.width / source.height) * canvas.height; - } - canvas.getContext("2d").drawImage(source, 0, 0, canvas.width, canvas.height); - return canvas.toDataURL("image/jpeg", 0.9); - } catch (err) { - return null; - } -}; - -var getDataURL = function (target) { - return new Promise(function (resolve, reject) { - browser.tabs.query( - { - active: true, - }, - function (tabs) { - browser.tabs.sendMessage( - tabs[0].id, - { - action: "getDataURL", - target: target, - }, - function (response) { - if (response && response.dataURL) { - resolve(response.dataURL); - } else { - reject(Error("CORS Error")); - } - } - ); - } - ); - }); -}; - -var fetchImage = function (src) { - var img = new Image(); - img.crossOrigin = "anonymous"; - img.onload = function () { - search(toDataURL(this)); - }; - img.src = src; -}; - -var getCurrentTime = function (target) { - return new Promise(function (resolve, reject) { - browser.tabs.query( - { - active: true, - }, - function (tabs) { - browser.tabs.sendMessage( - tabs[0].id, - { - action: "getCurrentTime", - target: target, - }, - function (response) { - resolve(response.currentTime); - } - ); - } - ); - }); -}; - -var fetchVideo = function (src, currentTime) { - var player = document.createElement("video"); - player.crossOrigin = "anonymous"; - player.src = src; - player.width = player.videoWidth; - player.height = player.height; - player.currentTime = currentTime; - player.volume = 0; - player.onloadeddata = function () { - search(toDataURL(player)); - }; -}; - -browser.contextMenus.onClicked.addListener(function (info, tab) { - if (info.srcUrl) { - if (info.srcUrl.indexOf("blob:") === 0) { - // must capture on context script - // for video it will return capture at currentTime - getDataURL(info.srcUrl).then(function (dataURL) { - search(dataURL); - }); - } else if (info.mediaType === "image" && info.srcUrl.indexOf("data:") === 0) { - search(info.srcUrl); - } else if (info.mediaType === "image") { - getDataURL(info.srcUrl).then( - function (dataURL) { - search(dataURL); - }, - function () { - fetchImage(info.srcUrl); - } - ); - } else if (info.mediaType === "video") { - getDataURL(info.srcUrl).then( - function (dataURL) { - search(dataURL); - }, - function () { - getCurrentTime(info.srcUrl).then(function (currentTime) { - fetchVideo(info.srcUrl, currentTime); - }); - } - ); - } - } -}); +if (!self.browser && self.chrome) { + browser = chrome; +} + +(async () => { + await browser.contextMenus.removeAll(); + await browser.contextMenus.create({ + id: "search-on-trace.moe", + title: "Search on trace.moe", + contexts: ["image", "video"], + }); +})(); + +let imageDataURL = null; +let targetSrc = null; +let targetCurrentTime = null; +let tempTab = null; +let newTab = null; + +browser.runtime.onMessage.addListener((request, { tab }, sendResponse) => { + if (newTab && tab.id === newTab.id && request.type === "getImageDataURL" && imageDataURL) { + sendResponse({ imageDataURL }); + imageDataURL = null; + newTab = null; + } + if (tempTab && tab.id === tempTab.id && request.type === "getTargetSrc" && targetSrc) { + browser.tabs.sendMessage( + tempTab.id, + { + action: "getSearchImage", + srcUrl: targetSrc, + currentTime: targetCurrentTime, + }, + (response) => { + browser.tabs.remove(tempTab.id).catch(() => {}); + tempTab = null; + if (!response) return; + if (response.searchImage) { + search(response.searchImage); + } + } + ); + targetSrc = null; + targetCurrentTime = null; + } + return true; +}); + +const search = async (dataURL) => { + imageDataURL = dataURL; + newTab = await browser.tabs.create({ + url: "https://trace.moe", + }); +}; + +browser.contextMenus.onClicked.addListener(async ({ srcUrl }) => { + if (!srcUrl) return; + const activeTabs = await browser.tabs.query({ active: true }); + browser.tabs.sendMessage( + activeTabs[0].id, + { + action: "getSearchImage", + srcUrl, + }, + async (response) => { + if (!response) return; + if (response.searchImage) { + search(response.searchImage); + return; + } + targetSrc = srcUrl; + targetCurrentTime = response.currentTime; + tempTab = await browser.tabs.create({ + active: response.currentTime !== null, + url: srcUrl, + }); + } + ); +}); diff --git a/content.js b/content.js index 7f7b221..eb31b8b 100644 --- a/content.js +++ b/content.js @@ -1,78 +1,53 @@ -if (!self.browser && self.chrome) { - browser = chrome; -} - -var toDataURL = function (source) { - try { - var canvas = document.createElement("canvas"); - canvas.crossOrigin = "anonymous"; - canvas.height = 720; - if (source.nodeName === "VIDEO") { - canvas.width = (source.videoWidth / source.videoHeight) * canvas.height; - } else { - canvas.width = (source.width / source.height) * canvas.height; - } - canvas.getContext("2d").drawImage(source, 0, 0, canvas.width, canvas.height); - return canvas.toDataURL("image/jpeg", 0.9); - } catch (err) { - return null; - } -}; - -var absolutePath = function (href) { - var link = document.createElement("a"); - link.href = href; - return link.protocol + "//" + link.host + link.pathname + link.search + link.hash; -}; - -var handleMessage = (request, sender, sendResponse) => { - if (request.action === "getCurrentTime") { - var currentTime = 0; - var videos = document.querySelectorAll("video"); - for (var i = 0; i < videos.length; ++i) { - if (absolutePath(videos[i].src).indexOf(request.target)) { - currentTime = videos[i].currentTime; - break; - } - var sources = videos[i].querySelectorAll("source"); - for (var j = 0; j < sources.length; ++j) { - if (absolutePath(sources[j].src).indexOf(request.target)) { - currentTime = videos[i].currentTime; - } - break; - } - } - sendResponse({ - currentTime: currentTime, - }); - } else if (request.action === "getDataURL") { - //assume only one element match the blob URL - var source = null; - var elem = document.querySelectorAll("img, video"); - - for (var i = 0; i < elem.length; ++i) { - if (absolutePath(elem[i].src) === request.target) { - source = elem[i]; - break; - } - var sources = elem[i].querySelectorAll("source"); - for (var j = 0; j < sources.length; ++j) { - if (absolutePath(sources[j].src) === request.target) { - source = elem[i]; - } - break; - } - } - - if (source) { - sendResponse({ - dataURL: toDataURL(source), - }); - } else { - alert("Failed to get search image"); - } - } - return true; -}; - -browser.runtime.onMessage.addListener(handleMessage); +if (!self.browser && self.chrome) { + browser = chrome; +} + +const toDataURL = (element) => { + try { + const canvas = document.createElement("canvas"); + canvas.crossOrigin = "anonymous"; + canvas.height = 720; + if (element instanceof HTMLVideoElement) { + canvas.width = (element.videoWidth / element.videoHeight) * canvas.height; + } else { + canvas.width = (element.width / element.height) * canvas.height; + } + canvas.getContext("2d").drawImage(element, 0, 0, canvas.width, canvas.height); + return canvas.toDataURL("image/jpeg", 0.9); + } catch (err) { + return null; + } +}; + +browser.runtime.sendMessage({ type: "getTargetSrc" }); + +browser.runtime.onMessage.addListener(({ action, srcUrl, currentTime }, sender, sendResponse) => { + if (action === "getSearchImage" && srcUrl) { + //assume only one element match the src + const element = Array.from(document.querySelectorAll("img, video")).find( + (e) => e.currentSrc === srcUrl + ); + if (!element) return alert("Fail to get search image"); + if (currentTime && element instanceof HTMLVideoElement) { + element.pause(); + element.volume = 0; + element.currentTime = currentTime; + element.oncanplay = () => { + const searchImage = toDataURL(element); + if (searchImage) { + sendResponse({ searchImage }); + } + }; + } else { + const searchImage = toDataURL(element); + if (searchImage) { + sendResponse({ searchImage }); + } else { + sendResponse({ + currentTime: element instanceof HTMLVideoElement ? element.currentTime : null, + }); + } + } + } + return true; +}); diff --git a/manifest.json b/manifest.json index 7c9a00f..73da387 100644 --- a/manifest.json +++ b/manifest.json @@ -1,26 +1,26 @@ -{ - "author": "soruly", - "background": { - "page": "background.html" - }, - "content_scripts": [ - { - "js": ["content.js"], - "matches": ["http://*/*", "https://*/*"] - }, - { - "js": ["trace.moe.js"], - "matches": ["https://trace.moe/"] - } - ], - "description": "Use anime screenshots to search the scene it is taken from.", - "icons": { - "128": "icon128.png", - "16": "icon16.png", - "48": "icon48.png" - }, - "manifest_version": 2, - "name": "Search Anime by Screenshot", - "permissions": ["contextMenus", "activeTab", "tabs", "http://*/*", "https://*/*"], - "version": "4.0.2" -} +{ + "author": "soruly", + "content_scripts": [ + { + "js": ["content.js"], + "matches": ["http://*/*", "https://*/*"] + }, + { + "js": ["trace.moe.js"], + "matches": ["https://trace.moe/"] + } + ], + "background": { + "service_worker": "bg.js" + }, + "description": "Use anime screenshots to search the scene it is taken from.", + "icons": { + "128": "icon128.png", + "16": "icon16.png", + "48": "icon48.png" + }, + "manifest_version": 3, + "name": "Search Anime by Screenshot", + "permissions": ["contextMenus", "activeTab"], + "version": "5.0.0" +} diff --git a/trace.moe.js b/trace.moe.js index e4a047d..79c0167 100644 --- a/trace.moe.js +++ b/trace.moe.js @@ -2,14 +2,8 @@ if (!self.browser && self.chrome) { browser = chrome; } -browser.runtime.sendMessage( - { - type: "getImageDataURL", - }, - (response) => { - if (response && response.imageDataURL) { - document.querySelector("#autoSearch").checked = true; - document.querySelector("#originalImage").src = response.imageDataURL; - } +browser.runtime.sendMessage({ type: "getImageDataURL" }, (response) => { + if (response && response.imageDataURL) { + document.querySelector("#originalImage").src = response.imageDataURL; } -); +});