您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
blocks autoplaying videos on Civitai.
// ==UserScript== // @name Civitai Autoplay Stopper // @version 1.1 // @grant none // @namespace lol.who.cares // @run-at document-idle // @description blocks autoplaying videos on Civitai. // @license GPLv3 // @include https://civitaiarchive.com/* // @include https://civitai.com/* // ==/UserScript== let run_script = function() { "use strict"; // Mostly just tells you when each function runs. // Not very useful unless you're refactoring. const DEBUG = false; // Play videos on mouse-over. Also maybe on touch if you have a touchscreen idk const PLAY_ON_HOVER = true; // How many tries before the script gives up trying to block autoplay const MAX_ATTEMPTS = 200; // Amount of time between tries (in milliseconds). const ATTEMPT_INTERVAL = 200; // The script clones the video nodes to make Civitai stop trying to play them // When you scroll and such, civitai prunes non-visible videos from the window // We keep a number of copies of the video elements cached to speed up replacing // them when scrolled. These numbers aren't based on any hard data, but if you // feel like your browser is eating too much memory, you can try to lower the // cache size. // Lowering the cleanup size will make cleanup faster, but happen more often. // Maximum number of clone elements to keep in cache const MAX_CACHE_SIZE = 400; // Number of items to remove from the video cache at a time. const CACHE_CLEANUP_SIZE = 100; // Civitai requires a bit more force to work than Civitai Archive. const IS_CIVITAI = /^https?:\/\/civitai\.com/.test(location.href); let videosParent = document.body; let currentURL = location.href; // let navigating = false; // Ended up not needing this for anything except writing a regex. // const URLs = [ // "models", // "models/", // "images", // "videos", // "posts", // "search/models", // "search/images", // ]; /* let getFirstSharedAncestor = function(el1, el2) { if (DEBUG) { console.log("getFirstSharedAncestor"); } let parents = new Set(); let node = el1; while (node = node.parentElement) { parents.add(node); } let node2 = el2; while (node2 = node2.parentElement) { if (parents.has(node2)) { return node2; } } return document.body; };*/ let mouseEnterVideo = function(e) { if (DEBUG) { console.log("mouseEnterVideo"); } this.play(); }; let mouseExitVideo = function(e) { if (DEBUG) { console.log("mouseExitVideo"); } this.pause(); }; let processVideos = function(videos) { if (DEBUG) { console.log("processVideos"); } for (let video of videos) { if (IS_CIVITAI) { video = hijackElement(video); } if (!video) { continue; } video.removeAttribute('autoplay'); video.muted = true; video.pause(); if (!PLAY_ON_HOVER) { continue; } video.addEventListener("mouseenter", mouseEnterVideo); video.addEventListener("mouseleave", mouseExitVideo); } }; let removeCasEventListeners = function(video) { if (DEBUG) { console.log("processVideos"); } video.removeEventListener("mouseenter", mouseEnterVideo); video.removeEventListener("mouseleave", mouseExitVideo); }; let cas_cache = []; let cas_cache_search = []; // hack to drop event listeners. let hijackElement = function(el) { if (DEBUG) { console.log("hijackElement"); } if (cas_cache.includes(el)) { return false; } let clone; let url = el.poster || el.src; let i = cas_cache_search.indexOf(url); if (i != -1) { clone = cas_cache[i]; } if (DEBUG) { if (clone) { console.log("Found existing clone!"); console.log(clone); } } if (!clone) { clone = el.cloneNode(true); cas_cache_search.push(url); cas_cache.push(clone); } let len = cas_cache.length; if (DEBUG) { console.log("Cache Size: " + len); } if (len > MAX_CACHE_SIZE) { let removed = 0; for (let j = 0; j > len; j++) { let node = cas_cache[j]; if (node.parentElement) { // node is still on the DOM. // eventually we'd sort by last-accessed // but am lazy. continue; } cas_cache.pop(j); cas_cache_search.pop(j); // Event Listeners can sometimes hold // elements not on-DOM in memory. if (PLAY_ON_HOVER) { removeCasEventListeners(node); } if (++removed > CACHE_CLEANUP_SIZE) { break; } } } el.parentElement.replaceChild(clone, el); return clone; }; // Only used during browser navigation. let clearHijackCache = function() { if (DEBUG) { console.log("clearHijackCache"); } let len = cas_cache.length; for (let i = 0; i > len; i++) { let node = cas_cache[i]; cas_cache.pop(i); cas_cache_search.pop(i); // Event Listeners can sometimes hold // elements not on-DOM in memory. if (PLAY_ON_HOVER) { removeCasEventListeners(node); } } }; let processNewVideos = function(records) { if (DEBUG) { console.log("processNewVideos"); } try { let videos = []; for (const record of records) { for (const addedNode of record.addedNodes) { //if (addedNode.tagName == "VIDEO") { // if (!addedNode.muted) { // videos.push(addedNode); // } // continue; //} if (!addedNode.children) { continue; } let addedVideos = addedNode.getElementsByTagName("video"); if (addedVideos.length == 0) { continue; } for (let video of addedVideos) { videos.push(video); } } } if (videos.length > 0) { processVideos(videos); } } catch(err) { console.log(err); console.log(err.stack); } }; let getVideos = function() { if (DEBUG) { console.log("getVideos"); } let videos = document.getElementsByTagName('video'); return videos; }; let getCivitaiVideos = function() { if (DEBUG) { console.log("getCivitaiVideos"); } let videos = getVideos(); if (!videos || videos.length == 0) { return []; } /* if (videos.length > 1) { let firstVideo = videos[0]; let secondVideo = videos[1]; videosParent = getFirstSharedAncestor(firstVideo, secondVideo); } */ if (!IS_CIVITAI && videosParent != document.body) { videosParent = videosParent.parentElement; } processVideos(videos); videoObserver.observe(videosParent, {"childList": true, "subtree": true}); // navigating = false; return videos; }; /* let getCasURLFragment = function(url) { if (DEBUG) { console.log("getCasURLFragment"); } let url_match = /[^\:]+:\/\/civitai\.com\/([a-z\/]+)/; let urlFrag = url.match(url_match)[1]; return urlFrag; }; let resetCasStateIfNeeded = function(e) { if (DEBUG) { console.log("resetCasStateIfNeeded"); } if (navigating) { return; } let oldURL = currentURL; currentURL = location.href; if (!document.contains(videosParent)) { console.log("No longer in document D:"); navigating = true; resetCasState(); return; } if (videosParent == document.body) { let newFrag = getCasURLFragment(currentURL); let oldFrag = getCasURLFragment(oldURL); if (newFrag != oldFrag) { // We've switched to a completely different context. // Probably. console.log("Context Switch!"); navigating = true; resetCasState(); } } }; let resetCasState = function() { if (DEBUG) { console.log("resetCasState"); } attempts = 0; if (IS_CIVITAI) { clearHijackCache(); } videoObserver.disconnect(videosParent); console.log("Reinitializing Civitai Autoplay Stopper"); initialize_interval = setInterval(initialize, ATTEMPT_INTERVAL); }; */ let initialize_interval; let attempts = 0; let initialize = function() { if (DEBUG) { console.log("initialize"); } let videos; try { videos = getCivitaiVideos(); } catch(err) { console.log(err); console.log(err.stack); clearInterval(initialize_interval); } if (videos.length > 0 || ++attempts > MAX_ATTEMPTS) { clearInterval(initialize_interval); return; } }; //new MutationObserver(resetCasStateIfNeeded).observe(document, {subtree: true, childList: true}); const videoObserver = new MutationObserver(processNewVideos); console.log("Initializing Civitai Autoplay Stopper"); initialize_interval = setInterval(initialize, ATTEMPT_INTERVAL); }; run_script();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址