// ==UserScript== // @name copymanga-自动存储浏览记录 // @namespace http://tampermonkey.net/ // @description 自动存储拷贝漫画的浏览记录,以防拷贝卷记录跑路。同时书架可以显示上次观看章节。 // @version 1.3.1 // @author Y_jun // @license MIT // @icon https://hi77-overseas.mangafuna.xyz/static/free.ico // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @grant GM_xmlhttpRequest // @match *://*.copymanga.com/* // @match *://*.copymanga.org/* // @match *://*.copymanga.net/* // @match *://*.copymanga.info/* // @match *://*.copymanga.site/* // @match *://*.copymanga.tv/* // @match *://*.mangacopy.com/* // @match *://copymanga.com/* // @match *://copymanga.org/* // @match *://copymanga.net/* // @match *://copymanga.info/* // @match *://copymanga.site/* // @match *://copymanga.tv/* // @match *://mangacopy.com/* // @run-at document-start // @downloadURL none // ==/UserScript== let token; function sleep(time) { return new Promise((resolve) => setTimeout(resolve, time)); } function completeDate(value) { return value < 10 ? "0" + value : value; } function getNowFormatTime() { let nowDate = new Date(); let colon = ":"; let char = "-"; let day = nowDate.getDate(); let month = nowDate.getMonth() + 1;//注意月份需要+1 let year = nowDate.getFullYear(); let h = nowDate.getHours(); let m = nowDate.getMinutes(); let s = nowDate.getSeconds(); //补全0,并拼接 return year + char + completeDate(month) + char + completeDate(day) + " " + completeDate(h) + colon + completeDate(m) + colon + completeDate(s); } function addNotice() { let button = document.createElement('button'); button.id = 'save-button'; button.style.marginLeft = '20px'; button.textContent = '开始保存完整记录'; button.onclick = () => { button.className = 'allow-save-liulan'; } let notice = document.createElement('span'); notice.id = 'save-liulan'; notice.style.marginLeft = '20px'; notice.textContent = '尚未保存浏览记录'; let collectActionArea = document.querySelector('.collectAction'); collectActionArea.appendChild(button) collectActionArea.appendChild(notice); } function editNotice(text) { let notice = document.getElementById('save-liulan'); notice.textContent = text; } function getPopularNum(popularStr) { if (popularStr.indexOf('W') > -1) { return Number(popularStr.substring(0, popularStr.length - 1)) * 10000; } if (popularStr.indexOf('K') > -1) { return Number(popularStr.substring(0, popularStr.length - 1)) * 1000; } return Number(popularStr); } function deleteAllValues() { const keys = GM_listValues(); keys.forEach(key => { GM_deleteValue(key); }) } async function getLiulanList() { while (!document.querySelector('.allow-save-liulan')) { await sleep(2000); } editNotice('正在删除旧记录……'); deleteAllValues(); let offset = 0; let limit = 25; let lastIndex = 1; const datetime = getNowFormatTime(); let totalStr = document.querySelector('.demonstration').innerText; let total = Number(totalStr.substring(3, totalStr.length - 2)); while (offset < total) { editNotice('保存浏览记录中,进度:' + Math.round(offset / total * 10000) / 100 + "%"); GM_xmlhttpRequest({ method: "get", url: `${window.location.origin}/api/kb/web/browses?limit=${limit}&offset=${offset}&free_type=1`, data: "", headers: { "Content-Type": "application/json", "Authorization": token, "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.160 Safari/537.36" }, onload: res => { if (res.status === 200) { const response = JSON.parse(res.response); if (response.code === 200) { const results = response.results; const mangaList = results.list; mangaList.forEach((manga) => { const authors = []; if (Array.isArray(manga.comic.author)) { const authorList = manga.comic.author; authorList.forEach((author) => { authors.push(author.name); }) } const mangaObj = { "name": manga.comic.name, "uuid": manga.comic.uuid, "path": manga.comic.path_word, "lastRead": manga.last_chapter_name, "lastUuid": manga.last_chapter_id, "lastIndex": lastIndex, "lastTime": datetime, "latestChapter": manga.comic.last_chapter_name, "latestTime": manga.comic.datetime_updated, "popular": manga.comic.popular, "authors": authors } lastIndex++; GM_setValue(manga.comic.path_word, mangaObj); }); } else { editNotice('保存浏览记录出错'); console.log('code不为200:\n' + res); total = -1; } } else { editNotice('保存浏览记录出错'); console.log('status不为200:\n' + res); total = -1; } }, onerror: () => { editNotice('保存浏览记录出错'); console.log('读取浏览记录失败'); total = -1; } }); offset += limit; await sleep(2000); } editNotice('保存完毕'); } function getLastRead(path, count = 1) { if (document.querySelector('.table-default') === null) { if (count <= 50) { const args = Array.from(arguments).slice(0, arguments.length); args.push(count + 1); setTimeout(getLastRead, 200, ...args); } return; } const name = document.querySelector('h6').textContent; const updateArr = document.querySelector('.table-default-right').textContent.split('更新'); const latestChapter = updateArr[1].substring(3); const updateTime = updateArr[2].substring(3); const authors = document.querySelectorAll('.comicParticulars-right-txt')[1].innerHTML.match(/>[^<]+<\/a>/g); for (let i = 0; i < authors.length; i++) { const author = authors[i]; authors[i] = author.substring(1, author.length - 4); } const popularStr = document.querySelectorAll('.comicParticulars-right-txt')[2].innerText; const popular = getPopularNum(popularStr); GM_xmlhttpRequest({ method: "get", url: `${window.location.origin}/api/v3/comic2/${path}/query?platform=1&_update=true`, data: "", headers: { "Content-Type": "application/json", "Authorization": token, "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.160 Safari/537.36" }, onload: res => { if (res.status === 200) { const response = JSON.parse(res.response); if (response.code === 200) { const results = response.results; if (results.browse) { const mangaObj = { "name": name, "uuid": results.browse.comic_uuid, "path": path, "lastRead": results.browse.chapter_name, "lastUuid": results.browse.chapter_uuid, "lastIndex": 0, "lastTime": getNowFormatTime(), "latestChapter": latestChapter, "latestTime": updateTime, "popular": popular, "authors": authors } GM_setValue(path, mangaObj); } } else { console.log('code不为200:\n' + res); } } else { console.log('status不为200:\n' + res); } }, onerror: () => { console.log('读取最近阅读失败'); } }); } function setCurrentRead(path, lastUuid, count = 1) { let storageManga = GM_getValue(path); if (!storageManga) return; if (document.querySelector('h4.header') === null) { if (count <= 50) { const args = Array.from(arguments).slice(0, arguments.length); args.push(count + 1); setTimeout(getLastRead, 200, ...args); } return; } let StrArr = document.querySelector('h4.header').innerText.split('/'); storageManga.lastRead = StrArr[1]; storageManga.lastUuid = lastUuid; storageManga.lastTime = getNowFormatTime(); GM_setValue(path, storageManga); } window.onload = () => { token = 'Token ' + document.cookie.split('; ').find((cookie) => cookie.startsWith('token='))?.replace('token=', ''); const pathArr = window.location.pathname.split('/'); if (window.location.pathname === '/web/person/liulan') { addNotice(); getLiulanList(); } else if (pathArr.length === 3 && pathArr[1] === 'comic') { document.addEventListener('visibilitychange', () => { getLastRead(pathArr[2].replace('#', '')); }); } else if (pathArr.length === 5 && pathArr[3] === 'chapter') { setCurrentRead(pathArr[2], pathArr[4]); } } setInterval(() => { const pathArr = window.location.pathname.split('/'); if (pathArr.length < 4 || pathArr[2] !== 'person') { return; } const barClass = document.querySelector('.el-menu').querySelectorAll('li')[1].className; if (barClass.indexOf('is-active') < 0) { return; } const main = document.querySelector('.man_'); Array.from(main.children).forEach((child, index) => { if (child.className.indexOf('is-injected') < 0) { child.style.position = 'relative'; const path = child.firstChild.href.split('/')[4]; const storageJson = GM_getValue(path, null); let lastRead; let lastUuid; if (storageJson) { lastRead = storageJson.lastRead; lastUuid = storageJson.lastUuid; } const lastP = child.querySelector(`[id='${path}']`); if (lastP) child.removeChild(lastP); const p = document.createElement('p'); p.id = path; p.innerHTML = lastUuid ? `上次阅读: ${lastRead}` : '还没看过'; p.style.width = '100%'; p.style.position = 'absolute'; p.style.bottom = '10px'; child.appendChild(p); child.classList.add('is-injected'); } }) }, 1000);