您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a download button and makes thumbnails a little bigger. Make shure to add (.png, .jpeg, .jpg, etc) to the tampermonkey download whitelist
// ==UserScript== // @name A Gelbooru Viewer // @namespace http://tampermonkey.net/ // @version 1.0.1 // @description Adds a download button and makes thumbnails a little bigger. Make shure to add (.png, .jpeg, .jpg, etc) to the tampermonkey download whitelist // @author Anonymous // @match https://gelbooru.com/*page=post*s=list* // @grant GM_download // @grant GM_xmlhttpRequest // ==/UserScript== (function() { 'use strict'; // Your code here... function setCustomStyles(){ const customStyles = document.createElement("style"); customStyles.innerHTML = "" + ".thumbnail-preview {" + "max-width: unset;"+ "width: 30%;" + "min-height: 200px;" + "max-height: unset;" + "height: 500px;"+ "}" + ".thumbnail-preview span {display: contents;}"+ ".thumbnail-preview a {display: contents;}"+ ".thumbnail-preview button {width: 100%; height: 25px; background-color: blue; color: white; text-decoration:none;border:none;}"+ ".thumbnail-preview button[disabled] {background-color: gray; color: black;}"+ ".thumbnail-preview button[data-done] {background-color: green;}"+ ".thumbnail-preview button.secondary {background-color: gray;}"+ ".thumbnail-preview img {"+ "object-fit: contain;" + "width: 100%;" + "height: calc(100% - 50px);" + "} "+ ".next-page-message-container {display:flex;align-items:center;width:100%;height:100px;margin:0;background-color:darkgray;} "+ ".next-page-message-container p {width:fit-content;margin:auto;font-size:3em;}"; document.head.appendChild(customStyles); } function parsePaginationInfo(doc){ let currentPage, nextPage, nextPageUrl, previousPage,previousPageUrl; const currentPageEl = doc.querySelector(".pagination b"); if (currentPageEl){ currentPage = currentPageEl.innerText; } else { console.debug("cant parse current page"); } const aList = Array.from(doc.querySelectorAll(".pagination a")); for (let i=0; i < aList.length; i++){ const a = aList[i]; const altText = a.getAttribute("alt"); if (!altText) { continue } switch(altText){ case "next": { nextPage = altText; nextPageUrl = a.href; break; } case "back": { previousPage = altText; previousPageUrl = a.href; break; } } } const alertIfNotFound = (name, value) => { if (!value) { console.debug( name + " was not found"); } } alertIfNotFound("nextPage", nextPage); alertIfNotFound("previousPage", previousPage); return {currentPage, nextPage, nextPageUrl, previousPage,previousPageUrl} } function parseThumbPreview(el){ const a = el.querySelector("a"); const img = el.querySelector("img"); if (!a || !img){ throw new Error("parse error " + el.outerHTML); } const thumbUrl = img.src; const pageUrl = a.href; if (!thumbUrl || !pageUrl) { throw new Error("parse error " + el.outerHTML); } return {thumbUrl, pageUrl} } function parseDetailPage(doc){ let originalImageUrl = undefined; doc.querySelectorAll("#tag-list a").forEach(a=>{ if (a.innerText === "Original image"){ originalImageUrl = a.href; } }); if (!originalImageUrl) { throw new Error("parse error"); } return {originalImageUrl} } async function getDetailPageDoc(url){ const response = await fetch(url, {method:"GET"}); if (!response.ok){ throw new Error("request error", response) } const text = await response.text(); return new DOMParser().parseFromString(text,"text/html"); } async function downloadImage(url, saveAs=false){ const name = url.substring(url.lastIndexOf("/")+1); return new Promise((resolve, reject)=>{ GM_download({ url, name, saveAs, onload: (data)=>{ console.debug("GM_download.onload", data); resolve(); }, onerror: (e)=>{ console.debug("GM_download.onerror", e); reject(e); } }); }); } async function downloadOriginalFromDetailPage(url){ const doc = await getDetailPageDoc(url); console.debug("page fetched"); const data = parseDetailPage(doc); console.debug("page parsed", data); await downloadImage(data.originalImageUrl); console.debug("image downloaded"); return; } // https://stackoverflow.com/questions/1038727/how-to-get-browser-width-using-javascript-code function getWidth() { return Math.max( document.body.scrollWidth, document.documentElement.scrollWidth, document.body.offsetWidth, document.documentElement.offsetWidth, document.documentElement.clientWidth ); } function getHeight() { return Math.max( document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight, document.documentElement.clientHeight ); } function openChildWindow(url,name="page",w=null,h=null) { // http://www.nigraphic.com/blog/java-script/how-open-new-window-popup-center-screen if (!w) { w = getWidth() * 0.8; } if (!h) { h = getHeight() * 0.8; } const dualScreenLeft = window.screenLeft != undefined ? window.screenLeft : screen.left; const dualScreenTop = window.screenTop != undefined ? window.screenTop : screen.top; const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width; const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height; const left = ((width / 2) - (w / 2)) + dualScreenLeft; const top = ((height / 2) - (h / 2)) + dualScreenTop; const windowref = window.open(url, name, `width=${w},height=${h},top=${top},left=${left},resizable,location=no,toolbar=no,menubar=no,status=no,titlebar=0`); } function setOnScrollBottomHandler(handler){ window.onscroll = function(e) { let pageHeight=document.documentElement.offsetHeight, windowHeight=window.innerHeight, scrollPosition=window.scrollY || window.pageYOffset || document.body.scrollTop + (document.documentElement && document.documentElement.scrollTop || 0); if (pageHeight*0.98 <= (windowHeight+scrollPosition)) { console.debug('bottom reached'); handler(e); } }; } function addButtons(){ document.querySelectorAll(".thumbnail-preview").forEach(el=>{ const item = parseThumbPreview(el); const button = document.createElement("button"); button.innerText = "download"; button.addEventListener("click", async e=> { try{ button.setAttribute("disabled","disabled"); button.innerText = "downloading..." await downloadOriginalFromDetailPage(item.pageUrl); button.removeAttribute("disabled"); button.dataset.done=true; button.innerText = "done"; } catch(e) { button.innerText = "error"; console.error(e); } }); const openPageButton = document.createElement("button"); openPageButton.classList.add("secondary"); openPageButton.innerText = "open details"; openPageButton.onclick = (e) => { e.preventDefault(); openChildWindow(item.pageUrl, "Detail"); } el.appendChild(button); el.appendChild(openPageButton); }); } function addLoadOnScrollBottom(){ const pagination = parsePaginationInfo(document); if (pagination.nextPage) { const nextPageMessageContainer = document.createElement("footer"); const nextPageMessageEl = document.createElement("p"); nextPageMessageEl.innerText = "loading next page"; nextPageMessageContainer.classList.add("next-page-message-container"); nextPageMessageContainer.appendChild(nextPageMessageEl); document.body.appendChild(nextPageMessageContainer); // Hide lasts br to show next page message sooner const containerPushBr = Array.from(document.querySelectorAll(".contain-push br")); let count = 0, limit=6; for (let i=containerPushBr.length - 1;i >= 0 && count < limit ;i--){ containerPushBr[i].style.display = "none"; count++; } setOnScrollBottomHandler(e=>{window.location.replace(pagination.nextPageUrl)}) } } function init(){ setCustomStyles(); addButtons(); addLoadOnScrollBottom(); } init(); })();