R34 batch favoriter

Mass favorites another user's favorites. Depends on this script: https://github.com/GoAwayNow/Rule34XXX-Enhanced-Favorites

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         R34 batch favoriter
// @namespace    http://tampermonkey.net/
// @version      2025-11-28
// @description  Mass favorites another user's favorites. Depends on this script: https://github.com/GoAwayNow/Rule34XXX-Enhanced-Favorites
// @author       jasper27
// @match        https://rule34.xxx/index.php?page=favorites&s=view&id=*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=rule34.xxx
// @grant        none
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // -- page helpers -- //

    function getPid() {
        const url = new URL(window.location.href);
        const val = url.searchParams.get('pid');
        return val !== null ? Number(val) : null;
    }

    function setPid(pid) {
        const url = new URL(window.location.href);
        url.searchParams.set('pid', pid);
        window.location.href = url.toString();
    }

    function getPage() {
        const pid = getPid();
        if (pid === null) {
            return 0;
        } else {
            return Math.floor(pid / 50);
        }
    }

    function setPage(page) {
        setPid(page * 50);
    }

    // -- storage helpers -- //

    const KEY_PREFIX = "R34_BATCH_FAVORITER_";

    function sessionSet(key, value) {
        sessionStorage.setItem(KEY_PREFIX + key, JSON.stringify(value));
    }

    function sessionGet(key, fallback = null) {
        const value = sessionStorage.getItem(KEY_PREFIX + key);
        if (value !== null) return JSON.parse(value);

        if (fallback !== null) sessionSet(key, fallback);
        return fallback;
    }

    function sessionRemove(key) {
        sessionStorage.removeItem(KEY_PREFIX + key);
    }

    function localSet(key, value) {
        localStorage.setItem(KEY_PREFIX + key, JSON.stringify(value));
    }

    function localGet(key, fallback = null) {
        const value = localStorage.getItem(KEY_PREFIX + key);
        if (value !== null) return JSON.parse(value);

        if (fallback !== null) localSet(key, fallback);
        return fallback;
    }

    function localRemove(key) {
        localStorage.removeItem(KEY_PREFIX + key);
    }

    // -- core logic -- //

    const TAB_ID = sessionGet('tabID', crypto.randomUUID());
    const FAVORITE_INTERVAL = 1000; // ms

    const currentPage = getPage();
    const toggleButton = createToggleButton();
    let favoriteLoopTimeout = null;

    function isTabActive() {
        return localGet('activeTabID') === TAB_ID;
    }

    function setTabActive() {
        localSet('activeTabID', TAB_ID);
    }

    function getFavoriteButtons() {
        return Array.from(document.querySelectorAll(".galFavBtn"))
            .filter(btn => getComputedStyle(btn).display !== "none") // filter out hidden buttons
            .filter(btn => btn.textContent.trim().toLowerCase().startsWith('add')) // filter out remove buttons
            .reverse(); // buttons should be pressed last to first to preserve order
    }

    function getNextFavoriteButton() {
        return getFavoriteButtons()[0];
    }

    function createToggleButton() {
        const toggleButton = document.createElement('button');
        toggleButton.textContent = "Loading..";
        toggleButton.style.background = "grey";
        toggleButton.style.color = "white";
        toggleButton.style.border = "none";
        toggleButton.style.padding = "10px 16px";
        toggleButton.style.cursor = "pointer";
        toggleButton.style.borderRadius = "8px";

        toggleButton.style.position = "fixed";
        toggleButton.style.top = "12px";
        toggleButton.style.right = "12px";
        toggleButton.style.zIndex = "9999";

        toggleButton.addEventListener('click', onToggleButtonClicked);
        document.body.appendChild(toggleButton);

        return toggleButton;
    }

    function updateToggleButton() {
        if (isTabActive()) {
            toggleButton.style.background = "#d16319";
            toggleButton.textContent = "Stop favoriting";
        } else {
            toggleButton.style.background = "#2ab8db";
            toggleButton.textContent = "Start favoriting";
        }
    };

    function onToggleButtonClicked() {
        if (isTabActive()) {
            stopFavoriteLoop();
        } else {
            resumeFavoriteLoop();
        }
        updateToggleButton();
    }

    function gotoPrevPage() {
        if (currentPage === 0) {
            console.log("Finished first page, stopping");
            stopFavoriteLoop();
        } else {
            console.log("Finished page, going to previous page");
            sessionSet('switchingPages', true);
            setPage(currentPage - 1);
        }
    }

    function stopFavoriteLoop() {
        clearTimeout(favoriteLoopTimeout);
        if (isTabActive()) {
            localRemove('activeTabID');
        }
        favoriteLoopTimeout = null;
        updateToggleButton();
    }

    function resumeFavoriteLoop() {
        clearTimeout(favoriteLoopTimeout);
        setTabActive();
        favoriteLoopTimeout = setTimeout(stepFavoriteLoop, FAVORITE_INTERVAL);
    }

    function stepFavoriteLoop() {
        if (!isTabActive()) {
            stopFavoriteLoop();
            return;
        };

        updateToggleButton();
        const favButton = getNextFavoriteButton();
        if (favButton) {
            favButton.click();
            resumeFavoriteLoop();
        } else {
            gotoPrevPage();
        }
    }

    function onPageOpened() {
        updateToggleButton();
        if (isTabActive() && sessionGet('switchingPages') === true) {
            console.log('Resuming favorite loop');
            sessionRemove('switchingPages');
            resumeFavoriteLoop();
        } else {
            stopFavoriteLoop();
        }
    }

    onPageOpened();
})();