// ==UserScript== // @name Android手机视频全屏增强版 // @description 自动横屏、长按分区倍速(左2x,右3x)、左右滑动调节亮度/音量/进度 // @version 2.0.1 // @author Gemini Assistant // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_addValueChangeListener // @grant GM_registerMenuCommand // @license MIT // ==/UserScript== (function () { 'use strict'; // 屏蔽不支持非Android设备(可选) if (navigator.userAgent.search("Android") < 0) { /* return; */ } // 全局状态记录 let globalBrightness = 1.0; // 时间格式化工具 function formatTime(seconds) { if (isNaN(seconds)) return "00:00"; const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); const s = Math.floor(seconds % 60); const pad = (n) => n.toString().padStart(2, '0'); return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`; } // 通用监听启动 listen(); function listen() { let listenTarget = document; let lastTapTime = 0; // 针对 YouTube 的特殊处理 if (window.location.host === "m.youtube.com") { // 原有 YouTube 逻辑保持,但在获取到视频后执行增强监听 } listenTarget.addEventListener("touchstart", (e) => { if (e.touches.length !== 1) return; let target = e.target; let videoElement = (target.tagName === "VIDEO") ? target : (target.parentElement ? target.parentElement.querySelector("video") : null); if (!videoElement) { // 深度查找视频逻辑(沿用原脚本的核心查找逻辑) let temp = target; while (temp && temp.tagName !== "HTML") { let v = temp.getElementsByTagName("video"); if (v.length > 0) { videoElement = v[0]; break; } temp = temp.parentElement; } } if (!videoElement) return; // --- 增加触碰范围检查 (防止非视频区域误触) --- const rect = videoElement.getBoundingClientRect(); const touch = e.touches[0]; const isInsideVideo = ( touch.clientX >= rect.left && touch.clientX <= rect.right && touch.clientY >= rect.top && touch.clientY <= rect.bottom ); // 如果不是全屏状态,且触碰点不在视频矩形区域内,则不响应脚本逻辑 if (!document.fullscreenElement && !isInsideVideo) return; // --- 双击播放/暂停逻辑 --- const now = Date.now(); if (now - lastTapTime < 300) { // 彻底阻止浏览器默认全屏行为 e.preventDefault(); e.stopPropagation(); if (videoElement.paused) videoElement.play(); else videoElement.pause(); showNotice(videoElement.paused ? "已暂停" : "继续播放"); lastTapTime = 0; return; } lastTapTime = now; const screenW = window.innerWidth; const screenH = window.innerHeight; const startX = e.touches[0].clientX; const startY = e.touches[0].screenY; let direction = null; // horizontal, vertical let subDirection = null; // brightness, volume let timeChange = 0; let longPress = false; let haveControls = videoElement.controls; let componentContainer = videoElement.parentElement; // --- 1. 分区长按逻辑 --- // 左侧 2x,右侧 3x let pressRate = startX > screenW / 2 ? 3 : 2; let rateTimer = setTimeout(() => { videoElement.playbackRate = pressRate; videoElement.controls = false; showNotice(`${pressRate}x 倍速中`); longPress = true; }, 600); // --- 2. 进度提示 UI --- let notice = null; function showNotice(text) { // 确保全屏时提示显示在正确容器(如果是全屏,挂载到全屏元素,否则挂载到视频父级) const fsElem = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement; const container = fsElem || videoElement.parentElement || document.body; if (!notice || notice.parentElement !== container) { if (notice) notice.remove(); notice = document.createElement("div"); notice.id = "v-notice"; notice.style.cssText = ` position: absolute; top: 10px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.7); color: white; padding: 10px 20px; border-radius: 8px; z-index: 2147483647; font-size: 14px; font-family: sans-serif; backdrop-filter: blur(8px); pointer-events: none; border: 1px solid rgba(255,255,255,0.2); box-shadow: 0 4px 15px rgba(0,0,0,0.5); white-space: nowrap; `; container.appendChild(notice); if (container !== document.body && getComputedStyle(container).position === "static") { container.style.position = "relative"; } } notice.innerText = text; notice.style.display = "block"; } // --- 3. 滑动处理 --- const touchmoveHandler = (moveEvent) => { if (rateTimer) { clearTimeout(rateTimer); rateTimer = null; } if (longPress) return; const curX = moveEvent.touches[0].clientX; const curY = moveEvent.touches[0].screenY; const diffX = curX - startX; const diffY = startY - curY; // 向上为正 // 首次移动确定方向 if (!direction) { // 降低方向识别阈值,一旦识别即锁定,防止触发系统滚动 if (Math.abs(diffX) > 10 || Math.abs(diffY) > 10) { if (Math.abs(diffX) > Math.abs(diffY)) { direction = 'horizontal'; } else { direction = 'vertical'; subDirection = (startX < screenW / 2) ? 'brightness' : 'volume'; } } } // 只要确定了方向,就立即阻止默认的页面滚动行为 if (direction) { moveEvent.preventDefault(); } if (direction === 'horizontal') { timeChange = Math.round(diffX / 5); const targetTime = Math.max(0, Math.min(videoElement.duration, videoElement.currentTime + timeChange)); showNotice(`进度: ${formatTime(targetTime)} / ${formatTime(videoElement.duration)} (${timeChange > 0 ? '+' : ''}${timeChange}s)`); } else if (direction === 'vertical') { if (subDirection === 'brightness') { let b = globalBrightness + (diffY / 200); b = Math.min(Math.max(b, 0.1), 1.8); videoElement.style.filter = `brightness(${b})`; showNotice(`亮度: ${Math.round(b * 100)}%`); } else { let v = videoElement.volume + (diffY / 400); videoElement.volume = Math.min(Math.max(v, 0), 1); showNotice(`音量: ${Math.round(videoElement.volume * 100)}%`); } } }; // --- 4. 抬起清理 --- const touchendHandler = () => { if (rateTimer) clearTimeout(rateTimer); if (longPress) { videoElement.playbackRate = 1; videoElement.controls = haveControls; } else if (direction === 'horizontal' && timeChange !== 0) { videoElement.currentTime += timeChange; } // 记录当前亮度系数 let filterMatch = videoElement.style.filter.match(/brightness\((.+)\)/); if (filterMatch) globalBrightness = parseFloat(filterMatch[1]); if (notice) notice.style.display = "none"; target.removeEventListener("touchmove", touchmoveHandler); }; target.addEventListener("touchmove", touchmoveHandler, { passive: false }); target.addEventListener("touchend", touchendHandler, { once: true }); }); } // --- 自动横屏模块 (保持原脚本精髓) --- window.tempLock = screen.orientation.lock; screen.orientation.lock = function() { console.log("阻止网页锁定") }; if (top === window) { GM_setValue("doLock", false); GM_addValueChangeListener("doLock", async (key, old, newVal) => { if (document.fullscreenElement && newVal) { screen.orientation.lock = window.tempLock; await screen.orientation.lock("landscape").catch(()=>{}); screen.orientation.lock = function() {}; GM_setValue("doLock", false); } }); } window.addEventListener("resize", () => { setTimeout(() => { let fullElem = document.fullscreenElement; if (fullElem) { let video = fullElem.tagName === "VIDEO" ? fullElem : fullElem.querySelector("video"); if (video && video.videoWidth > video.videoHeight) { GM_setValue("doLock", true); } } }, 500); }); })();