Greasy Fork

Greasy Fork is available in English.

拷貝漫畫

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

当前为 2025-05-16 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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);// 延遲執行以避免頁面未完全加載
    }
})();