您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Dynamically finds the content container to work on all video list pages. Extracts links, auto-navigates, and lets you name the output .txt file.
// ==UserScript== // @name Rule34Video Universal Link Extractor // @namespace http://tampermonkey.net/ // @version 2.2 // @description Dynamically finds the content container to work on all video list pages. Extracts links, auto-navigates, and lets you name the output .txt file. // @author BohemianCorporal // @match https://rule34video.com/* // @grant none // @run-at document-idle // ==/UserScript== (function() { 'use strict'; let allLinks = new Set(); let isAutoGrabbing = false; let observer; // --- Create the UI Panel --- const panel = document.createElement('div'); panel.style.position = 'fixed'; panel.style.bottom = '20px'; panel.style.right = '20px'; panel.style.backgroundColor = '#2c2c2e'; panel.style.color = '#fff'; panel.style.border = '1px solid #555'; panel.style.padding = '15px'; panel.style.zIndex = '10000'; panel.style.borderRadius = '8px'; panel.style.fontFamily = 'Arial, sans-serif'; panel.style.fontSize = '14px'; panel.style.boxShadow = '0 4px 8px rgba(0,0,0,0.3)'; panel.style.width = '230px'; const title = document.createElement('h3'); title.textContent = 'Link Extractor'; title.style.margin = '0 0 10px 0'; title.style.textAlign = 'center'; title.style.borderBottom = '1px solid #555'; title.style.paddingBottom = '5px'; const getLinksButton = document.createElement('button'); getLinksButton.textContent = 'Get Links for Current Page'; getLinksButton.style.cssText = 'display:block; width:100%; margin-bottom:8px; padding:8px; cursor:pointer;'; const autoGrabButton = document.createElement('button'); autoGrabButton.textContent = 'Start Auto-Grab'; autoGrabButton.style.cssText = 'display:block; width:100%; margin-bottom:15px; padding:8px; cursor:pointer; background-color: #4CAF50; color: white; border: none;'; const filenameLabel = document.createElement('label'); filenameLabel.textContent = 'Download Filename:'; filenameLabel.style.cssText = 'display: block; margin-bottom: 5px; font-weight: bold;'; const filenameInput = document.createElement('input'); filenameInput.type = 'text'; filenameInput.placeholder = 'e.g., my_collection'; filenameInput.style.cssText = 'display:block; box-sizing: border-box; width: 100%; padding: 6px; margin-bottom: 10px; background-color: #444; color: #fff; border: 1px solid #666; border-radius: 4px;'; const downloadButton = document.createElement('button'); downloadButton.textContent = 'Download All Links (.txt)'; downloadButton.style.cssText = 'display:block; width:100%; padding:8px; cursor:pointer;'; const statusDisplay = document.createElement('div'); statusDisplay.textContent = 'Status: Idle'; statusDisplay.style.cssText = 'margin-top: 10px; text-align: center; font-style: italic;'; const linkCountDisplay = document.createElement('div'); linkCountDisplay.textContent = 'Collected Links: 0'; linkCountDisplay.style.marginTop = '5px'; linkCountDisplay.style.textAlign = 'center'; panel.appendChild(title); panel.appendChild(getLinksButton); panel.appendChild(autoGrabButton); panel.appendChild(filenameLabel); panel.appendChild(filenameInput); panel.appendChild(downloadButton); panel.appendChild(statusDisplay); panel.appendChild(linkCountDisplay); document.body.appendChild(panel); // --- Core Functions --- function updateLinkCount() { linkCountDisplay.textContent = `Collected Links: ${allLinks.size}`; } function getLinksFromCurrentPage() { const linkElements = document.querySelectorAll('a.th.js-open-popup'); let newLinksFound = 0; linkElements.forEach(link => { const href = link.href; if (href && !allLinks.has(href)) { allLinks.add(href); newLinksFound++; } }); console.log(`Found ${newLinksFound} new links. Total unique links: ${allLinks.size}`); updateLinkCount(); } function runAutoGrabSequence() { if (!isAutoGrabbing) return; statusDisplay.textContent = 'Status: Grabbing links...'; getLinksFromCurrentPage(); const nextButton = document.querySelector('div.item.pager.next > a'); // If there's no next button, we're done. if (!nextButton) { console.log("No 'next' button found. Reached the last page."); statusDisplay.textContent = 'Status: Last page reached.'; stopAutoGrab(); return; } // ** DYNAMICALLY FIND THE CONTAINER ID ** const targetContainerId = nextButton.getAttribute('data-block-id'); const targetNode = document.getElementById(targetContainerId); if (!targetNode) { console.error(`Video container with ID "${targetContainerId}" not found. Stopping.`); alert(`Error: Could not find the video container to watch. Stopping auto-grab.`); stopAutoGrab(); return; } console.log(`Watching container: #${targetContainerId}`); observer = new MutationObserver((mutationsList, obs) => { for (const mutation of mutationsList) { if (mutation.type === 'childList') { console.log("New content loaded."); statusDisplay.textContent = 'Status: Page loaded.'; obs.disconnect(); // Stop observing until the next cycle // Short delay to ensure scripts on the page have finished processing setTimeout(runAutoGrabSequence, 500); break; } } }); // Start observing before we click the next button observer.observe(targetNode, { childList: true, subtree: true }); // Click the next button to load the new content console.log("Navigating to the next page..."); statusDisplay.textContent = 'Status: Going to next page...'; nextButton.click(); } function startAutoGrab() { isAutoGrabbing = true; autoGrabButton.textContent = 'Stop Auto-Grab'; autoGrabButton.style.backgroundColor = '#f44336'; statusDisplay.textContent = 'Status: Starting...'; runAutoGrabSequence(); } function stopAutoGrab() { isAutoGrabbing = false; if (observer) observer.disconnect(); autoGrabButton.textContent = 'Start Auto-Grab'; autoGrabButton.style.backgroundColor = '#4CAF50'; statusDisplay.textContent = 'Status: Idle'; } // --- Button Event Listeners --- getLinksButton.addEventListener('click', getLinksFromCurrentPage); autoGrabButton.addEventListener('click', () => { if (isAutoGrabbing) { stopAutoGrab(); } else { startAutoGrab(); } }); downloadButton.addEventListener('click', () => { if (allLinks.size === 0) { alert('No links have been collected yet. Use "Get Links" or "Start Auto-Grab" first.'); return; } let filename = filenameInput.value.trim(); if (filename === '') { filename = `video_links_${new Date().toISOString().slice(0, 10)}`; } const sanitizedFilename = filename.replace(/[\/\\?%*:|"<>]/g, '_'); const fileContent = Array.from(allLinks).join('\n'); const blob = new Blob([fileContent], { type: 'text/plain;charset=utf-8' }); const downloadLink = document.createElement('a'); downloadLink.href = URL.createObjectURL(blob); downloadLink.download = `${sanitizedFilename}.txt`; document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); URL.revokeObjectURL(downloadLink.href); }); })();