拷貝漫畫

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

// ==UserScript==
// @name         拷貝漫畫
// @namespace    http://tampermonkey.net/
// @version      15.7
// @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 processingLockKey = 'processingLock';
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;
    let queue = JSON.parse(localStorage.getItem(queueKey) || '[]');
    const unvisitedLinks = JSON.parse(localStorage.getItem(listKey) || '[]');
    if (queue.includes(listKey) && unvisitedLinks.length === 0) {
        localStorage.removeItem(listKey);
        queue = queue.filter(item => item !== listKey);
        localStorage.setItem(queueKey, JSON.stringify(queue));
        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 = [];
        const allUnvisited = [];
        for (const link of links) {
            const hashBuffer = await sha256(link.href);
            if (!hashBuffer) continue;
            const hashBase64 = arrayBufferToBase64(hashBuffer);
            if (!visitedPages.has(hashBase64)) {
                allUnvisited.push(link.href);
                visitedPages.add(hashBase64);
            }
        }
        if (allUnvisited.length > 0) {
            const targetLink = allUnvisited.length >= 2 ? allUnvisited[1] : allUnvisited[0];
            newUnvisitedLinks.push(targetLink);
            newUnvisitedLinks.push(currentPageUrl);
            localStorage.setItem(listKey, JSON.stringify(newUnvisitedLinks));
            localStorage.setItem(visitedPagesKey, JSON.stringify([...visitedPages]));
            queue.push(listKey);
            localStorage.setItem(queueKey, JSON.stringify(queue));
            showStatus('已加入隊列,等待3秒...');
            await new Promise(resolve => setTimeout(resolve, 3000));
        } else {
            return;
        }
    }
    const now = Date.now();
    const lock = JSON.parse(localStorage.getItem(processingLockKey) || 'null');
    if (lock && now - lock.timestamp < 5000) {
        showStatus('等待其他分頁處理完成...');
        setTimeout(checkAndStoreUnvisitedLinks, 1000);
        return;
    }
    if (queue[0] === listKey) {
        localStorage.setItem(processingLockKey, JSON.stringify({
            timestamp: now,
            listKey: listKey
        }));
        const storedLinks = JSON.parse(localStorage.getItem(listKey) || '[]');
        if (storedLinks.length === 0) {
            localStorage.removeItem(listKey);
            queue = queue.filter(item => item !== listKey);
            localStorage.setItem(queueKey, JSON.stringify(queue));
            localStorage.removeItem(processingLockKey);
            showStatus('清單已清理');
            return;
        }
        if (storedLinks[0] === currentPageUrl) {
            localStorage.removeItem(listKey);
            queue = queue.filter(item => item !== listKey);
            localStorage.setItem(queueKey, JSON.stringify(queue));
            localStorage.removeItem(processingLockKey);
            showStatus('沒有更新');
            return;
        }
        if (storedLinks.length > 0 && storedLinks[0]) {
            showStatus('正在處理連結...');
            setTimeout(() => {
                window.location.href = storedLinks[0];
            }, 100);
        } else {
            showStatus('錯誤:無有效連結');
            localStorage.removeItem(processingLockKey);
        }
    } 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\/.*/, '');
return JSON.parse(localStorage.getItem(listKey) || '[]');
}
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, 1000);
}
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址