Greasy Fork

Greasy Fork is available in English.

yahvt

yet another html5 video tool

当前为 2017-02-05 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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
// ==/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 = '<video controls width="100%" height="412" src="' + vid_file + '" allowfullscreen="true" autoplay></video>';
        };
    }
    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(/<param\sname="movie"\svalue="\S+&fpath=([^&]+)&/i)[1]).replace(/\+/g,'%20'));
    console.log('yahvt: movie param in noscript found.');
  } catch(e){}
  a = na('PARAM'); for(i of a) { p = i.getAttribute('value'); m = p.match(/video=(https?:\/\/[^'"]+\.mp4[^'"&]*)/i); addm1(m); }
  a = na('EMBED'); for(i of a) { p = i.getAttribute('src'); m = p.match(/video=(https?:\/\/[^'"]+\.mp4[^'"&]*)/i); addm1(m); }
  if(domain[1]=='mp4upload') {
    i = v.length;
    if(i) do {
      a = v[--i];
      if(a.match(/\.com\/[a-z]+\.mp4$/)) {
        console.log('yahvt:', a, 'ignored.');
        delete v[i];
      }
    } while(i);
  }
  return v;
}

function yahvt(loadevent) {
function insertVideo() {
function ShowSomeInfo(meta) {
  var div = nn('DIV.info'), txt = domainName() + (nvp ? (' video part '+nvp.now) : ' video'), src = v.currentSrc, m, adr = ['mailto:gnblizz'];
  if(meta) {
    var t = v.duration + .5;
    txt += '<br>pixel format: ' + v.videoWidth + 'x' + v.videoHeight
    + '<br>duration: ' + Math.floor(t/60) + ':' + ('0' + Math.floor(t%60)).slice(-2);
  } else div.style.color = '#666';
  m = src.match(/https?:\/\/([^:?/]+)/);
  if(m) txt += '<br>video host name: ' + m[1];
  m = src.match(/.*\/([^?/]*)/);
  if(m) txt += '<br>video file name: ' + m[1];
  txt += '<br><br>A - toggle stretch mode<br>F - toggle full screen mode<br>I - '+(meta ? 'this info<br>Q - quick viewing<br>S - slow motion' : 'some info')+'<br>Z - toggle zoom to 100%';
  if(meta) {
    adr.push('@web.de?subject=yahvt%20at%20', document.domain);
    txt += '<br><br><small><a target="_newtab" href="http://greasyfork.icu/en/scripts/14476-yahvt" title="info, code, feedback and stats of yahvt">yahvt</a> is public domain by <a href="' + adr.join('')
        + '" title="email the author directly">gnblizz</a>.</small>';
  }
  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 = '<video controls ' + (autoplay ? 'autoplay ' : '') + 
    'width="100%" height="100%" tabindex="0', altClick, smw;
  av.push(av[0]+'&noflash');
  av.push(av[0]+'&html5=true');
  av.forEach(function(x){ txt += '"><source src="' + x.replace(/\?/g,'&').replace('&','?'); });
  txt += '"></video>';
  if(fn('#flowplayer')) txt += '<div id="flowplayer"></div>';
  else if(fn('#player')) txt += '<div id="player"></div>';
  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 = '<center style="color:orange;"><br><br>No part '+nvp.now+' could be found, so this must be...<br><b style="font-size:333%;"><br>The End</b></center>';
    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 = '<center><p>Video '+((/(part|clip)_?\d/.test(av[0]+doc.URL)) ? 'part ' : '')+'found at ' + domainName(doc.URL) + '.</p><button type="button" style="padding:10px;width:98%">play</button></center><div id="flowplayer" style="display:none"></div>';
          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 = '<p>Video not found.</p>'; break; }
        }
      }
    }
  } catch(e) { console.log('yahvt:', e); }
}

// public domain by gnblizz
// contact me with my username + '@web.de'