From b14c254d929df339033ae3fe2d1abab007a24804 Mon Sep 17 00:00:00 2001 From: Laurens Date: Wed, 7 Jun 2023 12:01:23 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=89=20Init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 16 +++++ .eslintrc.js | 10 +++ .github/FUNDING.yml | 3 + .gitignore | 2 + .vscode/settings.json | 12 ++++ README.md | 24 +++++++ browser-polyfill.min.js | 8 +++ contentscript.js | 35 ++++++++++ icons/128.png | Bin 0 -> 14461 bytes icons/video-seeking-everywhere.svg | 20 ++++++ manifest.json | 45 +++++++++++++ options.html | 62 ++++++++++++++++++ options/options.css | 99 +++++++++++++++++++++++++++++ options/options.js | 43 +++++++++++++ video-seeking-everywhere.js | 30 +++++++++ 15 files changed, 409 insertions(+) create mode 100644 .editorconfig create mode 100644 .eslintrc.js create mode 100644 .github/FUNDING.yml create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 browser-polyfill.min.js create mode 100644 contentscript.js create mode 100644 icons/128.png create mode 100644 icons/video-seeking-everywhere.svg create mode 100644 manifest.json create mode 100644 options.html create mode 100644 options/options.css create mode 100644 options/options.js create mode 100644 video-seeking-everywhere.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..cc62ec7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{js,ts,json}] +indent_size = 2 +indent_style = space diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..7067280 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + "extends": "eslint:recommended", + "env": { + "browser": true, + "es6": true + }, + "parserOptions": { + "ecmaVersion": 2017 + } +}; diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..b91f40f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +ko_fi: lauwerens diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08b8c1f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +web-ext-artifacts \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..cada016 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "editor.formatOnSave": true, + "editor.tabSize": 2, + "eslint.validate": [ + "javascript", + "typescript", + ], + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "eslint.format.enable": true, +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d22b36d --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/L3L0BR8QG) + +# Video Seeking Everywhere +_A [Firefox](https://addons.mozilla.org/en-US/firefox/addon/video-seeking-everywhere/) extension that allows you to seek through any playing video on most websites using the < or > keys._ + +- Use < or , to rewind with 5 seconds +- Use > or . to seek forward with 5 seconds + +🛠️ It's possible to change the amount of seconds using the Options page of this extension. + +## Development +Please feel free to contribute by sending a Pull Request. + +### Dependencies +You can either use `npm` or `pnpm` with the commands below or run `web-ext` directly: + +### Running the extension +`pnpx web-ext run` + +### Building +`pnpx web-ext build` + +## Reference +See https://extensionworkshop.com/documentation/develop/getting-started-with-web-ext/ for information about the `web-ext` command. diff --git a/browser-polyfill.min.js b/browser-polyfill.min.js new file mode 100644 index 0000000..37f6ee9 --- /dev/null +++ b/browser-polyfill.min.js @@ -0,0 +1,8 @@ +(function(a,b){if("function"==typeof define&&define.amd)define("webextension-polyfill",["module"],b);else if("undefined"!=typeof exports)b(module);else{var c={exports:{}};b(c),a.browser=c.exports}})("undefined"==typeof globalThis?"undefined"==typeof self?this:self:globalThis,function(a){"use strict";if(!globalThis.chrome?.runtime?.id)throw new Error("This script should only be loaded in a browser extension.");if("undefined"==typeof globalThis.browser||Object.getPrototypeOf(globalThis.browser)!==Object.prototype){a.exports=(a=>{const b={alarms:{clear:{minArgs:0,maxArgs:1},clearAll:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getAll:{minArgs:0,maxArgs:0}},bookmarks:{create:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},getChildren:{minArgs:1,maxArgs:1},getRecent:{minArgs:1,maxArgs:1},getSubTree:{minArgs:1,maxArgs:1},getTree:{minArgs:0,maxArgs:0},move:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeTree:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}},browserAction:{disable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},enable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},getBadgeBackgroundColor:{minArgs:1,maxArgs:1},getBadgeText:{minArgs:1,maxArgs:1},getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},openPopup:{minArgs:0,maxArgs:0},setBadgeBackgroundColor:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setBadgeText:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},browsingData:{remove:{minArgs:2,maxArgs:2},removeCache:{minArgs:1,maxArgs:1},removeCookies:{minArgs:1,maxArgs:1},removeDownloads:{minArgs:1,maxArgs:1},removeFormData:{minArgs:1,maxArgs:1},removeHistory:{minArgs:1,maxArgs:1},removeLocalStorage:{minArgs:1,maxArgs:1},removePasswords:{minArgs:1,maxArgs:1},removePluginData:{minArgs:1,maxArgs:1},settings:{minArgs:0,maxArgs:0}},commands:{getAll:{minArgs:0,maxArgs:0}},contextMenus:{remove:{minArgs:1,maxArgs:1},removeAll:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},cookies:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:1,maxArgs:1},getAllCookieStores:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},devtools:{inspectedWindow:{eval:{minArgs:1,maxArgs:2,singleCallbackArg:!1}},panels:{create:{minArgs:3,maxArgs:3,singleCallbackArg:!0},elements:{createSidebarPane:{minArgs:1,maxArgs:1}}}},downloads:{cancel:{minArgs:1,maxArgs:1},download:{minArgs:1,maxArgs:1},erase:{minArgs:1,maxArgs:1},getFileIcon:{minArgs:1,maxArgs:2},open:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},pause:{minArgs:1,maxArgs:1},removeFile:{minArgs:1,maxArgs:1},resume:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},extension:{isAllowedFileSchemeAccess:{minArgs:0,maxArgs:0},isAllowedIncognitoAccess:{minArgs:0,maxArgs:0}},history:{addUrl:{minArgs:1,maxArgs:1},deleteAll:{minArgs:0,maxArgs:0},deleteRange:{minArgs:1,maxArgs:1},deleteUrl:{minArgs:1,maxArgs:1},getVisits:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1}},i18n:{detectLanguage:{minArgs:1,maxArgs:1},getAcceptLanguages:{minArgs:0,maxArgs:0}},identity:{launchWebAuthFlow:{minArgs:1,maxArgs:1}},idle:{queryState:{minArgs:1,maxArgs:1}},management:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},getSelf:{minArgs:0,maxArgs:0},setEnabled:{minArgs:2,maxArgs:2},uninstallSelf:{minArgs:0,maxArgs:1}},notifications:{clear:{minArgs:1,maxArgs:1},create:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:0},getPermissionLevel:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},pageAction:{getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},hide:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},permissions:{contains:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},request:{minArgs:1,maxArgs:1}},runtime:{getBackgroundPage:{minArgs:0,maxArgs:0},getPlatformInfo:{minArgs:0,maxArgs:0},openOptionsPage:{minArgs:0,maxArgs:0},requestUpdateCheck:{minArgs:0,maxArgs:0},sendMessage:{minArgs:1,maxArgs:3},sendNativeMessage:{minArgs:2,maxArgs:2},setUninstallURL:{minArgs:1,maxArgs:1}},sessions:{getDevices:{minArgs:0,maxArgs:1},getRecentlyClosed:{minArgs:0,maxArgs:1},restore:{minArgs:0,maxArgs:1}},storage:{local:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},managed:{get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1}},sync:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}}},tabs:{captureVisibleTab:{minArgs:0,maxArgs:2},create:{minArgs:1,maxArgs:1},detectLanguage:{minArgs:0,maxArgs:1},discard:{minArgs:0,maxArgs:1},duplicate:{minArgs:1,maxArgs:1},executeScript:{minArgs:1,maxArgs:2},get:{minArgs:1,maxArgs:1},getCurrent:{minArgs:0,maxArgs:0},getZoom:{minArgs:0,maxArgs:1},getZoomSettings:{minArgs:0,maxArgs:1},goBack:{minArgs:0,maxArgs:1},goForward:{minArgs:0,maxArgs:1},highlight:{minArgs:1,maxArgs:1},insertCSS:{minArgs:1,maxArgs:2},move:{minArgs:2,maxArgs:2},query:{minArgs:1,maxArgs:1},reload:{minArgs:0,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeCSS:{minArgs:1,maxArgs:2},sendMessage:{minArgs:2,maxArgs:3},setZoom:{minArgs:1,maxArgs:2},setZoomSettings:{minArgs:1,maxArgs:2},update:{minArgs:1,maxArgs:2}},topSites:{get:{minArgs:0,maxArgs:0}},webNavigation:{getAllFrames:{minArgs:1,maxArgs:1},getFrame:{minArgs:1,maxArgs:1}},webRequest:{handlerBehaviorChanged:{minArgs:0,maxArgs:0}},windows:{create:{minArgs:0,maxArgs:1},get:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:1},getCurrent:{minArgs:0,maxArgs:1},getLastFocused:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}}};if(0===Object.keys(b).length)throw new Error("api-metadata.json has not been included in browser-polyfill");class c extends WeakMap{constructor(a,b=void 0){super(b),this.createItem=a}get(a){return this.has(a)||this.set(a,this.createItem(a)),super.get(a)}}const d=a=>a&&"object"==typeof a&&"function"==typeof a.then,e=(b,c)=>(...d)=>{a.runtime.lastError?b.reject(new Error(a.runtime.lastError.message)):c.singleCallbackArg||1>=d.length&&!1!==c.singleCallbackArg?b.resolve(d[0]):b.resolve(d)},f=a=>1==a?"argument":"arguments",g=(a,b)=>function(c,...d){if(d.lengthb.maxArgs)throw new Error(`Expected at most ${b.maxArgs} ${f(b.maxArgs)} for ${a}(), got ${d.length}`);return new Promise((f,g)=>{if(b.fallbackToNoCallback)try{c[a](...d,e({resolve:f,reject:g},b))}catch(e){console.warn(`${a} API method doesn't seem to support the callback parameter, `+"falling back to call it without a callback: ",e),c[a](...d),b.fallbackToNoCallback=!1,b.noCallback=!0,f()}else b.noCallback?(c[a](...d),f()):c[a](...d,e({resolve:f,reject:g},b))})},h=(a,b,c)=>new Proxy(b,{apply(b,d,e){return c.call(d,a,...e)}});let i=Function.call.bind(Object.prototype.hasOwnProperty);const j=(a,b={},c={})=>{let d=Object.create(null),e=Object.create(a);return new Proxy(e,{has(b,c){return c in a||c in d},get(e,f){if(f in d)return d[f];if(!(f in a))return;let k=a[f];if("function"==typeof k){if("function"==typeof b[f])k=h(a,a[f],b[f]);else if(i(c,f)){let b=g(f,c[f]);k=h(a,a[f],b)}else k=k.bind(a);}else if("object"==typeof k&&null!==k&&(i(b,f)||i(c,f)))k=j(k,b[f],c[f]);else if(i(c,"*"))k=j(k,b[f],c["*"]);else return Object.defineProperty(d,f,{configurable:!0,enumerable:!0,get(){return a[f]},set(b){a[f]=b}}),k;return d[f]=k,k},set(b,c,e){return c in d?d[c]=e:a[c]=e,!0},defineProperty(a,b,c){return Reflect.defineProperty(d,b,c)},deleteProperty(a,b){return Reflect.deleteProperty(d,b)}})},k=a=>({addListener(b,c,...d){b.addListener(a.get(c),...d)},hasListener(b,c){return b.hasListener(a.get(c))},removeListener(b,c){b.removeListener(a.get(c))}}),l=new c(a=>"function"==typeof a?function(b){const c=j(b,{},{getContent:{minArgs:0,maxArgs:0}});a(c)}:a),m=new c(a=>"function"==typeof a?function(b,c,e){let f,g,h=!1,i=new Promise(a=>{f=function(b){h=!0,a(b)}});try{g=a(b,c,f)}catch(a){g=Promise.reject(a)}const j=!0!==g&&d(g);if(!0!==g&&!j&&!h)return!1;const k=a=>{a.then(a=>{e(a)},a=>{let b;b=a&&(a instanceof Error||"string"==typeof a.message)?a.message:"An unexpected error occurred",e({__mozWebExtensionPolyfillReject__:!0,message:b})}).catch(a=>{console.error("Failed to send onMessage rejected reply",a)})};return j?k(g):k(i),!0}:a),n=({reject:b,resolve:c},d)=>{a.runtime.lastError?a.runtime.lastError.message==="The message port closed before a response was received."?c():b(new Error(a.runtime.lastError.message)):d&&d.__mozWebExtensionPolyfillReject__?b(new Error(d.message)):c(d)},o=(a,b,c,...d)=>{if(d.lengthb.maxArgs)throw new Error(`Expected at most ${b.maxArgs} ${f(b.maxArgs)} for ${a}(), got ${d.length}`);return new Promise((a,b)=>{const e=n.bind(null,{resolve:a,reject:b});d.push(e),c.sendMessage(...d)})},p={devtools:{network:{onRequestFinished:k(l)}},runtime:{onMessage:k(m),onMessageExternal:k(m),sendMessage:o.bind(null,"sendMessage",{minArgs:1,maxArgs:3})},tabs:{sendMessage:o.bind(null,"sendMessage",{minArgs:2,maxArgs:3})}},q={clear:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}};return b.privacy={network:{"*":q},services:{"*":q},websites:{"*":q}},j(a,p,b)})(chrome)}else a.exports=globalThis.browser}); +//# sourceMappingURL=browser-polyfill.min.js.map + +// webextension-polyfill v.0.10.0 (https://github.com/mozilla/webextension-polyfill) + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ diff --git a/contentscript.js b/contentscript.js new file mode 100644 index 0000000..1e8164f --- /dev/null +++ b/contentscript.js @@ -0,0 +1,35 @@ +; +(function () { + const settings = { + rewindSec: 5, + seekForwardSec: 5 + } + + function onError(error) { + console.warn(`Could not load settings for Video Seeking Everywhere, falling back to defaults...`, error); + inject(); + } + + function onGot(item) { + if (item.rewindSec > 0) { + settings.rewindSec = item.rewindSec; + } + if (item.seekForwardSec > 0) { + settings.seekForwardSec = item.seekForwardSec; + } + inject() + } + + let getting = browser.storage.sync.get(); + getting.then(onGot, onError); + + // Inject the script to the page: + function inject() { + var s = document.createElement('script'); + s.src = chrome.runtime.getURL('video-seeking-everywhere.js?') + new URLSearchParams(settings); + s.onload = function () { + this.remove(); + }; + (document.head || document.documentElement).appendChild(s); + } +})() diff --git a/icons/128.png b/icons/128.png new file mode 100644 index 0000000000000000000000000000000000000000..d3d1c88fb209aa612f342d39f4393bae8902185f GIT binary patch literal 14461 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 P00000NkvXXu0mjfX&EAa literal 0 HcmV?d00001 diff --git a/icons/video-seeking-everywhere.svg b/icons/video-seeking-everywhere.svg new file mode 100644 index 0000000..fadff44 --- /dev/null +++ b/icons/video-seeking-everywhere.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..8b48be5 --- /dev/null +++ b/manifest.json @@ -0,0 +1,45 @@ +{ + "manifest_version": 3, + "name": "Video Seeking Everywhere", + "version": "1.0.0", + "description": "Allows you to seek through any playing video on most websites using the < or > keys.", + "icons": { + "48": "icons/128.png" + }, + "content_scripts": [ + { + "matches": [ + "http://*/*", + "https://*/*" + ], + "js": [ + "browser-polyfill.min.js", + "contentscript.js" + ] + } + ], + "web_accessible_resources": [ + { + "resources": [ + "video-seeking-everywhere.js" + ], + "matches": [ + "http://*/*", + "https://*/*" + ] + } + ], + "permissions": [ + "storage" + ], + "options_ui": { + "page": "options.html", + "open_in_tab": true + }, + "browser_specific_settings": { + "gecko": { + "id": "@video-seeking-everywhere", + "strict_min_version": "109.0" + } + } +} \ No newline at end of file diff --git a/options.html b/options.html new file mode 100644 index 0000000..6334ece --- /dev/null +++ b/options.html @@ -0,0 +1,62 @@ + + + + + + + Options for Video Seeking Everywhere + + + + + + + + + + + + \ No newline at end of file diff --git a/options/options.css b/options/options.css new file mode 100644 index 0000000..0b993a0 --- /dev/null +++ b/options/options.css @@ -0,0 +1,99 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: sans-serif; + line-height: 1.5; +} + +.wrapper { + max-width: 46rem; + margin: 2.5em auto; +} + +p { + margin: .5em 0 2em; +} + +.footer { + margin-top: 1em; + text-align: center; + display: flex; + justify-content: space-around; +} + +kbd { + background-color: #eee; + border: 1px solid #ccc; + border-radius: 0.5em; + padding: 0.2em 0.5em; + font-size: 0.9em; + font-family: monospace; + margin: 0 0.2em; + box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2); + white-space: nowrap; +} + +a { + text-decoration: none; + padding: .5em; + display: inline-block; + color: rgb(108, 128, 144); + border-radius: 0.5em; +} + +a span { + white-space: nowrap; +} + +a:hover { + background-color: black; + color: white; +} + +fieldset { + border: 1px solid #ccc; + padding: 1em; + border-radius: 0.5em; +} + +form { + display: grid; + flex-wrap: wrap; + gap: 2.5em; + justify-content: space-between; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); +} + +label { + font-weight: bold; + grid-column: span 1; +} + +input { + display: block; + padding: 0.5em; + width: 100%; + border: 1px solid #ccc; + border-radius: 0.5em; + font-size: 1.2em; +} + +button { + grid-column: span 1; + padding: 0.5em; + border: 1px solid #ccc; + border-radius: 0.5em; +} + +button#save { + color: white; + background-color: rgb(51, 51, 51); +} + +button#save:hover { + background-color: black; +} diff --git a/options/options.js b/options/options.js new file mode 100644 index 0000000..3bd77e6 --- /dev/null +++ b/options/options.js @@ -0,0 +1,43 @@ +let infoMessageTimeout; + +function saveOptions(e, reset = false) { + if (!reset) e.preventDefault(); + + const defaults = { + rewindSec: 5, + seekForwardSec: 5 + }; + + browser.storage.sync.set({ + rewindSec: !reset ? document.querySelector("#rewind-seconds").value : defaults.rewindSec, + seekForwardSec: !reset ? document.querySelector("#forward-seconds").value : defaults.seekForwardSec + }); + + document.querySelector('[data-rewind-sec]').innerText = !reset ? document.querySelector("#rewind-seconds").value : defaults.rewindSec; + document.querySelector('[data-forward-sec]').innerText = !reset ? document.querySelector("#forward-seconds").value : defaults.seekForwardSec; + + let btn = document.querySelector(reset ? "#reset" : "#save"); + btn.textContent = `✓ ${reset ? 'Defaults saved' : 'Saved'}!`; + clearTimeout(infoMessageTimeout); + infoMessageTimeout = setTimeout(() => { + btn.textContent = reset ? "Reset to defaults" : "Save"; + }, 750); +} + +function restoreOptions() { + function setCurrentChoice(result) { + document.querySelector("#rewind-seconds").value = result.rewindSec || defaults.rewindSec; + document.querySelector("#forward-seconds").value = result.seekForwardSec || defaults.seekForwardSec; + } + + function onError(error) { + console.log(`Error: ${error}`); + } + + let getting = browser.storage.sync.get(); + getting.then(setCurrentChoice, onError); +} + +document.addEventListener("DOMContentLoaded", restoreOptions); +document.querySelector("form").addEventListener("submit", saveOptions); +document.querySelector("form").addEventListener("reset", (e) => saveOptions(e, true)); diff --git a/video-seeking-everywhere.js b/video-seeking-everywhere.js new file mode 100644 index 0000000..4fdd82f --- /dev/null +++ b/video-seeking-everywhere.js @@ -0,0 +1,30 @@ +console.debug("💾 'Video Seeking Everywhere' plugin loaded."); +const params = new URLSearchParams(document.currentScript.src.split('?')[1]); + +const config = { + rewindSec: params.get('rewindSec'), + seekForwardSec: params.get('seekForwardSec') +}; + +function onSeek(e) { + const players = [...document.querySelectorAll('video')].filter(vid => !vid.paused); + players.forEach((player) => { + if (player) { + switch (e.key) { + case '<': + case ',': + console.debug(`⏪ Seeking backwards by ${config.rewindSec} second${config.rewindSec > 1 ? 's' : ''}`); + player.currentTime -= parseInt(config.rewindSec); + break; + case '>': + case '.': + console.debug(`⏩ Seeking forwards by ${config.seekForwardSec} second${config.seekForwardSec > 1 ? 's' : ''}`); + player.currentTime += parseInt(config.seekForwardSec); + break; + } + } + }); +} + +// Listen to keydown events: +document.addEventListener('keydown', onSeek);