yahvt

yet another html5 video tool

目前為 2016-01-18 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        yahvt
// @description yet another html5 video tool
// @namespace   gnblizz
// @include     http://anilinkz.tv/*
// @include     http://www.animeboy.org/*
// @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://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://www.gooddrama.net/*
// @include     http://www.kumby.com/*
// @include     http://www.lovemyanime.net/*
// @include     http://www.otakucenter.com/*
// @include     http://www.theanime.tv/*
// @include     http://www.videozoo.me/*
// @include     http://www.waoanime.tv/*
// @include     *embed*
// @include     *gogo/*
// @include     *widget/*
// @include     http://player.arkvid.tv/*
// @version     1.06
// @grant       GM_xmlhttpRequest
// @icon        data:image/gif;base64,R0lGODlhMAAwAKECAAAAAICAgP///////yH5BAEKAAMALAAAAAAwADAAAALQnI+py+0Po5y02ouz3rz7D4biBJTmiabqyrbuC8fyHAf2jedpzuOvAAwKh6mhUfg7Hks3gHLpehptwIBTioxig0zrdStIgrslMFA8pCKp1oAZjXW6w/Mt/Nl2t8HeFl7o5QZgBagEYyawNxhUl7h4dlelFlZG+QVY6aglmIjjuKd50xla9RKI0mSCqaPJSMM0aEK4mhfbpSnTabM4WXrShtpHI6gqKvmKnCwns0tm2lOsLP3aUy08aK0zvc3d7b09Ei4+Tl5ufo6err7O3n5QAAA7
// @compatible  firefox
// ==/UserScript==
"use strict";
var domain; try { domain = document.domain.match(/^(?:www\.)?(.*)$/)[1]; } catch(e) { domain = 'unknown'; }

