From bad8db583ccbf6dd72bea9012ecf6013e64dacf5 Mon Sep 17 00:00:00 2001 From: Joel2B Date: Thu, 30 Jun 2022 15:47:22 -0600 Subject: [PATCH] fix: multiple issues - service worker shuts down every 5 minutes (more or less works) - extension doesn't work until you open the pop-up - customize cURL timeouts --- src/extension/css/content-script.css | 3 +- src/extension/manifest.json | 2 +- src/extension/modules/background.js | 53 ++++++++++- src/extension/modules/content-script.js | 115 +++++++++++++++++------- src/extension/modules/defaults.js | 17 ++++ src/extension/modules/subtitles.js | 6 +- src/pop-up/index.html | 4 +- src/pop-up/modules/input.js | 1 + src/pop-up/modules/script.js | 9 +- src/translation-api/config.php | 4 + src/translation-api/index.php | 1 + src/translation-api/utils.php | 4 +- src/utils/chrome/runtime.js | 44 +++++++-- 13 files changed, 207 insertions(+), 56 deletions(-) create mode 100644 src/extension/modules/defaults.js create mode 100644 src/translation-api/config.php diff --git a/src/extension/css/content-script.css b/src/extension/css/content-script.css index 3da8fa7..c39424b 100644 --- a/src/extension/css/content-script.css +++ b/src/extension/css/content-script.css @@ -15,7 +15,8 @@ font-size: 16px; } -#line1, #line2 { +#line1, +#line2 { width: 100%; height: 50%; } diff --git a/src/extension/manifest.json b/src/extension/manifest.json index aca1ab2..c8a9931 100644 --- a/src/extension/manifest.json +++ b/src/extension/manifest.json @@ -20,7 +20,7 @@ } ], "background": { - "service_worker": "background.js" + "service_worker": "js/background.js" }, "permissions": ["activeTab", "storage", "tabs", "scripting"], "host_permissions": ["*://*.youtube.com/*"], diff --git a/src/extension/modules/background.js b/src/extension/modules/background.js index 1f81800..cd4470f 100644 --- a/src/extension/modules/background.js +++ b/src/extension/modules/background.js @@ -1,12 +1,61 @@ import { getLocalStorage, setLocalStorage } from 'utils/chrome/storage'; -import { onMessage } from 'utils/chrome/runtime'; +import { onMessage, onInstalled, getAll, getManifest, executeScript, insertCSS } from 'utils/chrome/runtime'; +import defaults from './defaults'; onMessage(async (request) => { const id = request.id; const value = request.value; + if (id == 'analytics') { const data = await getLocalStorage(id); data[value] += 1; setLocalStorage(id, data); } -}, true); +}); + +async function setDefaultSettings() { + const config = defaults; + + for (const option in config) { + if ((await getLocalStorage(option)) !== undefined) { + continue; + } + + setLocalStorage(option, config[option]); + } +} + +async function init() { + await setDefaultSettings(); + + const navigatorWindows = await getAll(); + const contentScripts = getManifest().content_scripts[0]; + const match = contentScripts.matches[0].replaceAll('*', '.*').replaceAll('/', '\\/'); + const regex = new RegExp(match); + const js = contentScripts.js[0]; + const css = contentScripts.css[0]; + + navigatorWindows.forEach((window) => { + for (const tab of window.tabs) { + if (!regex.test(tab.url)) { + continue; + } + + executeScript({ + target: { + tabId: tab.id, + }, + files: [js], + }); + + insertCSS({ + target: { + tabId: tab.id, + }, + files: [css], + }); + } + }); +} + +onInstalled(init); diff --git a/src/extension/modules/content-script.js b/src/extension/modules/content-script.js index 69b0978..48ec382 100644 --- a/src/extension/modules/content-script.js +++ b/src/extension/modules/content-script.js @@ -1,4 +1,4 @@ -import { onMessage } from 'utils/chrome/runtime'; +import { onMessage, sendMessageBackground } from 'utils/chrome/runtime'; import { setOption, getAllOptions, getOption } from 'extension/modules/data'; import { setupSubtitles, @@ -13,32 +13,18 @@ let observer; const tasks = { captions: (value) => { - if (value) { - connectObserver(); - } else { - disconnectObserver(); - } - }, - translateTo: () => { - translateSubtitles(); - }, - 'created-translations': () => { - translateSubtitles(); + (value ? connectObserver : disconnectObserver)(); }, + translateTo: () => translateSubtitles(), + 'created-translations': () => translateSubtitles(), 'auto-translate': () => { - if (!getOption('created-translations')) { - translateSubtitles(); - } - }, - 'time-close-translation': () => { - removeTranslation(); + if (!getOption('created-translations')) translateSubtitles(); }, + 'time-close-translation': () => removeTranslation(), }; function performTask(id, value) { - if (tasks[id]) { - tasks[id](value); - } + if (tasks[id]) tasks[id](value); } async function connectObserver() { @@ -55,8 +41,8 @@ async function connectObserver() { continue; } - if (mutation.type == 'childList' && classCss.includes('ytp-caption-segment')) { - if (line2 == '') { + if (mutation.type === 'childList' && classCss.includes('ytp-caption-segment')) { + if (line2 === '') { line2 = text; } else { if (RegExp(escape(line2)).test(text)) { @@ -67,11 +53,13 @@ async function connectObserver() { if (captions) { captions.style.display = 'none'; } + line1 = line2; line2 = text; } } } + document.querySelector('#line1').innerHTML = wordToSpan(line1); document.querySelector('#line2').innerHTML = wordToSpan(line2); } @@ -82,7 +70,9 @@ async function connectObserver() { return new Promise((resolve) => { const timer = setInterval(() => { console.log('[Extension] Trying to connect the observer'); + const player = document.querySelector('#movie_player'); + if (player) { clearInterval(timer); resolve(player); @@ -90,9 +80,10 @@ async function connectObserver() { }, 50); setTimeout(() => { const player = document.querySelector('#movie_player'); + if (!player) { - clearInterval(timer); console.log('[Extension] Observer error (time limit exceeded)'); + clearInterval(timer); resolve(); } }, 10 * 1000); @@ -100,10 +91,13 @@ async function connectObserver() { }; const player = await playerAvailable(); + if (!player) { return; } + await setupSubtitles(player); + const config = { attributes: true, childList: true, @@ -115,55 +109,110 @@ async function connectObserver() { observer = new MutationObserver(callback); observer.observe(player, config); + console.log('[Extension] Observer connected'); } function disconnectObserver() { - if (!observer) { - return; - } + if (!observer) return; + observer.disconnect(); deactivateSubtitles(); + console.log('[Extension] Observer disconnected'); } function restartExecution() { - setInterval(() => { + const timer = setInterval(() => { + if (!getId()) { + clearInterval(timer); + return; + } + const url = window.location.href; - if (getOption('curent-path') != url && url.includes('watch')) { + + if (getOption('curent-path') !== url && url.includes('watch')) { if (observer) { disconnectObserver(); setOption('subtitlesActivated', false); } + removeSubtitles(); connectObserver(); } - if (getOption('curent-path') != url) { + + if (getOption('curent-path') !== url) { setOption('curent-path', url); } }, 100); } +function getId() { + // eslint-disable-next-line no-undef + return chrome.runtime.id; +} + +function getDate() { + return Number(Date.now().toString().slice(0, 10)); +} + async function app() { + if (!getId()) return; + + if (process.env.NODE_ENV === 'development') { + console.log('Extension id:', getId()); + } + await getAllOptions(); + // TODO: provisional + const timeKeepAlive = getOption('timeKeepAlive'); + let keepAlive = (getDate() + timeKeepAlive) * 1000; + + const timer = setInterval(() => { + if (keepAlive < Date.now()) { + sendMessageBackground(''); + + keepAlive = (getDate() + timeKeepAlive) * 1000; + + if (process.env.NODE_ENV === 'development') { + console.log('keep alive', keepAlive, Date.now()); + } + } + + if (getId()) return; + + disconnectObserver(); + removeSubtitles(); + + if (process.env.NODE_ENV === 'development') { + console.log('lost extension connection'); + } + + clearInterval(timer); + }, 1000); + if (!getOption('curent-path')) { setOption('curent-path', window.location.href); restartExecution(); } onMessage((request) => { + if (!getId()) return; + const id = request.id; - if (id == 'analytics') { + + if (id === 'analytics') { return; } + const value = request.value; + setOption(id, value); performTask(id, value); }); - const captions = getOption('captions'); - if (!captions) { + if (!getOption('captions')) { return; } diff --git a/src/extension/modules/defaults.js b/src/extension/modules/defaults.js new file mode 100644 index 0000000..a671abb --- /dev/null +++ b/src/extension/modules/defaults.js @@ -0,0 +1,17 @@ +const defaults = { + captions: true, + subtitlesActivated: false, + 'curent-path': '', + 'auto-translate': true, + 'created-translations': true, + 'primary-language': 'Spanish#es', + 'server-translate': 'Deepl*#deepl', + 'time-close-translation': 3, + translateTo: 'English#en', + analytics: { + 'translated-words': 0, + }, + timeKeepAlive: 240, +}; + +export default defaults; diff --git a/src/extension/modules/subtitles.js b/src/extension/modules/subtitles.js index 81b6c5c..767df3d 100644 --- a/src/extension/modules/subtitles.js +++ b/src/extension/modules/subtitles.js @@ -182,6 +182,8 @@ async function translateWord(e) { } export function deactivateSubtitles() { + removeSubtitles(); + if (!isSubtitlesEnabled()) { return; } @@ -189,8 +191,8 @@ export function deactivateSubtitles() { if (getSubtitleButton().getAttribute('aria-pressed') == 'true') { getSubtitleButton().click(); } + setOption('subtitlesActivated', false); - removeSubtitles(); } function createSubtitles() { @@ -254,7 +256,7 @@ function dragSubtitles() { export async function translateSubtitles() { let translateTo = getOption('translateTo'); - if (translateTo.includes('#')) { + if (translateTo && translateTo.includes('#')) { translateTo = translateTo.split('#')[0]; } diff --git a/src/pop-up/index.html b/src/pop-up/index.html index ac6eacd..b09e3c5 100644 --- a/src/pop-up/index.html +++ b/src/pop-up/index.html @@ -5,7 +5,7 @@ Auto Translate Youtube Subtitles - + - + diff --git a/src/pop-up/modules/input.js b/src/pop-up/modules/input.js index df19844..95a574f 100644 --- a/src/pop-up/modules/input.js +++ b/src/pop-up/modules/input.js @@ -12,6 +12,7 @@ export async function setupNumberInput(numberInput) { value = Number(input.value); setValue(id, value); } + input.value = value; input.addEventListener( diff --git a/src/pop-up/modules/script.js b/src/pop-up/modules/script.js index 71189b9..657034b 100644 --- a/src/pop-up/modules/script.js +++ b/src/pop-up/modules/script.js @@ -4,13 +4,8 @@ import { setupNumberInput } from 'pop-up/modules/input'; import { sendMessage } from 'utils/chrome/runtime'; async function loadAnalytics() { - let analytics = await getLocalStorage('analytics'); - if (!analytics) { - analytics = { - 'translated-words': 0, - }; - setLocalStorage('analytics', analytics); - } + const analytics = await getLocalStorage('analytics'); + document.getElementById('translated-words').textContent = analytics['translated-words']; } diff --git a/src/translation-api/config.php b/src/translation-api/config.php new file mode 100644 index 0000000..7c2ea13 --- /dev/null +++ b/src/translation-api/config.php @@ -0,0 +1,4 @@ + $headers, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 10, - CURLOPT_CONNECTTIMEOUT => 3, - CURLOPT_TIMEOUT => 4, + CURLOPT_CONNECTTIMEOUT => CONNECTTIMEOUT, + CURLOPT_TIMEOUT => TIMEOUT, CURLOPT_RETURNTRANSFER => true, // for debug only CURLOPT_SSL_VERIFYPEER => false, diff --git a/src/utils/chrome/runtime.js b/src/utils/chrome/runtime.js index 61354d1..4653ef8 100644 --- a/src/utils/chrome/runtime.js +++ b/src/utils/chrome/runtime.js @@ -1,14 +1,12 @@ /* global chrome */ -export function onMessage(callback, async) { +export function onMessage(callback) { try { - chrome.runtime.onMessage.addListener((request) => { + chrome.runtime.onMessage.addListener(async (request) => { if (process.env.NODE_ENV === 'development') { - console.log('onMessage', request, async); + console.log('onMessage', request); } + callback(request); - if (async) { - return true; - } }); } catch (error) { console.log(error); @@ -20,7 +18,9 @@ export function sendMessage(value) { if (process.env.NODE_ENV === 'development') { console.log('sendMessage', value); } + const manifest = chrome.runtime.getManifest(); + chrome.tabs.query( { url: manifest.content_scripts[0].matches, @@ -41,8 +41,40 @@ export function sendMessageBackground(value) { if (process.env.NODE_ENV === 'development') { console.log('sendMessageBackground', value); } + chrome.runtime.sendMessage(value); } catch (error) { console.log(error); } } + +export function onInstalled(callback) { + try { + chrome.runtime.onInstalled.addListener((details) => { + if ( + details.reason === chrome.runtime.OnInstalledReason.INSTALL || + details.reason === chrome.runtime.OnInstalledReason.UPDATE + ) { + callback(); + } + }); + } catch (error) { + console.log(error); + } +} + +export async function getAll() { + return await chrome.windows.getAll({ populate: true }); +} + +export function getManifest() { + return chrome.runtime.getManifest(); +} + +export function executeScript(...params) { + chrome.scripting.executeScript(...params); +} + +export function insertCSS(...params) { + chrome.scripting.insertCSS(...params); +}