// ==UserScript== // @name 拷貝漫畫 // @namespace http://tampermonkey.net/ // @version 14.0 // @description 清理符號,檢查連結訪問狀態,開啟未訪問的連結,並在章節頁面自動處理 // @match https://mangacopy.com/comic/* // @grant none // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/531564/%E6%8B%B7%E8%B2%9D%E6%BC%AB%E7%95%AB.user.js // @updateURL https://update.greasyfork.icu/scripts/531564/%E6%8B%B7%E8%B2%9D%E6%BC%AB%E7%95%AB.meta.js // ==/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);// 延遲執行以避免頁面未完全加載 } })();