// ==UserScript== // @name yahvt // @description yet another html5 video tool // @namespace gnblizz // @version 1.15 // @include http://anilinkz.tv/* // @include http://anilinkz.io/* // @include http://www.animeboy.org/* // @include http://www.animeboy.tv/* // @include http://www.animecenter.tv/* // @include http://www.animedreaming.tv/* // @include http://anime-exceed.com/* // @include http://www.animefreak.tv/* // @include http://www.animefushigi.co/* // @include http://www.animefushigi.com/* // @include http://www.animehere.com/* // @include http://www.animenova.org/* // @include http://www.animenova.tv/* // @include http://www.animeplus.tv/* // @include http://www.animeseason.com/* // @include http://www.animesky.net/* // @include http://ww1.animes-stream24.net/* // @include http://www.anime-sub.com/* // @include http://www.animetoon.eu/* // @include http://www.animetoon.org/* // @include http://www.animetoon.tv/* // @include http://www.animeultima.io/* // @include http://animewow.eu/* // @include http://www.animewow.eu/* // @include http://www.animewow.org/* // @include http://bestanimes.tv/* // @include http://www.chia-anime.tv/* // @include http://www.clipfish.de/* // @include http://dramago.com/* // @include http://www.dramago.com/* // @include http://www.dramagalaxy.eu/* // @include http://www.dramagalaxy.com/* // @include http://www.dramagalaxy.tv/* // @include http://dubbedanime.net/* // @include http://www.dubzonline.cm/* // @include http://www.dubzonline.com/* // @include http://freeanime.com/* // @include http://www.gogoanime.com/* // @include http://www.goodanime.co/* // @include http://www.goodanime.eu/* // @include http://www.goodanime.net/* // @include http://gooddrama.to/* // @include http://www.gooddrama.to/* // @include http://www.gooddrama.net/* // @include http://www.lovemyanime.net/* // @include http://www.theanime.tv/* // @include http://www.videozoo.me/* // @include about:blank?video=* // @include h*embed* // @include h*gogo/* // @include h*widget/* // @match http://*.mangaotaku.org/* // @include http://player.arkvid.tv/* // @include http://www.dramastream.org/* // @exclude https://openload.co/embed/* // @run-at document-start // @grant GM_xmlhttpRequest // @icon data:image/gif;base64,R0lGODlhMAAwAKECAAAAAICAgP///////yH5BAEKAAMALAAAAAAwADAAAALQnI+py+0Po5y02ouz3rz7D4biBJTmiabqyrbuC8fyHAf2jedpzuOvAAwKh6mhUfg7Hks3gHLpehptwIBTioxig0zrdStIgrslMFA8pCKp1oAZjXW6w/Mt/Nl2t8HeFl7o5QZgBagEYyawNxhUl7h4dlelFlZG+QVY6aglmIjjuKd50xla9RKI0mSCqaPJSMM0aEK4mhfbpSnTabM4WXrShtpHI6gqKvmKnCwns0tm2lOsLP3aUy08aK0zvc3d7b09Ei4+Tl5ufo6err7O3n5QAAA7 // @compatible firefox // @compatible chrome // @downloadURL none // ==/UserScript== "use strict"; var doc=document, isTop=window.self==window.top, domain = (document.domain||'unknown.').split('.').reverse(), insertionPoint, isFF = /Firefox/i.test(navigator.userAgent), maxmsg=99; document.addEventListener('DOMContentLoaded', yahvt); !function earlyHacks(){ switch(domain[1]) { case 'clipfish'://.de if(isFF) nn('SCRIPT', doc.body).innerHTML = 'checkMobile=function(){isMobile=true;}'; else Object.defineProperty(navigator, "userAgent", {value: 'fake Android'}); break; } }(); function sites(){ var a,i,o,e,b; //console.log('yahvt: domain =', domain); switch(domain[1]) { case 'anilinkz'://.tv,io allowFullscreen('#player'); break; case 'animecenter'://.tv allowFullscreen('#video'); break; case 'animedreaming'://.tv allowFullscreen('.videoholder'); break; case 'anime-exceed'://.com allowFullscreen('#player', (/^\/cool\//.test(location.pathname)?'':'body'),0,999); break; case 'animefreak'://.tv a=na('.multi'); for(o of a) { e = o.getAttribute('onclick'); if(e && /loadParts\('http/.test(e)) o.onclick = function(event) { var vid_file = decodeURIComponent(this.getAttribute('onclick').match(/loadParts\('([^']+)'/)[1]); doc.getElementById("player").innerHTML = ''; }; } break; case 'animefushigi'://.co,com allowFullscreen('#vidboxx', '.videoloadbg'); break; case 'animehere'://.com allowFullscreen('#playbox'); break; case 'animenova'://.org,tv case 'animeplus'://.tv case 'animesky'://.net case 'animetoon'://.eu,org,tv case 'animewow'://.eu,org case 'dramagalaxy'://.com,eu,tv case 'dramago'://.com case 'gooddrama'://.net,to allowFullscreen('#streams'); break; case 'animeseason'://.com if(fn('#series_info')) SetStyle('table a:visited{color:gray;}table a:hover{color:#FC0;}'); allowFullscreen('#video_source', 0, '#player_list A'); break; case 'animeboy'://.org,tv case 'dramastream'://.org allowFullscreen('div[align]', 'center'); break; case 'anime-sub'://.com allowFullscreen('#movie-content'); break; case 'animeultima'://.io allowFullscreen('#pembed'); break; case 'bestanimes'://.tv allowFullscreen('.post'); break; case 'freeanime'://.com nn('SCRIPT', doc.body).innerHTML = '$(window).unbind();\n$("#header").css("background-attachment","scroll")'; allowFullscreen('.z-video', 0, 'ul.z-tabs-nav LI', 999); break; case 'dubzonline'://.cm,com case 'theanime'://.tv case 'goodanime'://.co,eu,net case 'videozoo'://.me allowFullscreen('#content'); return(location.pathname == '/embed.php'); case 'lovemyanime'://.net allowFullscreen('.player-area'); break; case 'animes-stream24'://.net allowFullscreen('#main'); break; case 'gogoanime'://.com return location.pathname=='/flowplayer/' || allowFullscreen('#content'); case 'arkvid'://.tv return domain[2]=='player'; case 'mangaotaku': // animeboy,dramastream return 1;// return(location.pathname == '/dr_video_player.php'); case 'unknown'://about:blank?video="..." if(/^about:blank\?video=/.test(location.href)) return nn('SCRIPT', doc.body).textContent = '("'+location.href.slice(18)+'")'; break; default: return(/(embed\b|\/gogo\/|\/widget\/)/.test(doc.URL)); } function allowFullscreen(selTop, selFrame, mirrors, delay) { if(delay) window.setTimeout( function() { allowFullscreen(selTop, selFrame, mirrors); }, delay); else { var a,i; if(isTop) { if(mirrors) { a = doc.querySelectorAll(mirrors); for(i of a) { i.addEventListener('click', function() { window.setTimeout( function() { doc.querySelector(selTop+' IFRAME').setAttribute('allowfullscreen', 'true'); }, 99); }); } } a = doc.querySelectorAll(selTop+' IFRAME'); if(!a.length)console.log('yahvt: couldn\'t apply fullscreen attribute because '+selTop+' IFRAME not found in '+doc.URL); for(i of a) { i.setAttribute('allowfullscreen', 'true'); } } else if(selFrame) { a = doc.querySelectorAll(selFrame+' IFRAME'); for(i of a) { i.setAttribute('allowfullscreen', 'true'); } } } }} // FULL SCREEN API //requires about:config full-screen-api.allow-trusted-requests-only=false function SetFullScreenMode(v) { (v.requestFullscreen||v.mozRequestFullScreen||v.webkitRequestFullscreen||v.webkitEnterFullscreen).call(v); //console.info('To allow full screen mode, you may want to set full-screen-api.allow-trusted-requests-only to false in about:config.'); } function FullScreenElement() { if(doc.exitFullscreen) return doc.fullscreenElement; if(doc.mozCancelFullScreen) return doc.mozFullScreenElement; if(doc.webkitExitFullscreen) return doc.webkitFullscreenElement; } function EndFullScreenMode() { if(!FullScreenElement()) return false; (doc.exitFullscreen||doc.mozCancelFullScreen||doc.webkitExitFullscreen).call(doc); return true; } function OnFullScreenChange(fn) { if(doc.exitFullscreen) doc.addEventListener("fullscreenchange", fn); if(doc.mozCancelFullScreen) doc.addEventListener("mozfullscreenchange", fn); if(doc.webkitExitFullscreen) doc.addEventListener("webkitfullscreenchange", fn); } // new node function nn(name, parent) { var a = name.split(/,(?! )/); name = a.shift(); var node = doc.createElement(name.match(/^\w+/)[0]), m = name.match(/#\w+/); if(m) node.id = m[0].slice(1); m = name.match(/\.\w+/); if(m) node.className = m[0].slice(1); while(a.length) { var t = a.shift(), l = t.indexOf('='); switch(l) { case -1: node.setAttribute(t, t); break; case 0: node.textContent = t.slice(1); break; case 1: if(t.charAt(0) == '?') { node.innerHTML = t.slice(2); break; } default: node.setAttribute(t.slice(0, l), t.slice(l+1)); } }; if(parent) parent.appendChild(node); return node; } // find node function fn(name, parent) { if(!parent) parent = doc; switch(name.charAt(0)) { case '#': return parent.getElementById(name.slice(1)); case '.': return parent.getElementsByClassName(name.slice(1))[0]; } return parent.getElementsByTagName(name)[0]; } // find nodes array function na(name, parent) { if(!parent) parent = doc; return (name.charAt(0)=='.') ? parent.getElementsByClassName(name.slice(1)) : parent.getElementsByTagName(name); } // remove node function rn(node) { if(typeof(node)=='string') node = obj(node); if(node) return node.parentNode.removeChild(node); } function domainName(href) { if(!href) href = location.href; var m = href.match(/\:\/\/(?:www\.|embed\.)?([^\/]+)/); if(m) return m[1]; return 'unknown'; } function Remember(name, value) { try { if(sessionStorage) switch(value) { case '': sessionStorage.removeItem(name); break; case undefined: value = 'yes'; default: sessionStorage.setItem(name, value); } }catch(e){} } function remembered(name, forget) { try { var x = sessionStorage.getItem(name); if(forget) sessionStorage.removeItem(name); return x; } catch(e) { if(name=='autoplay' && (domain[1]=='unknown' || /\bnoflash\b/.test(location.search) || /(part|clip)_?0?[2-9]/.test(location.pathname))) return 'yes'; } } // style function SetStyle(style) { nn('STYLE', doc.head).innerHTML = style; } function MakeCSSString(name, def) { var names = Object.getOwnPropertyNames(def), style = (names.indexOf('top') < 0 ? '' : 'position:absolute;' ), x, value; for(x of names) { value = def[x]; if(typeof(value) == 'number') value += 'px'; style += x.replace(/_/g,'-') + ': ' + value + '; '; } return name + '{' + style + '}\n'; } function findVideoFiles() { function getScript(o) { s = o.innerHTML; if(s.length) { var m = s.match(/^eval(\(function\(p(?:,[a-ek]){5}\).+\bsplit\b.+)$/m), timer; if(m) { try { s += eval(m[1]); console.log('yahvt: Packed script detected.'); } catch(e) { console.log('yahvt: Packed script parse error.'); } } return s = unescape(s.replace(/\s\/\/.*$/gm,'').replace(/\s+/gm,' ').replace(/\/\*.*\*\//g,'').replace(/'/g,'"')); } } function add(m) { if(m && v.indexOf(m)<0) v.push(m); } function addm1(m) { if(m && v.indexOf(m[1])<0) v.push(m[1]); } function find(pattern) { var g = /g/.test(pattern.flags); do { var m = pattern.exec(s); if(!m) break; add(decodeURIComponent(unescape(m[1]))); } while(g); } //findVideoFiles() { var v = [], s, m, a, i, p; a = fn('VIDEO', doc.body); if(a) {//usually doesn't exist yet add(a.getAttribute('src')); a = na('SOURCE', a); for(i of a) { add(i.getAttribute('src')); } } a = na('SCRIPT', (domain[1]=='videozoo' ? doc.documentElement : doc.body)); for(i of a) { if(getScript(i)) { //console.log('yahvt:' ,s); find(/"(https?:\/\/[^"]+\.(?:mp4|flv)\b[^"]*)"/gi); find(/"(.+\.php\b.+\.(?:mp4|flv))"/gi); find(/"([^"]+\bpicasa\.php\b[^"]+)"/gi); find(/"(https\:\/\/[^"]*\.google(?:video\.com\/videoplayback|usercontent.com\/)[^"]+)"/gi); } } switch(domain[1]) { case 'trollvid'://.net// Is this of any use today? TODO obsolete? for(i of a) { s = i.innerHTML; m = s.match(/['"](http.+?data.*?file.*?)['"]/i); addm1(m); } a = na('SCRIPT', doc.head); for(i of a) { m = i.innerHTML.match(/unescape\(atob\('(.+?)'/); if(m) { m = unescape(atob(m[1])); if(/\.(mp4|flv)/i.test(m)) add(m); } } break; case 'videobam'://.com// Is this of any use today? for(i of a) { s = i.innerHTML; m = s.match(/['"](http:\\\/\\\/[^'"]+\.(?:mp4|flv)\b[^'"]*)['"]/i); if(m) add(m[1].replace(/\\\//g,'/')); } case 'yourupload'://TODO } try {// even if it looks wierd... m = doc.querySelector('#flowplayer+script').innerHTML.replace(/\s+/g, ' ').match(/\/\* playlist\: \[ \{ url\: '(.*)'/)[1]; if(!/\<|\>/.test(m)) { console.log('yahvt: html5_path in comment found.'); add(m); } } catch(e){} try {// animeboy add(unescape(fn('NOSCRIPT').innerHTML.match(/duration: ' + Math.floor(t/60) + ':' + ('0' + Math.floor(t%60)).slice(-2); } else div.style.color = '#666'; m = src.match(/https?:\/\/([^:?/]+)/); if(m) txt += '
video host name: ' + m[1]; m = src.match(/.*\/([^?/]*)/); if(m) txt += '
video file name: ' + m[1]; txt += '

