Rule34DLNamer (modified version of SankakuDLNamer)

File naming for rule34

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name        Rule34DLNamer (modified version of SankakuDLNamer)
// @namespace   Rule34DLNamer
// @description File naming for rule34
// @author      SlimeySlither, sanchan, Dramorian
// @match       http*://rule34.xxx/index.php?page=post*
// @icon        https://www.google.com/s2/favicons?sz=64&domain=rule34.xxx
// @run-at      document-end
// @version     1.0
// @grant       GM_download
// @license     MIT
// ==/UserScript==

(function() {
    'use strict';

    const usePostId = false; // replaces hash with post ID if true
    const prefixPostId = false; // put post ID in front if true
    const maxEntries = 4;
    const showCopyFilenameButton = false; // workaround in case GM_download fails
    const debug = false;
    const tagDelimiter = ', ';

    function main() {
        const tags = getSidebarTags();
        if (debug) console.debug('tags', tags);

        const imageData = getImageData();
        if (debug) console.debug('imageData', imageData);

        const postId = getPostId();
        if (debug) console.debug('postId', postId);

        const downloadName = generateFilename(tags, imageData, postId);
        if (debug) console.debug('downloadName', downloadName);

        const details = getDLDetails(imageData, downloadName);
        if (debug) console.debug(details);

        if (showCopyFilenameButton) insertUnderDetails(createCopyFilenameButton(downloadName));
        insertUnderDetails(createDownloadButton(details));

    }

    function createDownloadButton(details) {
        const a = document.createElement('a');
        a.href = '#';
        a.innerText = 'Download';
        a.onclick = function() {
            console.log('downloading...');
            details.onload = () => {
                console.log('download complete');
            };
            details.ontimeout = () => {
                console.error('download timeout');
            };
            details.onerror = (error, errorDetails) => {
                console.error('download failed', error, errorDetails);
                alert('download failed with ' + error);
            };
            details.onprogress = () => {
                if (debug) console.debug('.');
            };
            GM_download(details);
            return false;
        };

        return a;
    }

    function createCopyFilenameButton(downloadName) {
        const a = document.createElement('a');
        a.href = '#';
        a.innerText = 'Copy Filename';
        a.onclick = function() {
            navigator.clipboard.writeText(downloadName);
            return false;
        };

        return a;
    }

    function insertUnderDetails(el) {
        const tagSearchDiv = document.querySelector('div.tag-search');
        if (!tagSearchDiv) throw new Error('couldn\'t find .tag-search div');

        const li = document.createElement('li');
        li.appendChild(el);

        tagSearchDiv.appendChild(li);
    }

    function tagSearchDiv(node, ref_node) {
        ref_node.parentNode.insertBefore(node, ref_node.nextSibling);
    }

    function getPostId() {
        const urlParams = new URLSearchParams(window.location.search);
        return urlParams.get('id') || ''; // Return an empty string if 'id' is not found
    }

    function cleanText(text) {
        // replace illegal filename characters https://stackoverflow.com/a/42210346
        return text.replaceAll(/[/\\?%*:|"<>]/g, '-');
    }

    function getSidebarTags() {
        const tagSidebar = document.getElementById('tag-sidebar');
        if (!tagSidebar) throw new Error('couldn\'t find tag-sidebar');

        const cats = {}; // category -> [tags]
        for (const tagItem of tagSidebar.getElementsByTagName('li')) {
            let tag;

            // find the second <a> tag within the <li> element
            const tagLinks = tagItem.getElementsByTagName('a');
            if (tagLinks.length > 1) {
                tag = cleanText(tagLinks[1].innerText); // get text from the second <a> tag
            }

            if (tag) {
                let cat = cleanText(tagItem.className);

                // insert tag in its category
                if (!(cat in cats)) {
                    cats[cat] = [tag];
                } else {
                    cats[cat].push(tag);
                }
            }
        }

        return cats;
    }

    function getImageData() {
        const imageLink = document.querySelector('a[href*="/images/"]');
        if (!imageLink) throw new Error('couldn\'t find image link');

        const url = new URL(imageLink.getAttribute('href'), document.baseURI);
        if (debug) console.log('image url', url);

        const filename = url.pathname.substring(url.pathname.lastIndexOf('/') + 1);
        if (debug) console.log('filename', filename);

        const j = filename.lastIndexOf('.');
        const hash = filename.substring(0, j);
        const extension = filename.substring(j); // including '.'

        return {
            url,
            hash,
            extension
        };
    }

    function sortAndShortenTagList(tags) {
        if (!tags) return;

        tags.sort();

        if (tags.length > maxEntries) {
            tags.splice(maxEntries);
            tags.push('...');
        }
    }

    function generateFilename(tags, imageData, postId) {
    let characters = tags['tag-type-character tag'];
    const copyrights = tags['tag-type-copyright tag'];
    const artists = tags['tag-type-artist tag'];

    if (characters) {
        // Remove round brackets from character tags
        for (let i = 0; i < characters.length; i++) {
            let j = characters[i].indexOf('(');
            if (j > 0) {
                if ([' ', '_'].includes(characters[i][j - 1])) j--;
                characters[i] = characters[i].substring(0, j);
            }
        }

        // Deduplicate
        characters = [...new Set(characters)];
    }

    sortAndShortenTagList(characters);
    sortAndShortenTagList(copyrights);
    sortAndShortenTagList(artists);

    const tokens = [];

    if (usePostId && prefixPostId) {
        tokens.push(postId);
        tokens.push('-');
    }

    if (characters) tokens.push(characters.join(tagDelimiter));
    if (copyrights) tokens.push('(' + copyrights.join(tagDelimiter) + ')');
    if (artists) {
        tokens.push('drawn by');
        tokens.push(artists.join(tagDelimiter));
    }

    if (!usePostId) {
        tokens.push('-'); // Add dash before the hash
        tokens.push(imageData.hash);
    } else if (!prefixPostId) {
        tokens.push('-'); // Add dash before the postId
        tokens.push(postId);
    }

    // Remove '-' if there's nothing after it
    if (tokens[tokens.length - 1] === '-') {
        tokens.splice(-1);
    }

    // Join tokens into a filename string, convert to lowercase, and append extension
    const filename = tokens.join(' ') + imageData.extension;

    return filename;
}

    function getDLDetails(imageData, downloadName) {
        return {
            url: imageData.url.href,
            name: downloadName,
            saveAs: true,
        };
    }

    if (document.readyState === 'complete' || document.readyState === 'loaded' || document.readyState === 'interactive') {
        main();
    } else {
        document.addEventListener('DOMContentLoaded', main, false);
    }

})();