diff --git a/dist/bookmarklet-video-speed.js b/dist/bookmarklet-video-speed.js index d093e65..f7a881f 100644 --- a/dist/bookmarklet-video-speed.js +++ b/dist/bookmarklet-video-speed.js @@ -1,8 +1,8 @@ -(()=>{var C=Object.defineProperty;var v=n=>{throw TypeError(n)};var k=(n,t,e)=>t in n?C(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var r=(n,t,e)=>k(n,typeof t!="symbol"?t+"":t,e),E=(n,t,e)=>t.has(n)||v("Cannot "+e);var T=(n,t,e)=>(E(n,t,"read from private field"),e?e.call(n):t.get(n)),b=(n,t,e)=>t.has(n)?v("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(n):t.set(n,e),L=(n,t,e,o)=>(E(n,t,"write to private field"),o?o.call(n,e):t.set(n,e),e),l=(n,t,e)=>(E(n,t,"access private method"),e);var p="nunsez-video-bookmarklet";var S="".concat(p,"-memory"),i="".concat(p,"-controller"),M="".concat(p,"-tickmarks"),x="".concat(p,"-stylesheet"),R="#".concat(i," * {box-sizing: border-box;color: #111;line-height:initial}")+"#".concat(i,", #").concat(i," .range, #").concat(i," .controls {margin: 0;padding: 4px;}")+"#".concat(i,", #").concat(i," .btn {border: 1px solid #444;border-radius: 4px;background-color: #eee;}")+"#".concat(i," {position: fixed;left: 8px;top: 8px;width: 150px;font: 15px monospace;box-shadow: 1px 1px 4px #444;user-select: none;z-index: 999999999;}")+"#".concat(i," .controls {display: flex;justify-content: space-between;align-items: center;}")+"#".concat(i," .btn {width: 20px;height: 20px;margin: 0;padding: 0;}")+"#".concat(i," .value {pointer-events: none;}")+"#".concat(i,' .value::after {content: "%";margin-left: 2px;}')+"#".concat(i," .range {width: 100%;}")+"#".concat(i," .range ~ * {display: none;}");function N(n){if(n.head.querySelector("#".concat(x)))return;let t=n.createElement("style");t.setAttribute("id",x),t.textContent=R,n.head.append(t)}var u,c=class c{constructor(t){r(this,"document");r(this,"speed");r(this,"videos",[]);r(this,"oldController");b(this,u,null);this.document=t,this.speed=c.getSpeed(),this.oldController=t.querySelector("#".concat(i))}static getSpeed(){let t=Number.parseInt(localStorage.getItem(S)||"");return Number.isNaN(t)?100:t}static getVideos(t){let e=[];return t.querySelectorAll("video, iframe").forEach(o=>{var a;if(o instanceof HTMLVideoElement)e.push(o);else if(o instanceof HTMLIFrameElement){let d=(a=c.getIDoc(o))==null?void 0:a.querySelector("video");d&&e.push(d)}}),e}static getIDoc(t){var e;try{return((e=t.contentWindow)==null?void 0:e.document)||t.contentDocument}catch(o){return console.log("iframe document is not reachable: "+t.src),null}}initialize(t){setTimeout(()=>{this.videos=c.getVideos(this.document.body),this.setSpeed(this.speed)},500)}get controller(){return T(this,u)}set controller(t){L(this,u,t)}setSpeed(t){if(Number.isNaN(t))return;t===5?this.speed>5?this.speed=0:this.speed=10:t>300?this.speed=300:t<0?this.speed=0:this.speed=t;let e=this.speed.toString();this.controller.value.textContent=e,this.controller.range.value=e,this.videos.forEach(o=>o.playbackRate=this.speed/100),localStorage.setItem(S,e)}};u=new WeakMap;var m=c,s,H,D,f,y,$,A,B,I,h=class h{constructor(t){b(this,s);r(this,"state");r(this,"el");r(this,"subBtn",document.createElement("button"));r(this,"value",document.createElement("div"));r(this,"addBtn",document.createElement("button"));r(this,"range",document.createElement("input"));this.state=t,this.el=document.createElement("div")}append(t){var e;return(e=t.querySelector(i))==null||e.remove(),l(this,s,D).call(this),l(this,s,H).call(this),t.append(this.el),this}};s=new WeakSet,H=function(){this.range.addEventListener("input",()=>{let t=Number.parseFloat(this.range.value);this.state.setSpeed(t)}),this.subBtn.addEventListener("click",()=>this.state.setSpeed(this.state.speed-5)),this.addBtn.addEventListener("click",()=>this.state.setSpeed(this.state.speed+5))},D=function(){this.el.setAttribute("id",i),this.el.append(l(this,s,f).call(this),l(this,s,B).call(this),l(this,s,I).call(this,h.tickMarks))},f=function(){let t=document.createElement("div");return t.classList.add("controls"),t.append(l(this,s,y).call(this),l(this,s,$).call(this),l(this,s,A).call(this)),t},y=function(){return this.subBtn=document.createElement("button"),this.subBtn.textContent="-",this.subBtn.classList.add("btn","sub"),this.subBtn},$=function(){return this.value=document.createElement("div"),this.value.textContent=this.state.speed.toString(),this.value},A=function(){return this.addBtn=document.createElement("button"),this.addBtn.textContent="+",this.addBtn.classList.add("btn","add"),this.addBtn},B=function(){return this.range=document.createElement("input"),this.range.classList.add("range"),this.range.setAttribute("type","range"),this.range.setAttribute("min","10"),this.range.setAttribute("max","300"),this.range.setAttribute("step","10"),this.range.setAttribute("list",M),this.range.value=this.state.speed.toString(),this.range},I=function(t){let e=document.createElement("datalist");e.setAttribute("id",M);let o=t.map(a=>{let d=document.createElement("option");return d.value=a.toString(),d});return e.append(...o),e},r(h,"tickMarks",[10,50,100,150,200,250,300]);var g=h;function w(n){let t=n.document,e=new m(t);if(e.oldController){e.oldController.remove();let a=m.getVideos(t.body);setTimeout(()=>{a.forEach(d=>d.playbackRate=100/100)},505);return}N(t),new MutationObserver(a=>e.initialize(a)).observe(t.body,{childList:!0}),e.controller=new g(e),e.controller.append(e.document.body)}w(window);})(); +(()=>{var H=Object.defineProperty;var b=n=>{throw TypeError(n)};var $=(n,e,t)=>e in n?H(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var o=(n,e,t)=>$(n,typeof e!="symbol"?e+"":e,t),A=(n,e,t)=>e.has(n)||b("Cannot "+t);var E=(n,e,t)=>e.has(n)?b("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(n):e.set(n,t);var a=(n,e,t)=>(A(n,e,"access private method"),t);var c="nunsez-video-bookmarklet";var h="".concat(c,"-state"),g="".concat(c,"-memory"),i="".concat(c,"-controller"),v="".concat(c,"-tickmarks"),T="".concat(c,"-stylesheet"),B="#".concat(i," * {box-sizing: border-box;color: #111;line-height:initial}")+"#".concat(i,", #").concat(i," .range, #").concat(i," .controls {margin: 0;padding: 4px;}")+"#".concat(i,", #").concat(i," .btn {border: 1px solid #444;border-radius: 4px;background-color: #eee;}")+"#".concat(i," {position: fixed;left: 8px;top: 8px;width: 150px;font: 15px monospace;box-shadow: 1px 1px 4px #444;user-select: none;z-index: 999999999;}")+"#".concat(i," .controls {display: flex;justify-content: space-between;align-items: center;}")+"#".concat(i," .btn {width: 20px;height: 20px;margin: 0;padding: 0;}")+"#".concat(i," .value {pointer-events: none;}")+"#".concat(i,' .value::after {content: "%";margin-left: 2px;}')+"#".concat(i," .range {width: 100%;}")+"#".concat(i," .range ~ * {display: none;}");function k(n){if(n.head.querySelector("#".concat(T)))return;let e=n.createElement("style");e.setAttribute("id",T),e.textContent=B,n.head.append(e)}var m=class n{constructor(e){o(this,"document");o(this,"speed");o(this,"videos",[]);o(this,"controller");o(this,"searchTimeoutId");o(this,"observer");this.document=e,this.speed=n.getSpeed(),this.observer=new MutationObserver(t=>this.refresh(t)),this.controller=new p(this)}static getSpeed(){let e=Number.parseInt(localStorage.getItem(g)||"");return Number.isNaN(e)?100:e}static getVideos(e){let t=[];return e.querySelectorAll("video, iframe").forEach(r=>{var l;if(r instanceof HTMLVideoElement)t.push(r);else if(r instanceof HTMLIFrameElement){let d=(l=n.getIDoc(r))==null?void 0:l.querySelector("video");d&&t.push(d)}}),t}static getIDoc(e){var t;try{return((t=e.contentWindow)==null?void 0:t.document)||e.contentDocument}catch(r){return console.log("iframe document is not reachable: "+e.src),null}}initialize(){this.observer.observe(document.body,{childList:!0}),this.controller.appendTo(document.body)}refresh(e){console.log("_mutationRecords",e),console.log("refresh",this.searchTimeoutId,this.controller),clearTimeout(this.searchTimeoutId),this.searchTimeoutId=setTimeout(()=>{this.videos=n.getVideos(this.document.body),this.setSpeed(this.speed)},500)}terminate(){console.log("terminate observer",this.observer),console.log("terminate oldController",this.controller),this.observer.disconnect(),this.controller.el.remove(),this.videos.forEach(e=>e.playbackRate=100/100)}setSpeed(e){if(!this.controller||Number.isNaN(e))return;e===5?this.speed>5?this.speed=0:this.speed=10:e>300?this.speed=300:e<0?this.speed=0:this.speed=e;let t=this.speed.toString();this.controller.value.textContent=t,this.controller.range.value=t,this.videos.forEach(r=>r.playbackRate=this.speed/100),console.log("set speed",this.videos,this.controller),localStorage.setItem(g,t)}},s,L,S,M,f,x,y,D,I,u=class u{constructor(e){E(this,s);o(this,"el");o(this,"subBtn",document.createElement("button"));o(this,"value",document.createElement("div"));o(this,"addBtn",document.createElement("button"));o(this,"range",document.createElement("input"));this.el=document.createElement("div"),this.el[h]=e}appendTo(e){var t;return(t=e.querySelector(i))==null||t.remove(),a(this,s,S).call(this),a(this,s,L).call(this),e.append(this.el),this}get state(){return this.el[h]}};s=new WeakSet,L=function(){this.range.addEventListener("input",()=>{let e=Number.parseFloat(this.range.value);this.state.setSpeed(e)}),this.subBtn.addEventListener("click",()=>this.state.setSpeed(this.state.speed-5)),this.addBtn.addEventListener("click",()=>this.state.setSpeed(this.state.speed+5))},S=function(){this.el.setAttribute("id",i),this.el.append(a(this,s,M).call(this),a(this,s,D).call(this),a(this,s,I).call(this,u.tickMarks))},M=function(){let e=document.createElement("div");return e.classList.add("controls"),e.append(a(this,s,f).call(this),a(this,s,x).call(this),a(this,s,y).call(this)),e},f=function(){return this.subBtn=document.createElement("button"),this.subBtn.textContent="-",this.subBtn.classList.add("btn","sub"),this.subBtn},x=function(){return this.value=document.createElement("div"),this.value.textContent=this.state.speed.toString(),this.value},y=function(){return this.addBtn=document.createElement("button"),this.addBtn.textContent="+",this.addBtn.classList.add("btn","add"),this.addBtn},D=function(){return this.range=document.createElement("input"),this.range.classList.add("range"),this.range.setAttribute("type","range"),this.range.setAttribute("min","10"),this.range.setAttribute("max","300"),this.range.setAttribute("step","10"),this.range.setAttribute("list",v),this.range.value=this.state.speed.toString(),this.range},I=function(e){let t=document.createElement("datalist");t.setAttribute("id",v);let r=e.map(l=>{let d=document.createElement("option");return d.value=l.toString(),d});return t.append(...r),t},o(u,"tickMarks",[10,50,100,150,200,250,300]);var p=u;function R(n){let e=n.document,t=e.querySelector("#".concat(i));if(t){let l=t[h];l==null||l.terminate(),t.remove();return}k(e),new m(e).initialize()}R(window);})(); /** * @file A script to control the speed of html5 video playback in the browser. * @author Alexander Mandrikov - * @version 2.0.1 + * @version 2.1.0 * @license AGPLv3 * @see {@link https://github.com/nunsez/bookmarklet-video-speed GitHub} for further information. */ diff --git a/main.ts b/main.ts index 655c434..d6a9ab9 100644 --- a/main.ts +++ b/main.ts @@ -1,7 +1,7 @@ /** * @file A script to control the speed of html5 video playback in the browser. * @author Alexander Mandrikov - * @version 2.0.1 + * @version 2.1.0 * @license AGPLv3 * @see {@link https://github.com/nunsez/bookmarklet-video-speed GitHub} for further information. */ @@ -9,6 +9,7 @@ const PREFIX = "nunsez-video-bookmarklet"; const DEFAULT_SPEED = 100; const SEARCH_TIMEOUT = 500; +const STATE_ID = `${PREFIX}-state`; const STORAGE_ID = `${PREFIX}-memory`; const CONTROLLER_ID = `${PREFIX}-controller`; const TICKMARKS_ID = `${PREFIX}-tickmarks`; @@ -25,6 +26,10 @@ const STYLES = `#${CONTROLLER_ID} .range {width: 100%;}` + `#${CONTROLLER_ID} .range ~ * {display: none;}`; +interface MyElement extends HTMLDivElement { + [STATE_ID]?: State +} + function addStyles(document: Document) { if (document.head.querySelector(`#${STYLESHEET_ID}`)) return; @@ -38,8 +43,9 @@ class State { document: Document; speed: number; videos: HTMLVideoElement[] = []; - oldController: HTMLDivElement | null; - #controller: Controller | null = null; + controller: Controller; + searchTimeoutId?: number; + observer: MutationObserver; static getSpeed(): number { const storageSpeed = Number.parseInt( @@ -78,27 +84,43 @@ class State { constructor(document: Document) { this.document = document; this.speed = State.getSpeed(); - this.oldController = document.querySelector( - `#${CONTROLLER_ID}`, + + this.observer = new MutationObserver((mutations) => + this.refresh(mutations) ); + + // create controller and his components + this.controller = new Controller(this); } - initialize(_mutationRecords: MutationRecord[]) { - setTimeout(() => { + initialize() { + this.observer.observe(document.body, { childList: true }); + + // append controller to body tag + this.controller.appendTo(document.body); + } + + refresh(_mutationRecords: MutationRecord[]) { + console.log('_mutationRecords', _mutationRecords) + console.log('refresh', this.searchTimeoutId, this.controller); + clearTimeout(this.searchTimeoutId); + + this.searchTimeoutId = setTimeout(() => { this.videos = State.getVideos(this.document.body); this.setSpeed(this.speed); }, SEARCH_TIMEOUT); } - get controller(): Controller { - return this.#controller!; - } - - set controller(controller: Controller) { - this.#controller = controller; + terminate() { + console.log('terminate observer', this.observer) + console.log('terminate oldController', this.controller) + this.observer.disconnect(); + this.controller.el.remove(); + this.videos.forEach((v) => v.playbackRate = DEFAULT_SPEED / 100); } setSpeed(newSpeed: number) { + if (!this.controller) return; if (Number.isNaN(newSpeed)) return; // The 0.05x playback rate is not in the supported playback range @@ -118,13 +140,14 @@ class State { this.controller.range.value = speedString; this.videos.forEach((v) => v.playbackRate = this.speed / 100); + console.log('set speed', this.videos, this.controller); + localStorage.setItem(STORAGE_ID, speedString); } } class Controller { - state: State; - el: HTMLDivElement; + el: MyElement; subBtn: HTMLButtonElement = document.createElement("button"); value: HTMLDivElement = document.createElement("div"); addBtn: HTMLButtonElement = document.createElement("button"); @@ -133,11 +156,11 @@ class Controller { static tickMarks = [10, 50, 100, 150, 200, 250, 300]; constructor(state: State) { - this.state = state; this.el = document.createElement("div"); + this.el[STATE_ID] = state; } - append(root: HTMLElement): Controller { + appendTo(root: HTMLElement): Controller { // remove existing controller if exists root.querySelector(CONTROLLER_ID)?.remove(); this.#build(); @@ -146,6 +169,10 @@ class Controller { return this; } + get state(): State { + return this.el[STATE_ID]!; + } + #addListeners() { this.range.addEventListener("input", () => { const speed = Number.parseFloat(this.range.value); @@ -231,31 +258,22 @@ class Controller { function main(window: Window) { const document = window.document; - const state = new State(document); + + const myElement = document.querySelector(`#${CONTROLLER_ID}`); // remove controller if exist and restore video playback speed / toggle effect - if (state.oldController) { - state.oldController.remove(); - const videos = State.getVideos(document.body); - setTimeout(() => { - videos.forEach((v) => v.playbackRate = DEFAULT_SPEED / 100); - }, SEARCH_TIMEOUT + 5); + if (myElement) { + const state = myElement[STATE_ID]; + state?.terminate(); + myElement.remove(); return; } // add stylesheet if not exist addStyles(document); - // reset reinitialize on body changes - const observer = new MutationObserver((mutations) => - state.initialize(mutations) - ); - observer.observe(document.body, { childList: true }); - - // create controller and his components - state.controller = new Controller(state); - // append controller to body tag - state.controller.append(state.document.body); + const state = new State(document); + state.initialize(); } main(window);