A - toggle stretch mode
F - toggle full screen mode
I - '+(meta ? 'this info
Q - quick viewing
S - slow motion' : 'some info')+'
Z - toggle zoom to 100%'; if(meta) { adr.push('@web.de?subject=yahvt%20at%20', document.domain); txt += '

yahvt is public domain by gnblizz.'; } div.innerHTML = txt; v.parentNode.appendChild(div); window.setTimeout(StopInfo, 15000); } function StopInfo() { return rn(p.querySelector('div.info,div.info2')); } function NextPart(name) { var m = name.match(/^(.*(?:part|clip)_?0?)(\d+)(.*)$/), i; if(m) return {now: i = parseInt(m[2]), next: ++i, URL: m[1]+i+m.pop()}; else { m = name.match(/^(.*%26sp%3D)a(%26episode%3D\d+)A(%26.*)$/);//animeboy,mangaotaku if(m) return {now: 'A', next: 'B', URL: m[1]+'b'+m[2]+'B'+m.pop()}; } } function StartStop() { if(v.paused) v.play(); else v.pause(); } function TestResource(url, btn) { var req = new XMLHttpRequest(); try { if(req) { req.open('HEAD', url, true); req.onloadend = function () { switch(req.status) { case 200: if(req.statusText != 'Not Found') btn.click(); case 404: rn(btn); } }; req.send(); } }catch(e){console.log('yahvt:', e, url, req);} } //insertVideo() { var nvp = NextPart(doc.URL), txt = ''; if(fn('#flowplayer')) txt += '
'; else if(fn('#player')) txt += '
'; if(insertionPoint);//TODO else doc.body.innerHTML = txt; console.log('yahvt: starting ' + domainName() + (nvp ? (' video part '+nvp.now) : ' video')); var v = fn('VIDEO'), p = v.parentNode; v.onloadedmetadata = function(event) { //console.log('yahvt: now playing: '+v.currentSrc); StopInfo(); if(remembered('fullscreen', true)=='yes') SetFullScreenMode(v); nn('DIV.info2,='+ v.videoWidth + 'x' + v.videoHeight, v.parentNode); window.setTimeout(StopInfo, 3000); v.focus(); if(!isFF) setTimeout(function(){ v.focus(); }, 0); // workaround chrome bug }; v.onkeypress = function(event) { if(event.altKey || event.metaKey || event.ctrlKey) return; switch(event.key.toUpperCase()) { case ' ': if(isFF) return; case 'P': StartStop(); break; case 'A':// toggle stretch mode v.style.objectFit = v.style.objectFit ? '' : 'fill'; p.style.height = '100%'; break; case 'F':// toggle full screen if(!EndFullScreenMode()) SetFullScreenMode(v); break; case 'H':// toggle LMB behavior ((altClick = !altClick) ? v.addEventListener : v.removeEventListener).call(v, 'click', StartStop, false); break; case 'I':// show some info if(!rn(p.querySelector('div.info'))) ShowSomeInfo(true); break; case 'Q':// quick view if(!smw) { nn('DIV.info2,=press "Q" to end quick view mode', v.parentNode); smw = window.setTimeout(StopInfo, 3000); } v.playbackRate = v.playbackRate != 1 ? 1 : 2; break; case 'S':// slow motion if(!smw) { nn('DIV.info2,=press "S" to end slow motion mode', v.parentNode); smw = window.setTimeout(StopInfo, 3000); } v.playbackRate = v.playbackRate != 1 ? 1 : .05; break; case 'Y': case 'Z':// toggle zoom mode if(p.style.width) { p.style.width = ''; p.style.minHeight = p.parentNode.clientHeight+'px'; p.style.margin = ''; } else if(v.videoWidth) { p.style.width = v.videoWidth+'px'; p.style.minHeight = v.videoHeight+'px'; p.style.margin = '0px auto'; } break; default: return; } event.preventDefault(); event.stopPropagation(); }; v.onkeydown = function(event) { if(event.altKey || event.metaKey || event.ctrlKey || event.shiftKey) return; switch(event.key) { case 'ArrowRight': v.currentTime += v.paused ? .5 : 5; break; case 'ArrowLeft': v.currentTime -= v.paused ? .5 : 5; break; case 'Escape': v.playbackRate = 1; default: return; } event.preventDefault(); event.stopImmediatePropagation(); }; if(!isFF) { v.onclick = function(event) { //console.info('click: active was', document.activeElement); if(!document.hasFocus()) v.focus(); else StartStop(); //console.info('click: now active', document.activeElement); } } v.lastChild.onerror = function(event) { StopInfo(); if(!/\bnoflash\b/.test(location.search)) { Remember('autoplay'); location.search = location.search ? location.search+'&noflash' : '?noflash'; throw 'redirecting to location + noflash'; } console.log('yahvt: video load error'); if(nvp && nvp.now>1) doc.body.innerHTML = '


