视频网页全屏

让所有视频网页全屏,新增画中画功能

当前为 2020-03-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Maximize Video
  3. // @name:zh-CN 视频网页全屏
  4. // @namespace http://www.icycat.com
  5. // @description Maximize all video players.Support Piture-in-picture.
  6. // @description:zh-CN 让所有视频网页全屏,新增画中画功能
  7. // @author 冻猫
  8. // @include *
  9. // @exclude *www.w3school.com.cn*
  10. // @version 11.7
  11. // @run-at document-end
  12. // ==/UserScript==
  13.  
  14. function exec(fn) {
  15. var script = document.createElement('script');
  16. script.setAttribute("type", "application/javascript");
  17. script.textContent = '(' + fn + ')();';
  18. document.body.appendChild(script);
  19. document.body.removeChild(script);
  20. }
  21.  
  22. exec(function() {
  23.  
  24. var gv = {
  25. isFull: false,
  26. isIframe: false,
  27. autoCheckCount: 0
  28. };
  29.  
  30. //Html5规则[播放器最外层],适用于自适应大小HTML5播放器
  31. var html5Rules = {
  32. 'www.acfun.cn': ['.player-container .player'],
  33. 'www.bilibili.com': ['#bilibiliPlayer'],
  34. 'www.douyu.com': ['#js-player-video-case'],
  35. 'www.huya.com': ['#videoContainer'],
  36. 'www.twitch.tv': ['.player'],
  37. 'www.youtube.com': ['#movie_player'],
  38. 'www.yy.com': ['#player']
  39. };
  40.  
  41. //通用html5播放器
  42. var generalPlayerRules = ['.dplayer', '.video-js', '.jwplayer', '[data-player]'];
  43.  
  44. if (window.top !== window.self) {
  45. gv.isIframe = true;
  46. }
  47.  
  48. if (navigator.language.toLocaleLowerCase() == 'zh-cn') {
  49. gv.btnText = {
  50. max: '网页全屏',
  51. pip: '画中画',
  52. tip: 'Iframe内视频,请用鼠标点击视频后重试'
  53. };
  54. } else {
  55. gv.btnText = {
  56. max: 'Maximize',
  57. pip: 'PicInPic',
  58. tip: 'Iframe video. Please click on the video and try again'
  59. };
  60. }
  61.  
  62. var tool = {
  63. getRect: function(element) {
  64. var rect = element.getBoundingClientRect();
  65. var scroll = tool.getScroll();
  66. return {
  67. pageX: rect.left + scroll.left,
  68. pageY: rect.top + scroll.top,
  69. screenX: rect.left,
  70. screenY: rect.top
  71. };
  72. },
  73. isHalfFullClient: function(element) {
  74. var client = tool.getClient();
  75. var rect = tool.getRect(element);
  76. if ((Math.abs(client.width - element.offsetWidth) < 21 && rect.screenX < 20) || (Math.abs(client.height - element.offsetHeight) < 21 && rect.screenY < 10)) {
  77. if (Math.abs(element.offsetWidth / 2 + rect.screenX - client.width / 2) < 21 && Math.abs(element.offsetHeight / 2 + rect.screenY - client.height / 2) < 21) {
  78. return true;
  79. } else {
  80. return false;
  81. }
  82. } else {
  83. return false;
  84. }
  85. },
  86. isAllFullClient: function(element) {
  87. var client = tool.getClient();
  88. var rect = tool.getRect(element);
  89. if ((Math.abs(client.width - element.offsetWidth) < 21 && rect.screenX < 20) && (Math.abs(client.height - element.offsetHeight) < 21 && rect.screenY < 10)) {
  90. return true;
  91. } else {
  92. return false;
  93. }
  94. },
  95. getScroll: function() {
  96. return {
  97. left: document.documentElement.scrollLeft || document.body.scrollLeft,
  98. top: document.documentElement.scrollTop || document.body.scrollTop
  99. };
  100. },
  101. getClient: function() {
  102. return {
  103. width: document.compatMode == 'CSS1Compat' ? document.documentElement.clientWidth : document.body.clientWidth,
  104. height: document.compatMode == 'CSS1Compat' ? document.documentElement.clientHeight : document.body.clientHeight
  105. };
  106. },
  107. addStyle: function(css) {
  108. var style = document.createElement('style');
  109. style.type = 'text/css';
  110. var node = document.createTextNode(css);
  111. style.appendChild(node);
  112. document.head.appendChild(style);
  113. return style;
  114. },
  115. matchRule: function(str, rule) {
  116. return new RegExp("^" + rule.split("*").join(".*") + "$").test(str);
  117. },
  118. createButton: function(id) {
  119. var btn = document.createElement('tbdiv');
  120. btn.id = id;
  121. btn.onclick = function() {
  122. maximize.playerControl();
  123. };
  124. document.body.appendChild(btn);
  125. return btn;
  126. },
  127. addTip: async function(str) {
  128. if (!document.getElementById('catTip')) {
  129. var tip = document.createElement('tbdiv');
  130. tip.id = 'catTip';
  131. tip.innerHTML = str;
  132. tip.style.cssText = 'transition: all 0.8s ease-out;background: none repeat scroll 0 0 #27a9d8;color: #FFFFFF;font: 1.1em "微软雅黑";margin-left: -250px;overflow: hidden;padding: 10px;position: fixed;text-align: center;bottom: 100px;z-index: 300;',
  133. document.body.appendChild(tip);
  134. tip.style.right = -tip.offsetWidth - 5 + 'px';
  135. await new Promise(resolve => {
  136. tip.style.display = 'block';
  137. setTimeout(() => {
  138. tip.style.right = '25px';
  139. resolve('OK');
  140. }, 300);
  141. });
  142. await new Promise(resolve => {
  143. setTimeout(() => {
  144. tip.style.right = -tip.offsetWidth - 5 + 'px';
  145. resolve('OK');
  146. }, 3500);
  147. });
  148. await new Promise(resolve => {
  149. setTimeout(() => {
  150. document.body.removeChild(tip);
  151. resolve('OK');
  152. }, 1000);
  153. });
  154. }
  155. }
  156. };
  157.  
  158. var setButton = {
  159. init: function() {
  160. if (!document.getElementById('playerControlBtn')) {
  161. init();
  162. }
  163. if (gv.isIframe && tool.isHalfFullClient(gv.player)) {
  164. window.parent.postMessage('iframeVideo', '*');
  165. return;
  166. }
  167. this.show();
  168. },
  169. show: function() {
  170. gv.player.removeEventListener('mouseleave', handle.leavePlayer, false);
  171. gv.player.addEventListener('mouseleave', handle.leavePlayer, false);
  172.  
  173. if (!gv.isFull) {
  174. document.removeEventListener('scroll', handle.scrollFix, false);
  175. document.addEventListener('scroll', handle.scrollFix, false);
  176. }
  177. gv.controlBtn.style.display = 'block';
  178. gv.controlBtn.style.visibility = 'visible';
  179. if (document.pictureInPictureEnabled && gv.player.nodeName != 'OBJECT' && gv.player.nodeName != 'EMBED') {
  180. gv.picinpicBtn.style.display = 'block';
  181. gv.picinpicBtn.style.visibility = 'visible';
  182. }
  183. this.locate();
  184. },
  185. locate: function() {
  186. var playerRect = tool.getRect(gv.player);
  187. gv.controlBtn.style.opacity = '0.5';
  188. gv.controlBtn.innerHTML = gv.btnText.max;
  189. gv.controlBtn.style.top = playerRect.screenY - 20 + 'px';
  190. gv.controlBtn.style.left = playerRect.screenX - 64 + gv.player.offsetWidth + 'px';
  191. gv.picinpicBtn.style.opacity = '0.5';
  192. gv.picinpicBtn.innerHTML = gv.btnText.pip;
  193. gv.picinpicBtn.style.top = gv.controlBtn.style.top;
  194. gv.picinpicBtn.style.left = playerRect.screenX - 64 + gv.player.offsetWidth - 54 + 'px';
  195. }
  196. };
  197.  
  198. var handle = {
  199. getPlayer: function(e) {
  200. if (gv.isFull) {
  201. return;
  202. }
  203. gv.mouseoverEl = e.target;
  204. var hostname = document.location.hostname;
  205. var players = [];
  206. for (var i in html5Rules) {
  207. if (tool.matchRule(hostname, i)) {
  208. for (var v of html5Rules[i]) {
  209. players = document.querySelectorAll(v);
  210. if (players.length > 0) {
  211. break;
  212. }
  213. }
  214. break;
  215. }
  216. }
  217. if (players.length == 0) {
  218. for (var v of generalPlayerRules) {
  219. players = document.querySelectorAll(v);
  220. if (players.length > 0) {
  221. break;
  222. }
  223. }
  224. }
  225. if (players.length == 0 && e.target.nodeName != 'VIDEO' && document.querySelectorAll('video').length > 0) {
  226. var videos = document.querySelectorAll('video');
  227. for (var v of videos) {
  228. var vRect = v.getBoundingClientRect();
  229. if (e.clientX >= vRect.x - 2 && e.clientX <= vRect.x + vRect.width + 2 && e.clientY >= vRect.y - 2 && e.clientY <= vRect.y + vRect.height + 2 && v.offsetWidth > 399 && v.offsetHeight > 220) {
  230. players = [];
  231. players[0] = handle.autoCheck(v);
  232. gv.autoCheckCount = 1;
  233. break;
  234. }
  235. }
  236. }
  237. if (players.length > 0) {
  238. var path = e.path || e.composedPath();
  239. for (var v of players) {
  240. if (path.indexOf(v) > -1) {
  241. gv.player = v;
  242. setButton.init();
  243. return;
  244. }
  245. }
  246. }
  247. switch (e.target.nodeName) {
  248. case 'VIDEO':
  249. case 'OBJECT':
  250. case 'EMBED':
  251. if (e.target.offsetWidth > 399 && e.target.offsetHeight > 220) {
  252. gv.player = e.target;
  253. setButton.init();
  254. }
  255. break;
  256. default:
  257. handle.leavePlayer();
  258. }
  259. },
  260. autoCheck: function(v) {
  261. var tempPlayer, el = v;
  262. gv.playerChilds = [];
  263. gv.playerChilds.push(v);
  264. while (el = el.parentNode) {
  265. if (Math.abs(v.offsetWidth - el.offsetWidth) < 15 && Math.abs(v.offsetHeight - el.offsetHeight) < 15) {
  266. tempPlayer = el;
  267. gv.playerChilds.push(el);
  268. } else {
  269. break;
  270. }
  271. }
  272. return tempPlayer;
  273. },
  274. leavePlayer: function() {
  275. if (gv.controlBtn.style.visibility == 'visible') {
  276. gv.controlBtn.style.opacity = '';
  277. gv.controlBtn.style.visibility = '';
  278. gv.picinpicBtn.style.opacity = '';
  279. gv.picinpicBtn.style.visibility = '';
  280. gv.player.removeEventListener('mouseleave', handle.leavePlayer, false);
  281. document.removeEventListener('scroll', handle.scrollFix, false);
  282. }
  283. },
  284. scrollFix: function(e) {
  285. clearTimeout(gv.scrollFixTimer);
  286. gv.scrollFixTimer = setTimeout(() => {
  287. setButton.locate();
  288. }, 20);
  289. },
  290. hotKey: function(e) {
  291. //默认退出键为ESC。需要修改为其他快捷键的请搜索"keycode",修改为按键对应的数字。
  292. if (e.keyCode == 27) {
  293. maximize.playerControl();
  294. }
  295. //默认画中画快捷键为F2。
  296. if (e.keyCode == 113) {
  297. pictureInPicture();
  298. }
  299. },
  300. receiveMessage: async function(e) {
  301. switch (e.data) {
  302. case 'iframePicInPic':
  303. console.log('messege:iframePicInPic');
  304. if (!document.pictureInPictureElement) {
  305. await document.querySelector('video').requestPictureInPicture()
  306. .catch(error => {
  307. tool.addTip(gv.btnText.tip);
  308. });
  309. } else {
  310. await document.exitPictureInPicture();
  311. }
  312. break;
  313. case 'iframeVideo':
  314. console.log('messege:iframeVideo');
  315. if (!gv.isFull) {
  316. gv.player = gv.mouseoverEl;
  317. setButton.init();
  318. }
  319. break;
  320. case 'parentFull':
  321. console.log('messege:parentFull');
  322. gv.player = gv.mouseoverEl;
  323. if (gv.isIframe) {
  324. window.parent.postMessage('parentFull', '*');
  325. }
  326. maximize.checkParent();
  327. maximize.fullWin();
  328. if (getComputedStyle(gv.player).left != '0px') {
  329. tool.addStyle('#htmlToothbrush #bodyToothbrush .playerToothbrush {left:0px !important;width:100vw !important;}');
  330. }
  331. gv.isFull = true;
  332. break;
  333. case 'parentSmall':
  334. console.log('messege:parentSmall');
  335. if (gv.isIframe) {
  336. window.parent.postMessage('parentSmall', '*');
  337. }
  338. maximize.smallWin();
  339. break;
  340. case 'innerFull':
  341. console.log('messege:innerFull');
  342. if (gv.player.nodeName == 'IFRAME') {
  343. gv.player.contentWindow.postMessage('innerFull', '*');
  344. }
  345. maximize.checkParent();
  346. maximize.fullWin();
  347. break;
  348. case 'innerSmall':
  349. console.log('messege:innerSmall');
  350. if (gv.player.nodeName == 'IFRAME') {
  351. gv.player.contentWindow.postMessage('innerSmall', '*');
  352. }
  353. maximize.smallWin();
  354. break;
  355. }
  356. }
  357. };
  358.  
  359. var maximize = {
  360. playerControl: function() {
  361. if (!gv.player) {
  362. return;
  363. }
  364. this.checkParent();
  365. if (!gv.isFull) {
  366. if (gv.isIframe) {
  367. window.parent.postMessage('parentFull', '*');
  368. }
  369. if (gv.player.nodeName == 'IFRAME') {
  370. gv.player.contentWindow.postMessage('innerFull', '*');
  371. }
  372. this.fullWin();
  373. if (gv.autoCheckCount > 0 && !tool.isHalfFullClient(gv.playerChilds[0])) {
  374. if (gv.autoCheckCount > 10) {
  375. for (var v of gv.playerChilds) {
  376. v.classList.add('videoToothbrush');
  377.  
  378. }
  379. return;
  380. }
  381. var tempPlayer = handle.autoCheck(gv.playerChilds[0]);
  382. gv.autoCheckCount++;
  383. maximize.playerControl();
  384. gv.player = tempPlayer;
  385. maximize.playerControl();
  386. } else {
  387. gv.autoCheckCount = 0;
  388. }
  389. } else {
  390. if (gv.isIframe) {
  391. window.parent.postMessage('parentSmall', '*');
  392. }
  393. if (gv.player.nodeName == 'IFRAME') {
  394. gv.player.contentWindow.postMessage('innerSmall', '*');
  395. }
  396. this.smallWin();
  397. }
  398. },
  399. checkParent: function() {
  400. if (gv.isFull) {
  401. return;
  402. }
  403. gv.playerParents = [];
  404. var full = gv.player;
  405. while (full = full.parentNode) {
  406. if (full.nodeName == 'BODY') {
  407. break;
  408. }
  409. if (full.getAttribute) {
  410. gv.playerParents.push(full);
  411. }
  412. }
  413. },
  414. fullWin: function() {
  415. if (!gv.isFull) {
  416. document.removeEventListener('mouseover', handle.getPlayer, false);
  417. gv.backHtmlId = document.body.parentNode.id;
  418. gv.backBodyId = document.body.id;
  419. if (document.location.hostname == 'www.youtube.com' && document.querySelector('#movie_player .ytp-size-button path').getBoundingClientRect().width == 20) {
  420. document.querySelector('#movie_player .ytp-size-button').click();
  421. gv.ytbStageChange = true;
  422. }
  423. gv.leftBtn.style.display = 'block';
  424. gv.rightBtn.style.display = 'block';
  425. gv.picinpicBtn.style.display = '';
  426. gv.controlBtn.style.display = '';
  427. this.addClass();
  428. }
  429. gv.isFull = true;
  430. },
  431. addClass: function() {
  432. document.body.parentNode.id = 'htmlToothbrush';
  433. document.body.id = 'bodyToothbrush';
  434. for (var v of gv.playerParents) {
  435. v.classList.add('parentToothbrush');
  436. //父元素position:fixed会造成层级错乱
  437. if (getComputedStyle(v).position == 'fixed') {
  438. v.classList.add('absoluteToothbrush');
  439. }
  440. }
  441. gv.player.classList.add('playerToothbrush');
  442. if (gv.player.nodeName == 'VIDEO') {
  443. gv.backControls = gv.player.controls;
  444. gv.player.controls = true;
  445. }
  446. window.dispatchEvent(new Event('resize'));
  447. },
  448. smallWin: function() {
  449. document.body.parentNode.id = gv.backHtmlId;
  450. document.body.id = gv.backBodyId;
  451. for (var v of gv.playerParents) {
  452. v.classList.remove('parentToothbrush');
  453. v.classList.remove('absoluteToothbrush');
  454. }
  455. gv.player.classList.remove('playerToothbrush');
  456. if (document.location.hostname == 'www.youtube.com' && gv.ytbStageChange) {
  457. document.querySelector('#movie_player .ytp-size-button').click();
  458. gv.ytbStageChange = false;
  459. }
  460. if (gv.player.nodeName == 'VIDEO') {
  461. gv.player.controls = gv.backControls;
  462. }
  463. gv.leftBtn.style.display = '';
  464. gv.rightBtn.style.display = '';
  465. gv.controlBtn.style.display = '';
  466. document.addEventListener('mouseover', handle.getPlayer, false);
  467. window.dispatchEvent(new Event('resize'));
  468. gv.isFull = false;
  469. }
  470. };
  471.  
  472. var pictureInPicture = function() {
  473. if (!document.pictureInPictureElement) {
  474. if (gv.player) {
  475. if (gv.player.nodeName == 'IFRAME') {
  476. gv.player.contentWindow.postMessage('iframePicInPic', '*');
  477. } else {
  478. gv.player.parentNode.querySelector('video').requestPictureInPicture();
  479. }
  480. } else {
  481. document.querySelector('video').requestPictureInPicture();
  482. }
  483. } else {
  484. document.exitPictureInPicture();
  485. }
  486. }
  487.  
  488. var init = function() {
  489. gv.picinpicBtn = document.createElement('tbdiv');
  490. gv.picinpicBtn.id = "picinpicBtn";
  491. gv.picinpicBtn.onclick = function() {
  492. pictureInPicture();
  493. };
  494. document.body.appendChild(gv.picinpicBtn);
  495. gv.controlBtn = tool.createButton('playerControlBtn');
  496. gv.leftBtn = tool.createButton('leftFullStackButton');
  497. gv.rightBtn = tool.createButton('rightFullStackButton');
  498. if (getComputedStyle(gv.controlBtn).position != 'fixed') {
  499. tool.addStyle([
  500. '#htmlToothbrush #bodyToothbrush .parentToothbrush .bilibili-player-video {margin:0 !important;}',
  501. '#htmlToothbrush, #bodyToothbrush {overflow:hidden !important;zoom:100% !important;}',
  502. '#htmlToothbrush #bodyToothbrush .parentToothbrush {overflow:visible !important;z-index:auto !important;transform:none !important;-webkit-transform-style:flat !important;transition:none !important;contain:none !important;}',
  503. '#htmlToothbrush #bodyToothbrush .absoluteToothbrush {position:absolute !important;}',
  504. '#htmlToothbrush #bodyToothbrush .playerToothbrush {position:fixed !important;top:0px !important;left:0px !important;width:100vw !important;height:100vh !important;max-width:none !important;max-height:none !important;min-width:0 !important;min-height:0 !important;margin:0 !important;padding:0 !important;z-index:2147483647 !important;border:none !important;background-color:#000 !important;transform:none !important;}',
  505. '#htmlToothbrush #bodyToothbrush .parentToothbrush video {object-fit:contain !important;}',
  506. '#htmlToothbrush #bodyToothbrush .parentToothbrush .videoToothbrush {width:100vw !important;height:100vh !important;}',
  507. '#playerControlBtn {text-shadow: none;visibility:hidden;opacity:0;display:none;transition: all 0.5s ease;cursor: pointer;font: 12px "微软雅黑";margin:0;width:64px;height:20px;line-height:20px;border:none;text-align: center;position: fixed;z-index:2147483647;background-color: #27A9D8;color: #FFF;} #playerControlBtn:hover {visibility:visible;opacity:1;background-color:#2774D8;}',
  508. '#picinpicBtn {text-shadow: none;visibility:hidden;opacity:0;display:none;transition: all 0.5s ease;cursor: pointer;font: 12px "微软雅黑";margin:0;width:53px;height:20px;line-height:20px;border:none;text-align: center;position: fixed;z-index:2147483647;background-color: #27A9D8;color: #FFF;} #picinpicBtn:hover {visibility:visible;opacity:1;background-color:#2774D8;}',
  509. '#leftFullStackButton{display:none;position:fixed;width:1px;height:100vh;top:0;left:0;z-index:2147483647;background:#000;}',
  510. '#rightFullStackButton{display:none;position:fixed;width:1px;height:100vh;top:0;right:0;z-index:2147483647;background:#000;}'
  511. ].join('\n'));
  512. }
  513. };
  514.  
  515. init();
  516.  
  517. document.addEventListener('mouseover', handle.getPlayer, false);
  518. document.addEventListener('keydown', handle.hotKey, false);
  519. window.addEventListener('message', handle.receiveMessage, false);
  520.  
  521. });

QingJ © 2025

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