Auto-retry for Doubledouble.top

Automatically retries downloads on doubledouble.top after 10 seconds if an error is encountered. Gives up after 15 retries. Also ensures the 'private' checkbox is checked. Based on 'Auto Retry for lucida.to' by cracktorio.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Auto-retry for Doubledouble.top
// @author       ap
// @namespace    https://github.com/appel/userscripts
// @version      0.1
// @description  Automatically retries downloads on doubledouble.top after 10 seconds if an error is encountered. Gives up after 15 retries. Also ensures the 'private' checkbox is checked. Based on 'Auto Retry for lucida.to' by cracktorio.
// @match        *://doubledouble.top/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    const MAX_RETRIES = 15; // Max retry attempts
    const RETRY_SECONDS = 10; // Configurable countdown time in seconds
    let retryCount = 0;
    let isRetryScheduled = false;
    let retriesHalted = false;
    let countdownIntervalId = null;

    const errorDivSelector = '#error';
    const downloadButtonSelector = '#dl-button';
    const privateCheckboxSelector = '#private';

    console.log(`Retry countdown set to ${RETRY_SECONDS} seconds.`);

    const bannerElement = document.createElement('div');
    const messageSpan = document.createElement('span');
    const cancelButton = document.createElement('button');

    Object.assign(bannerElement.style, {
        position: 'fixed',
        top: '0',
        left: '0',
        width: '100%',
        padding: '12px',
        backgroundColor: 'rgba(20, 20, 20, 0.9)',
        color: 'white',
        zIndex: '9999',
        fontFamily: 'sans-serif',
        fontSize: '16px',
        textAlign: 'center',
        display: 'none',
        boxSizing: 'border-box'
    });

    Object.assign(cancelButton.style, {
        marginLeft: '20px',
        padding: '5px 10px',
        border: '1px solid #ccc',
        borderRadius: '4px',
        backgroundColor: '#555',
        color: 'white',
        cursor: 'pointer'
    });
    cancelButton.textContent = 'Cancel retries';

    bannerElement.appendChild(messageSpan);
    bannerElement.appendChild(cancelButton);
    document.body.appendChild(bannerElement);

    cancelButton.addEventListener('click', () => {
        console.log("Retry cancelled by user.");
        clearInterval(countdownIntervalId);
        retriesHalted = true;
        isRetryScheduled = false;
        messageSpan.textContent = 'Auto-retry cancelled. Reload to try again.';
        bannerElement.style.color = '#FF4136'; // Red
        cancelButton.style.display = 'none';
    });


    const runTasks = () => {
        const privateCheckbox = document.querySelector(privateCheckboxSelector);
        if (privateCheckbox) {
            if (privateCheckbox.disabled) privateCheckbox.disabled = false;
            if (!privateCheckbox.checked) privateCheckbox.checked = true;
        }

        const isErrorVisible = document.querySelector(errorDivSelector) && window.getComputedStyle(document.querySelector(errorDivSelector)).display !== 'none';

        if (isErrorVisible) {
            if (isRetryScheduled || retriesHalted) return; // A retry is busy or has been stopped

            if (retryCount < MAX_RETRIES) {
                isRetryScheduled = true;
                retryCount++;
                let countdown = RETRY_SECONDS; // Use the configurable variable

                console.log(`Error detected. Starting ${RETRY_SECONDS}s countdown for retry #${retryCount}.`);
                messageSpan.textContent = `Error detected. Auto-retry attempt ${retryCount} of ${MAX_RETRIES} in ${countdown}...`;
                bannerElement.style.color = '#FFD700'; // Gold
                cancelButton.style.display = 'inline-block';
                bannerElement.style.display = 'block';

                countdownIntervalId = setInterval(() => {
                    countdown--;
                    messageSpan.textContent = `Error detected. Auto-retry attempt ${retryCount} of ${MAX_RETRIES} in ${countdown}...`;
                    if (countdown <= 0) {
                        clearInterval(countdownIntervalId);
                        console.log(`Executing retry attempt #${retryCount}.`);
                        document.querySelector(downloadButtonSelector)?.click();
                        isRetryScheduled = false;
                        bannerElement.style.display = 'none'; // Hide banner after click
                    }
                }, 1000);

            } else {
                retriesHalted = true;
                console.log("Max retry limit reached. Halting auto-retry.");
                messageSpan.textContent = `Max retry limit of ${MAX_RETRIES} was reached. Please intervene manually.`;
                bannerElement.style.color = '#FF4136';
                cancelButton.style.display = 'none';
                bannerElement.style.display = 'block';
            }

        } else {
            if (isRetryScheduled) {
                clearInterval(countdownIntervalId);
                isRetryScheduled = false;
            }
            if (retryCount > 0 || retriesHalted) {
                console.log("No error detected. Resetting retry state.");
                retryCount = 0;
                retriesHalted = false;
                bannerElement.style.display = 'none';
            }
        }
    };

    // Run the main task function every second to keep checking the page state.
    setInterval(runTasks, 1000);

})();