Greasy Fork

Greasy Fork is available in English.

拷貝漫畫

清理符號,檢查連結訪問狀態,開啟未訪問的連結,並在章節頁面自動處理

// ==UserScript==
// @name         拷貝漫畫
// @namespace    http://tampermonkey.net/
// @version      14.0
// @description  清理符號,檢查連結訪問狀態,開啟未訪問的連結,並在章節頁面自動處理
// @match        https://mangacopy.com/comic/*
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';
    const visitedPagesKey = 'visitedPages';
    const listPrefix = 'unvisitedLinks_';
    const queueKey = 'processingQueue';
    const scriptEnabledKey = 'scriptEnabled';
    const scriptDisabledKey = 'copyMangaCleanerDisabled';

    const isChapterPage = window.location.href.includes('/chapter/');

    function showStatus(message) {
        let statusBar = document.getElementById('status-bar');
        if (!statusBar) {
            statusBar = document.createElement('div');
            statusBar.id = 'status-bar';
            statusBar.style.position = 'fixed';
            statusBar.style.top = '10px';
            statusBar.style.left = '10px';
            statusBar.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
            statusBar.style.color = 'white';
            statusBar.style.padding = '10px';
            statusBar.style.borderRadius = '5px';
            statusBar.style.zIndex = '9999';
            document.body.appendChild(statusBar);
        }
        statusBar.textContent = message;
    }

    async function sha256(message) {
        try {
            const msgBuffer = new TextEncoder().encode(message);
            const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
            return hashBuffer;
        } catch (error) {
            console.error('哈希生成失敗:', error);
            return null;
        }
    }

    function arrayBufferToBase64(buffer) {
        let binary = '';
        const bytes = new Uint8Array(buffer);
        for (let i = 0; i < bytes.byteLength; i++) {
            binary += String.fromCharCode(bytes[i]);
        }
        return btoa(binary);
    }

    if (!isChapterPage) {
        let scriptEnabled = localStorage.getItem(scriptEnabledKey) !== 'false'; // 默認啟用

        function cleanLinks() {
            const links = document.querySelectorAll('a[href*="/chapter/"]:not([href*="#"])');
            const visitedPages = new Set(JSON.parse(localStorage.getItem(visitedPagesKey) || '[]'));

            links.forEach(async link => {
                const originalHref = link.href;
                link.href = link.href.replace(/-(?=[^/]*$)/g, '');

                const hashBuffer = await sha256(link.href);
                if (!hashBuffer) return;

                const hashBase64 = arrayBufferToBase64(hashBuffer);
                if (visitedPages.has(hashBase64)) {
                    link.style.color = 'red'; // 已訪問的連結顯示為紅色
                } else {
                    link.style.color = 'green'; // 未訪問的連結顯示為綠色
                }
            });
        }

        async function checkAndStoreUnvisitedLinks() {
            if (!scriptEnabled) return;

            const currentPageUrl = window.location.href;
            const listKey = listPrefix + currentPageUrl;
            const queue = JSON.parse(localStorage.getItem(queueKey) || '[]');
            const unvisitedLinks = JSON.parse(localStorage.getItem(listKey) || '[]');

            if (queue.includes(listKey) && unvisitedLinks.length === 0) {
                localStorage.removeItem(listKey);
                const updatedQueue = queue.filter(item => item !== listKey);
                localStorage.setItem(queueKey, JSON.stringify(updatedQueue));
                showStatus('清單已清理');
                return;
            }

            if (!queue.includes(listKey)) {
                const visitedPages = new Set(JSON.parse(localStorage.getItem(visitedPagesKey) || '[]'));
                const links = Array.from(document.querySelectorAll('a[href*="/chapter/"]:not([href*="#"])'));
                const newUnvisitedLinks = [];

                let firstUnvisitedLinkFound = false;

                for (const link of links) {
                    const hashBuffer = await sha256(link.href);
                    if (!hashBuffer) continue;

                    const hashBase64 = arrayBufferToBase64(hashBuffer);
                    if (!visitedPages.has(hashBase64)) {
                        if (!firstUnvisitedLinkFound) {
                            newUnvisitedLinks.push(link.href);
                            firstUnvisitedLinkFound = true;
                        }
                        visitedPages.add(hashBase64);
                    }
                }

                if (firstUnvisitedLinkFound) {
                    newUnvisitedLinks.push(currentPageUrl);
                }

                if (newUnvisitedLinks.length > 0) {
                    localStorage.setItem(listKey, JSON.stringify(newUnvisitedLinks));
                    localStorage.setItem(visitedPagesKey, JSON.stringify([...visitedPages]));
                    queue.push(listKey);
                    localStorage.setItem(queueKey, JSON.stringify(queue));
                    showStatus('已加入隊列,等待10秒...');
                    await new Promise(resolve => setTimeout(resolve, 10000));
                } else {
                    return;
                }
            }

            if (queue[0] === listKey) {
                const storedLinks = JSON.parse(localStorage.getItem(listKey) || '[]');

                if (storedLinks.length === 0) {
                    localStorage.removeItem(listKey);
                    const updatedQueue = queue.filter(item => item !== listKey);
                    localStorage.setItem(queueKey, JSON.stringify(updatedQueue));
                    showStatus('清單已清理');
                    return;
                }

                if (storedLinks[0] === currentPageUrl) {
                    localStorage.removeItem(listKey);
                    const updatedQueue = queue.filter(item => item !== listKey);
                    localStorage.setItem(queueKey, JSON.stringify(updatedQueue));
                    showStatus('沒有更新');
                    return;
                }

                if (storedLinks.length > 0 && storedLinks[0]) {
                    showStatus('正在處理連結...');
                    setTimeout(() => {
                        window.location.href = storedLinks[0];
                    }, 100);
                } else {
                    showStatus('錯誤:無有效連結');
                }
            } else {
                showStatus('正在排隊中...');
                setTimeout(checkAndStoreUnvisitedLinks, 1000);
            }
        }

        function addClearHistoryButton() {
            const clearButton = document.createElement('button');
            clearButton.textContent = '清除歷史';
            clearButton.style.position = 'fixed';
            clearButton.style.top = '60px';
            clearButton.style.left = '10px';
            clearButton.style.zIndex = '9999';
            clearButton.style.padding = '5px 10px';
            clearButton.style.backgroundColor = '#ff4444';
            clearButton.style.color = 'white';
            clearButton.style.border = 'none';
            clearButton.style.borderRadius = '5px';
            clearButton.style.cursor = 'pointer';

            clearButton.addEventListener('click', async () => {
                const links = Array.from(document.querySelectorAll('a[href*="/chapter/"]:not([href*="#"])'));
                const visitedPages = new Set(JSON.parse(localStorage.getItem(visitedPagesKey) || '[]'));
                await Promise.all(links.map(async link => {
                    const hashBuffer = await sha256(link.href);
                    if (!hashBuffer) return;

                    const hashBase64 = arrayBufferToBase64(hashBuffer);
                    if (visitedPages.has(hashBase64)) {
                        visitedPages.delete(hashBase64);
                    }
                }));

                localStorage.setItem(visitedPagesKey, JSON.stringify([...visitedPages]));
                showStatus('已清除當前頁面的連結記錄!');
            });

            document.body.appendChild(clearButton);
        }

        function addClearQueueButton() {
            const clearButton = document.createElement('button');
            clearButton.textContent = '清除隊列';
            clearButton.style.position = 'fixed';
            clearButton.style.top = '60px';
            clearButton.style.left = '100px';
            clearButton.style.zIndex = '9999';
            clearButton.style.padding = '5px 10px';
            clearButton.style.backgroundColor = '#ffaa44';
            clearButton.style.color = 'white';
            clearButton.style.border = 'none';
            clearButton.style.borderRadius = '5px';
            clearButton.style.cursor = 'pointer';

            clearButton.addEventListener('click', () => {
                const listKey = listPrefix + window.location.href.replace(/\/chapter\/.*/, '');
                const queue = JSON.parse(localStorage.getItem(queueKey) || '[]');

                localStorage.removeItem(listKey);
                const updatedQueue = queue.filter(item => item !== listKey);
                localStorage.setItem(queueKey, JSON.stringify(updatedQueue));
                showStatus('已清除當前隊列!');
            });

            document.body.appendChild(clearButton);
        }

        function addToggleButton() {
            const toggleButton = document.createElement('button');
            toggleButton.textContent = scriptEnabled ? '停用' : '啟用';
            toggleButton.style.position = 'fixed';
            toggleButton.style.top = '60px';
            toggleButton.style.left = '190px';
            toggleButton.style.zIndex = '9999';
            toggleButton.style.padding = '5px 10px';
            toggleButton.style.backgroundColor = scriptEnabled ? '#ff4444' : '#44aa44';
            toggleButton.style.color = 'white';
            toggleButton.style.border = 'none';
            toggleButton.style.borderRadius = '5px';
            toggleButton.style.cursor = 'pointer';

            toggleButton.addEventListener('click', () => {
                scriptEnabled = !scriptEnabled;
                localStorage.setItem(scriptEnabledKey, scriptEnabled);
                toggleButton.textContent = scriptEnabled ? '停用' : '啟用';
                toggleButton.style.backgroundColor = scriptEnabled ? '#ff4444' : '#44aa44';
                showStatus(scriptEnabled ? '腳本已啟用' : '腳本已停用');

                if (scriptEnabled) {
                    checkAndStoreUnvisitedLinks();
                }
            });

            document.body.appendChild(toggleButton);
        }

        function addDisableLinksButton() {
            const disableButton = document.createElement('button');
            disableButton.textContent = '停用連結';
            disableButton.style.position = 'fixed';
            disableButton.style.top = '60px';
            disableButton.style.left = '280px';
            disableButton.style.zIndex = '9999';
            disableButton.style.padding = '5px 10px';
            disableButton.style.backgroundColor = '#4444ff';
            disableButton.style.color = 'white';
            disableButton.style.border = 'none';
            disableButton.style.borderRadius = '5px';
            disableButton.style.cursor = 'pointer';
            disableButton.addEventListener('click', () => {
                const newState = localStorage.getItem(scriptDisabledKey) !== 'true';
                localStorage.setItem(scriptDisabledKey, newState.toString());
                showStatus(newState ? '章節頁面腳本已停用' : '章節頁面腳本已啟用');
            });

            document.body.appendChild(disableButton);
        }

        function runScript() {
            cleanLinks();
            addClearHistoryButton();
            addClearQueueButton();
            addToggleButton();
            addDisableLinksButton();

            if (scriptEnabled) {
                checkAndStoreUnvisitedLinks();
            } else {
                showStatus('腳本當前已停用');
            }
        }
        setTimeout(runScript, 1500);// 延遲執行以避免頁面未完全加載
    }

    if (isChapterPage) {
        function getUnvisitedLinks() {
            const listKey = listPrefix + window.location.href.replace(/\/chapter\/.*/, '');
            const unvisitedLinks = JSON.parse(localStorage.getItem(listKey) || '[]');
            return unvisitedLinks;
        }

        function openNextLink(unvisitedLinks) {
            if (unvisitedLinks.length > 0) {
                const nextLink = unvisitedLinks.shift();
                const listKey = listPrefix + window.location.href.replace(/\/chapter\/.*/, '');
                localStorage.setItem(listKey, JSON.stringify(unvisitedLinks));

                if (unvisitedLinks.length === 0) {
                    const parentUrl = window.location.href.replace(/\/chapter\/.*/, '');
                    showStatus('所有連結已處理完畢,返回目錄頁。');
                    setTimeout(() => {
                        window.location.href = parentUrl;
                    }, 200);
                } else {
                    showStatus('正在開啟下一個連結...');
                    setTimeout(() => {
                        window.location.href = nextLink;
                    }, 200);
                }
            }
        }

        function runScript() {
            const isDisabled = localStorage.getItem(scriptDisabledKey) === 'true';

            if (!isDisabled) {
                const unvisitedLinks = getUnvisitedLinks();
                if (unvisitedLinks.length > 0) {
                    openNextLink(unvisitedLinks);
                } else {
                    const parentUrl = window.location.href.replace(/\/chapter\/.*/, '');
                    showStatus('沒有未訪問的連結,返回目錄頁。');
                    setTimeout(() => {
                        window.location.href = parentUrl;
                    }, 200);
                }
            } else {
                showStatus('腳本當前已停用');
            }
        }
        setTimeout(runScript, 500);// 延遲執行以避免頁面未完全加載
    }
})();