No part '+nvp.now+' could be found, so this must be...

The End
'; remembered('fullscreen', true);// discard value }; v.onended = function(event) { var wasFullScreen = EndFullScreenMode(); if(!isTop && !fn('#VideoCloseButton')) { nn('BUTTON#VideoCloseButton,type=button,=x,title=close', p).onclick = function(event){ location.replace('about:blank'); return false; }; } if(nvp) { var btnNext = nn('BUTTON#NextPartButton,type=button,='+nvp.next+',title=on to part '+nvp.next, p); btnNext.onclick = function(event) { console.log('yahvt: redirecting to part '+nvp.next); Remember('autoplay'); Remember('fullscreen', (wasFullScreen ? 'yes' : '')); location.replace(nvp.URL); return false; }; btnNext.focus(); TestResource(nvp.URL, btnNext); } else {//nfv var vf = v.currentSrc, m = vf.match(/^(.*(?:part|clip)_?0?)(\d+)(.*)$/); if(m) { console.log('yahvt: using alternate next part algorithm (experimental)'); // This is when no part information was found in the page location, but in the video file name instead. // I forgot long time ago where I saw such scenario and don't know how to test it anymore. TODO var inp = parseInt(m[2])+1; vf = m[1] + inp + m[3]; var btnNext2 = nn('BUTTON#NextPartButton,type=button,='+inp+',title=on to part '+inp, p); btnNext2.onclick = function(event) { Remember('fullscreen', (wasFullScreen ? 'yes' : '')); av = [vf]; insertVideo(); }; console.log('yahvt: about to insert video part '+inp); try { var rv = GM_xmlhttpRequest({ url: vf, method: "HEAD", onload: function(response) { if(/^Content-Type:\s*video\b/m.test(response.responseHeaders)) { console.log('yahvt: video resource found'); btnNext2.click(); } else { console.log('yahvt: invalid video resource'); } rn(btnNext2); }, onerror: function(response) { console.log('yahvt: --onerror-response:\n'+response.responseHeaders); rn(btnNext2); } }); } catch(e) { TestResource(vf, btnNext2); } }//m }//!nfv };//v.onended ShowSomeInfo(false); OnFullScreenChange(function(event) { fn('VIDEO').focus(); }); } function Block(event) { event.preventDefault(); if(maxmsg>=0) { console.warn("yahvt: blocking script element:", event.target.src, event.defaultPrevented); if(!(--maxmsg)) { console.log('yahvt:', event); throw('to many alien script requests.'); } //trollvid tries again and again, causing heavy CPU load, so we give up }else document.defaultView.removeEventListener('beforescriptexecute', Block); } //yahvt() { //console.log('yahvt:', loadevent); try { if(sites()) { var av = findVideoFiles(); if(av && av.length) { // we block things only after we are sure they aren't needed if(!insertionPoint) { document.defaultView.addEventListener('beforescriptexecute', Block); var n = setTimeout(function(){},0); while(--n){clearTimeout(n);}; n = setInterval(function(){},999); while(n){clearInterval(n--);}; } console.log('yahvt: Found '+av.length+' video source'+(av.length==1 ? '' : 's')+' at '+domainName()+'.'); if(/\/blank.mp4$/.test(av[0])) av.push(av.shift()); var v = fn('VIDEO'); if(v) { v.pause(); v.removeAttribute('src'); } SetStyle( MakeCSSString('body,html',{padding:0, margin:0, height:'100%', overflow:'hidden', background:'#000', color:'#fff', font_size:14}) + MakeCSSString('video', {outline:0}) + MakeCSSString('#player,#flowplayer', {display:'none'}) + MakeCSSString('button#VideoCloseButton', {top:0, right:0, height:25, min_width:25}) + MakeCSSString('button#NextPartButton', {top:0, right:30, height:25, min_width:25}) + MakeCSSString('.info a', {color:'unset'}) + MakeCSSString('.info', {top:50, left:50, text_shadow:'1px 1px black'}) + MakeCSSString('.info2', {top:1, left:2, text_shadow:'1px 1px black', font_size:'large'}) ); var autoplay = remembered('autoplay', true) == 'yes'; if(!isTop && !autoplay) { doc.body.innerHTML = '

Video '+((/(part|clip)_?\d/.test(av[0]+doc.URL)) ? 'part ' : '')+'found at ' + domainName(doc.URL) + '.

'; fn('BUTTON').onclick = function() { autoplay = true; insertVideo(); } fn('BUTTON').focus(); } else { insertVideo(); } } else { if(/^video(wing|zoo)$/.test(domain[1]) && !/\bnoflash\b/i.test(location.search)) { console.log('yahvt: redirecting to location + noflash'); location.search = location.search ? location.search+'&noflash' : '?noflash'; } var as = na('SCRIPT'), i; for(i of as) { if(/_url\s*=\s*\"video not found\"/i.test(i.innerHTML)) { doc.body.innerHTML = '

Video not found.

'; break; } } } } } catch(e) { console.log('yahvt:', e); } } // public domain by gnblizz // contact me with my username + '@web.de'