From ccfa0cfb09884a13f5be6929b4d5e96c36b8ee29 Mon Sep 17 00:00:00 2001 From: amarcu5 Date: Wed, 31 May 2017 19:17:12 +0100 Subject: [PATCH] Added closed captioning support Added closed caption support for Amazon Video, CuriosityStream, Netflix, and YouTube --- out/PiPer.safariextz | Bin 8255 -> 8758 bytes src/scripts/externs.js | 3 + src/scripts/main.js | 145 +++++++++++++++++++++++++++++++++++++++-- update.plist | 2 +- 4 files changed, 143 insertions(+), 7 deletions(-) diff --git a/out/PiPer.safariextz b/out/PiPer.safariextz index f7ed9c80a9d711b02af1761b126ec14695d1b564..ea918b1c2fbb1fffc5aeaae75bb0a6b6a2c7bd21 100644 GIT binary patch delta 7644 zcmV<29V6nuK(<7GcwurO02}}T00000000mR00000001Il00001c$~#r*>a=UvVG?( zD(Y2HCFbFTyG}5JB#=OW1TQyWGY=XO2)_PqS!I{)a=W_v^u6c7VYG+bDb~uBnLG6G z?`uXOsB*zf-MjD1Bve@b>>9~ZmqkI$~7d|^NT`y29qa1Br2kWZf;+C8}ahfm;? zge~0kr7fD|8;)Yk7nJ%!pbn1yi{k!8kzZ*P3m&Gg*L~lcC!jiCnos9;sFOcFpYss= ze4m4}=VH7We3(8yJdN(t^qjQc;PCgSyQ%4ut?!@DMYapu%PINt`d|uUSO#UdN4IXK z-(U8|$diMA_L~3v))V}VE6<)&^c&ab$)NAj{U`XUcjrGo=WToV*L2$V{nwuOx<6%8 z97WM7%1z;d7@qxn6x_R|P)-Wl@W;^iK`}YCMOuKcO}-hru7r*#3W}QXy)N*Xu7`pg zC1V2CtI5YjCJ47v&XrGB$`ii+lZK*kwA+Nf$CfyoW+5y!YbY)gYs)B|a?gBh}iDe@M} z-xi4396rlDYIv@`G4-t;65`qniaHG^rLaS=?!5H^x#3+)X@MVLW44-vdu|?%TY`?R ziwPUfriU}utMd!d^37z67lf(LEqr#MjT+HFWMtv>I$3Bop-*dxEQ7^N3FdQu zx7@hbVZEL+OIDf>S(g@Rx#gz?=Zys6(FKCj%dOXQnXh#+ce0)NWQ}1!)Gw+YGnG|# zvpri0iDnL2Z!b5=;w)^Kvgr32VXye=h@7y!%^Xp@m|MHqxX;jG%V92=EGGvS#k;y% zSbK7}Katw0GGuEJm7^rsS9K&ihNA=E6dmCy)OustHHM~8xG$Y zBC3kwS>-KP5k_9f3uPZ`cE2px4vBDc1R|B+1LP6!?mmVQF>Virr1#n_$ZT|fDG0u- ztR|hA@|IKcMn6%1Xw!aqsKvJ)a? zH<1A1ZDE@Y!Wp}Ta-BzwS?el)chNO_A8w7ze-)K(=I^S^-%u2yYeEioHU(p01ccWH zx5XQ?47LIa7nD#qx}Xry5N?XqyoTZY_`Yt2{N`Fi1LkdE1tYG4r`-Z+gvKs`D8Ls= zRpHh?!2fWJYjn*UgLVJP8|3B=P`R5$>S<%Jok2AU4nBTYCFtb&9~eVXRdVbZSmjsoJ2<6~BHr)ztEb0Emm;M6)NEgQE( zCp9<;RC81^wLz)vrJnGjh@3?tov4h=^d(VvE)>on$n!}M*Y{Y4g*^_yt)$LB1}$25DD>UJ;i?`C1YlotkHJti4M$PLl+ zjH^8I_B}c59kIHzOqc76Kakg1$yfKev!l6Bb7l>L?SKSLUHXrKL1xz0gVDteFiQR?CEmL*LBmOq7wntaf8K0Wge5-hY54C@CRT*KucPERfl;pxO_t>UOuH`iB z_MTi-o+vbb=VN<5BE|*Z_;|in9NZ`_!!*1W9?37y{1)E|n{+AdH=c=~S}_VQ0gKte zY^rWemGy9HXZiw;n8&r2ZAh}@vXkOc%q-5s4yC2uC)O3`h9L5?-3%R_dP~W5X5^=A zBLS_B(T(ZK1&z#M2-o^Hr;T-dZHoFlU$Fr}^_Oga7OaU_tel480FgV(DZfMu<8X2F zZHgVk(`BcWUWTBTuw_yIFeIf^OlqoE4|#Ci4D5=i!^O4O;>@aviB82=ld9RA_{_JXt7NBfo= zHkEOK-0C!N#?-W!28VW8*MJ}mp^N61d7}(}=WuF5F7_q8ln@D!vCTNwiaeI_>{9hq zKIG$hazfojRj(=Ibg@*$Jc$))wQUuiMcU<(yTQsd={nL7@~EbyBhJ++*_GK z-ssd-DPfWJD7l6sJqf=u>mtI+EL83v`ya{gO-aXKzk@<8H|`D4=)wkwr*4CfW99oU zzx5dTU6Mx;*11Xv#(}j~ z4j|ny)@JHu?;1)HSC0<#VT^?Q);cGOk9hMoGS%| zVoq=3xV}M-w0W(n*ztf0OE`6$thy2yM6*Y)N}6*pPQ{Jo@8nq37qV^&`M8{Ck=$>V z*HDn=1`;loD=nsKFy(gJX2Y)I9N&$G8-xl`8&&%(y_rJanuEB!*RM~1LlJx_jo)$<^KrRn*o z=Ry8T)ALc!gZ!1I=cAr~2l*>a&j+aqAm*Vq7Tbbg{2?M%Ceci1IJyJ9!^&yyFBO%l zID1ToW6<(tF}mk~U*ih~1ypxI8*7COM7=W2qF8k`ImJ6&m|VG~zg)P3Bj+603*H}Df7`HK~C zxs;1(?Syq(^{O$(Q$P#DbwhLe6{1^Ac-a|&!)ya{Hx+Vh8yu*HGO7iGo8EP|qjUEJ zqAoDSnV=u9JsY=w6%Mw|N?()w7IEjtIToL)sR=9SG3n#2n}3j+?o%%>03W8`uM?jx zK%XxwzYnBEoqQ_dKR&;>Qr(43@@-YDlGE37n1-h!xwgsfcIW#reVlt**bax}TU?wH z(C$ye!}Rg<)8w;d{N)Yh<9;3NcV94^{d(ACuT4G~y={JfZ4b`GZ*MlA?p7K4?d9r^ z3s!<6Uth33PSwdSYx8e72PeNjznl(FATPQk{__6aGe@9QOb}6u0y4rt9CBEiBxFp- z3CqPy1UQPOK>{A8KN)&;41BMr5VU`K4wcYkl+s~Br3A*}&^nspIKXKN;|MY$H~^xU zjKhcN&z4?)AN2i_{>glJIMg@pKuD+8FWFceK2KlLD1Jqu8yG*sO8g8fRr5HxAM9Q#$AQFsbxD;b)ib4tcKSdR1 zuqa{@28+=IkSPERj;CzOFfl{nAwVe#r*M>^{wt_|l0TtZG+~yU-$(RMV*jN<>hmj~ zAFaem8eZ!5>$&?W{6FH#J8b#E7mT3E_XzV$8Ri|vyifv^eUCH0W(|p^bizPF(rgUa zm`3R^Vi+odWa45jPSXfVK^RiMV9j6P4Ff5HCKR5~7!^X@iLsDQX*y&g3Q$-~ftX-9 zc;c^rc=OxLdCQ%*?0E?{{SMr_q)qGM`p?2mG5AMta~Sp!+$4eiJ-DePVInkwx{Svd zj>kljgi%T)5Nr}eD8-=(9Ra}p=WvG!$>4y%VTvJGm|dtiN&%HbAqgPdQHUiZ8>6?A z{-5FgGwEP{l8!St6^HikYX|!dnZ8Qv|5?L-ZOK1IB^*URq!Nvh?1$1paDR_W2^XVS z%ETy6!=R>UkTMYy5lIxMG#7;|$|L}1nSX#wQH-M;8z(fLQd|OP0A&h55vC!t2$<%m zl*0f?{4(YIpGpV!4%$sv?7o&iG3f7Ojg#0%ShFc_J zAp;^hWn+qi!dqJIEv7Ug0b+D6$pBxsZauyje1l)#~cU_?y8??3_^WB#kc z@#7idcl(*h!}Rv6k^cqB9(t_2XO(AN+9MI9oRxw)hjsdk+Cmq-9P!s#&`hxKK2Gm6 zJqY?#@iP zNr+3fQ^GLryZIoiF7rGCXGu{3VeH*n$tO&!9Fl1vGKA)A6)VYzfAhg_z0%wHu0Lm< z7~f%B?fm%BeV;%Fte1i<^SmslA_QH(KM5YI*r#x?y+4YCdJeX+dF~O!ds1Yw~g&QkI!?wwzEJHHUWQz&>~uepg=%E z3N$o%6<$+3Wwj96t@OP%gN!X!vk3xVc2F144RvE`X5!xQ~W|o6#6~4MMK& zTBwy;oBtqWKA&S+pm2YpvI1zxP^%oYT@Jk1fhS;QS<7!zslRc@|7AR}-QAte3oN@m zXH%s+q*(i3#%k|rx5sFPZmCY3>5%$gO}KDeFwp3+65VDVwNDP)_$xl$=3QQ2ILQR` zxyc1x-q8%UxkEk#9pc?Ekd{w5ymQt^0n@B>78^RHn&X`35hZ^)Rec`*Jaz&TFv zVTeES0kbu2R3f&?hqxW42R1xzzauDV{h8OmI&GD4lxUt?vogL~EAy3PYtv4LYE7%< zdOK6`Nk+NEe>b!2F=S2XkX5me&8q1`#sA}|)V4>J%&-G@u#;{N2m{q^?x z`=4#{)xyWN$it_q;plxjeW_;L6kzjlhJM4ua0{rHBw&Jx>ItFdz))WmnK1FB(K0~( z+GF?fCSz;F7u<|lBeF(~0HU@}b%Q_%P@5Sv@I!!=f3$z;00BUvgn(yAov*7{96pCj zoaV}>dg;G`W>LY#gihlsjTo6w z8dHedWz}h-0!tOi!h@epKVqE>DHMkc~BKqE+s;W(tvu-W^q zc0gXygJRNcm}}*cdVA2ouztuH^>gT!N|s24gS^2sOhiI?Z~OL(V*O1Mux z7Ch^I5}~R}-W7|-G>%h*a!1qnL|vtb8&Y3|^qj8JN?p06c}q7M_; z?B7BHJiN3>n27L_*mEi%5tvhye;lX%Y!I@jXECq?vncKLm~+L0OQAk<^{M0%rZHUj zs}`y-cJ7O;Uq2GxZ&s{Me@jG`h>R0=-8wp>Gv0Fy05Z2i8_e+*{(_V)l9|pH z94=6F!*LI0TZS^sQGnP<$-+x%M_|;1XJZw6QNQm|V0=wrg%dzjKRAuff1HE`Ji*Oy z(jZb0`MX-g} z2KjxS!3hc%275l>m-vC6e-Ih&cq0ehA6zBQuS5n8H*y%9J>l;wE^EW|dLyUgX|7*d z?FpxYGc8m=rvV~^HfF2NbOM@vVaX-v%xv_Ne~Lu$()s+ak6+GDKK%E@)-iBzNIm9c zjW1D;2K%5zSd}I0h3}jU{`rF?6*{>{EeH}_EpQZ}U>U#a{&8}Ge~(eK>_nv8UK;Jc z9%ztfGoDD^ZS;?XWI@6sVlxN`>)<7A!<0y#UJtC&>wPnZ*_*39=Bf@L?{0pHtmSx2 z7Zwd*A)bbj9=s^Q{Spgho1X)IKAjI3;1`+YaTDdqK41nR*$YnNGe7o94iwJ1Eso># zvA;<;Tt>Y;jQwNZe*_b23|Ry`3qSlAJO+2hfgSaFku`>>Ri84R%?#P5Gw@^WEG0G2vKo0@{6qi^2l%?!{5HP2yOY(w+fVVr`6KRo`ie-w}vD=P? z_36y{R`K%YGf#M^fOy1NIMO=}_LmaFZ3&jM19t@ywhB37!Q{0feD(nO9lV5A!51U7e~5hSc;Ly$8;* zSL9NlsBXyEd(kcAF>!C5_VAcsGi`&gWXH^TP))YRS_NnC9ZhbzU#yRd2A(ap$p0 zlcRH12%`0`N`Y*-kqUxsy0Z&CQh)HPd-Z-9iOH$1e;5DM7SrKWo}br9NNqg;V(1L* z4tPsL*Mm95^&@B}!LCIvAdQu6S8BYg?KUYZA@lZ2z186W)-OD+?Q3ft!lc8tMrN{3 zJS#>8<=iY+meKHxKE(f>(KCc!gP;DQrhZ%{m8QG|Gben`bm5|aPi=AF7~d``q~Ovm zs=u+5fA%pt?cG^Zl=&EHEs5*;x?aO$mTLbF6n05yq3?J5-NR$BFD0|x{3tZ#P+!A1 z*9NeJ6c2YK6$87Z#`NN5RC6TxweC8CAmLfydp{?s|Om2;H|iWTAK#GG=mh%MBYFEi!gzEhQdImG|r^jrHe& z>4|c1#?>eh*V>MdVnFoVCH2e4#DI2L)ByVSuF+~8C0tJ{D8cmQTN8R-KC(dONu{`F ze^TN=^Lx-FSe`aDB*xU$TzjWj{o&v>{vqqaxrx(tdj8$?W{x00Lwx>=skYHl!PPCo z8!Z~IvrDmAZ(w+F)!YcSV`^UTEXi5)9V^Q`sgHkDRSDMDSB0ZHG3+6F*XOQK8|sQ{ zt$d&$P>bx6*qaK>-FVKgAJDKiTU6#ne^nS|wNE(4kaN$Tdvr&;^mrlN`kRyv@rOIZ zgRrz>w~Be5;l6)Qtq`t>5PwVR+ftA<*-u>w6~-FB+SH9dB|DtI+*+|a*!RK~(q|EX z4aT~=xG|yt3U-E#GVG7rSAOI~Ofj$!yeT}^^v#L4u6vi}mE_pqC3Z{Pr7ir-e~aqS z7zv5T`Xos5@RH2vJ$qJaWy_Tjst z`kv0_QgKliU}huq_V%w;l}nN>e-{?VJGsyk3%2mE37_O`|EeRwQzxvYG;}XyCjQJ@ z08JIvZkBDjuK zOg}v?AJvQf2r>jF5C%C{FVw%5n5WiKN_WXj4{izbDdr|4Lx!p3X<3!#6T#=K+9q<| z(`F_PT6ArYdVRc84}EvBw&}9>o9oXm5#UKhF`>eQSKZ~Chm4t5Iodb;`ZM7AXU2&S z^^UG8oH&;Xf2r#A?!0nRUL=wA$~yhiIoqdS9qX+d=3gE9R=@biD|$LS^Ewm!Ukerf zyyRD>lQUac)Yhc?wKA2<$3XHDcx;uFI=LClf2_BsArcHZ?d}b1Sp^CT33!}QyR?9( Ko&N(ohyLuFK=1-wfS&qhdTNF<0X%X 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 Xz?Nm8AZx>Lj_t$(UUdEePs9;+tUeH0 diff --git a/src/scripts/externs.js b/src/scripts/externs.js index 1cabfd0..4a56482 100644 --- a/src/scripts/externs.js +++ b/src/scripts/externs.js @@ -12,3 +12,6 @@ HTMLVideoElement.prototype.webkitPresentationMode; /** @return {undefined} */ HTMLVideoElement.prototype.webkitSetPresentationMode = function(mode) {} + +/** @type {string} */ +TextTrack.prototype.label; diff --git a/src/scripts/main.js b/src/scripts/main.js index 23a83de..4881c4f 100644 --- a/src/scripts/main.js +++ b/src/scripts/main.js @@ -11,6 +11,7 @@ * buttonParent: function(): ?Element, * buttonScale: (number|undefined), * buttonStyle: (string|undefined), + * captionElement: (function(): ?Element|undefined), * videoElement: function(): ?Element, * }} */ @@ -20,10 +21,14 @@ let PIPResource; /** @define {boolean} - Flag used by closure compiler to remove logging */ const COMPILED = false; -const BUTTON_ID = 'PIPButton'; +const BUTTON_ID = 'PiPer_button'; +const TRACK_ID = 'PiPer_track'; let /** ?Element */ button = null; let /** ?PIPResource */ currentResource = null; +let /** ?TextTrack */ track = null; +let /** boolean */ showingCaptions = false; +let /** string */ lastUnprocessedCaption = ''; /** * Logs message to console @@ -84,12 +89,85 @@ const addButton = function(parent) { // Inject button into correct place const referenceNode = currentResource.buttonInsertBefore ? currentResource.buttonInsertBefore(parent) : null; parent.insertBefore(button, referenceNode); -} +}; /** - * Tracks injected button + * Prepares video for captions + * @param {HTMLVideoElement} video - an unprepared video element */ -const buttonObserver = function() { +const prepareCaptions = function(video) { + + // Find existing caption track (if video element id changes function can be called twice) + track = null; + const allTracks = video.textTracks; + for (let trackId = allTracks.length; trackId--;) { + if (allTracks[trackId].label === TRACK_ID) { + track = allTracks[trackId]; + log('Existing caption track found'); + break; + } + } + if (track) return; + + // Otherwise create new caption track + log('Caption track created'); + track = video.addTextTrack('captions', TRACK_ID, 'en'); + + // Toggle captions when Picture in Picture mode changes + const toggleCaptions = function() { + showingCaptions = video.webkitPresentationMode == 'picture-in-picture'; + lastUnprocessedCaption = ''; + processCaptions(); + log('Video presentation mode changed (showingCaptions: ' + showingCaptions + ')'); + } + video.addEventListener('webkitbeginfullscreen', toggleCaptions); + video.addEventListener('webkitendfullscreen', toggleCaptions); +}; + +/** + * Updates visible captions + */ +const processCaptions = function() { + const captionElement = currentResource.captionElement(); + + // Hide Picture in Picture mode captions and show native captions if no longer showing captions or encountered an error + if (!showingCaptions || !captionElement) { + track.mode = 'disabled'; + if (captionElement) captionElement.style.visibility = ''; + return; + } + + // Otherwise ensure native captions remain hidden + captionElement.style.visibility = 'hidden'; + + // Check if a new native caption needs to be processed + const unprocessedCaption = captionElement.textContent; + if (unprocessedCaption == lastUnprocessedCaption) return; + lastUnprocessedCaption = unprocessedCaption; + + // Get handle to video (called before accessing 'track' to guarentee valid) + const video = /** @type {?HTMLVideoElement} */ (currentResource.videoElement()); + + // Remove old caption + track.mode = 'showing'; + if (track.activeCues.length) track.removeCue(track.activeCues[0]); + + if (!unprocessedCaption) return; + + // Show correctly spaced Picture in Picture mode caption + let caption = ''; + const walk = document.createTreeWalker(captionElement, NodeFilter.SHOW_TEXT, null, false); + while (walk.nextNode()) caption += walk.currentNode.nodeValue.trim() + ' '; + log('Showing caption "' + caption.trim() + '"'); + track.addCue(new VTTCue(video.currentTime, video.currentTime + 60, caption)); +}; + +/** + * Tracks injected button and captions + */ +const mutationObserver = function() { + + if (showingCaptions && currentResource.captionElement) processCaptions(); if (document.getElementById(BUTTON_ID)) return; @@ -101,6 +179,43 @@ const buttonObserver = function() { } }; +/** + * Initialises caching for button, video, and caption elements + */ +const initialiseCaches = function() { + const cacheElementIds = {}; + + // Return element by native id or assign id for faster lookups + const cacheElementWrapper = function(/** (function(): ?Element|undefined) */ elementFunction, elementChangedCallback) { + const uniqueLabel = 'PiPer_' + elementFunction.name; + cacheElementIds[uniqueLabel] = uniqueLabel; + + return function() { + let element = document.getElementById(cacheElementIds[uniqueLabel]); + + if (!element) { + element = elementFunction(); + + if (element) { + if (!element.id) element.id = uniqueLabel; + cacheElementIds[uniqueLabel] = element.id; + if (elementChangedCallback) elementChangedCallback(element); + } + } + return element; + }; + }; + + // Performance optimisation - prepare captions when new video found + let videoElementChanged = null; + if (currentResource.captionElement) { + currentResource.captionElement = cacheElementWrapper(currentResource.captionElement); + videoElementChanged = prepareCaptions; + } + currentResource.videoElement = cacheElementWrapper(currentResource.videoElement, videoElementChanged); + currentResource.buttonParent = cacheElementWrapper(currentResource.buttonParent); +}; + /** @type {!IObject} */ const resources = { @@ -114,6 +229,10 @@ const resources = { return e && e.querySelector('.hideableTopButtons'); }, buttonStyle: 'border:0;padding:0;background-color:transparent;opacity:0.8;position:relative;left:8px;width:3vw;height:2vw;min-width:35px;min-height:24px', + captionElement: function() { + const e = document.getElementById('dv-web-player'); + return e && e.querySelector('.captions'); + }, videoElement: function() { const e = document.querySelector('.rendererContainer'); return e && e.querySelector('video[width="100%"]'); @@ -146,6 +265,10 @@ const resources = { }, buttonScale: 1.1, buttonStyle: 'height:22px;width:22px;cursor:pointer;padding:0;border:0;opacity:0.8;margin-right:30px;background:transparent', + captionElement: function() { + const e = currentResource.videoElement(); + return /** @type {?Element} */ (e && e.parentNode.querySelector('div:not([class])')); + }, videoElement: function() { const e = document.getElementById('app'); return e && e.querySelector('video[class^="styles__video"]'); @@ -238,6 +361,10 @@ const resources = { buttonDidAppear: function() { currentResource.buttonParent().style.paddingRight = '50px'; }, + captionElement: function() { + const e = currentResource.videoElement(); + return /** @type {?Element} */ (e && e.parentNode.querySelector('.player-timedtext')); + }, videoElement: function() { const e = document.querySelector('.player-video-wrapper'); return e && e.querySelector('video'); @@ -395,6 +522,10 @@ const resources = { return e && e.querySelector('.ytp-right-controls'); }, buttonScale: 0.68, + captionElement: function() { + const e = document.getElementById('movie_player') || document.getElementById('player'); + return e && e.querySelector('.captions-text'); + }, videoElement: function() { const e = document.getElementById('movie_player') || document.getElementById('player'); return e && e.querySelector('video.html5-main-video'); @@ -413,12 +544,14 @@ if (domainName in resources) { log('Matched site ' + domainName + ' (' + location + ')'); currentResource = resources[domainName]; - const observer = new MutationObserver(buttonObserver); + initialiseCaches(); + + const observer = new MutationObserver(mutationObserver); observer.observe(document, { childList: true, subtree: true, }); - buttonObserver(); + mutationObserver(); } diff --git a/update.plist b/update.plist index 4b645ee..bf08ef7 100644 --- a/update.plist +++ b/update.plist @@ -10,7 +10,7 @@ CFBundleShortVersionString 0.2.0 CFBundleVersion - 31 + 32 Developer Identifier BQ6Q24MF9X URL