Gelbooru Favorites Checker

Highlights favorited images in the search results by checking without opening the images themselves.

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 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         Gelbooru Favorites Checker
// @namespace    https://gelbooru.com/
// @version      1.4
// @description  Highlights favorited images in the search results by checking without opening the images themselves. 
// @author       Du
// @match        https://gelbooru.com/index.php?page=post&s=list*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    const PARALLEL_BATCH_SIZE = 6;
    const BATCH_DELAY = 80; // ms zwischen Batches, damit Server nicht zu stark belastet wird

    console.log('[GFC] Gelbooru Favorite Checker gestartet.');

    // Warte, bis die Thumbnails da sind
    function waitForThumbnails(callback, retries = 20) {
        const images = document.querySelectorAll('a[href*="index.php?page=post&s=view&id="] > img');
        if (images.length > 0) {
            callback(images);
        } else if (retries > 0) {
            setTimeout(() => waitForThumbnails(callback, retries - 1), 500);
        } else {
            console.warn('[GFC] Keine Thumbnails gefunden – sogar nach Wartezeit.');
        }
    }

    // Einzelbildseite abrufen und prüfen, ob "Unfavorite" sichtbar ist
    function checkIfFavorited(imageId) {
        return fetch(`https://gelbooru.com/index.php?page=post&s=view&id=${imageId}`, {
            credentials: 'include'
        })
        .then(res => res.text())
        .then(html => {
            const doc = new DOMParser().parseFromString(html, 'text/html');
            const unfav = Array.from(doc.querySelectorAll('a')).find(a => a.textContent.trim() === 'Unfavorite');
            return !!unfav;
        })
        .catch(err => {
            console.error(`[GFC] Fehler bei Bild ${imageId}:`, err);
            return false;
        });
    }

    // Visualisierung am Thumbnail-Link
    function markAsFavorite(link) {
        const overlay = document.createElement('div');
        overlay.textContent = '❤';
        overlay.style.position = 'absolute';
        overlay.style.top = '3px';
        overlay.style.right = '3px';
        overlay.style.background = 'rgba(255, 105, 180, 0.8)';
        overlay.style.color = 'white';
        overlay.style.padding = '2px 6px';
        overlay.style.borderRadius = '6px';
        overlay.style.fontSize = '14px';
        overlay.style.zIndex = '999';
        overlay.style.pointerEvents = 'none';
        link.style.position = 'relative';
        link.appendChild(overlay);
    }

    // Prozesse in Batches von 4 gleichzeitig
    async function processInBatches(imageLinkPairs) {
        for (let i = 0; i < imageLinkPairs.length; i += PARALLEL_BATCH_SIZE) {
            const batch = imageLinkPairs.slice(i, i + PARALLEL_BATCH_SIZE);

            const promises = batch.map(async ({ id, link }) => {
                const isFav = await checkIfFavorited(id);
                if (isFav) {
                    console.log(`[GFC] Bild ${id} ist Favorit.`);
                    markAsFavorite(link);
                } else {
                    console.log(`[GFC] Bild ${id} ist nicht favorisiert.`);
                }
            });

            await Promise.all(promises);
            if (i + PARALLEL_BATCH_SIZE < imageLinkPairs.length) {
                await new Promise(resolve => setTimeout(resolve, BATCH_DELAY));
            }
        }
    }

    // Startvorgang
    waitForThumbnails((images) => {
        const links = Array.from(images).map(img => img.closest('a'));
        const imageLinkPairs = links
            .map(link => {
                const match = link.href.match(/id=(\d+)/);
                return match ? { id: match[1], link } : null;
            })
            .filter(Boolean);

        console.log(`[GFC] ${imageLinkPairs.length} Bilder gefunden. Prüfe Favoritenstatus in 4er-Gruppen...`);

        processInBatches(imageLinkPairs);
    });

})();