Sleazy Fork is available in English.

Sadpanda Save/Export All Favorites

Load all favorites to the page and save it or just copy the textbox contents. Works on exhentai and e-hentai.

Versão de: 02/02/2022. Veja: a última versão.

// ==UserScript==
// @name        Sadpanda Save/Export All Favorites
// @namespace   SaddestPanda
// @description Load all favorites to the page and save it or just copy the textbox contents. Works on exhentai and e-hentai.
// @include     /^https?:\/\/e(-|x)hentai\.org\/favorites.php.*/
// @homepage    https://sleazyfork.org/en/scripts/23406-sadpanda-save-export-all-favorites
// @supportURL  https://sleazyfork.org/en/scripts/23406-sadpanda-save-export-all-favorites/feedback
// @version     2.4.3
// @grant       GM.getValue
// @grant       GM.setValue
// @grant       GM.deleteValue
// @grant       GM_addValueChangeListener
// @grant       GM.registerMenuCommand
// @require     https://cdn.jsdelivr.net/npm/gm-webext-pref@0.4.2/dist/GM_webextPref.user.js
// @require     https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @require     https://cdnjs.cloudflare.com/ajax/libs/he/1.2.0/he.min.js
// ==/UserScript==

//TODO Maybe add json export functionality with tags/categories. For now the exported html can be used instead. 
//TODO ditch jquery, redo the whole crawling method (make it async)

//Top text box inspiration and its code from http://userscripts-mirror.org/scripts/show/173553. But this one won't have an import feature like that one.
//Using Jquery for jquery purposes and he.js for he.js purposes.
//Using GM_webextPref for the preferences menu. Many thanks to the author "eight" for the library.

//Use the options menu to change these values.
var inserttoTable = true; //Disables inserting new pages to the table. Set this to false if your browser gets real slow. You can still use the textbox to save your favorites.
var pageTimer = 3000; //Waiting timer for each page in miliseconds. Default is 2500. Making it too short might prevent pages from loading.
var playsilentAudio = false; //Play silent audio while loading the pages. Enable this if you will be using your computer while waiting. It might prevent the tab from going into background mode and stop loading the pages.

