video tabs

tabbed video sources on certain drama and anime sites

  1. // ==UserScript==
  2. // @name video tabs
  3. // @namespace gnblizz
  4. // @description tabbed video sources on certain drama and anime sites
  5. // @version 1.10
  6. // @include http://www.animehere.com/*
  7. // @include http://www.animenova.org/*
  8. // @include http://www.animeplus.tv/*
  9. // @include http://www.animesky.net/*
  10. // @include http://www.animetoon.org/*
  11. // @include http://www.animewow.eu/*
  12. // @include http://www.animewow.org/*
  13. // @include http://dramago.com/*
  14. // @include http://www.dramago.com/*
  15. // @include http://www.dramagalaxy.tv/*
  16. // @include http://www.gogoanime.to/*
  17. // @include http://www.goodanime.co/*
  18. // @include http://gooddrama.to/*
  19. // @include http://www.gooddrama.to/*
  20. // @include http://www.videozoo.me/*
  21. // @noframes
  22. // @run-at document-start
  23. // @grant none
  24. // @compatible firefox
  25. // @compatible chrome
  26. // @icon data:image/gif;base64,R0lGODlhMAAwAKECAAAAAICAgP///////yH5BAEKAAMALAAAAAAwADAAAALQnI+py+0Po5y02ouz3rz7D4biBJTmiabqyrbuC8fyHAf2jedpzuOvAAwKh6mhUfg7Hks3gHLpehptwIBTioxig0zrdStIgrslMFA8pCKp1oAZjXW6w/Mt/Nl2t8HeFl7o5QZgBagEYyawNxhUl7h4dlelFlZG+QVY6aglmIjjuKd50xla9RKI0mSCqaPJSMM0aEK4mhfbpSnTabM4WXrShtpHI6gqKvmKnCwns0tm2lOsLP3aUy08aK0zvc3d7b09Ei4+Tl5ufo6err7O3n5QAAA7
  27. // ==/UserScript==
  28. "use strict";
  29. var doc = document, domain = doc.domain.match(/(\w+)\.\w+$/)[1], darkTheme;
  30.  
  31. function TabSites() {
  32. switch(domain) {
  33. case 'gooddrama':
  34. return MakeTabs('html,body,#body,#header,#top_block,.info_block,.note span,#eps_blocks,#downloads_heading,#comments_heading,.reply,.info_box{color:#888;background-color:#111;height:auto}table[width="790"],#eps_blocks,.right_col,.info_block .ad{opacity:.6;}.note>font>span{background-color:inherit!important;}');
  35. case 'dramago':
  36. return MakeTabs('#menu-bar,.bar,#content,#top_block,#search-box-banner-inner,.info_block,#eps_blocks,.s_right_col #sidebar,#footer{background-color:#111;color:#aaa;border:1px solid #777}body{background-color:#444;background-blend-mode:color-burn;}.note span{background-color:#111!important;color:#aaa!important}');
  37. case 'dramagalaxy':
  38. return MakeTabs('#content,#top_block,#search-box-banner-inner,.info_block,#eps_blocks,.s_right_col #sidebar{background-color:#121B23;color:#aaa;border:1px solid #777}img[src$="2egfcQR.png"]{display:none}');
  39. case 'animenova':
  40. return MakeTabs('html,body,#body,#header,#comments_heading,.comment,.info_box{color:#555;background-color:#111;height:auto}br:empty,.note img{display:none;}.comment{border:0px}.right_col,table,.ad,.sc_ad,#g_promo,#upper_header{opacity:.6;}');
  41. case 'animeplus':
  42. case 'animesky':
  43. return MakeTabs('html,body,#body,#header,.part,.note>span,.report_video,#comments_heading,.comment,.info_box{background-color:#000!important;color:#aaa!important;}a.report_video:hover{color:red!important;}.right_col,table,.ad,.sc_ad{opacity:.6;}#comments,.comment,#body,#streams{border:1px solid #555}');
  44. case 'animewow':
  45. return MakeTabs('#page,#body>.s_left_col,#body>.s_right_col,#search-box-banner-inner,#eps_blocks{color:#aaa;background-color:#111;}.vmargin,#top_block,.info_block{border:1px solid #aaa;background-color:#111;}.info_block *{color:#aaa!important}');
  46. case 'gogoanime':
  47. case 'videozoo':
  48. return MakeTabs2('.postcontent', '');
  49. case 'goodanime':
  50. return MakeTabs2('.postcontent', '.topad,#wrapper,.premiumdll,.postcontent,td[bgcolor]{background-color:#111;color:#aaa}#header,#footer,html,body{background-color:#000;color:#aaa}a h5,td[bgcolor] *{color:inherit!important;}div#headerimg{top:0px;left:0px;width:930px;height:129px;background-color:black;opacity:.83;}div#announcement{background-color:#000;}');
  51. case 'animehere':
  52. var div = fn('#playbox');
  53. if(div)
  54. div.innerHTML = div.innerHTML.replace(/<br>/gi,'</p><p>') + '<p></p>';
  55. SetStyle('#streams+div.playpage{margin-top:5px;}');
  56. return MakeTabs2('#playbox', '#html,body{background-color:#111;}body,.content,.cfix,.side-title{color:#ccc!important;background-color:#111!important;}.related li a{color:unset;}.tipbot,.sidebar,.banner{background-color:black;color:white;}.tipbot>*,.sidebar>*,.banner>*{opacity:.7;}.related,.like{opacity:.5;}body>footer{background:unset;}a{color: #036;}#stOverlay{display:block!important;z-index:0!important;}');
  57. default:
  58. return MakeTabs('');
  59. }
  60. }
  61.  
  62. function RemoveSomeIframes() {
  63. var streams = fn('#streams');
  64. if(streams) {
  65. var o, name, a=na('IFRAME'), i=a.length;
  66. if(i) do {
  67. o = a[--i];
  68. if(!streams.contains(o)) {
  69. if(o.id)
  70. name = '#' + o.id;
  71. else {
  72. try {
  73. name = o.src.match(/\/\/(?:www\.)?([^/]+)/)[1];
  74. } catch(e) {
  75. name = 'unknown';
  76. }
  77. }
  78. var div = nn('DIV'), btn = nn('BUTTON,type=button,title='+o.getAttribute('src')+',=show '+name+' content', div);
  79. if(o.parentNode.nodeName == 'TD') o = o.parentNode;
  80. btn.setAttribute('onclick', 'this.parentNode.innerHTML=decodeURIComponent("'+encodeURIComponent(o.outerHTML)+'");');
  81. o.parentNode.insertBefore(div, o);
  82. o.parentNode.removeChild(o);
  83. }
  84. } while(i);
  85. return true;
  86. }
  87. return false;
  88. }
  89.  
  90. // new node
  91. function nn(name, parent) {
  92. //console.log('create',name);
  93. //try{
  94. var a = name.split(/,(?! )/); name = a.shift();
  95. var node = doc.createElement(name.match(/^\w+/)[0]),
  96. m = name.match(/#\w+/); if(m) node.id = m[0].slice(1);
  97. m = name.match(/\.\w+/); if(m) node.className = m[0].slice(1);
  98. while(a.length) {
  99. var t = a.shift(), l = t.indexOf('=');
  100. switch(l) {
  101. case -1:
  102. node.setAttribute(t, t);
  103. break;
  104. case 0:
  105. node.textContent = t.slice(1);
  106. break;
  107. case 1:
  108. if(t.charAt(0) == '?') {
  109. node.innerHTML = t.slice(2);
  110. break;
  111. }
  112. default:
  113. node.setAttribute(t.slice(0, l), t.slice(l+1));
  114. }
  115. };
  116. if(parent) parent.appendChild(node);
  117. return node;
  118. //}catch(e){console.log(e)}
  119. }
  120.  
  121. // find node
  122. function fn(name, parent) {
  123. if(!parent) parent = doc;
  124. switch(name.charAt(0)) {
  125. case '#':
  126. return parent.getElementById(name.slice(1));
  127. case '.':
  128. return parent.getElementsByClassName(name.slice(1))[0];
  129. }
  130. return parent.getElementsByTagName(name)[0];
  131. }
  132.  
  133. // find nodes array
  134. function na(name, parent) {
  135. if(!parent) parent = doc;
  136. return (name.charAt(0)=='.') ? parent.getElementsByClassName(name.slice(1)) : parent.getElementsByTagName(name);
  137. }
  138.  
  139. // remove node
  140. function rn(node) {
  141. if(node) return node.parentNode.removeChild(node);
  142. }
  143.  
  144. function SetStyle(style) {
  145. if(style) {
  146. var o = nn('STYLE', fn('HEAD'));
  147. o.textContent = style;
  148. return o;
  149. }
  150. }
  151.  
  152. function domainName(href) {
  153. var m = href.match(/\:\/\/(?:www\.|embed\.)?([^\/]+)/);
  154. return(m ? m[1] : 'unknown');
  155. }
  156.  
  157. function Remember(name, value) {
  158. if(localStorage) {
  159. if(value) localStorage.setItem(name, value);
  160. else localStorage.removeItem(name);
  161. }
  162. }
  163.  
  164. function remembered(name) {
  165. if(localStorage)
  166. return localStorage.getItem(name);
  167. }
  168.  
  169. function TabOnMouseUp(event) {
  170. if(!event.ctrlKey) switch(event.button) {
  171. case 1:
  172. break;
  173. case 0:
  174. TabSelect(this);
  175. default:
  176. return;
  177. }
  178. NewWindow(this);
  179. }
  180.  
  181. function NewWindow(o) {
  182. var m = decodeURI(o.dataset.content).match(/src="(\S+?)"/);
  183. if(m)
  184. window.open(m[1].replace(/&amp;/g,'&'), '_newtab');
  185. }
  186.  
  187. function TabSelect(n) {
  188. //console.log('TabSelect(',n,')');
  189. var o, a;
  190. switch(typeof(n)) {
  191. default:
  192. o = n || fn('#player_tab_0');
  193. break;
  194. case 'string':
  195. a = fn('#player_tabs').childNodes;
  196. for(o = 0;; o++) {
  197. if(o >= a.length) { n = 0; break; }
  198. if(a[o].innerHTML == n) { n = a[o].id.slice(-1); break; }
  199. }
  200. case 'number':
  201. o = fn('#player_tab_'+n) || fn('#player_tab_0');
  202. }
  203. if(o.getAttribute('class') != 'active_tab') {
  204. a = fn('.active_tab');
  205. if(a) a.removeAttribute('class');
  206. o.setAttribute('class', 'active_tab');
  207. fn('#tabplayer').innerHTML = decodeURI(o.dataset.content);
  208. Remember('preferedServer', o.innerHTML);
  209. }
  210. }
  211.  
  212. function MakeTabs(style) {
  213. if(fn('#player_tabs')) return true;
  214. var content = fn('#streams');
  215. if(content) {
  216. var va = na('.vmargin', content), i=va.length;
  217. if(i) {
  218. var tabs = nn('UL#player_tabs');
  219. do {
  220. var o = va[--i];
  221. var ifr = fn('IFRAME', o), tab = nn('LI#player_tab_'+i), ttl;
  222. if(ifr) {
  223. var src = ifr.getAttribute('src');
  224. if(src.match(/[?&]/) == '&') ifr.setAttribute('src', src.replace('&', '?')); // bugfix
  225. if(!ifr.getAttribute('allowfullscreen')) ifr.setAttribute('allowfullscreen', 'true'); // enable fs for html5 video
  226. tab.textContent = domainName(src);
  227. ttl = 'span.playlist';
  228. } else {
  229. tab.textContent = '?';
  230. ttl = '.error_box';
  231. }
  232. ttl = o.querySelector(ttl); if(ttl) tab.title = ttl.textContent;
  233. tab.setAttribute('data-content', encodeURI(o.innerHTML));
  234. rn(o);
  235. tab.onmouseup = TabOnMouseUp;
  236. tabs.insertBefore(tab, tabs.firstChild);
  237. } while(i);
  238. nn('LI,title=about videotabs,?=&#9432;,style=float:right;padding:5px;', tabs).onclick = About;
  239. //if(!/Chrome/.test(navigator.userAgent))
  240. nn('LI,title=dim the light,?=&#10038;,style=float:right;padding:5px;', tabs).onclick = Dimmer;
  241. if(style) {
  242. nn('LI,title=toggle dark theme,?=&#9635;,style=float:right;padding:5px;', tabs).onclick = ToggleTheme;
  243. if(remembered('noDarkTheme')) darkTheme = style;
  244. else SetStyle(style).id = 'darkTheme';
  245. }
  246. nn('LI,?=&nbsp;', tabs);
  247. content.insertBefore(tabs, va[0]);
  248. nn('DIV#tabplayer', content);
  249. SetStyle('#player_tabs li{background-color:#393939;color:white;display:block;float:left;width:auto;padding:5px 10px;cursor:pointer;}#player_tabs li:hover{color:yellow;}#player_tabs li:last-child{cursor:auto;float:unset}#player_tabs .active_tab{background-color:#505050}#player_tabs .active_tab:hover{color:gray}a.report_video:link{color:#0047AB}html{height:unset;}\n');
  250. TabSelect(remembered('preferedServer'));
  251. Disclaimer();
  252. return true;
  253. }
  254. }
  255. return false;
  256. }
  257.  
  258. function Dimmer() {
  259. function DimPart(desc) {
  260. var style = '', names = Object.getOwnPropertyNames(desc), name, value;
  261. for(name of names) {
  262. value = desc[name];
  263. if(typeof(value) == 'number') value += 'px';
  264. style += name.replace(/_/g, '-') + ':' + value + '; ';
  265. }
  266. nn('DIV,style=position:absolute;background-color:black;opacity:.85;z-index:1001;'+style, div);
  267. }
  268. var div = fn('#dimmer');
  269. console.log('#dimmer', div);
  270. if(div)
  271. rn(div);
  272. else {
  273. div = nn('DIV#dimmer', doc.body);
  274. console.log('#dimmer', div);
  275. var po = doc.documentElement,
  276. rc = doc.querySelector('#tabplayer iframe').getBoundingClientRect(),
  277. rcp = po.getBoundingClientRect(),
  278. above = rc.top-rcp.top,
  279. below = rcp.height - above - rc.height;
  280. //console.log('RECT(#tabplayer iframe)', rc);
  281. DimPart({top:0, left:0, width:'100%', height:above, min_width:rcp.width});
  282. DimPart({top:above, left:0, width:'calc(50% - ' + (rcp.width/2 - rc.left) + 'px)', height:rc.height});
  283. DimPart({top:above, left:'calc(50% + ' + (rc.width - rcp.width/2 + rc.left) + 'px)', width:'calc(50% - ' + (rc.width + rc.left - rcp.width/2) + 'px)', height:rc.height});
  284. DimPart({top:above+rc.height, left:0, width:'100%', height:below, min_width:rcp.width, bottom:0});
  285. div.onclick = function(event) { rn(div); };
  286. console.log('#dimmer', div);
  287. return div;
  288. }
  289. }
  290.  
  291. function About() {
  292. var dlg = nn('DIALOG#aboutvideotabs,open,style=position:fixed;top:20%;right:30%;z-index:2147483647;text-align:center;color:black;background-color:antiquewhite;border:7px ridge greenyellow;', Dimmer()), adr = ['mailto:gnblizz'];
  293. adr.push('@web.de?subject=videotabs%20at%20',doc.domain);
  294. nn('H1,=about videotabs', dlg);
  295. nn('P,?=<small> videotabs is public domain by <a href="'+adr.join('')+'" title="email the author directly">gnblizz</a>.</small>', dlg);
  296. nn('BUTTON,type=button,=videotabs web page,title=info, code, feedback and stats of videotabs', nn('A,href=https://gf.qytechs.cn/en/scripts/11480-video-tabs,target=_newtab', dlg));
  297. }
  298.  
  299. function ToggleTheme() {
  300. if(darkTheme) {
  301. SetStyle(darkTheme).id = 'darkTheme';
  302. Remember('noDarkTheme', darkTheme = '');
  303. } else {
  304. var style = fn('#darkTheme');
  305. darkTheme = style.textContent;
  306. doc.head.removeChild(style);
  307. Remember('noDarkTheme', 'noDarkTheme');
  308. }
  309. }
  310.  
  311. //gogoanime.com, goodanime.eu | .postcontent
  312. //animehere.com | #playbox
  313. function MakeTabs2(select, style) {
  314. var streams = fn(select), o;
  315. if(streams && fn('IFRAME', streams)) {
  316. streams.id = 'streams';
  317. for(o of streams.children) {
  318. if(o.nodeType == 1 && fn('IFRAME', o))
  319. o.className = 'vmargin';
  320. }
  321. return MakeTabs(style);
  322. }
  323. return false;
  324. }
  325.  
  326. function Disclaimer() {
  327. var o = fn('#footer');
  328. if(o && o.textContent.match(/(Copyright|©)/i))
  329. nn('P,?=<br>Disclaimer: Video materials used here are the property of their respective and rightful owners.', o);
  330. o = doc.querySelector('.info_block .note>span.imp:last-of-type');
  331. if(o && o.textContent == '95%')
  332. o.textContent = '0.5%';// a slightly more realistic value
  333. }
  334.  
  335. function FixNovaBug() {
  336. var spn = fn('#full_notes');
  337. if(spn) {
  338. var m = spn.textContent.replace(/\n/gm,'<br>').match(/\u2026\smore(?:<br>)?(.*)\sless\sless/);//u2026 = '...'
  339. if(m) {
  340. spn.parentNode.innerHTML = m[1];
  341. console.log('description text fixed');
  342. return 1;
  343. }
  344. }
  345. }
  346.  
  347. SetStyle('iframe,.vmargin{display:none}').id = 'no_frames';
  348. doc.addEventListener("DOMContentLoaded", function() {
  349. if(TabSites()) {
  350. RemoveSomeIframes();
  351. window.setTimeout(function(){try{window.autoClose=-1;MHideBar();}catch(e){}},1999);
  352. } else {
  353. // auto expand description - it's foolish to hide the last few words of a sen...[expand]
  354. if(fn('#brief_notes') && !FixNovaBug()) SetStyle('#full_notes{display:inline!important;}#full_notes>a[href="#"],#brief_notes{display:none!important;}');
  355. // mark watched episodes red
  356. if(fn('#videos')) SetStyle('#videos a:visited{color:red;}');
  357. }
  358. rn(fn('#no_frames'));
  359. });
  360.  
  361. // public domain by gnblizz
  362. // contact me with my username + '@web.de'

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址