Rule34 Artist favorites

A simple script to add the possibility to favorite artists to rule34

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

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

(I already have a user script manager, let me install it!)

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.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Rule34 Artist favorites
// @description  A simple script to add the possibility to favorite artists to rule34
// @namespace    User_314159_AFav
// @version      1.32
// @author       User_314159
// @license      MIT
// @match        https://rule34.xxx/index.php?page=account&s=home
// @match        https://rule34.xxx/index.php?page=post&s=view*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=rule34.xxx
// @grant        none
// ==/UserScript==

/* Functions used to interact with the local storage */
function getArtists() { // gets the localstorage array and parses it into an actual array, creates a new one if there is no key in localStorage
    let artists;
    if (localStorage.getItem('rule34FavArtists') === null) {
        console.log('generated new artists array');
        artists = [];
        saveArtists(artists);
    }
    else {
        artists = JSON.parse(localStorage.getItem('rule34FavArtists'));
    }
    return artists;
}

function deleteArtist(artist) { // deletes the specified artist out of the array and saves it
    let artists = getArtists();
    let target = artists.indexOf(artist);
    artists.splice(target, 1);
    saveArtists(artists);
    console.log('Deleted ' + artist);
}

function saveArtists(artists) { // saves an updated version of the artists to local storage
    localStorage.setItem('rule34FavArtists', JSON.stringify(artists.sort()));
}

function exportArtists() { // gets the local storage and logs it to the console as a string
    let artists = localStorage.getItem('rule34FavArtists');
    console.log(artists); // Don't need to stringify here the localstorage got it stringified
    navigator.clipboard.writeText(artists);
    alert("Copied artists to clipboard");
}

function importArtists() { // takes an inputted array string and overwrites the local storage with it
    let input = window.prompt("Enter previously exported artists"); // window.prompt seems to return a double-escaped string
    let data = JSON.parse(input); // due to this, we need to de-escape twice to get the actual json
    let current_artists = getArtists();
    for(let index = 0; index < data.length; index++) {
        if(current_artists.includes(data[index])) {
            continue;
        } else {
            current_artists.push(data[index]);
            console.log("New artist: " + data[index]);
        }
    }
    localStorage.setItem('rule34FavArtists', JSON.stringify(current_artists));
}

/* These are the functions for the Favorites displaying */
function generateArtistEntry(name) { // generates a container with a delete button calling the deleteArtist function and a link to the artist page
    let link = document.createElement('a');
    let deleteButton = document.createElement('button');
    let container = document.createElement('div');

    deleteButton.innerHTML = 'delete';
    deleteButton.setAttribute('id', 'artistFavDel');
    link.innerHTML = name;

    link.setAttribute('href', 'https://rule34.xxx/index.php?page=post&s=list&tags=' + name);
    deleteButton.setAttribute('name', name);

    deleteButton.addEventListener("click", function () {
        deleteArtist(name)
    }, false);

    container.appendChild(deleteButton);
    container.appendChild(link);

    return container;
}

/* Functions for the adding of artists to the favorites */
function generateAddButton(name) { // generates a button that is added to the bottom of the post to add an artist
    var button = document.createElement('button');
    button.innerHTML = ('Add ' + name + ' to favorites');
    button.addEventListener("click", function () {
        addArtist(name)
    }, false);
    return button;
}

function generateArtistAddable() { // adds the artists as single buttons to the bottom of the page, the buttons are generated in the generateAddButton function
    let possibles = document.getElementsByClassName('tag-type-artist tag');
    let artists = document.getElementsByClassName('link-list')[0];
    for (let i = 0; i < possibles.length; i++) {
        let artist = possibles[i].children[1].firstChild.data.split(' ').join('_'); // this hell of a selector is the name of the artist with the spaces replaced with underscores
        console.log(artist);
        artists.appendChild(generateAddButton(artist));
    }

}

function addArtist(artist) { // adds the name of the artist to the array and saves the array, cancels if the artist already is in the most recent save
    let artists = getArtists();
    if (artists.includes(artist)) {
        console.log(artist + ' already in favorites.');
        return;
    }
    artists.push(artist);
    saveArtists(artists);
    console.log('Added ' + artist + ' to favorites');

}

/* Main Function */
(function () {
    'use strict';

    let url = window.location.href;
    if (url.includes('page=post&s=view')) {
        generateArtistAddable();
    }; // this part checks if you are on a post and if so, the 'add to favorites' gets added
    if (url.includes('&s=home')) {
        let artists = getArtists(); // gets the artists and parses them from a string to an iterable
        let favoritesHeader = document.createElement('h4'); //header 4 element
        let favoritesCollapse = document.createElement('details'); // collapsible list of favorites
        let favoritesTitle = document.createElement('summary'); // title element of the collapsible
        let exp = document.createElement('button');
        let imp = document.createElement('button');
        // the two buttons are for import and export
        exp.addEventListener("click", function () {
            exportArtists();
        }, false);
        imp.addEventListener("click", function () {
            importArtists();
            location.reload();
        }, false);
        imp.innerHTML = 'Import';
        exp.innerHTML = 'Export';
        // the buttons values get declared and the onclick functions get set to the export / import functions

        favoritesTitle.innerHTML = 'Favorite Artists'; //setting the title
        for (let i = 0; i < artists.length; i++) {
            favoritesCollapse.appendChild(generateArtistEntry(artists[i]));
        } // this adds one link for each favorite artist to the collapsible

        let desc = document.createElement('p'); //description, paragraph element
        desc.innerHTML = 'View all of your favorite artists and remove them if you wish.';

        favoritesCollapse.appendChild(favoritesTitle); // the title of the collapsible gets added

        favoritesHeader.appendChild(favoritesCollapse); // the collapsible gets added to the main container

        let mainPosts = document.getElementById("user-index").lastElementChild; // the last child of the user-index div is gotten, which is the "To main post page"
        let space = document.getElementById('user-index'); // the space variable is the container element for the whole thing

        space.insertBefore(favoritesHeader, mainPosts); // main container for the artist entries
        space.insertBefore(desc, mainPosts); // the description
        space.insertBefore(exp, mainPosts); // export button
        space.insertBefore(imp, mainPosts); // import button
        if (artists.length > 0) { // this adds a button that resets the data if there is any data
            let delete_all_artists = document.createElement('button');
            delete_all_artists.addEventListener("click", function () {
                if(window.confirm("WARNING: This will delete all favourites and cannot be undone")) {
                    if(window.confirm("Are you sure?")) {
                        localStorage.removeItem('rule34FavArtists');
                        console.log("removed all favorites");
                        alert("To confirm reset, reload the page");
                        return;
                    }
                }
                console.log('cancelled reset'); // this only executes if one of the confirms were cancelled because of the return
            }, false);
            delete_all_artists.innerHTML = 'Reset';
            space.insertBefore(delete_all_artists, mainPosts);
        }
        // this block just adds all the objects to the page before the "To main post page" link
    }
})();