//Play an silent audio file to prevent throttling (different files for firefox/chromium)
//Might require autoplay to be turned on for the website
var silentAudio, silentAudioFile;
var isFirefox = typeof InstallTrigger !== 'undefined';
if (isFirefox == true) {
    silentAudioFile = 'data:audio/ogg;base64,T2dnUwACAAAAAAAAAAAHA8coAAAAAIF47n4BE09wdXNIZWFkAQE0AEAfAAAAAABPZ2dTAAAAAAAAAAAAAAcDxygBAAAAUzJlNAGOT3B1c1RhZ3MNAAAATGF2ZjU1LjMzLjEwMAQAAAAVAAAAYXV0aG9yPUFkdmVudHVyZSBMYW5kFAAAAGFsYnVtPUFkdmVudHVyZSBMYW5kIwAAAHRpdGxlPUVtcHR5IExvb3AgRm9yIEpTIFBlcmZvcm1hbmNlFQAAAGVuY29kZXI9TGF2ZjU1LjMzLjEwME9nZ1MAAFi9AAAAAAAABwPHKAIAAAAyAzleMwoNCgsPCQkMDgwKDA0ODQ0NDAwMDAwMCw4MDQ4LDA0ODAwNDQwLCwoPDgwLCg0MDgsMDAgDQvtgoSdsKEAINjpmG6kyq3UB2fEgCDZ/rdQl7AWg4Ag2dYShfx2/4UHACDZ/ow5omm9mOzA0aJyXCDZ1OLVrIJjzCAVr7b2iKamgCDZ1SDnykImIDYOACDa3TfB0Gr+bWrtGb+AINnUv+ROFBR8zDoAINnVnnsPAbLa6CDZ6sGCGuOv3nlNwCDZ6I3ut9dAaHCqX3wg2XDBwFzhT1bJXG4SgCDZ1SFKCeQWBogMPggg2daDavCMN1fLmYOAINmjtoTkep+3zvsjPCDa5WDHLsGmySt1gCDZ6KI6PqMV1GKI4CDZ2BdGcG3TAu1uACDa9TAnYduSD+H3wCDZ1bhTJjA3kXrTACDU4w/g6h585SxZ0CDZ1oxPBDPDre7gINnVBqj1sCfuTiZIzsAg2dgRZAKiazM0ItQg2dgXiRKSpPCjR/SgINnXJOQWzDETgs/tm4Ag2O4gbz8WYlj2wCDWdjVsms44oglYaCDZ1S85AzQVEE/b83Ag2eW35JDbXlk8eCJfMCDcki/+9RVLZ3O4fCDVey7IEoi5wDZcVCDZ0LF6mtpuWlXG8YAg2V2QIXTjvaCEIOIwINnWjFnyIy+83bLgINnXL0yMgbqjfyAg2uVeivlfw1LfQCDZ1y8XIVqvYVQg2daJgNh1l6Ul2oTOYaAg2doUKwX0fVXOgLHrQCDZ1pGelDVg0sabgCDZC/Z1+dxdRPIwINnQw8kKLxYt5CDZ2BBYNLi6nUoeI4Ag2V5BoAlPMrQ57YAg2dW1ns/tgS0qGpA1ACDZX214nEzghqKQINacJO/rHa02gVEYINleU2/9UWr02NuBPZ2dTAAQwwwAAAAAAAAcDxygDAAAARL86cAEKCAVTo0DgY4cToA==';
    silentAudio = new Audio(silentAudioFile);
    silentAudio.volume = 0.01;
} else {
    silentAudioFile = 'data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU3LjcxLjEwMAAAAAAAAAAAAAAA/+M4wAAAAAAAAAAAAEluZm8AAAAPAAAAEAAABVgANTU1NTU1Q0NDQ0NDUFBQUFBQXl5eXl5ea2tra2tra3l5eXl5eYaGhoaGhpSUlJSUlKGhoaGhoaGvr6+vr6+8vLy8vLzKysrKysrX19fX19fX5eXl5eXl8vLy8vLy////////AAAAAExhdmM1Ny44OQAAAAAAAAAAAAAAACQCgAAAAAAAAAVY82AhbwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+MYxAALACwAAP/AADwQKVE9YWDGPkQWpT66yk4+zIiYPoTUaT3tnU487uNhOvEmQDaCm1Yz1c6DPjbs6zdZVBk0pdGpMzxF/+MYxA8L0DU0AP+0ANkwmYaAMkOKDDjmYoMtwNMyDxMzDHE/MEsLow9AtDnBlQgDhTx+Eye0GgMHoCyDC8gUswJcMVMABBGj/+MYxBoK4DVpQP8iAtVmDk7LPgi8wvDzI4/MWAwK1T7rxOQwtsItMMQBazAowc4wZMC5MF4AeQAGDpruNuMEzyfjLBJhACU+/+MYxCkJ4DVcAP8MAO9J9THVg6oxRMGNMIqCCTAEwzwwBkINOPAs/iwjgBnMepYyId0PhWo+80PXMVsBFzD/AiwwfcKGMEJB/+MYxDwKKDVkAP8eAF8wMwIxMlpU/OaDPLpNKkEw4dRoBh6qP2FC8jCJQFcweQIPMHOBtTBoAVcwOoCNMYDI0u0Dd8ANTIsy/+MYxE4KUDVsAP8eAFBVpgVVPjdGeTEWQr0wdcDtMCeBgDBkgRgwFYB7Pv/zqx0yQQMCCgKNgonHKj6RRVkxM0GwML0AhDAN/+MYxF8KCDVwAP8MAIHZMDDA3DArAQo3K+TF5WOBDQw0lgcKQUJxhT5sxRcwQQI+EIPWMA7AVBoTABgTgzfBN+ajn3c0lZMe/+MYxHEJyDV0AP7MAA4eEwsqP/PDmzC/gNcwXUGaMBVBIwMEsmB6gaxhVuGkpoqMZMQjooTBwM0+S8FTMC0BcjBTgPwwOQDm/+MYxIQKKDV4AP8WADAzAKQwI4CGPhWOEwCFAiBAYQnQMT+uwXUeGzjBWQVkwTcENMBzA2zAGgFEJfSPkPSZzPXgqFy2h0xB/+MYxJYJCDV8AP7WAE0+7kK7MQrATDAvQRIwOADKMBuA9TAYQNM3AiOSPjGxowgHMKFGcBNMQU1FMy45OS41VVU/31eYM4sK/+MYxKwJaDV8AP7SAI4y1Yq0MmOIADGwBZwwlgIJMztCM0qU5TQPG/MSkn8yEROzCdAxECVMQU1FMy45OS41VTe7Ohk+Pqcx/+MYxMEJMDWAAP6MADVLDFUx+4J6Mq7NsjN2zXo8V5fjVJCXNOhwM0vTCDAxFpMYYQU+RlVMQU1FMy45OS41VVVVVVVVVVVV/+MYxNcJADWAAP7EAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MYxOsJwDWEAP7SAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MYxPMLoDV8AP+eAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/+MYxPQL0DVcAP+0AFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV';
    silentAudio = new Audio(silentAudioFile);
}
silentAudio.loop = true;

