视频网HTML5播放小工具

三大功能 。启用HTML5播放;万能网页全屏;添加快捷键:快进、快退、暂停/播放、音量、下一集、切换(网页)全屏、上下帧、播放速度。支持视频站点:油管、TED、优.土、QQ、B站、西瓜视频、爱奇艺、A站、PPTV、芒果TV、咪咕视频、新浪、微博、网易[娱乐、云课堂、新闻]、搜狐、风行、百度云视频等;直播:斗鱼、YY、虎牙、龙珠、战旗。可增加自定义站点

目前为 2021-01-09 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name 视频网HTML5播放小工具
  3. // @description 三大功能 。启用HTML5播放;万能网页全屏;添加快捷键:快进、快退、暂停/播放、音量、下一集、切换(网页)全屏、上下帧、播放速度。支持视频站点:油管、TED、优.土、QQ、B站、西瓜视频、爱奇艺、A站、PPTV、芒果TV、咪咕视频、新浪、微博、网易[娱乐、云课堂、新闻]、搜狐、风行、百度云视频等;直播:斗鱼、YY、虎牙、龙珠、战旗。可增加自定义站点
  4. // @homepage https://bbs.kafan.cn/thread-2093014-1-1.html
  5. // @include https://*.qq.com/*
  6. // @exclude https://user.qzone.qq.com/*
  7. // @include https://www.weiyun.com/video_*
  8. // @include https://v.youku.com/v_show/id_*
  9. // @include https://vku.youku.com/live/*
  10. // @include https://video.tudou.com/v/*
  11. // @include https://www.iqiyi.com/*
  12. // @include https://www.bilibili.com/*
  13. // @include https://www.ixigua.com/*
  14. // @include http://www.le.com/ptv/vplay/*
  15. // @include https://www.le.com/ptv/vplay/*
  16. // @include https://www.acfun.cn/*
  17. // @include http://v.pptv.com/show/*
  18. // @include https://v.pptv.com/show/*
  19. // @include http://www.miguvideo.com/*
  20. // @include https://www.miguvideo.com/*
  21. // @include https://tv.sohu.com/*
  22. // @include https://film.sohu.com/album/*
  23. // @include https://www.mgtv.com/*
  24. // @include http://m.fun.tv/*
  25. // @include https://m.fun.tv/*
  26. // @include http://www.fun.tv/vplay/*
  27. // @include https://www.fun.tv/vplay/*
  28. // @version 1.7.1
  29. // @include https://*.163.com/*
  30. // @include https://*.icourse163.org/*
  31. // @include https://*.sina.com.cn/*
  32. // @include https://video.sina.cn/*
  33. // @include https://k.sina.cn/*
  34. // @include https://weibo.com/*
  35. // @include https://*.weibo.com/*
  36. // @include https://pan.baidu.com/*
  37. // @include https://yun.baidu.com/*
  38. // @include http://v.ifeng.com/*
  39. // @include https://v.ifeng.com/*
  40. // @include http://news.mtime.com/*
  41. // @include http://video.mtime.com/*
  42. // @GM_info
  43. // @include https://www.youtube.com/watch?v=*
  44. // @include https://www.ted.com/talks/*
  45. // @noframes
  46. // @include https://www.yy.com/*
  47. // @include https://v.huya.com/play/*
  48. // @include https://www.huya.com/*
  49. // @include https://v.douyu.com/*
  50. // @include https://www.douyu.com/*
  51. // @include http://star.longzhu.com/*
  52. // @include https://star.longzhu.com/*
  53. // @include https://www.zhanqi.tv/*
  54. // @run-at document-start
  55. // @include https://www.yunbtv.com/vodplay/*
  56. // @include *://www.dililitv.com/*
  57. // @include *://www.tomisun.net/tv-play-*
  58. // @include https://www.mkvdo.com/play/*
  59. // @grant unsafeWindow
  60. // @grant GM_addStyle
  61. // @grant GM_registerMenuCommand
  62. // @grant GM_setValue
  63. // @grant GM_getValue
  64. // @namespace https://gf.qytechs.cn/users/7036
  65. // ==/UserScript==
  66.  
  67. 'use strict';
  68. const w = unsafeWindow || window;
  69. const { host, pathname: path } = location;
  70. const d = document, find = [].find;
  71. let v, _fp, _fs, by; // document.body
  72. const observeOpt = {childList : true, subtree : true};
  73. const noopFn = () => {};
  74. const q = (css, p = d) => p.querySelector(css);
  75. const delElem = e => e.remove();
  76. const $$ = function(c, cb = delElem, doc = d) {
  77. if (!c || !c.length) return;
  78. if (typeof c === 'string') c = doc.querySelectorAll(c);
  79. if (!cb) return c;
  80. for (let e of c) if (e && cb(e)=== !1) break;
  81. };
  82. const gmFuncOfCheckMenu = (title, saveName, defaultVal = true) => {
  83. const r = GM_getValue(saveName, defaultVal);
  84. if (r) title = '√ '+ title;
  85. GM_registerMenuCommand(title, () => {
  86. GM_setValue(saveName, !r);
  87. location.reload();
  88. });
  89. return r;
  90. };
  91. const r1 = (regp, s) => regp.test(s) && RegExp.$1;
  92. const log = console.log.bind(console, '%c脚本[%s] 反馈:%s\n%s', 'color:#c3c;font-size:1.5em',
  93. GM_info.script.name, GM_info.script.homepage);
  94. const sleep = ms => new Promise(resolve => { setTimeout(resolve, ms) });
  95. const getStyle = (o, s) => {
  96. if (o.style[s]) return o.style[s];
  97. if (getComputedStyle) {
  98. const x = getComputedStyle(o, '');
  99. s = s.replace(/([A-Z])/g,'-$1').toLowerCase();
  100. return x && x.getPropertyValue(s);
  101. }
  102. };
  103. const doClick = e => {
  104. if (typeof e === 'string') e = q(e);
  105. if (e) { e.click ? e.click() : e.dispatchEvent(new MouseEvent('click')) };
  106. };
  107. const clickDualButton = btn => { // 2合1 按钮
  108. !btn.nextSibling || getStyle(btn, 'display') !== 'none' ? doClick(btn) : doClick(btn.nextSibling);
  109. };
  110. const intervalQuery = (cb, condition, stop = true) => {
  111. const fn = typeof condition === 'string' ? q.bind(null, condition) : condition;
  112. const t = setInterval(() => {
  113. const r = fn();
  114. if (r) {
  115. stop && clearInterval(t);
  116. cb(r);
  117. }
  118. }, 300);
  119. };
  120. const goNextMV = () => {
  121. const m = path.match(/(\d+)(\D*)$/);
  122. const d = +m[1] + 1;
  123. location.assign(path.slice(0, m.index) + d + m[2]);
  124. };
  125. const firefoxVer = r1(/Firefox\/(\d+)/, navigator.userAgent);
  126. const isEdge = / Edge?\//.test(navigator.userAgent);
  127. const fakeUA = ua => Object.defineProperty(navigator, 'userAgent', {
  128. value: ua,
  129. writable: false,
  130. configurable: false,
  131. enumerable: true
  132. });
  133. const getMainDomain = host => {
  134. const a = host.split('.');
  135. let i = a.length - 2;
  136. if (/^(com?|cc|tv|net|org|gov|edu)$/.test(a[i])) i--;
  137. return a[i];
  138. };
  139. const ua_chrome = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3626.121 Safari/537.36';
  140.  
  141. class FullScreen {
  142. constructor(e) {
  143. let fn = d.exitFullscreen || d.webkitExitFullscreen || d.mozCancelFullScreen || d.msExitFullscreen || noopFn;
  144. this.exit = fn.bind(d);
  145. fn = e.requestFullscreen || e.webkitRequestFullScreen || e.mozRequestFullScreen || e.msRequestFullScreen || noopFn;
  146. this.enter = fn.bind(e);
  147. }
  148.  
  149. static isFull() {
  150. return !!(d.fullscreen || d.webkitIsFullScreen || d.mozFullScreen ||
  151. d.fullscreenElement || d.webkitFullscreenElement || d.mozFullScreenElement);
  152. }
  153.  
  154. toggle() {
  155. FullScreen.isFull() ? this.exit() : this.enter();
  156. }
  157. }
  158.  
  159. //万能网页全屏, 参考了:https://github.com/gooyie/ykh5p
  160. class FullPage {
  161. constructor(video, onSwitch) {
  162. this._video = video;
  163. this._isFull = !1;
  164. this._onSwitch = onSwitch;
  165. this._checkContainer();
  166. GM_addStyle(
  167. `.gm-fp-body .gm-fp-zTop {
  168. position: relative !important;
  169. z-index: 2147483647 !important;
  170. }
  171. .gm-fp-wrapper, .gm-fp-body{ overflow:hidden !important; }
  172. .gm-fp-wrapper .gm-fp-innerBox {
  173. width: 100% !important;
  174. height: 100% !important;
  175. }
  176. .gm-fp-wrapper {
  177. display: block !important;
  178. position: fixed !important;
  179. width: 100% !important;
  180. height: 100% !important;
  181. top: 0 !important;
  182. left: 0 !important;
  183. background: #000 !important;
  184. z-index: 2147483647 !important;
  185. }`
  186. );
  187. }
  188.  
  189. getPlayerContainer(video) {
  190. let e = video, p = e.parentNode;
  191. const { clientWidth: wid, clientHeight: h } = e;
  192. do {
  193. e.classList.add('gm-fp-innerBox');
  194. e = p;
  195. p = e.parentNode;
  196. } while (e.nodeName == 'DIV' && p.clientWidth-wid < 5 && p.clientHeight-h < 5);
  197. //e 为返回值,在此之后不能变了
  198. while (p !== by) {
  199. p.classList.add('gm-fp-zTop');
  200. p = p.parentNode;
  201. }
  202. return e;
  203. }
  204.  
  205. _checkContainer() {
  206. this._container = this._container || this.getPlayerContainer(this._video);
  207. }
  208.  
  209. get container() {
  210. this._checkContainer();
  211. return this._container;
  212. }
  213.  
  214. static isFull(e) {
  215. return w.innerWidth - e.clientWidth < 5 && w.innerHeight - e.clientHeight < 5;
  216. }
  217.  
  218. toggle() {
  219. const cb = this._onSwitch;
  220. if (!this._isFull && cb) cb(true);
  221. by.classList.toggle('gm-fp-body');
  222. this.container.classList.toggle('gm-fp-wrapper');
  223. this._isFull = !this._isFull;
  224. if (!this._isFull && cb) setTimeout(cb, 199, !1);
  225. }
  226. }
  227.  
  228. const u = getMainDomain(host);
  229. const events = {
  230. on(name, fn) {
  231. this[name] = fn;
  232. }
  233. };
  234. const app = {
  235. isLive: !1,
  236. disableSpace: !1,
  237. multipleV: !1, //多视频页面
  238. isNumURL: !1, //网址数字分集
  239. checkMV() {
  240. if (!v || !v.offsetWidth) v = this.findMV();
  241. return v;
  242. },
  243. checkDPlayer() {
  244. if (this.dpShell) return true;
  245. if (this.dpShell = v.closest('.dplayer')) {
  246. this.btnFP = q('.dplayer-full-in-icon > span', this.dpShell);
  247. this.btnFS = q('.dplayer-full-icon', this.dpShell);
  248. this.btnPlay = this.btnNext = _fs = _fp = null;
  249. if (this.nextCSS) this.btnNext = q(this.nextCSS);
  250. this.disableSpace = !1;
  251. this.dpShell.closest('body > *').classList.add('gm-dp-zTop');
  252. v.parentNode.addEventListener('dblclick', ev => {
  253. this.btnFP.click();
  254. ev.stopPropagation();
  255. });
  256. }
  257. return !!this.dpShell;
  258. },
  259. hotKey(e) {
  260. if (e.ctrlKey || e.altKey || e.target.contentEditable=='true' ||
  261. /INPUT|TEXTAREA|SELECT/.test(e.target.nodeName)) return;
  262. if (e.shiftKey && ![32,13,37,39].includes(e.keyCode)) return;
  263. if (this.isLive && [37,39,78,88,67,90].includes(e.keyCode)) return;
  264. if (this.extPlayer && this.extPlayer.contains(e.target) && [32,37,39].includes(e.keyCode)) return;
  265. if (!this.checkMV()) return;
  266. !this.checkDPlayer() && this.checkUI();
  267. if (events.keydown && events.keydown(e)) return;
  268. let n;
  269. switch (e.keyCode) {
  270. case 32: //space
  271. if (e.shiftKey) {
  272. v.scrollIntoView();
  273. v.focus();
  274. } else {
  275. if (this.disableSpace && by != e.target) return;
  276. if (this.btnPlay) clickDualButton(this.btnPlay);
  277. else v.paused ? v.play() : v.pause();
  278. }
  279. e.preventDefault();
  280. break;
  281. case 37: n = e.shiftKey ? -20 : -5; //left 快退5秒,shift加速
  282. //right 快进5秒,shift加速
  283. case 39: n = n || (e.shiftKey ? 20 : 5);
  284. v.currentTime += n;
  285. break;
  286. case 78: // N 下一首
  287. if (this.btnNext) doClick(this.btnNext);
  288. else if (this.isNumURL) goNextMV();
  289. break;
  290. case 38: n = 0.1; //加音量
  291. case 40: n = n || -0.1; //降音量
  292. n += v.volume;
  293. if (0 <= n && n <= 1) v.volume = +n.toFixed(1);
  294. e.preventDefault();
  295. break;
  296. case 13: //回车键。 全屏
  297. if (e.shiftKey) {
  298. _fp ? _fp.toggle() : clickDualButton(this.btnFP);
  299. } else {
  300. _fs ? _fs.toggle() : clickDualButton(this.btnFS);
  301. }
  302. break;
  303. case 27: //esc
  304. if (this.dpShell) return;
  305. if (FullScreen.isFull()) {
  306. _fs ? _fs.exit() : clickDualButton(this.btnFS);
  307. } else if (FullPage.isFull(v)) {
  308. _fp ? _fp.toggle() : clickDualButton(this.btnFP);
  309. }
  310. break;
  311. case 67: n = 0.1; //按键C:加速播放 +0.1
  312. case 88: n = n || -0.1; //按键X:减速播放 -0.1
  313. n += v.playbackRate;
  314. if (0 < n && n <= 16) v.playbackRate = +n.toFixed(2);
  315. break;
  316. case 90: //按键Z:正常速度播放
  317. v.playbackRate = 1;
  318. break;
  319. case 70: n = 0.03; //按键F:下一帧
  320. case 68: n = n || -0.03; //按键D:上一帧
  321. v.currentTime += n;
  322. v.pause();
  323. break;
  324. default: return;
  325. }
  326. e.stopPropagation();
  327. },
  328. checkUI() {
  329. if (this.webfullCSS && !this.btnFP) this.btnFP = q(this.webfullCSS);
  330. if (this.btnFP) _fp = null;
  331. else if (!_fp) _fp = new FullPage(v, this.switchFP);
  332.  
  333. if (this.fullCSS && !this.btnFS) this.btnFS = q(this.fullCSS);
  334. if (this.btnFS) _fs = null;
  335. else if (!_fs) _fs = new FullScreen(v);
  336.  
  337. if (this.nextCSS && !this.btnNext) this.btnNext = q(this.nextCSS);
  338. if (this.playCSS && !this.btnPlay) this.btnPlay = q(this.playCSS);
  339. },
  340. switchFP(toFull) {
  341. if (toFull) {
  342. for (let e of this.vSet) this.viewObserver.unobserve(e);
  343. } else {
  344. for (let e of this.vList) this.viewObserver.observe(e);
  345. }
  346. },
  347. onGrowVList() {
  348. if (this.vList.length == this.vCount) return;
  349. if (this.viewObserver) {
  350. for (let e of this.vList) {
  351. if (!this.vSet.has(e)) this.viewObserver.observe(e);
  352. }
  353. } else {
  354. const config = {
  355. rootMargin: '0px',
  356. threshold: 0.9
  357. };
  358. this.viewObserver = new IntersectionObserver(this.onIntersection.bind(this), config);
  359. for (let e of this.vList) this.viewObserver.observe(e);
  360. }
  361. this.vSet = new Set(this.vList);
  362. this.vCount = this.vList.length;
  363. },
  364. onIntersection(entries) {
  365. if (this.vList.length < 2) return;
  366. const entry = find.call(entries, k => k.isIntersecting);
  367. if (!entry || v == entry.target) return;
  368. v = entry.target;
  369. _fs = new FullScreen(v);
  370. _fp = new FullPage(v, this.switchFP);
  371. events.switchMV && events.switchMV();
  372. },
  373. bindEvent() {
  374. by = d.body;
  375. log('bind event\n', v);
  376. events.foundMV && events.foundMV();
  377. if (!this.isLive) {
  378. const onCanplay = ev => {
  379. v.removeEventListener('canplay', onCanplay);
  380. v.playbackRate = localStorage.mvPlayRate || 1;
  381. v.addEventListener('ratechange', ev => {
  382. localStorage.mvPlayRate = v.playbackRate;
  383. });
  384. };
  385. v.addEventListener('canplay', onCanplay);
  386. }
  387. const fn = ev => {
  388. events.canplay && events.canplay();
  389. v.removeEventListener('canplaythrough', fn);
  390. };
  391. if (v.readyState > 3) fn();
  392. else v.addEventListener('canplaythrough', fn);
  393. by.addEventListener('keydown', this.hotKey);
  394. if (this.extPlayerCSS) {
  395. const x = this.extPlayer = v.closest(this.extPlayerCSS);
  396. x && x.addEventListener('keydown', this.hotKey);
  397. }
  398.  
  399. if (this.multipleV) {
  400. new MutationObserver(this.onGrowVList.bind(this)).observe(by, observeOpt);
  401. this.vCount = 0;
  402. this.onGrowVList();
  403. }
  404. this.checkDPlayer();
  405. },
  406. init() {
  407. this.hotKey = this.hotKey.bind(this);
  408. this.switchFP = this.multipleV ? this.switchFP.bind(this) : null;
  409. this.vList = d.getElementsByTagName('video');
  410. const fn = e => this.cssMV ? e.matches(this.cssMV) : e.offsetWidth > 9;
  411. this.findMV = find.bind(this.vList, fn);
  412. intervalQuery(e => {
  413. v = e;
  414. $$(this.adsCSS);
  415. this.bindEvent();
  416. }, this.findMV);
  417. }
  418. };
  419.  
  420. let router = {
  421. ted() {
  422. app.fullCSS = 'button[title="Enter Fullscreen"]';
  423. app.playCSS = 'button[title="play video"]';
  424. if (!gmFuncOfCheckMenu('TED强制高清', 'ted_forceHD')) return;
  425. const getHDSource = async () => {
  426. const pn = r1(/^(\/talks\/\w+)/, path);
  427. const resp = await fetch(pn + '/metadata.json');
  428. const data = await resp.json();
  429. return data.talks[0].downloads.nativeDownloads.high;
  430. };
  431. const check = async (rs) => {
  432. if (!v.src || v.src.startsWith('http')) return;
  433. $$(app.vList, e => { e.removeAttribute('src') }); // 取消多余的媒体资源请求
  434. try {
  435. v.src = await getHDSource();
  436. } catch(ex) {
  437. console.error(ex);
  438. }
  439. };
  440. events.on('foundMV', () => {
  441. new MutationObserver(check).observe(v, {
  442. attributes: true,
  443. attributeFilter: ['src']
  444. });
  445. check();
  446. });
  447. },
  448. youtube() {
  449. app.playCSS = 'button.ytp-play-button';
  450. app.fullCSS = 'button.ytp-fullscreen-button';
  451. },
  452. qq() {
  453. app.nextCSS = '.txp_btn_next';
  454. app.webfullCSS = '.txp_btn_fake';
  455. app.fullCSS = '.txp_btn_fullscreen';
  456. app.extPlayerCSS = '#mod_player';
  457. events.on('canplay', () => {
  458. let e, a = [...app.vList];
  459. a.shift();
  460. while (e = a.pop()) e.playbackRate = 16;
  461. });
  462. },
  463. youku() {
  464. if (host.startsWith('vku.')) {
  465. events.on('canplay', () => {
  466. app.isLive = !q('.spv_progress');
  467. });
  468. app.fullCSS = '.live_icon_full';
  469. } else {
  470. events.on('foundMV',() => {
  471. by.addEventListener('keyup', e => e.stopPropagation());
  472. });
  473. app.webfullCSS = '.control-webfullscreen-icon';
  474. app.fullCSS = '.control-fullscreen-icon';
  475. app.nextCSS = 'span.icon-next';
  476. }
  477. },
  478. bilibili() {
  479. app.nextCSS = '.bilibili-player-video-btn-next';
  480. app.webfullCSS = '.bilibili-player-video-web-fullscreen';
  481. app.fullCSS = '.bilibili-player-video-btn-fullscreen';
  482. app.extPlayerCSS = '#playerWrap';
  483. const danmu = gmFuncOfCheckMenu('弹幕', 'bili_danmu');
  484. const danmuCSS = '.bilibili-player-video-danmaku-switch input';
  485. events.on('canplay', () => {
  486. intervalQuery(e => {if (e.checked != danmu) e.click()}, danmuCSS);
  487. });
  488.  
  489. const fName= path.startsWith('/bangumi/') ? 'replaceState' : 'pushState';
  490. const rawFn = history[fName]; //二方法不触发onpopstate事件
  491. history[fName] = function(...args) {
  492. rawFn.apply(this, args);
  493. events.canplay();
  494. app.btnNext = app.btnFP = app.btnFS = null;
  495. };
  496. },
  497. pptv() {
  498. app.fullCSS = '.w-zoom-container > div';
  499. app.webfullCSS = '.w-expand-container > div';
  500. app.nextCSS = '.w-next';
  501. },
  502. mgtv() {
  503. app.fullCSS = 'mango-screen';
  504. app.webfullCSS = 'mango-webscreen > a';
  505. app.nextCSS = 'mango-control-playnext-btn';
  506. },
  507. ixigua() {
  508. app.fullCSS = '.xgplayer-fullscreen';
  509. app.webfullCSS = '.xgplayer-cssfullscreen';
  510. app.nextCSS = '.xgplayer-playNext';
  511. events.on('foundMV', () => {
  512. v.addEventListener('keydown', app.hotKey);
  513. });
  514. },
  515. miguvideo() {
  516. app.playCSS = '.play-btn';
  517. app.fullCSS = '.zoom-btn';
  518. app.webfullCSS = '.page-zoom-btn';
  519. app.nextCSS = '.next-btn';
  520. },
  521. weibo() {
  522. app.multipleV = path.startsWith('/u/');
  523. },
  524. baidu() {
  525. events.on('keydown', e => {
  526. if (!w.videojs) return;
  527. let n, p = w.videojs.getPlayers("video-player").html5player.tech_;
  528. switch (e.keyCode) {
  529. case 67: n = 0.1;
  530. case 88: n = n || -0.1;
  531. n += p.playbackRate();
  532. p.setPlaybackRate(n);
  533. return true;
  534. case 90:
  535. p.setPlaybackRate(1);
  536. return true;
  537. }
  538. });
  539. app.extPlayerCSS = '.video-content';
  540. },
  541. acfun() {
  542. app.nextCSS = '.btn-next-part .control-btn';
  543. app.webfullCSS = '.fullscreen-web';
  544. app.fullCSS = '.fullscreen-screen';
  545. },
  546. ['163']() {
  547. app.multipleV = host.startsWith('news.');
  548. GM_addStyle('div.video,video{max-height: 100% !important;}');
  549. return host.split('.').length > 3;
  550. },
  551. sohu() {
  552. app.nextCSS = 'li.on[data-vid]+li a';
  553. app.fullCSS = '.x-fullscreen-btn';
  554. app.webfullCSS = '.x-pagefs-btn';
  555. },
  556. fun() {
  557. app.nextCSS = '.btn-item.btn-next';
  558. },
  559. dililitv() {
  560. app.nextCSS = `a[href="${path}"]+a`;
  561. GM_addStyle('.dplayer-loaded{ background-color:orange !important; }');
  562. }
  563. };
  564. app.disableSpace = /^(youtube|ixigua|qq|pptv|fun)$/.test(u);
  565. // www.dililitv.com,www.hmtv.me,imeiju.io,www.mkvdo.com,www.zxfun.net,www.lzvod.net
  566. router.mkvdo = router.lzvod = router.hmtv = router.imeiju = router.zxfun = router.dililitv;
  567.  
  568. if (!router[u]) { //直播站点
  569. router = {
  570. douyu() {
  571. const inRoom = host.startsWith('www.');
  572. events.on('foundMV', () => {
  573. if (inRoom && path != '/') {
  574. intervalQuery(doClick, '.roomSmallPlayerFloatLayout-closeBtn');
  575. q('#js-player-aside-state').checked = true;
  576. }
  577. });
  578. if (inRoom) {
  579. app.cssMV = '[src^=blob]';
  580. app.webfullCSS = 'div[class|=wfs]';
  581. app.fullCSS = 'div[class|=fs]';
  582. app.playCSS = 'div[class|=play]';
  583. } else
  584. intervalQuery(async(e) => {
  585. app.vList = null;
  586. v = e;
  587. $$(app.adsCSS);
  588. app.bindEvent();
  589. await sleep(3333);
  590. let r = q('demand-video').shadowRoot;
  591. r = q('demand-video-controller-bar', r).shadowRoot;
  592. app.btnFP = q('.ControllerBar-PageFull', r);
  593. app.btnFS = q('.ControllerBar-WindowFull', r);
  594. log('test shadowRoot ', app.btnFP, app.btnFS);
  595. }, () => {
  596. const r = q('demand-video').shadowRoot;
  597. return q('video', r);
  598. });
  599. app.adsCSS = 'a[href*="wan.douyu.com"]';
  600. },
  601. yy() {
  602. app.isLive = !path.startsWith('/x/');
  603. if (app.isLive) {
  604. app.fullCSS = '.yc__fullscreen-btn';
  605. app.webfullCSS = '.yc__cinema-mode-btn';
  606. app.playCSS = '.yc__play-btn';
  607. }
  608. },
  609. huya() {
  610. if (firefoxVer && firefoxVer < 57) return true;
  611. app.webfullCSS = '.player-fullpage-btn';
  612. app.fullCSS = '.player-fullscreen-btn';
  613. app.playCSS = '#player-btn';
  614. app.adsCSS = '#player-subscribe-wap,#wrap-income';
  615. intervalQuery(doClick, '.login-tips-close');
  616. localStorage['sidebar/ads'] = '{}';
  617. localStorage['sidebar/state'] = 0;
  618. localStorage.TT_ROOM_SHIELD_CFG_0_ = '{"10000":1,"20001":1,"20002":1,"20003":1,"30000":1}';
  619. },
  620. longzhu() {
  621. app.fullCSS = 'a.ya-screen-btn';
  622. },
  623. zhanqi() {
  624. localStorage.lastPlayer = 'h5';
  625. app.fullCSS = '.video-fullscreen';
  626. }
  627. };
  628. if (router[u]) {
  629. app.isLive = app.isLive || !host.startsWith('v.');
  630. (!w.chrome || isEdge) && fakeUA(ua_chrome);
  631. }
  632. }
  633.  
  634. Reflect.defineProperty(navigator, 'plugins', {
  635. get() { return { length: 0 } }
  636. });
  637. GM_registerMenuCommand('脚本功能快捷键表' , alert.bind(w,
  638. `左右方向键:快退、快进5秒; +shift: 20
  639. 上下方向键:音量调节
  640. 空格键:暂停/播放; +shift: 定位播放器窗口
  641. N:播放下一集
  642. 回车键:切换全屏; +shift: 切换网页全屏
  643. ESC:退出(网页)全屏
  644. C:加速0.1倍播放 X:减速0.1倍播放 Z:正常速度播放
  645. D:上一帧 F:下一帧`
  646. ));
  647. if (!router[u] || !router[u]()) app.init();
  648. if (!router[u] && !app.isNumURL) app.isNumURL = /\W\d+(\/|\.[a-z]{3,8})?$/.test(path);

QingJ © 2025

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