A Gelbooru Viewer

Adds a download button and makes thumbnails a little bigger. Make shure to add (.png, .jpeg, .jpg, etc) to the tampermonkey download whitelist

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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();

})();