const pref = GM_webextPref({
    default: {
        inserttoTable: true,
        playsilentAudio: true,
        pageTimer: 3000
    },
    body: [{
            key: "inserttoTable",
            type: "checkbox",
            label: "Insert new pages into the current page. Disable this if your browser freezes."
        },
        {
            key: "playsilentAudio",
            type: "checkbox",
            label: "Play silent audio while loading the pages. This might prevent the tab from going into background mode and stop loading the pages."
        },
        {
            key: "pageTimer",
            type: "number",
            label: "Time (ms) to load each page. Keep this higher than 3000 especially if you set the thumbnails to load with the page."
        }
    ]
});

var pageCounterURL = 0,
    showExport = 0,
    pageNumber = "",
    titlesBackup = "",
    winlocsea = "?";
var displayMode = $("#dms [selected='selected']")[0].value; // m:minimal, p:minimal+, l:compact, e:extended, t:thumbnail
//console.log(displayMode);
if (window.location.search == "") {
    winlocsea = "?";
} else {
    winlocsea = window.location.search;
}
var PageUrl = "favorites.php" + winlocsea + "&page=";

function destroyBox() {
    $('.uselessbr').each(function() {
        $(this).remove();
    });
    $('#exportFav').remove();
    $('#exportStatus').remove();
}

function getPageNumber() {
    var finalcell = $(".ptt")[0].rows[0].cells[$(".ptt")[0].rows[0].cells.length - 2];
    pageNumber = finalcell.textContent;
}

function addRemoveButton(newbutton) {
    if (newbutton) {
        $('#exportFav').after("<div>");
        $('#exportFav').next().append($('<input/>').attr('type', 'button').attr('value', 'Remove Titles').attr('id', 'removeTitles').attr('style', 'margin-top:5px;min-height:18px;'));
    } else {
        document.querySelector("#removeTitles").removeEventListener("click", restoreTitles, false);
        document.querySelector("#removeTitles").value = "Remove Titles";
    }
    document.querySelector("#removeTitles").addEventListener("click", removeTitles, false);
}

function addRestoreButton() {
    document.querySelector("#removeTitles").removeEventListener("click", removeTitles, false);
    document.querySelector("#removeTitles").value = "Restore Titles";
    document.querySelector("#removeTitles").addEventListener("click", restoreTitles, false);
}

function removeTitles() {
    titlesBackup = $('#exportFav')[0].textContent;
    $('#exportFav')[0].textContent = $('#exportFav')[0].textContent.replace(/( - .*)/g, "");
    addRestoreButton();
}

function restoreTitles() {
    $('#exportFav')[0].textContent = titlesBackup;
    addRemoveButton(0);
}

