您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
让所有视频网页全屏,开启画中画功能
当前为
// ==UserScript== // @name 视频网页全屏(改) // @name:en Maximize Video(Modify) // @name:zh-CN 视频网页全屏(改) // @name:zh-TW 視頻網頁全屏(改) // @namespace https://greasyfork.org/zh-CN/users/178351-yesilin // @description 让所有视频网页全屏,开启画中画功能 // @description:en Maximize all video players.Support Piture-in-picture. // @description:zh-CN 让所有视频网页全屏,开启画中画功能 // @description:zh-TW 讓所有視頻網頁全屏,開啟畫中畫功能 // @author 冻猫, ryomahan, YeSilin // @include * // @exclude *www.w3school.com.cn* // @version 12.5.10 // @run-at document-end // @license MIT // @icon data:image/svg+xml;base64,PHN2ZyBpZD0i5Zu+5bGCXzEiIGRhdGEtbmFtZT0i5Zu+5bGCIDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDIwMCAxOTUuNDMiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDojZmYzMzVmO308L3N0eWxlPjwvZGVmcz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0xMDEsMTE0Ljg5czY1LjIzLS42NCw3NS4wNyw0Ni44OWwtMTguNTYsNS4zMSwzLjE0LDIwLjQyYTUyLDUyLDAsMCwxLTIzLjUtMS4xNyw1NC41NSw1NC41NSwwLDAsMS0zNy02Ny43MXEuMzgtMS4yNy44MS0yLjUyWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtMi4yOSkiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0xMDcuNiwxMDAuMTFzMzEuOS01Ni43Myw3OC4wNS00MS40N2wtNC45LDE4LjcxTDIwMCw4NC44NWE1Mi40NCw1Mi40NCwwLDAsMS0xMi43NiwxOS43OCw1NC41LDU0LjUsMCwwLDEtNzcuMDUtMS40OWMtLjY1LS42OC0xLjI4LTEuMzctMS45LTIuMDhaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0yLjI5KSIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTk2Ljg2LDk1LjY0UzUwLjQsNTAuMTMsNzcsOS41N0w5My44OSwxOSwxMDYuMzMsMi4yOWE1Miw1MiwwLDAsMSwxNiwxNy40Myw1NC42MSw1NC42MSwwLDAsMS0yMS41MSw3NC4xNmMtLjc4LjQzLTEuNTcuODQtMi4zNiwxLjIzWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtMi4yOSkiLz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik04OC4zLDEwMy4zcy01NiwzMy04OC4zLTMuMTlMMTMuNDUsODYuMjNsLTEzLTE2QTUxLjc0LDUxLjc0LDAsMCwxLDIxLjQyLDU5LjY1YTU0LjU0LDU0LjU0LDAsMCwxLDY2LDM5Ljg4Yy4yMi44OC40MiwxLjc2LjU5LDIuNjVaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0yLjI5KSIvPjxwYXRoIGNsYXNzPSJjbHMtMSIgZD0iTTkwLjM4LDExMy4yNHMxNy40OSw2Mi42OC0yNS44NCw4NC40N2wtOS43OC0xNi41My0xOSw4LjI5YTUxLjQxLDUxLjQxLDAsMCwxLTQuODktMjMsNTQuNDksNTQuNDksMCwwLDEsNTUuNjYtNTMuM2MuOTQsMCwxLjg4LjA2LDIuODIuMTNaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0yLjI5KSIvPjwvc3ZnPg== // ==/UserScript== (() => { // 全局变量存储对象 (global variables) const gv = { isFull: false, // 是否处于全屏状态 isIframe: false, // 当前页面是否在iframe中 autoCheckCount: 0, // 自动检测计数器 btnText: {}, // 按钮文本(多语言支持) player: null, // 当前激活的视频播放器元素 playerChilds: [], // 播放器子元素列表 playerParents: [], // 播放器父元素列表 backHtmlId: "", // 全屏前html元素的id backBodyId: "", // 全屏前body元素的id backControls: null, // 全屏前视频控件状态 ytbStageChange: false, // YouTube舞台模式切换标记 scrollFixTimer: null, // 滚动修正定时器 mouseoverEl: null, // 鼠标悬停元素 // 按钮元素 picinpicBtn: null, controlBtn: null, leftBtn: null, rightBtn: null, }; // Html5播放器规则[播放器最外层],适用于无法自动识别的自适应大小HTML5播放器 // 键为域名,值为该域名下播放器元素的选择器数组 const html5Rules = { "www.acfun.cn": [".player-container .player"], "www.bilibili.com": ["#bilibiliPlayer"], "www.douyu.com": ["#js-player-video-case"], "www.huya.com": ["#videoContainer"], "www.twitch.tv": [".player"], "www.youtube.com": ["#ytd-player"], "www.miguvideo.com": ["#mod-player"], "www.yy.com": ["#player"], "*weibo.com": ['[aria-label="Video Player"]', ".html5-video-live .html5-video"], "v.huya.com": ["#video_embed_flash>div"], }; // 通用html5播放器选择器,用于匹配常见的视频播放器类名 const generalPlayerRules = [".dplayer", ".video-js", ".jwplayer", "[data-player]"]; // 判断当前页面是否在iframe中 if (window.top !== window.self) { gv.isIframe = true; } // 根据浏览器语言设置按钮文本 if (navigator.language.toLocaleLowerCase() == "zh-cn") { gv.btnText = { max: "网页全屏", pip: "画中画", tip: "Iframe内视频,请用鼠标点击视频后重试", }; } else { gv.btnText = { max: "Maximize", pip: "PicInPic", tip: "Iframe video. Please click on the video and try again", }; } // 工具函数集合 const tool = { /** * 带时间戳的日志打印 * @param {string} log - 日志内容 */ print(log) { const now = new Date(); const year = now.getFullYear(); const month = (now.getMonth() + 1 < 10 ? "0" : "") + (now.getMonth() + 1); const day = (now.getDate() < 10 ? "0" : "") + now.getDate(); const hour = (now.getHours() < 10 ? "0" : "") + now.getHours(); const minute = (now.getMinutes() < 10 ? "0" : "") + now.getMinutes(); const second = (now.getSeconds() < 10 ? "0" : "") + now.getSeconds(); const timenow = "[" + year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second + "]"; console.log(timenow + "[Maximize Video] > " + log); }, /** * 获取元素的位置信息 * @param {HTMLElement} element - 目标元素 * @returns {Object} 包含页面坐标和屏幕坐标的对象 */ getRect(element) { const rect = element.getBoundingClientRect(); const scroll = tool.getScroll(); return { pageX: rect.left + scroll.left, // 元素左上角在页面中的X坐标 pageY: rect.top + scroll.top, // 元素左上角在页面中的Y坐标 screenX: rect.left, // 元素左上角在视口中的X坐标 screenY: rect.top, // 元素左上角在视口中的Y坐标 }; }, /** * 判断元素是否半全屏显示(宽或高接近视口) * @param {HTMLElement} element - 目标元素 * @returns {boolean} 是否半全屏 */ isHalfFullClient(element) { const client = tool.getClient(); const rect = tool.getRect(element); // 宽或高接近视口,且元素居中显示 if ( (Math.abs(client.width - element.offsetWidth) < 21 && rect.screenX < 20) || (Math.abs(client.height - element.offsetHeight) < 21 && rect.screenY < 10) ) { if ( Math.abs(element.offsetWidth / 2 + rect.screenX - client.width / 2) < 21 && Math.abs(element.offsetHeight / 2 + rect.screenY - client.height / 2) < 21 ) { return true; } else { return false; } } else { return false; } }, /** * 判断元素是否完全全屏显示(宽和高都接近视口) * @param {HTMLElement} element - 目标元素 * @returns {boolean} 是否完全全屏 */ isAllFullClient(element) { const client = tool.getClient(); const rect = tool.getRect(element); // 宽和高都接近视口,且位置在左上角附近 if ( Math.abs(client.width - element.offsetWidth) < 21 && rect.screenX < 20 && Math.abs(client.height - element.offsetHeight) < 21 && rect.screenY < 10 ) { return true; } else { return false; } }, /** * 获取页面滚动距离 * @returns {Object} 包含左右和上下滚动距离的对象 */ getScroll() { return { left: document.documentElement.scrollLeft || document.body.scrollLeft, top: document.documentElement.scrollTop || document.body.scrollTop, }; }, /** * 获取视口尺寸 * @returns {Object} 包含视口宽高的对象 */ getClient() { return { width: document.compatMode == "CSS1Compat" ? document.documentElement.clientWidth : document.body.clientWidth, height: document.compatMode == "CSS1Compat" ? document.documentElement.clientHeight : document.body.clientHeight, }; }, /** * 向页面添加CSS样式 * @param {string} css - CSS样式字符串 * @returns {HTMLElement} 创建的style元素 */ addStyle(css) { const style = document.createElement("style"); style.type = "text/css"; const node = document.createTextNode(css); style.appendChild(node); document.head.appendChild(style); return style; }, /** * 匹配字符串与规则(支持通配符*) * @param {string} str - 要匹配的字符串 * @param {string} rule - 包含*的规则字符串 * @returns {boolean} 是否匹配 */ matchRule(str, rule) { return new RegExp("^" + rule.split("*").join(".*") + "$").test(str); }, /** * 创建按钮元素 * @param {string} id - 按钮id * @returns {HTMLElement} 创建的按钮元素 */ createButton(id) { const btn = document.createElement("tbdiv"); btn.id = id; btn.onclick = () => { maximize.playerControl(); }; document.body.appendChild(btn); return btn; }, /** * 显示提示信息 * @param {string} str - 提示文本 * @returns {Promise} 提示显示完成的Promise */ async addTip(str) { if (!document.getElementById("catTip")) { const tip = document.createElement("tbdiv"); tip.id = "catTip"; tip.innerHTML = str; 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;'; document.body.appendChild(tip); tip.style.right = -tip.offsetWidth - 5 + "px"; // 显示提示动画 await new Promise((resolve) => { tip.style.display = "block"; setTimeout(() => { tip.style.right = "25px"; resolve("OK"); }, 300); }); // 停留一段时间 await new Promise((resolve) => { setTimeout(() => { tip.style.right = -tip.offsetWidth - 5 + "px"; resolve("OK"); }, 3500); }); // 移除提示元素 await new Promise((resolve) => { setTimeout(() => { document.body.removeChild(tip); resolve("OK"); }, 1000); }); } }, }; // 按钮设置相关方法 const setButton = { /** * 初始化按钮 */ init() { if (!document.getElementById("playerControlBtn")) { init(); } // 如果在iframe中且播放器半全屏,向父窗口发送消息 if (gv.isIframe && tool.isHalfFullClient(gv.player)) { window.parent.postMessage("iframeVideo", "*"); return; } this.show(); }, /** * 显示按钮并设置事件监听 */ show() { // 移除并重新添加鼠标离开事件监听,避免重复绑定 gv.player.removeEventListener("mouseleave", handle.leavePlayer, false); gv.player.addEventListener("mouseleave", handle.leavePlayer, false); // 非全屏状态下添加滚动监听,用于修正按钮位置 if (!gv.isFull) { document.removeEventListener("scroll", handle.scrollFix, false); document.addEventListener("scroll", handle.scrollFix, false); } gv.controlBtn.style.display = "block"; gv.controlBtn.style.visibility = "visible"; // 支持画中画功能且播放器不是OBJECT/EMBED时显示画中画按钮 if (document.pictureInPictureEnabled && gv.player.nodeName != "OBJECT" && gv.player.nodeName != "EMBED") { gv.picinpicBtn.style.display = "block"; gv.picinpicBtn.style.visibility = "visible"; } this.locate(); }, /** * 定位按钮位置(基于播放器位置) */ locate() { let escapeHTMLPolicy; // 处理可信类型(Trusted Types)安全策略 const hasTrustedTypes = Boolean(window.trustedTypes && window.trustedTypes.createPolicy); if (hasTrustedTypes) { escapeHTMLPolicy = window.trustedTypes.createPolicy("myEscapePolicy", { createHTML: (string, sink) => string, }); } const playerRect = tool.getRect(gv.player); // 设置全屏按钮样式和位置 gv.controlBtn.style.opacity = "0.5"; gv.controlBtn.innerHTML = hasTrustedTypes ? escapeHTMLPolicy.createHTML(gv.btnText.max) : gv.btnText.max; gv.controlBtn.style.top = playerRect.screenY - 20 + "px"; gv.controlBtn.style.left = playerRect.screenX - 64 + gv.player.offsetWidth + "px"; // 设置画中画按钮样式和位置 gv.picinpicBtn.style.opacity = "0.5"; gv.picinpicBtn.innerHTML = hasTrustedTypes ? escapeHTMLPolicy.createHTML(gv.btnText.pip) : gv.btnText.pip; gv.picinpicBtn.style.top = gv.controlBtn.style.top; gv.picinpicBtn.style.left = playerRect.screenX - 64 + gv.player.offsetWidth - 54 + "px"; }, }; // 事件处理相关方法 const handle = { /** * 获取鼠标悬停的播放器元素 * @param {MouseEvent} e - 鼠标事件对象 */ getPlayer(e) { if (gv.isFull) { return; } gv.mouseoverEl = e.target; const hostname = document.location.hostname; let players = []; // 1. 优先使用站点特定规则匹配播放器 for (let i in html5Rules) { if (tool.matchRule(hostname, i)) { for (let html5Rule of html5Rules[i]) { const elements = document.querySelectorAll(html5Rule); if (elements.length > 0) { players.push(...elements); // 直接扩展,避免二次查询 } } break; } } // 2. 站点规则未匹配到,使用通用规则 if (players.length == 0) { for (let generalPlayerRule of generalPlayerRules) { if (document.querySelectorAll(generalPlayerRule).length > 0) { for (let player of document.querySelectorAll(generalPlayerRule)) { players.push(player); } } } } // 3. 通用规则未匹配到,尝试直接匹配video元素 if (players.length == 0 && e.target.nodeName != "VIDEO" && document.querySelectorAll("video").length > 0) { const videos = document.querySelectorAll("video"); for (let v of videos) { const vRect = v.getBoundingClientRect(); // 匹配鼠标在视频区域内且尺寸足够大的视频 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 ) { players = []; players[0] = handle.autoCheck(v); gv.autoCheckCount = 1; break; } } } // 4. 从匹配到的播放器中找到鼠标所在的那个 if (players.length > 0) { const path = e.path || e.composedPath(); for (let v of players) { if (path.indexOf(v) > -1) { gv.player = v; setButton.init(); return; } } } // 5. 直接处理视频相关元素(VIDEO/OBJECT/EMBED) switch (e.target.nodeName) { case "VIDEO": case "OBJECT": case "EMBED": // 只处理尺寸足够大的播放器 if (e.target.offsetWidth > 399 && e.target.offsetHeight > 220) { gv.player = e.target; setButton.init(); } break; default: handle.leavePlayer(); } }, /** * 自动检测播放器的父容器(寻找与视频尺寸相近的容器) * @param {HTMLElement} v - 视频元素 * @returns {HTMLElement} 最合适的播放器容器 */ autoCheck(v) { let tempPlayer, el = v; gv.playerChilds = []; gv.playerChilds.push(v); // 向上遍历父节点,寻找与视频尺寸相近的容器 while ((el = el.parentNode)) { if (Math.abs(v.offsetWidth - el.offsetWidth) < 15 && Math.abs(v.offsetHeight - el.offsetHeight) < 15) { tempPlayer = el; gv.playerChilds.push(el); } else { break; } } return tempPlayer; }, /** * 处理鼠标离开播放器的事件(隐藏按钮) */ leavePlayer() { if (gv.controlBtn.style.visibility == "visible") { gv.controlBtn.style.opacity = ""; gv.controlBtn.style.visibility = ""; gv.picinpicBtn.style.opacity = ""; gv.picinpicBtn.style.visibility = ""; gv.player.removeEventListener("mouseleave", handle.leavePlayer, false); document.removeEventListener("scroll", handle.scrollFix, false); } }, /** * 处理滚动事件(延迟修正按钮位置,避免频繁触发) */ scrollFix(e) { clearTimeout(gv.scrollFixTimer); gv.scrollFixTimer = setTimeout(() => { setButton.locate(); }, 20); }, /** * 处理键盘快捷键 * @param {KeyboardEvent} e - 键盘事件对象 */ hotKey(e) { // ESC键:切换全屏状态(默认) if (e.keyCode == 27) { maximize.playerControl(); } // F2键:切换画中画(默认) if (e.keyCode == 113) { handle.pictureInPicture(); } }, /** * 处理鼠标中键点击事件 * @param {MouseEvent} e - 鼠标事件对象 */ mouseMiddleClick(e) { // 只在全屏状态下响应鼠标中键(button=1表示中键) if (gv.isFull && e.button === 1) { e.preventDefault(); // 阻止默认行为(如打开链接) maximize.playerControl(); // 退出全屏 } }, /** * 处理跨窗口消息 * @param {MessageEvent} e - 消息事件对象 */ async receiveMessage(e) { switch (e.data) { case "iframePicInPic": tool.print("messege:iframePicInPic"); // 处理iframe中的画中画请求 if (!document.pictureInPictureElement) { await document .querySelector("video") .requestPictureInPicture() .catch((error) => { tool.addTip(gv.btnText.tip); }); } else { await document.exitPictureInPicture(); } break; case "iframeVideo": tool.print("messege:iframeVideo"); // 处理iframe中的视频全屏请求 if (!gv.isFull) { gv.player = gv.mouseoverEl; setButton.init(); } break; case "parentFull": tool.print("messege:parentFull"); // 处理父窗口的全屏请求 gv.player = gv.mouseoverEl; if (gv.isIframe) { window.parent.postMessage("parentFull", "*"); } maximize.checkParent(); maximize.fullWin(); // 修正特定播放器的位置 if (getComputedStyle(gv.player).left != "0px") { tool.addStyle( "#htmlToothbrush #bodyToothbrush .playerToothbrush {left:0px !important;width:100vw !important;}" ); } gv.isFull = true; break; case "parentSmall": tool.print("messege:parentSmall"); // 处理父窗口的退出全屏请求 if (gv.isIframe) { window.parent.postMessage("parentSmall", "*"); } maximize.smallWin(); break; case "innerFull": tool.print("messege:innerFull"); // 处理iframe内部的全屏请求 if (gv.player.nodeName == "IFRAME") { gv.player.contentWindow.postMessage("innerFull", "*"); } maximize.checkParent(); maximize.fullWin(); break; case "innerSmall": tool.print("messege:innerSmall"); // 处理iframe内部的退出全屏请求 if (gv.player.nodeName == "IFRAME") { gv.player.contentWindow.postMessage("innerSmall", "*"); } maximize.smallWin(); break; } }, /** * 处理画中画功能切换 */ pictureInPicture() { if (!document.pictureInPictureElement) { if (gv.player) { if (gv.player.nodeName == "IFRAME") { // 向iframe发送画中画请求 gv.player.contentWindow.postMessage("iframePicInPic", "*"); } else { // 直接请求画中画 gv.player.parentNode.querySelector("video").requestPictureInPicture(); } } else { // 没有指定播放器时使用第一个video元素 document.querySelector("video").requestPictureInPicture(); } } else { // 退出画中画 document.exitPictureInPicture(); } }, }; // 最大化相关方法 const maximize = { /** * 播放器控制(切换全屏/退出全屏) */ playerControl() { if (!gv.player) { return; } this.checkParent(); if (!gv.isFull) { // 进入全屏 if (gv.isIframe) { window.parent.postMessage("parentFull", "*"); } if (gv.player.nodeName == "IFRAME") { gv.player.contentWindow.postMessage("innerFull", "*"); } this.fullWin(); // 自动调整播放器容器(最多尝试10次) if (gv.autoCheckCount > 0 && !tool.isHalfFullClient(gv.playerChilds[0])) { if (gv.autoCheckCount > 10) { for (let v of gv.playerChilds) { v.classList.add("videoToothbrush"); } return; } const tempPlayer = handle.autoCheck(gv.playerChilds[0]); gv.autoCheckCount++; maximize.playerControl(); gv.player = tempPlayer; maximize.playerControl(); } else { gv.autoCheckCount = 0; } } else { // 退出全屏 if (gv.isIframe) { window.parent.postMessage("parentSmall", "*"); } if (gv.player.nodeName == "IFRAME") { gv.player.contentWindow.postMessage("innerSmall", "*"); } this.smallWin(); } }, /** * 记录播放器的父元素链(用于退出全屏时恢复) */ checkParent() { if (gv.isFull) { return; } gv.playerParents = []; let full = gv.player; // 遍历父节点直到body while ((full = full.parentNode)) { if (full.nodeName == "BODY") { break; } if (full.getAttribute) { gv.playerParents.push(full); } } }, /** * 进入全屏状态 */ fullWin() { if (!gv.isFull) { // 移除鼠标悬停监听(全屏状态不需要) document.removeEventListener("mouseover", handle.getPlayer, false); // 保存原始id(用于恢复) gv.backHtmlId = document.body.parentNode.id; gv.backBodyId = document.body.id; // 显示辅助按钮 gv.leftBtn.style.display = "block"; gv.rightBtn.style.display = "block"; gv.picinpicBtn.style.display = ""; gv.controlBtn.style.display = ""; // 添加全屏相关样式类 this.addClass(); const hostname = document.location.hostname; // YouTube特殊处理:切换舞台模式 if ( hostname.includes("www.youtube.com") && document.querySelector(".html5-video-container").clientWidth - document.querySelector(".ytp-chrome-bottom").clientWidth > 24 ) { document.querySelector("#movie_player .ytp-size-button").click(); gv.ytbStageChange = true; } // B站特殊处理:显示被隐藏的元素 if (hostname.includes("bilibili")) { document.querySelector(".right-container")?.style.removeProperty("display"); document.querySelector("#biliMainHeader")?.style.removeProperty("display"); } } gv.isFull = true; }, /** * 为全屏状态添加样式类 */ addClass() { // 修改根元素id(用于CSS选择器定位) document.body.parentNode.id = "htmlToothbrush"; document.body.id = "bodyToothbrush"; // 为父元素添加样式类 for (let v of gv.playerParents) { v.classList.add("parentToothbrush"); // 处理fixed定位的父元素(避免层级问题) if (getComputedStyle(v).position == "fixed") { v.classList.add("absoluteToothbrush"); } } // 为播放器添加样式类 gv.player.classList.add("playerToothbrush"); // 确保video元素显示控件 if (gv.player.nodeName == "VIDEO") { gv.backControls = gv.player.controls; gv.player.controls = true; } // 触发resize事件(刷新播放器尺寸) window.dispatchEvent(new Event("resize")); }, /** * 退出全屏状态(恢复原始状态) */ smallWin() { // 恢复原始id document.body.parentNode.id = gv.backHtmlId; document.body.id = gv.backBodyId; // 移除父元素的样式类 for (let v of gv.playerParents) { v.classList.remove("parentToothbrush"); v.classList.remove("absoluteToothbrush"); } // 移除播放器的样式类 gv.player.classList.remove("playerToothbrush"); // YouTube特殊处理:恢复舞台模式 if (document.location.hostname == "www.youtube.com" && gv.ytbStageChange) { document.querySelector("#movie_player .ytp-size-button").click(); gv.ytbStageChange = false; } // 恢复video控件状态 if (gv.player.nodeName == "VIDEO") { gv.player.controls = gv.backControls; } // 隐藏辅助按钮 gv.leftBtn.style.display = ""; gv.rightBtn.style.display = ""; gv.controlBtn.style.display = ""; // 恢复鼠标悬停监听 document.addEventListener("mouseover", handle.getPlayer, false); // 触发resize事件 window.dispatchEvent(new Event("resize")); // B站特殊处理:隐藏侧边栏和头部 const hostname = document.location.hostname; if (hostname.includes("bilibili")) { document.querySelector(".right-container")?.style.setProperty("display", "none"); document.querySelector("#biliMainHeader")?.style.setProperty("display", "none"); } gv.isFull = false; }, }; /** * 初始化脚本 */ const init = () => { // 创建画中画按钮 gv.picinpicBtn = document.createElement("tbdiv"); gv.picinpicBtn.id = "picinpicBtn"; gv.picinpicBtn.onclick = () => { handle.pictureInPicture(); }; document.body.appendChild(gv.picinpicBtn); // 创建控制按钮和辅助按钮 gv.controlBtn = tool.createButton("playerControlBtn"); gv.leftBtn = tool.createButton("leftFullStackButton"); gv.rightBtn = tool.createButton("rightFullStackButton"); // 添加样式(如果按钮还没有固定定位样式) if (getComputedStyle(gv.controlBtn).position != "fixed") { tool.addStyle( [ // 针对B站播放器的特殊样式 "#htmlToothbrush #bodyToothbrush .parentToothbrush .bilibili-player-video {margin:0 !important;}", // 全屏状态下禁止页面滚动 "#htmlToothbrush, #bodyToothbrush {overflow:hidden !important;zoom:100% !important;}", // 父元素样式重置 "#htmlToothbrush #bodyToothbrush .parentToothbrush {overflow:visible !important;z-index:auto !important;transform:none !important;-webkit-transform-style:flat !important;transition:none !important;contain:none !important;}", // 修正fixed定位的父元素 "#htmlToothbrush #bodyToothbrush .absoluteToothbrush {position:absolute !important;}", // 播放器全屏样式 "#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:2147483646 !important;border:none !important;background-color:#000 !important;transform:none !important;}", // 视频内容适配 "#htmlToothbrush #bodyToothbrush .parentToothbrush video {object-fit:contain !important;}", // 视频元素全屏样式 "#htmlToothbrush #bodyToothbrush .parentToothbrush .videoToothbrush {width:100vw !important;height:100vh !important;}", // 全屏按钮样式 '#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;}', // 画中画按钮样式 '#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;}', // 左右辅助按钮样式(用于遮挡边缘) "#leftFullStackButton{display:none;position:fixed;width:1px;height:100vh;top:0;left:0;z-index:2147483647;background:#000;}", "#rightFullStackButton{display:none;position:fixed;width:1px;height:100vh;top:0;right:0;z-index:2147483647;background:#000;}", ].join("\n") ); } // 添加事件监听 document.addEventListener("mouseover", handle.getPlayer, false); document.addEventListener("keydown", handle.hotKey, false); window.addEventListener("message", handle.receiveMessage, false); // 添加鼠标中键点击事件监听 document.addEventListener("mousedown", handle.mouseMiddleClick, false); tool.print("Ready"); }; // 初始化脚本 init(); })();