function sites(){
  var a,i,o,e,b;
  //console.log('domain='+domain);
  switch(location.hostname.match(/([^.]+)\.\w+$/)[1]) {
  case 'anilinkz'://.tv
    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=objs('.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]);
	  document.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 'animetoon'://.eu,org,tv
  case 'animewow'://.eu,org
  case 'dramagalaxy'://.com,eu,tv
  case 'dramago'://.com
  case 'gooddrama'://.net
    allowFullscreen('#streams');
    break;
  case 'animeseason'://.com
    if(obj('#series_info'))
      SetStyle('table a:visited{color:gray;}table a:hover{color:#FC0;}');
    allowFullscreen('#video_source', 0, '#player_list A');
    break;
  case 'anime-sub'://.com
    allowFullscreen('#movie-content');
    break;
  case 'animeultima'://.io
    allowFullscreen('#pembed');
    break;
  case 'bestanimes'://.tv
    allowFullscreen('.post');
    break;
  case 'clipfish'://.de
    obj('+SCRIPT', document.body).innerHTML = 'checkMobile=function(){isMobile=true;}';
    break;
  case 'freeanime'://.com
    obj('+SCRIPT', document.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');
    break;
  case 'lovemyanime'://.net
    allowFullscreen('.player-area');
    break;
  case 'animes-stream24'://.net
    allowFullscreen('#main');
    break;
  case 'gogoanime'://.com
    return location.pathname=='/flowplayer/' || allowFullscreen('#content');
  //case 'chia-anime':return !top; // ok
  //case 'dubbedanime':break;      // ok
  //case 'waoanime':break;   // how did that get in here?
  //case 'animeboy':break;   // broken, flash only
  //case 'kumby':break;      // server down
  //case 'otakucenter':break;// server down
  case 'arkvid':
    return domain=='player.arkvid.tv';
  //case 'yucache'://.net
  //case 'yourupload'://.com
  //  if(document.URL.match(/[?&]/) == '&') // bad URL?
  //     document.location.replace(document.URL.replace('&', '?'));
  default:
    return(/(embed\b|\/gogo\/|\/widget\/)/.test(document.URL));
  }

function allowFullscreen(selTop, selFrame, mirrors, delay) {
  if(delay)
    window.setTimeout( function() { allowFullscreen(selTop, selFrame, mirrors); }, delay);
  else {
    var a,i;
    if(window.self==window.top) {
      if(mirrors) {
	a = document.querySelectorAll(mirrors);
	for(i of a) {
	  i.addEventListener('click', function() {
	    window.setTimeout( function() { document.querySelector(selTop+' IFRAME').setAttribute('allowfullscreen', 'true'); }, 99);
	  });
	}
      }
      a = document.querySelectorAll(selTop+' IFRAME');
      if(!a.length)console.log('couldn\'t applay fullscreen attribute because '+selTop+' IFRAME not found in '+document.URL);
      for(i of a) { i.setAttribute('allowfullscreen', 'true'); }
    } else if(selFrame) {
      a = document.querySelectorAll(selFrame+' IFRAME');
      for(i of a) { i.setAttribute('allowfullscreen', 'true'); }
    }
  }
}}

function findVideoFiles() {
  var v = [], s, m, a, i, p;
  a = obj('VIDEO', document.body); if(a) {//usually doesn't exist yet
    if(m = a.getAttribute('src')) v.push(m);
    a = objs('SOURCE', a);
    for(i of a) {
      if(m = i.getAttribute('src')) v.push(m);
    }
  }
  a = objs('SCRIPT', document.body); for(i of a) { 
    if(s=getScript(i)) {
      find(/"(http:\/\/[^"]+\.(?:mp4|flv)\b[^"]*)"/gi);
      find(/"(.+\.php\b.+\.(?:mp4|flv))"/gi);
      find(/"([^"]+\bpicasa\.php\b[^"]+)"/gi);
      find(/"(https\:\/\/[^"]*\.google(?:video\.com\/videoplayback|usercontent.com\/)[^"]+)"/gi);
    }
  }
  if(/trollvid\.net$/i.test(domain)) {//Is this of any use today?
    for(i of a) {
      s = i.innerHTML; m = s.match(/['"](http.+?data.*?file.*?)['"]/i); if(m) v.push(m[1]);
    }
    a = objs('SCRIPT', document.head); for(i of a) {
      m = i.innerHTML.match(/unescape\(atob\('(.+?)'/);
      if(m) { m = unescape(atob(m[1])); if(/\.(mp4|flv)/i.test(m)) v.push(m); }
    }
  } else if('videobam.com' == domain) {//Is this of any use today?
    for(i of a) {
      s = i.innerHTML; m = s.match(/['"](http:\\\/\\\/[^'"]+\.(?:mp4|flv)\b[^'"]*)['"]/i); if(m) v.push(m[1].replace(/\\\//g,'/'));
    }
  }
  try {//even if it looks wierd...
    m = document.querySelector('#flowplayer+script').innerHTML.replace(/\s+/g, ' ').match(/\/\* playlist\: \[ \{ url\: '(.*)'/)[1];
    v.push(m);
  } catch(e){}
  a = objs('PARAM'); for(i of a) { p = i.getAttribute('value'); m = p.match(/video=(http:\/\/[^'"]+\.mp4[^'"&]*)/i); if(m) v.push(m[1]); }
  a = objs('EMBED'); for(i of a) { p = i.getAttribute('src'); m = p.match(/video=(http:\/\/[^'"]+\.mp4[^'"&]*)/i); if(m) v.push(m[1]); }
  return v;
function find(pattern /*,v */) {
  var g = /g[imy]*$/.test(pattern.toSource());
  do {
    var m = pattern.exec(s);
    if(!m) break;
    m = decodeURIComponent(unescape(m[1]));
    if(v.indexOf(m)<0) v.push(m);
  } while(g);
}
function getScript(s) {
  if(!(s=s.innerHTML).length) return s;
  var m = s.match(/^eval(\(function\(p(?:,[a-ek]){5}\).+\bsplit\b.+)$/m), timer;
  if(m) { try { s += eval(m[1]); } catch(e) { console.log('Packed script parse error.'); } }
  return unescape(s.replace(/\s\/\/.*$/gm,'').replace(/\s+/gm,' ').replace(/\/\*.*\*\//g,'').replace(/'/g,'"'));
}}

(function yahvt() {
  try {
  if(sites()) {
    var av = findVideoFiles();
    if(av && av.length) {
      console.log('found '+av.length+' video source'+(av.length==1 ? '' : 's')+' at '+domain+'.');
      var autoplay = remembered('autoplay', true) == 'yes';
      if(window.self != window.top && !autoplay) {
	document.body.innerHTML = '<center><p>Video '+((/(part|clip)_?\d/.test(av[0]+document.URL)) ? 'part ' : '')+'found at ' + domainName(document.URL) + '.</p><button type="button" style="padding:10px;width:98%">play</button></center><div id="flowplayer" style="display:none"></div>';
	obj('BUTTON').onclick = function() { autoplay = true; insertVideo(); }
      } else {
	insertVideo();
      }
    } else {
      if(/^video(?:wing|zoo).me$/.test(domain) && !/\bnoflash\b/i.test(location.search))
	location.search = location.search ? (location.search+'&noflash') : '?noflash';
      var as = objs('SCRIPT'), i;
      for(i of as) {
	if(/_url\s*=\s*\"video not found\"/i.test(i.innerHTML)) { document.body.innerHTML = '<p>Video not found.</p>'; break; }
      }
    }
  }
  } catch(e) { console.log('yahvt: ' + e); }

function insertVideo() {
  av.push(av[0]+'&noflash'); av.push(av[0]+'&html5=true');
  var nvf = NextPart(document.URL), txt = '<video controls ' + (autoplay ? 'autoplay ' : '') + 'width="100%" height="100%" tabindex="0';
  av.forEach(function(x){ txt += '"><source src="' + x.replace(/\?/g,'&').replace('&','?'); });
  txt += '"></video>';
  if(obj('#flowplayer')) txt += '<div id="flowplayer" style="display:none"></div>';
  else if(obj('#player')) txt += '<div id="player" style="display:none"></div>';
  document.body.innerHTML = txt;
  console.log('starting ' + domain + (nvf ? (' video part '+nvf[2]) : ' video'));
  var v = obj('VIDEO');
  v.oncanplay = function(e) {//note: multiple calls
    //console.log('now playing: '+v.currentSrc);
    if(remembered('fullscreen', true)=='yes')
      SetFullScreenMode();
    v.oncanplay = '';
  };
  v.lastChild.onerror = function(e) {
    console.log('video load error');
    if(!/\bnoflash\b/.test(location.search)) {
      Remember('autoplay');
      console.log('redirecting to location + noflash');
      location.search = location.search ? location.search + '&noflash' : '?noflash';
    }
    if(nvf && nvf[2]>1)
      document.body.innerHTML = '<center><br>No Part '+nvf[2]+' could be found, so this must be...<br><br><big><b>The End</b></big><br></center>';
    remembered('fullscreen', true);// discard value
  };
  v.onended = function(e) {
    //console.log('video.onended()');
    var wasFullScreen = EndFullScreenMode();
    if(window.self != window.top && !obj('#VideoCloseButton')) {
      var btnClose = MakeAButton('x', 'close', 0);
      btnClose.id = 'VideoCloseButton';
      btnClose.onclick = function(e){ location.replace('about:blank'); };
    }
    if(nvf) {
      var btnNext = MakeAButton(nvf[0], 'on to part '+nvf[0], 1);
      btnNext.onclick = function(e) {
        console.log('redirecting to part '+nvf[0]);
	Remember('autoplay');
	Remember('fullscreen', (wasFullScreen ? 'yes' : ''));
	location.replace(nvf[1]);
      };
      switch(resourceExists(nvf[1])) { // can't tell if it's valid, though
      case true: btnNext.click();
      case false: RemoveElement(btnNext);
      }
    } else {//nfv
      var vf = v.currentSrc, m = vf.match(/^(.*(?:part|clip)_?0?)(\d+)(.*)$/);
      if(m) {
	var inp = parseInt(m[2])+1;
	vf = m[1] + inp + m[3];
	var btnNext2 = MakeAButton(inp, 'on to part '+inp, 1);
	btnNext2.onclick = function(e) {
	  Remember('fullscreen', (wasFullScreen ? 'yes' : ''));
	  av = [vf];
	  insertVideo();
	};
	console.log('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('video resource found');
		btnNext2.click();
	      } else {
		console.log('invalid video resource');
	      }
	      RemoveElement(btnNext2);
	    },
	    onerror: function(response) {
	      console.log('--onerror-response:\n'+response.responseHeaders);
	      RemoveElement(btnNext2);
	    }
	  });
	} catch(e) {
	  switch(resourceExists(vf)) { // CORS blocked most likely
	  case true: btnNext2.click();
	  case false: RemoveElement(btnNext2);
	  }
	}
      }//m
    }//!nfv
  };//v.onended

function NextPart(name) {
  var m = name.match(/^(.*(?:part|clip)_?0?)(\d+)(.*)$/);
  if(m) {
    m[2] = parseInt(m[2]);
    m[1] = m[1]+(m[2]+1)+m.pop();
    m[0] = m[2]+1;
    }
  return m;
}}}());

function resourceExists(url){
  try {
    var http = new XMLHttpRequest();
    http.open('HEAD', url, false);
    http.send();
    return http.status != 404;
  } catch(e){}
}

//requires about:config full-screen-api.allow-trusted-requests-only=false
function SetFullScreenMode() {
  var v = obj('VIDEO'); // full-screen-api.allow-trusted-requests-only;false (about:config)
  try{
    v.mozRequestFullScreen();
  }catch(e){}
}

function EndFullScreenMode() {
  try {
    if(document.mozFullScreenElement) {
      document.mozCancelFullScreen();
      return true;
    }
  }catch(e){}
  return false;
}

function obj(name, parent) {
  if(!parent) parent = document;
  var node = null;
  switch (name.charAt(0)) {
  case '#': node = parent.getElementById(name.slice(1)); break;
  case '.': node = parent.getElementsByClassName(name.slice(1))[0]; break;
  case '+': node = document.createElement(name.slice(1).toUpperCase()); if(parent != document) parent.appendChild(node); break;
  default:  node = parent.getElementsByTagName(name)[0]; break;
  }
  return node;
}

function objs(name, parent) {
  if(!parent) parent = document;
  return (name.charAt(0)=='.') ? parent.getElementsByClassName(name.slice(1)) : parent.getElementsByTagName(name);
}

function SetStyle(style) {
  obj('+STYLE', obj('HEAD')).innerHTML = style;
}

function RemoveElement(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) {
  switch(value) {
  case '':
    sessionStorage.removeItem(name);
    break;
  case undefined:
    value = 'yes';
  default:
    sessionStorage.setItem(name, value);
  }
}

function remembered(name, forget) {
  var x = sessionStorage.getItem(name);
  if(forget) sessionStorage.removeItem(name);
  return x;
}

function MakeAButton(caption, help, style, parent) {
  var b = obj('+BUTTON');
  b.setAttribute('type', 'button');
  b.innerHTML = caption;
  if(help) b.setAttribute('title', help);
  if(typeof(style)=='number') style = 'top:0px;right:'+(30*style)+'px;height:25px;min-width:25px;';
  b.setAttribute('style', style);
  if(!b.style.position) b.style.position = 'absolute';
  if(!parent) parent = document.body;
  parent.appendChild(b);
  return b;
}

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