function getText(thisa) {
    var thetext = "";
    if (displayMode == "e") {
        thetext = he.encode(thisa.href + " - " + thisa.children[0].children[0].innerText);
    } else if (displayMode == "l") {
        thetext = he.encode(thisa.href + " - " + thisa.children[0].innerText);
    } else {
        thetext = he.encode(thisa.href + " - " + thisa.text);
    }
    $('#exportFav').append(thetext + '\n');
}

function getThisPage() {
    var trlen, thislink, j = 0;
    if (displayMode == "e") {
        trlen = $(".glink").length;
        for (j = 0; j < trlen; j++) {
            thislink = $(".glink")[j].parentNode.parentNode;
            getText(thislink);
        }
    } else {
        trlen = $(".itg .glname").length;
        for (j = 0; j < trlen; j++) {
            thislink = $(".itg .glname a")[j];
            getText(thislink);
        }
    }
}

function exportFavorites() {
    if (showExport === 0) {
        showExport = 1;
        $('#nb').after('<br class="uselessbr"/>');
        $('#nb').after($('<input/>').attr('type', 'button').attr('value', 'START').attr('id', 'startBut').attr('href', 'javascript:void(0)').attr('style', 'margin-left:5px;margin-top:5px;min-height:18px;'));
        $('#startBut').click(getThePages);
        $('#nb').after($('<input/>').attr('type', 'button').attr('value', 'Options').attr('id', 'optionsBut').attr('href', 'javascript:void(0)').attr('style', 'margin-top:5px;min-height:18px;'));
        $('#optionsBut').click(pref.openDialog);
        $('#nb').after('<br class="uselessbr"/>');
        $('#nb').after('<textarea id="exportStatus" name="Text2" cols="80" rows="8" ... />');
        $('#nb').after('<h2 class="uselessbr">Status');
        $('#nb').after('<textarea id="exportFav" name="Text1" cols="80" rows="7" ... />');
        $('#nb').after('<h2 class="uselessbr">Text List of Favorites');
        $('#nb').after('<br class="uselessbr"/><br class="uselessbr"/>');
        $('#exportStatus').append("Currently the website doesn't have any problems but keep the delay between pages higher than 3000 or your pages & thumbnails might not load." + '\n\n');
        $('#exportStatus').append("If you want to backup the most info: 1. Use extended layout, 2. Set thumbnails to load with page (in e-h settings)" + '\n');
        $('#exportStatus').append("Chrome and some other browsers throttle background tabs so if you switch to another tab it might prevent you from loading all the pages." + '\n');
        $('#exportStatus').append("To bypass that; turn this tab into its own window before you start and don't minimize it." + '\n');
        $('#exportStatus').append("Don't change layouts in another tab while waiting." + '\n');
        if (pageNumber > 10) {
            $('#exportStatus').append("You have more than 10 pages. If your browser crashes or freezes disable inserting to the page from the options." + '\n\n');
        }
        $('#exportStatus').append("Press START below to start loading the pages." + '\n');
    } else {
        //destroyBox();
        //showExport = 0;
        return;
    }

    if (window.location.search.indexOf("page=") != -1) {
        $('#exportFav').append("GO TO THE FIRST PAGE AND TRY AGAIN" + '\n');
        $('#exportStatus').append("GO TO THE FIRST PAGE AND TRY AGAIN" + '\n');
        return;
    }
}

