您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A script to improve the use of rule34!
当前为
// ==UserScript== // @name Better Rule34 // @name:fr Meilleure règle 34 // @namespace http://tampermonkey.net/ // @version 0.70 // @description A script to improve the use of rule34! // @description:fr Un script pour améliorer l'utilisation de rule34! // @author You // @match https://rule34.xxx/* // @icon https://www.google.com/s2/favicons?sz=64&domain=rule34.xxx // @grant GM_addStyle // @license MIT // ==/UserScript== const settings = `{ "imageResizeNotice" : "resize", "theme": "dark", "undeletePosts" : "true", "clickAnywhereToStart" : "true", "htmlVideoPlayer" : "false", "dynamicResizing" : "true", "scrollPostsIntoView" : "false", "downloadFullSizedImages" : "true", "fitImageToScreen" : "false", "hideAlerts" : "true" }`; const settingsObject = JSON.parse(settings); const dark = { "primary": "#121212", "secondary": "#000011", "contrast" : "#4a4a4a", "complementary" : "#666666", "tableBackground" : "transparent", "linkColor" : "#00f" }; const themes = { "dark": dark } if(settingsObject.dynamicResizing == "true"){ const css2 = ` div.sidebar { max-width: 30%; } div.sidebar li { font-size: 120%; } div.content { width: 100%; } .thumb { height: 20%; width: auto; } ` GM_addStyle(css2); } const params = new URLSearchParams(window.location.search); function setTheme(){ let currentTheme = themes[settingsObject.theme]; if(currentTheme){ const css = ` table a:link { color: ${currentTheme.linkColor}; } table a:visited { color: ${currentTheme.linkColor}; } body { background-color: ${currentTheme.primary}; } .flat-list{ background-color: ${currentTheme.secondary}; } div#header ul#subnavbar { background-color: ${currentTheme.secondary}; } div#header ul#navbar li.current-page { background-image: url(https://imgs.search.brave.com/77L3MmxBu09NuN5WiX4HlbmWjjUe7eAsmBbakS7-DTo/rs:fit:120:120:1/g:ce/aHR0cHM6Ly91cGxv/YWQud2lraW1lZGlh/Lm9yZy93aWtpcGVk/aWEvY29tbW9ucy90/aHVtYi8wLzAyL1Ry/YW5zcGFyZW50X3Nx/dWFyZS5zdmcvMTIw/cHgtVHJhbnNwYXJl/bnRfc3F1YXJlLnN2/Zy5wbmc);; } .current-page { background-color: ${currentTheme.secondary}; background-color: brightness(110%); } .manual-page-chooser>input[type=text]{ background-color: ${currentTheme.secondary}; } .manual-page-chooser>input[type=submit]{ background-color: ${currentTheme.secondary}; color: ${currentTheme.contrast}; } div.tag-search input[type=text]{ background-color: ${currentTheme.secondary}; color: ${currentTheme.contrast}; } div.tag-search input[type=submit]{ background-color: ${currentTheme.secondary}; color: ${currentTheme.contrast}; } .col2 { color: ${currentTheme.contrast}; } h6 { color: ${currentTheme.contrast}; } h5 { color: ${currentTheme.contrast}; } .tag-count { color: ${currentTheme.contrast}; } b { color: ${currentTheme.contrast}; } li { color: ${currentTheme.contrast}; } ul { color: ${currentTheme.contrast}; } button { background-color: ${currentTheme.secondary}; color: ${currentTheme.contrast}; box-sizing: border-box; border: 1px solid; margin-top: 3px; border-color: ${currentTheme.contrast}; } table.highlightable td { color: ${currentTheme.contrast}; } h2 { color: ${currentTheme.contrast}; } table.form p { color: ${currentTheme.contrast}; } table { color: ${currentTheme.contrast}; } label { color: ${currentTheme.contrast}; } table { background-color: ${currentTheme.tableBackground}; } div { color: gray; } `; GM_addStyle(css); const thumbs = document.querySelectorAll(".thumb"); thumbs.forEach(thumb => { const images = thumb.getElementsByTagName("img"); for (let i = 0; i < images.length; i++) { images[i].style.border = `3px solid ${currentTheme.complementary}`; } }); const e=document.getElementById("user-index");e&&[...e.getElementsByTagName("p")].map(e=>(e.style.color=currentTheme.contrast)); if(settingsObject.resizePosts == "true" && window.location.href.startsWith("https://rule34.xxx/index.php?page=post&s=view")){ GM_addStyle(".content{max-height: 45%; max-width: 45%; overflow: auto;}"); document.getElementById("image").style.maxHeight = "50%"; document.getElementById("image").style.maxWidth = "fit-content"; document.getElementById("image").style.overflow = "auto"; } } } let randNum function getFromRule34(tags, index, limit, useBlacklist = false) { if(tags=="all"){tags = ""} let pid = index if(useBlacklist){tags += (" -"+decodeURIComponent(getCookie("tag_blacklist")).replaceAll("%20", " -").replaceAll("%2F", "/"))} console.log(tags) const url = `https://api.rule34.xxx/index.php?page=dapi&s=post&q=index&tags=${encodeURIComponent(tags)}&limit=${limit}&pid=${pid}&json=1`; return fetch(url) .then(response => response.json()) .then(data => { console.log(data) return data; }); } function getFromRule34WithId(id) { const url = `https://api.rule34.xxx/index.php?page=dapi&s=post&q=index&id=${id}&json=1`; return fetch(url) .then(response => response.json()) .then(data => { return data[0]; }); } function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); } function getTagsFromUrl(currentUrl) { if(currentUrl.startsWith("https://rule34.xxx/index.php?page=post&s=list&tags=")) { return currentUrl.replace("https://rule34.xxx/index.php?page=post&s=list&tags=", ""); } } function creatLinks() { try { if (window.location.href.startsWith("https://rule34.xxx/index.php?page=post&s=list&tags=")) { var anchors = document.getElementsByClassName("image-list")[0].getElementsByTagName("a"); if (anchors.length > 0) { for (var i = 0; i < anchors.length; i++) { const urlParams = new URLSearchParams(window.location.search); let pageNum = parseInt(urlParams.get("pid")); if(!pageNum){pageNum=0} anchors[i].href = (anchors[i].href + "&srchTags=" + getTagsFromUrl(window.location.href) + "&index=" + (i + pageNum).toString()).replace(/[\?&]pid=\d*/g, '');; } } else { throw new Error("No elements found with class name 'image-list' or no anchor elements found within that class."); } } else { throw new Error("The current URL does not start with 'https://rule34.xxx/index.php?page=post&s=list&tags='."); } } catch (error) { console.error("An error occurred in creatLinks: " + error); } } var preloadedData; // Variable to store the preloaded JSON data // Function to preload data for the next post function preloadNextPost(srchTags, nextIndex, limit) { getFromRule34(srchTags, nextIndex, limit, true) .then(jsonInfo => { console.log(jsonInfo) preloadedData = jsonInfo; console.log(preloadedData) }) .catch(error => { console.error("An error occurred during API request:", error); }); } // Function to navigate to the next post using preloaded data function navigateToNextPost(srchTags, nextIndex) { if (!preloadedData || !preloadedData.length) { console.error("No preloaded data available. Cannot proceed."); return; } const nextPostId = preloadedData[0].id; const newUrl = `https://rule34.xxx/index.php?page=post&s=view&id=${nextPostId}&srchTags=${encodeURIComponent(srchTags)}&index=${nextIndex}`; window.location.href = newUrl; } // Event listener for the "DOMContentLoaded" event setTimeout(function(){ const urlParams = new URLSearchParams(window.location.search); const srchTags = urlParams.get("srchTags"); const currentIndex = parseInt(urlParams.get("index")); if (!srchTags || isNaN(currentIndex)) { console.error("Invalid URL parameters. Cannot proceed."); return; } const nextIndex = currentIndex + 1; console.log(nextIndex) const limit = 1000; // Preload data for the next post preloadNextPost(srchTags, nextIndex, 1); // Event listener for when the user tries to navigate to the next post document.getElementById("nextButton").addEventListener("click", () => { navigateToNextPost(srchTags, nextIndex); }); },1000); function backPost() { const urlParams = new URLSearchParams(window.location.search); const srchTags = urlParams.get("srchTags"); const currentIndex = parseInt(urlParams.get("index")); if (!srchTags || isNaN(currentIndex)) { console.error("Invalid URL parameters. Cannot proceed."); return; } const nextIndex = currentIndex - 1; const limit = 1000; // Preload data for the next post in parallel const nextPostPromise = getFromRule34(srchTags, nextIndex, 1); getFromRule34(srchTags, currentIndex, limit) .then(jsonInfo => { if (!jsonInfo || !jsonInfo.length) { console.error("No data received from API. Cannot proceed."); return; } const nextPostId = jsonInfo[0].id; const newUrl = `https://rule34.xxx/index.php?page=post&s=view&id=${nextPostId}&srchTags=${encodeURIComponent(srchTags)}&index=${nextIndex}`; // Navigate to the next post once it's ready (and the data is preloaded) nextPostPromise.then(() => { window.location.href = newUrl; }); }) .catch(error => { console.error("An error occurred during API request:", error); }); } async function randomVideo() { const urlParams = new URLSearchParams(window.location.search); let srchTags = urlParams.get("tags"); if (!srchTags) { // If tags parameter is not found in the URL, get the value from the input element const tagsInput = document.querySelector("input[name='tags']"); srchTags = tagsInput.value.replace(/ /g, "+"); } const posts = await getFromRule34(srchTags, 1, 1000); if (posts.length === 0) { console.error("No posts found for the given tags. Cannot proceed."); return; } const randNum = Math.floor(Math.random() * posts.length); const postId = posts[randNum].id; const newUrl = `https://rule34.xxx/index.php?page=post&s=view&id=${postId}&tags=${encodeURIComponent(srchTags)}&index=${randNum}`; window.location.href = newUrl; } async function downloadAllPostFiles() { const urlParams = new URLSearchParams(window.location.search); let srchTags = urlParams.get("tags"); if (!srchTags) { // If tags parameter is not found in the URL, get the value from the input element const tagsInput = document.querySelector("input[name='tags']"); srchTags = tagsInput.value.replace(/ /g, "+"); } const posts = await getFromRule34(srchTags, 0, 1000); if (posts.length === 0) { console.error("No posts found for the given tags. Cannot proceed."); return; } // Loop through each post and download the file for (const post of posts) { const postId = post.image; const fileUrl = post.file_url; try { const response = await fetch(fileUrl); const blob = await response.blob(); // Create a temporary link element to trigger the download const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = postId; // You can adjust the filename as needed link.click(); // Clean up the temporary URL object URL.revokeObjectURL(link.href); } catch (error) { console.error(`Error downloading post ${postId}:`, error); } } } function makeButtons(){ let btn = document.createElement("button"); btn.innerHTML = "Random"; btn.onclick = randomVideo; let btn4 = document.createElement("button"); btn4.innerHTML = "↓"; btn4.onclick = downloadAllPostFiles; if(document.getElementsByClassName("tag-search")[0]){document.getElementsByClassName("tag-search")[0].appendChild(btn); document.getElementsByClassName("tag-search")[0].appendChild(btn4)}; if(document.getElementsByClassName("image-sublinks")[0]){ let btn3 = document.createElement("button"); btn3.innerHTML = "back"; btn3.onclick = backPost; document.getElementsByClassName("image-sublinks")[0].appendChild(btn3); let btn2 = document.createElement("button"); btn2.innerHTML = "next"; btn2.id = "nextButton" document.getElementsByClassName("image-sublinks")[0].appendChild(btn2); } } function allowInputResize(){ const awesompleteElement = document.getElementsByClassName("awesomplete")[0].childNodes[0]; // Add input event listener awesompleteElement.addEventListener('input', resizeInput.bind(awesompleteElement)); // Add click event listener awesompleteElement.addEventListener('click', resizeInput.bind(awesompleteElement)); // Add blur (focus loss) event listener awesompleteElement.addEventListener('blur', restoreNormalSize.bind(awesompleteElement)); awesompleteElement.style.position = "relative" awesompleteElement.style.zIndex = 99 // The resizeInput function function resizeInput() { this.style.minWidth = "100%" this.style.width = this.value.length + "ch"; } // Function to restore normal size function restoreNormalSize() { this.style.width = "100%"; // Clear the inline width style } } const imageSublinks = document.getElementsByClassName("image-sublinks")[0]; if (imageSublinks) { document.addEventListener("keydown", function(event) { // Check if the active element is an input element const activeElement = document.activeElement; const isInputElement = activeElement.tagName === "INPUT" || activeElement.tagName === "TEXTAREA"; // If it's an input element, don't execute the functions if (isInputElement) { return; } // If it's not an input element, execute the functions based on the key press if (event.keyCode === 39) { navigateToNextPost(); } else if (event.keyCode === 37) { backPost(); } }); } async function addDeletedPosts(id){ if(document.getElementById("status-notices")){ if(document.getElementsByClassName("status-notice")[0].firstChild.data.startsWith("This post was")){ const urlParts = window.location.href.split("&"); let notices = document.getElementById("status-notices"); let video = document.createElement("video"); try { const videoJson = await getFromRule34WithId(id); video.src = videoJson.file_url; video.controls = true; video.style = "max-height: 70%; max-width: 70%; overflow: auto;"; document.getElementById("fit-to-screen").appendChild(video); document.getElementById("status-notices").remove() } catch (e) { console.error(e); } } else { console.log("This post is not deleted.") } } else { console.log("The status-notices element is not present on this page.") } } function getLinksInDiv(element) { var elements = element.parentNode.querySelectorAll("a"); return(elements[elements.length - 1]) } function resizePostPopup(){ try { if(settingsObject.imageResizeNotice == "resize"){$('resized_notice').hide()} if(settingsObject.imageResizeNotice == "orignal"){Post.highres(); $('resized_notice').hide();} } catch (e) { console.error(e); } } function startVideo(){ if (document.getElementById("gelcomVideoPlayer_fluid_initial_play")) { document.getElementById("gelcomVideoPlayer").autoplay = true; } } function addTagButtons(){ const classList = ["tag-type-copyright", "tag-type-general", "tag-type-character", "tag-type-artist","tag-type-metadata"] for (const curClass of classList) { const elements = document.getElementsByClassName(curClass); for (const element of elements) { const button = document.createElement("button"); button.innerHTML = "+"; button.onclick = function() {console.log(" " + getLinksInDiv(this).innerText); document.getElementsByName("tags")[0].value += " " + (getLinksInDiv(this).innerText.trim()).replaceAll(" ","_")} element.insertBefore(button, element.firstChild); } } } function stretchyDiv(isImage){ let div; if(isImage == 0){div = document.getElementById("fluid_video_wrapper_gelcomVideoPlayer")} else {div = document.getElementById("image")} if(isImage == 1){ let newDiv = document.createElement("div"); newDiv.style.position = "relative"; div.parentNode.insertBefore(newDiv, div); newDiv.appendChild(div); div = newDiv; document.getElementById("image").maxHeight = 9999 } const resizer = document.createElement("div"); resizer.style.width = "10px"; resizer.style.height = "10px"; resizer.style.backgroundColor = "white"; resizer.style.position = "absolute"; resizer.style.bottom = "0"; resizer.style.right = "0"; resizer.style.cursor = "se-resize"; resizer.style.zIndex = "10"; let isResizing = false; let currentX; let currentY; let initialWidth; let initialHeight; resizer.addEventListener("mousedown", function(e) { document.body.style.userSelect = 'none'; isResizing = true; currentX = e.clientX; currentY = e.clientY; initialWidth = parseFloat(getComputedStyle(div, null).getPropertyValue("width").replace("px", "")); initialHeight = parseFloat(getComputedStyle(div, null).getPropertyValue("height").replace("px", "")); }); document.addEventListener("mouseup", function() { document.body.style.userSelect = ''; isResizing = false; }); document.addEventListener("mousemove", function(e) { if (isResizing) { let inner = div.getElementsByTagName("img")[0]; let newWidth = initialWidth + (e.clientX - currentX); let newHeight = initialHeight + (e.clientY - currentY); if (!e.shiftKey) { // Resize both width and height at the same rate let ratio = initialWidth / initialHeight; newHeight = newWidth / ratio; } if(isImage == 1){inner.style.width = newWidth + "px"; inner.style.height = newHeight + "px";} div.style.width = newWidth + "px"; div.style.height = newHeight + "px"; if(document.getElementById("image")){ document.getElementById("image").style.width = newWidth + "px"; document.getElementById("image").style.height = newHeight + "px"; } } }); let strechySquare = div.appendChild(resizer); } function addInputBox(){ let inputBox = document.createElement("input"); let tagsElement = document.querySelector("[name='tags']"); inputBox.type = "text" tagsElement.after(inputBox); } setTimeout(function(){ if (document.getElementById("fluid_video_wrapper_gelcomVideoPlayer")) { stretchyDiv(0); } else if (document.getElementById("image")) { stretchyDiv(1); } }, 300); function setTags(tags){ document.getElementsByName("tags")[0].value = tags } async function htmlVideoPlayer(id){ const urlParts = window.location.href.split("&"); let notices = document.getElementById("status-notices"); let video = document.createElement("video"); try { const videoUrl = await getFromRule34(id, 0 ,1); video.src = videoUrl; video.controls = true; video.style = "max-height: 70%; max-width: 70%; overflow: auto;"; document.getElementById("fit-to-screen").appendChild(video); document.getElementById("status-notices").remove() } catch (e) { console.error(e); } } function addCloseButtonToStatusNotice() { const statusNoticeElements = document.querySelectorAll('.status-notice'); statusNoticeElements.forEach(element => { const closeButton = document.createElement('button'); closeButton.textContent = 'x'; closeButton.addEventListener('click', () => { element.parentNode.removeChild(element); }); closeButton.style.background = 'none'; closeButton.style.border = 'none'; closeButton.style.cursor = 'pointer'; element.appendChild(closeButton); }); } async function overlayFullSizeImage(){ const urlParams = new URLSearchParams(window.location.search); let id = urlParams.get("id"); const postJson = await getFromRule34WithId(id); // Get the element with id "image" const originalImage = document.getElementById("image"); // Create a new transparent image element const newImage = document.createElement("img"); newImage.src = postJson.file_url; newImage.style.opacity = "0"; // Set opacity to 0 for transparency // Set the size of the new image to match the size of the original image newImage.style.width = originalImage.width + "px"; newImage.style.height = originalImage.height + "px"; // Position the new image on top of the original image newImage.style.position = "absolute"; newImage.style.top = originalImage.offsetTop + "px"; newImage.style.left = originalImage.offsetLeft + "px"; newImage.style.zIndex = "1"; // Set a higher z-index to overlay on top // Insert the new transparent image before the original image originalImage.parentNode.insertBefore(newImage, originalImage); } function convertSearchToLink(){ document.getElementsByName("commit")[0].innerHTML = `<a href="https://rule34.xxx/index.php?page=post&s=list&tags=all">${document.getElementsByName("commit")[0].innerHTML}</a>` } function fitPostToScreen(){ if(settingsObject.downloadFullSizedImages == "true" && settingsObject.hideAlerts == "false"){window.alert(`downloadFullSizedImage and fitImageToScreen often cause bugs when used together. To disable this alert turn hide alerts on in settings.`)} let postElement if(document.getElementById("fluid_video_wrapper_gelcomVideoPlayer")){postElement = document.getElementById("fluid_video_wrapper_gelcomVideoPlayer")} else {postElement = document.getElementById("image")} postElement.style.maxHeight = "85vh" postElement.style.width = "auto" } if(window.location.href.startsWith("https://rule34.xxx/index.php?page=post&s=view")){ setTags(params.get("srchTags")) } else if(window.location.href.startsWith("https://rule34.xxx/index.php?page=post&s=list")){ setTags(params.get("tags")) } let isFirstClick = true; document.addEventListener("click", function() { if (isFirstClick) { startVideo() isFirstClick = false; } }); if(settingsObject.scrollPostsIntoView == "true"){ // Function to scroll an element into view function scrollIntoView(element) { if (element) { setTimeout(function(){element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' })},250); } } // Try to find the element with id "image" var imageElement = document.querySelector('#image'); // If not found, try to find the element with id "gelcomVideoPlayer" if (!imageElement) { var videoPlayerElement = document.querySelector('#gelcomVideoPlayer'); scrollIntoView(videoPlayerElement); } else { scrollIntoView(imageElement); } } makeButtons(); creatLinks() addCloseButtonToStatusNotice() setTheme() setTimeout(resizePostPopup, 100) setTimeout(addTagButtons, 100) setTimeout(allowInputResize, 100) if(settingsObject.undeletePosts == "true"){setTimeout(addDeletedPosts(window.location.href.split("&").find(part => part.startsWith("id=")).replace("id=", "")), 500);} if(settingsObject.htmlVideoPlayer == "true"){setTimeout(htmlVideoPlayer(window.location.href.split("&").find(part => part.startsWith("id=")).replace("id=", "")), 500);} if(settingsObject.downloadFullSizedImages == "true"){setTimeout(overlayFullSizeImage, 500);} if(settingsObject.fitImageToScreen == "true"){setTimeout(fitPostToScreen, 500);} let noteBoxes = document.querySelectorAll(".note-box"); noteBoxes.forEach(function(noteBox) { noteBox.style.zIndex = "999"; });