// ==UserScript== // @run-at document-start // @name MissAV去广告、影院模式 // @description missav 广告拦截与界面优化 // @icon https://static.missav.com/img/favicon.png // @namespace loadingi.local // @version 3.1.1 // @author ch // @match https://missav.ai/* // @match https://missav.ws/* // @grant GM_setValue // @grant GM_getValue // @grant unsafeWindow // @grant GM_xmlhttpRequest // @compatible chrome // @compatible firefox // @compatible edge // @license GPL-3.0-only // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 统一管理所有选择器 const SELECTORS = { // 样式相关选择器 STYLES: { PROGRESS_BUTTONS: '.isolate.inline-flex.rounded-md.shadow-sm', PROGRESS_CONTROL: '.sm\\:hidden', LOOP_BUTTON: '.sm\\:ml-6 button', // INFO_TEXT: '.mb-1.text-secondary.break-all.line-clamp-2', // ASPECT_RATIO: '.aspect-w-16.aspect-h-9', PLAYER_CONTAINER: 'div.relative.-mx-4.sm\\:m-0.-mt-6', // VIDEO_WRAPPER: '.aspect-w-16.aspect-h-9.relative' }, // 广告相关选择器 ADS: { SCRIPTS: [ "script[src*='app.1aad5686.js']", "script[src*='inpage.push.js']", "script[src*='hartattenuate.com']", "script[src*='ads']", "script[src*='pop']", "script[src*='banner']" ], ELEMENTS: [ 'div.sm\\:container.mx-auto.mb-5.px-4', 'ul.mb-4.list-none.text-nord14.grid.grid-cols-2.gap-2', // class="relative ml-4" 无聊的东西 myavlive 之类的 'div.relative.ml-4', 'div.root--ujvuu', // 隐藏底部右下角浮窗广告 'div.under_player', 'div.space-y-5.mb-5', 'div[class^="rootContent--"]', 'div[class^="fixed right-2 bottom-2"]', 'div[class^="space-y-6 mb-6"]', 'div.space-y-2.mb-4.ml-4.list-disc.text-nord14', 'div[id*="ads"]', 'div[id*="banner"]', 'div[class*="ads"]', 'div[class*="banner"]', '.ad-container', '#ad-container' ], SCRIPT_PATTERNS: [ 'htmlAds', 'popAds', 'bannerAds', 'adsConfig' ] }, THEATER: { PLAYER_CONTAINER: 'body > div:nth-child(3) > div.sm\\:container.mx-auto.px-4.content-without-search.pb-12 > div > div.flex-1.order-first > div:nth-child(2) > div.relative.-mx-4.sm\\:m-0.-mt-6', PLAYER_WRAPPER: '.aspect-w-16.aspect-h-9', VIDEO_ELEMENT: 'video#player', PROGRESS: 'div.sm\\:hidden.flex.justify-between.-mx-4.px-4.pt-3.pb-1.bg-black', AB_LOOP: 'div.flex.items-center.flex-nowrap.leading-5', AB_LOOP_CONTROLS: '.theater-controls-abloop' } }; // 添加统一的按钮样式常量 const BUTTON_STYLES = { BASE: { backgroundColor: '#222', borderRadius: '15px', borderColor: 'black', borderWidth: '1px', color: 'burlywood', cursor: 'pointer', transition: 'all 0.3s ease', outline: 'none', minWidth: '80px', padding: '2px 4px', marginBottom: '10px', }, HOVER: { backgroundColor: '#333' } }; // 创建统一的按钮工厂函数 function createStyledButton(text, onClick) { const button = document.createElement('button'); Object.assign(button.style, BUTTON_STYLES.BASE); button.innerText = text; button.addEventListener('mouseover', () => Object.assign(button.style, BUTTON_STYLES.HOVER)); button.addEventListener('mouseout', () => Object.assign(button.style, { backgroundColor: BUTTON_STYLES.BASE.backgroundColor })); button.addEventListener('click', onClick); return button; } // 统一的样式更新函数 function updateStyles() { // 使用更高效的选择器 const styleUpdates = [ { selector: SELECTORS.STYLES.PROGRESS_BUTTONS, styles: { background: 'rgba(85, 35, 49, 0.37)' } }, { selector: SELECTORS.STYLES.PROGRESS_CONTROL, styles: { display: 'flex', visibility: 'visible', opacity: '1' } }, { selector: SELECTORS.STYLES.LOOP_BUTTON, styles: { borderWidth: '0px' } } ]; styleUpdates.forEach(({selector, styles}) => { document.querySelectorAll(selector).forEach(el => { Object.assign(el.style, styles); }); }); // 设置背景 document.body.style.backgroundColor = 'black'; } // 优化的广告拦截函数 function blockAds() { // 合并所有选择器为一个字符串 const allSelectors = [ ...SELECTORS.ADS.SCRIPTS, ...SELECTORS.ADS.ELEMENTS ].join(','); // 一次性查询所有需要删除的元素 document.querySelectorAll(allSelectors).forEach(el => el?.remove()); // 优化 iframe 移除 const iframes = document.getElementsByTagName('iframe'); Array.from(iframes).forEach(iframe => iframe.remove()); // 优化脚本检查 const scriptPattern = new RegExp(SELECTORS.ADS.SCRIPT_PATTERNS.join('|')); document.querySelectorAll('script').forEach(script => { if (scriptPattern.test(script.innerText)) { script.remove(); } }); } // 优化的播放器设置函数 function setupPlayer() { // 移除所有带有 @click="pop()" 的元素的点击事件 document.querySelectorAll('[\\@click="pop()"]').forEach(element => { element.removeAttribute('@click'); }); // 移除窗口失焦暂停 const aspectElements = document.getElementsByClassName('aspect-w-16 aspect-h-9'); if(aspectElements[11]) { aspectElements[11].removeAttribute('@click'); aspectElements[11].removeAttribute('@keyup.space.window'); } } // 更新影院模式样式 function addTheaterModeStyles() { const style = document.createElement('style'); style.textContent = ` .theater-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0, 0, 0, 0.95); z-index: 9998; display: none; } /* 修改播放器容器样式 */ .theater-mode-container { position: fixed !important; top: 0 !important; left: 0 !important; width: 100vw !important; height: 100vh !important; transform: none !important; z-index: 9999 !important; margin: 0 !important; padding: 0 !important; display: flex !important; align-items: center !important; justify-content: center !important; background: transparent !important; pointer-events: auto !important; } /* 修改视频包装器样式 */ .theater-mode-container .aspect-w-16.aspect-h-9 { position: relative !important; width: 100vw !important; max-width: none !important; height: 100vh !important; margin: 0 auto !important; pointer-events: auto !important; } /* 修改视频元素样式 */ .theater-mode-container video { position: absolute !important; top: 0 !important; left: 0 !important; width: 100% !important; height: 100% !important; object-fit: contain !important; pointer-events: auto !important; } /* 确保所有父容器不限制尺寸和层级 */ .theater-mode-container * { max-width: none !important; max-height: none !important; pointer-events: auto !important; } /* 修改播放器控制栏样式 */ .theater-mode-container .plyr__controls { position: fixed !important; bottom: 0 !important; left: 0 !important; width: 100% !important; z-index: 10000 !important; // background: rgba(254, 98, 142, 0.27) !important; padding: 10px !important; opacity: 1 !important; visibility: visible !important; display: flex !important; } /* 降低导航栏层级*/ .fixed.z-max.w-full.bg-gradient-to-b.from-darkest { z-index: 1 !important; } /* 影院模式下不显示导航栏*/ .theater-mode-container .fixed.z-max.w-full.bg-gradient-to-b.from-darkest { display: none !important; } /* 确保时间显示可见 */ .theater-mode-container .plyr__time { display: inline-block !important; color: white !important; opacity: 1 !important; visibility: visible !important; } /* 控制条样式 */ .theater-controls-progress { position: fixed !important; bottom: 104px !important; z-index: 10000 !important; // background: rgba(254, 98, 142, 0.27) !important; background: transparent !important; padding: 10px !important; width: 100% !important; max-width: none !important; pointer-events: auto !important; } .theater-controls-abloop { position: fixed !important; bottom: 52px !important; left: 0px !important; width: 100% !important; z-index: 10000 !important; // background: rgba(254, 98, 142, 0.27) !important; padding: 10px !important; pointer-events: auto !important; } /* 添加音量控制器宽度设置 */ .theater-mode-container .plyr__controls__item.plyr__volume { width: 40px !important; } /* 隐藏指定的控制按钮 */ .theater-mode-container .plyr__controls__item[data-plyr="rewind"], .theater-mode-container .plyr__controls__item[data-plyr="fast-forward"], .theater-mode-container .plyr__control[data-plyr="settings"], .theater-mode-container .plyr__controls__item[data-plyr="pip"], .theater-mode-container .plyr__controls__item[data-plyr="fullscreen"] { display: none !important; } `; document.head.appendChild(style); } // 更新 toggleTheaterMode 函数 function toggleTheaterMode() { const playerContainer = document.querySelector(SELECTORS.THEATER.PLAYER_CONTAINER); const progress = document.querySelector(SELECTORS.THEATER.PROGRESS); const abLoop = document.querySelector(SELECTORS.THEATER.AB_LOOP); // 获取或创建遮罩层 let overlay = document.querySelector('.theater-overlay'); if (!overlay) { overlay = document.createElement('div'); overlay.className = 'theater-overlay'; document.body.appendChild(overlay); } const isTheaterMode = overlay.style.display === 'none' || overlay.style.display === ''; // 获取按钮并更新文本 const theaterButton = document.querySelector('.theater-mode-button'); const abLoopButton = document.querySelector('.ab-loop-button'); if (theaterButton) { theaterButton.innerText = isTheaterMode ? '关闭影院' : '影院模式'; } // 控制 AB 循环按钮的显示/隐藏 if (abLoopButton) { abLoopButton.style.display = isTheaterMode ? 'block' : 'none'; } if (isTheaterMode) { // 进入影院模式 overlay.style.display = 'block'; if (playerContainer) { playerContainer.classList.add('theater-mode-container'); // 确保所有父容器都不限制尺寸和层级 let parent = playerContainer.parentElement; while (parent && parent !== document.body) { parent.style.setProperty('max-width', 'none', 'important'); parent.style.setProperty('max-height', 'none', 'important'); parent.style.setProperty('overflow', 'visible', 'important'); parent.style.setProperty('z-index', 'auto', 'important'); parent = parent.parentElement; } } if (progress) { progress.classList.add('theater-controls-progress'); } if (abLoop) { abLoop.classList.add('theater-controls-abloop'); // 默认隐藏 AB 循环控制栏 abLoop.style.display = 'none'; } document.addEventListener('keydown', handleEscKey); } else { // 退出影院模式 overlay.style.display = 'none'; if (playerContainer) { playerContainer.classList.remove('theater-mode-container'); // 恢复父容器的原始样式 let parent = playerContainer.parentElement; while (parent && parent !== document.body) { parent.style.removeProperty('max-width'); parent.style.removeProperty('max-height'); parent.style.removeProperty('overflow'); parent.style.removeProperty('z-index'); parent = parent.parentElement; } } if (progress) { progress.classList.remove('theater-controls-progress'); } if (abLoop) { abLoop.classList.remove('theater-controls-abloop'); } document.removeEventListener('keydown', handleEscKey); } } // ESC键处理函数 function handleEscKey(e) { if (e.key === 'Escape') { toggleTheaterMode(); } } // 更新浮动按钮创建函数 function createFloatingButtons() { const buttonContainer = document.createElement('div'); Object.assign(buttonContainer.style, { position: 'fixed', top: '14px', right: '98px', zIndex: '10001', display: 'flex', flexDirection: 'row', // 改为水平排列 gap: '10px' }); // 创建影院模式按钮 const theaterButton = createStyledButton('影院模式', toggleTheaterMode); theaterButton.className = 'theater-mode-button'; buttonContainer.appendChild(theaterButton); // 创建 AB 循环按钮(初始文本为"显示AB循环") const abLoopButton = createStyledButton('A/B', toggleABLoopControls); abLoopButton.className = 'ab-loop-button'; abLoopButton.style.display = 'none'; // 初始隐藏 buttonContainer.appendChild(abLoopButton); document.body.appendChild(buttonContainer); } // 添加防抖函数 function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // 优化观察者 function initObserver() { const debouncedUpdate = debounce(() => { blockAds(); updateStyles(); setupPlayer(); }, 100); const observer = new MutationObserver(debouncedUpdate); observer.observe(document.body, { childList: true, subtree: true }); } // 简化清理函数 function cleanup() { document.removeEventListener('keydown', handleEscKey); } // 添加 AB 循环控制函数 function toggleABLoopControls() { const abLoopControls = document.querySelector(SELECTORS.THEATER.AB_LOOP_CONTROLS); const abLoopButton = document.querySelector('.ab-loop-button'); if (abLoopControls) { const isVisible = abLoopControls.style.display !== 'none'; abLoopControls.style.display = isVisible ? 'none' : 'flex'; // 更新按钮文本,默认显示"显示AB循环" if (abLoopButton) { abLoopButton.innerText = isVisible ? 'A/B' : 'no A/B'; } } } // 主函数 function init() { updateStyles(); blockAds(); setupPlayer(); addTheaterModeStyles(); // 添加影院模式样式 createFloatingButtons(); initObserver(); } // 当 DOM 加载完成后执行初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();