function getThePages() {
    inserttoTable = pref.get("inserttoTable");
    playsilentAudio = pref.get("playsilentAudio");
    pageTimer = pref.get("pageTimer");

    //Use crawl status to prevent the timer from going crazy. Should refactor this whole function later.
    let crawlStatus = "idle";
    getThisPage();
    if (pageNumber > 1) {
        if (playsilentAudio) {
            silentAudio.play();
        }
        $('#exportStatus').append("Loading all pages will take " + (Math.ceil(pageTimer / 100) * (pageNumber)) / 10 + " seconds." + '\n');
        //$('#exportStatus').append("NOTICE - If your browser completely freezes, try using the newest Firefox/Chrome with tampermonkey/violentmonkey or help me fix it." + '\n');

        let crawlInterval = setInterval(function() {

            if (crawlStatus == "idle") {
                //Set crawl status to working
                crawlStatus = "working";
                pageCounterURL++;
                //console.log("Current page: " + counter);
                $('#exportStatus').append("Loading Page " + (pageCounterURL + 1) + " of " + pageNumber + "..." + '\n');
                if ((pageCounterURL + 1) == pageNumber) {
                    //All pages are done.
                    //Disable interval
                    clearInterval(crawlInterval);
                    setTimeout(function() {
                        if (playsilentAudio) {
                            silentAudio.pause();
                        }
                        $('#exportStatus').append("Loading complete." + '\n' + "Copy above list and/or press Ctrl+S to save the page (use the \"complete\" or the \".mhtml\" option)." + '\n');
                        addRemoveButton(1);
                        $('#exportStatus').append("Confirmation: " + ($('#exportFav')[0].value.split("\n").length - 1) + " favorites in above text area, " + ($(".itg .glname a").length + $(".gl4e .glink").length) + " favorites in below page." + '\n');
                        if (pageNumber > 10) {
                            $('#exportStatus').append("If your gallery count doesn't add up, try again. You can load the pages faster now that you cached all the thumbnails." + '\n\n');
                        }
                        $('#exportStatus').scrollTop($('#exportStatus')[0].scrollHeight);
                        crawlStatus = "idle";
                    }, pageTimer + 2000);
                }
                $('#exportStatus').scrollTop($('#exportStatus')[0].scrollHeight);
                
                let tryCount = 0;
                let retryLimit = 10;

                $.ajax({
                    url: PageUrl + pageCounterURL,
                    type: 'GET',
                    success: function(sss) {
                        //do something
                        var trlen, thislink, j = 0;
                        trlen = (displayMode == "e") ? $(sss).find(".glink").length : $(sss).find(".itg .glname").length;
                        for (j = 0; j < trlen; j++) {
                            thislink = (displayMode == "e") ? $(sss).find(".glink")[j].parentNode.parentNode : $(sss).find(".itg .glname a")[j];
                            getText(thislink);
                            if (inserttoTable) {
                                if (displayMode != "t") {
                                    $(".itg tbody")[0].append($(thislink).parents("tr:first")[0]);
                                } else {
                                    $(".itg")[0].append($(thislink).parents(".gl1t")[0]);
                                }
                            }
                        }
                        //Set crawl status to idle
                        crawlStatus = "idle";
                    },
                    error: function(xhr, textStatus, errorThrown) {
                        tryCount++;
                        if (tryCount <= retryLimit) {
                            //try again
                            $.ajax(this);
                            return;
                        }
                        //Must have failed all retries for this page
                        crawlStatus = "idle";
                        return;
                        /*
                        if (textStatus == 'timeout') {

                        }
                        if (xhr.status == 500) {
                            //handle error
                        } else {
                            //handle error
                        }
                        */
                    }
                });
            }

        }, 200 + pageTimer);

    } else {
        $('#exportStatus').append("Loading complete." + '\n' + "Copy above list and/or press Ctrl+S to save the page (use the \"complete\" or the \".mhtml\" option)." + '\n');
        $('#exportStatus').append("Confirmation: " + ($('#exportFav')[0].value.split("\n").length - 1) + " favorites in above text area, " + ($(".itg .glname a").length + $(".gl4e .glink").length) + " favorites in below page." + '\n');
        addRemoveButton(1);
    }
}

$(document).ready(function() {
    var topmenu = document.querySelector("#nb");
    topmenu.style.justifyContent = "center";
    topmenu.style.maxWidth = "1400px";
    $('#nb').append($('<div/>').attr('id', 'exportFavsdiv'));
    $('#exportFavsdiv').append($('<a/>').attr('href', 'javascript:void(0)').attr('id', 'exportFavorites').text('Load All Favorites'));
    $('#exportFavorites').click(exportFavorites);
    document.querySelector("#exportFavsdiv").style.marginLeft = "8px";
    getPageNumber();
});