您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Replays a segment back and forth between two user-defined markers
// ==UserScript== // @name A-B looper (Audio and Video) // @namespace Continuously replays a user-defined segment from A to B // @match *://*/* // @grant none // @version Alpha-v1 // @author JesusisLord // @description Replays a segment back and forth between two user-defined markers // @license MIT // ==/UserScript== // Variables for looping functionality, initialized for clarity let timeA = null; // Time positions for looping let timeB = null; let click = 0; // Counter for click-based actions let looper_video = null; // Reference to the video element // Helper function to check for text field focus function isAnyTextFieldFocused() { const activeElement = document.activeElement; // Early return for null or undefined activeElement if (!activeElement) { return false; } // Optimized matching for text fields const tagName = activeElement.tagName.toLowerCase(); return ( tagName === "textarea" || // Direct check for textareas (tagName === "input" && activeElement.type === "text") // Text input validation ); } // Function to retrieve the currently playing video function getVideo() { // Directly select all videos using querySelectorAll const videos = document.querySelectorAll('video'); // Check if any videos are found if (videos.length > 0) { // Find the currently playing video based on paused state let currentVideo = Array.from(videos).find(video => !video.paused); // If no video is currently playing, find the one with the highest currentTime if (!currentVideo) { currentVideo = Array.from(videos).reduce((currentVid, nextVid) => { return nextVid.currentTime > currentVid.currentTime ? nextVid : currentVid; }, videos[0]); // Start with the first video as the initial value } return currentVideo; } else { // Return null if no videos are found return null; } } // Initialize MutationObserver to detect video changes const observer = new MutationObserver(() => { getVideo(); // Refresh video detection on changes }); // Configure the observer to watch for changes in video elements observer.observe(document.body, { childList: true, subtree: true }); // Core looping algorithm function loopingAlgorithm() { click++; if (click === 1) { pointA(looper_video); } else if (click === 2) { pointB(looper_video); } else { // Reset click counter after the third click click = 0; } } // Functions for setting loop points function pointA(vid) { timeA = vid.currentTime; } function pointB(vid) { timeB = vid.currentTime; vid.ontimeupdate = function () { gotoPointA(vid) }; } function gotoPointA(vid) { if (vid.currentTime >= timeB && click == 2) { vid.currentTime = timeA; } } function run() { looper_video = getVideo(); if (looper_video && looper_video.src) { loopingAlgorithm(); } } // Event listener for key press document.addEventListener("keydown", (event) => { if (!isAnyTextFieldFocused() && event.code === "KeyA") { run(); } });