diff --git a/package.json b/package.json index 4b986ee..efb1939 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "psnine-enhanced-version", - "version": "1.0.26", + "version": "1.0.27", "description": "数折价格走势图,显示人民币价格,奖杯统计和筛选,发帖字数统计和即时预览,楼主高亮,自动翻页,屏蔽黑名单用户发言,被@用户的发言内容显示等多项功能优化P9体验", "main": "night-mode-css.js", "scripts": { @@ -22,4 +22,4 @@ "eslint-config-airbnb-base": "^14.2.1", "eslint-plugin-import": "^2.22.1" } -} +} \ No newline at end of file diff --git a/psnineplus.js b/psnineplus.js index cbdd68a..159dcde 100644 --- a/psnineplus.js +++ b/psnineplus.js @@ -1,7 +1,7 @@ // ==UserScript== // @name PSN中文网功能增强 // @namespace https://swsoyee.github.io -// @version 1.0.26 +// @version 1.0.27 // @description 数折价格走势图,显示人民币价格,奖杯统计和筛选,发帖字数统计和即时预览,楼主高亮,自动翻页,屏蔽黑名单用户发言,被@用户的发言内容显示等多项功能优化P9体验 // eslint-disable-next-line max-len // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAMFBMVEVHcEw0mNs0mNs0mNs0mNs0mNs0mNs0mNs0mNs0mNs0mNs0mNs0mNs0mNs0mNs0mNuEOyNSAAAAD3RSTlMAQMAQ4PCApCBQcDBg0JD74B98AAABN0lEQVRIx+2WQRaDIAxECSACWLn/bdsCIkNQ2XXT2bTyHEx+glGIv4STU3KNRccp6dNh4qTM4VDLrGVRxbLGaa3ZQSVQulVJl5JFlh3cLdNyk/xe2IXz4DqYLhZ4mWtHd4/SLY/QQwKmWmGcmUfHb4O1mu8BIPGw4Hg1TEvySQGWoBcItgxndmsbhtJd6baukIKnt525W4anygNECVc1UD8uVbRNbumZNl6UmkagHeRJfX0BdM5NXgA+ZKESpiJ9tRFftZEvue2cS6cKOrGk/IOLTLUcaXuZHrZDq3FB2IonOBCHIy8Bs1Zzo1MxVH+m8fQ+nFeCQM3MWwEsWsy8e8Di7meA5Bb5MDYCt4SnUbP3lv1xOuWuOi3j5kJ5tPiZKahbi54anNRaaG7YElFKQBHR/9PjN3oD6fkt9WKF9rgAAAAASUVORK5CYII= @@ -402,7 +402,7 @@ $('body,html').animate({ scrollTop: document.body.clientHeight, }, - 500); + 500); }).css({ cursor: 'pointer', }); @@ -611,10 +611,8 @@ // 页面加载时执行约战监测功能 if (cacheBattleInfo.lastUpdate && new Date().getTime() - cacheBattleInfo.lastUpdate < settings.BattleInfoUpdateInterval) { updateTopMenuNotice(userBattleMonitors, cacheBattleInfo.list); - console.log('use cached BattleRecuritInfo'); } else { updateBattleRecuritInfo(); - console.log('update BattleRecuritInfo'); } // 在游戏的约战页面添加约战监控按钮 @@ -718,55 +716,159 @@ ); /* - 在 LocatStorage 中保存个人游戏完成度函数 - 添加于 /psnid\/[A-Za-z0-9_-]+\/?$/ 页面,以及该页自动翻页函数内部 - */ + 在 LocatStorage 中保存个人游戏完成度函数,为避免过多的页面重复请求,逻辑梳理如下: + + 场景: + 1. 更新的奖杯一定在前,但用户可能会隐藏游戏或解除隐藏,导致内容不再确定。 + 2. 隐藏游戏条目不影响本地已有数据,也不要求本地数据作对应删除,但影响页码数和对应的更新记录 + 3. 由于设置为 Ajax 5 秒翻页,所以存在前几页数据已更新,后几页数据因为用户关闭页面而未更新的情况 + 4. 用户新开坑,可能导致页面数量增长,此时,最后几页也未记录更新时间,但实际是不需要更新的 + + 简化: + 1. 没有时间记录的页面,都需要更新,有时间记录的页面,从前往后更新,遇到无变化奖杯条目的就停止 + 2. 当用户进行异常操作时,需要自行通过翻页刷新数据 + */ + + // GM_setValue('personalGameCompletions', []); + // console.log(GM_getValue('personalGameCompletions', [])); + // GM_setValue('personalGameCompletionsLastUpdate', []); + + const parseCompletionPage = (content) => { + // 游戏进度信息 + const tdElements = content.querySelectorAll('table.list tbody > tr'); + const thisPageCompletions = Array.from(tdElements).map((tr) => { + const completionElement = tr.querySelector('div.progress > div'); + const completion = completionElement ? parseFloat(completionElement.textContent) : 0; + const platinumElement = tr.querySelector('span.text-platinum'); + const platinum = platinumElement ? platinumElement.textContent === '白1' : false; + const gameIDElement = tr.querySelector('a'); + const gameID = gameIDElement.href.match(/\/psngame\/(\d+)/)[1]; + return [gameID, completion, platinum]; + }); - const savePersonalGameCompletions = (configifneeded) => { - // if GM_setValue && GM_getValue is enabled - const thisFeatureEnabled = (configifneeded || true) && (typeof GM_setValue === 'function' && typeof GM_getValue === 'function'); - - if (thisFeatureEnabled) { - // 获得当前页的游戏完成度 - const tdElements = document.querySelectorAll('table.list tbody > tr'); - const personalGameCompletions = Array.from(tdElements).map((tr) => { - const completionElement = tr.querySelector('div.progress > div'); - const completion = completionElement ? parseFloat(completionElement.textContent) : 0; - const platinumElement = tr.querySelector('span.text-platinum'); - const platinum = platinumElement ? platinumElement.textContent === '白1' : false; - const gameIDElement = tr.querySelector('a'); - const gameID = gameIDElement.href.match(/\/psngame\/(\d+)/)[1]; - return [gameID, completion, platinum]; - }); + // 获得总页数和总条目数 + let totalPages = 0; let + totalItems = 0; + const PaginationEle = content.querySelectorAll('.page > ul > li > a'); + if (PaginationEle.length > 2) { + totalPages = parseInt(PaginationEle[PaginationEle.length - 2].innerText, 10); + totalItems = parseInt(PaginationEle[PaginationEle.length - 1].innerText, 10); + } + + return { totalPages, totalItems, thisPageCompletions }; + }; - // 读取已保存的历史 - const history = GM_getValue('personalGameCompletions', []); + const updateCompletions = (updateList) => { + const gameCompletionHistory = GM_getValue('personalGameCompletions', []); + let addCounts = 0; - // 用当前覆盖历史 - personalGameCompletions.forEach((currentItem) => { - const index = history.findIndex((historyItem) => historyItem[0] === currentItem[0]); - if (index !== -1) { - history[index] = currentItem; - } else { - history.push(currentItem); + updateList.forEach((completion) => { + const index = gameCompletionHistory.findIndex((historyItem) => historyItem[0] === completion[0]); + if (index !== -1) { + if (gameCompletionHistory[index][1] !== completion[1]) { addCounts += 1; } + gameCompletionHistory[index] = completion; + } else { + gameCompletionHistory.push(completion); + addCounts += 1; + } + }); + + GM_setValue('personalGameCompletions', gameCompletionHistory); + const totalRecords = gameCompletionHistory.length; + return { addCounts, totalRecords }; + }; + + // 后台更新主函数 + const loadGameCompletions = (userid, startPageID) => { + // console.log(`https://psnine.com/psnid/${userid}/psngame?page=${startPageID}`) + $.ajax({ + type: 'GET', + url: `https://psnine.com/psnid/${userid}/psngame?page=${startPageID}`, + dataType: 'html', + async: true, + success: (data, status) => { + if (status === 'success') { + // 读取历史数据 + const pagesUpdateTime = GM_getValue('personalGameCompletionsLastUpdate', []); + // 2024.07.30 bug fix: 错误地保存他人的游戏完成度 - 已经修复,但用户端的旧数据需要清除 + if (Array.isArray(pagesUpdateTime) === false + || pagesUpdateTime[0] === undefined + || pagesUpdateTime[0] < 1722333600000 // 2024-07-30 18:00 GMT+0800 + ) { + GM_setValue('personalGameCompletions', []); + pagesUpdateTime = []; + } + + // 读取当前页奖杯完成数据 + const page = document.createElement('html'); + page.innerHTML = data; + const o = parseCompletionPage(page); + const { thisPageCompletions } = o; + const totalPages = o.totalPages || pagesUpdateTime.length || 1; + const { addCounts } = updateCompletions(thisPageCompletions); + + // 更新页面记录时间 + pagesUpdateTime[startPageID - 1] = new Date().getTime(); + GM_setValue('personalGameCompletionsLastUpdate', pagesUpdateTime); + + // 根据规则计算下一页 + if (addCounts === thisPageCompletions.length && startPageID < totalPages - 1) { + setTimeout(() => { loadGameCompletions(userid, startPageID + 1); }, 5000); + return true; + } + const fullfilledUpdateTime = pagesUpdateTime.concat(Array(totalPages - pagesUpdateTime.length).fill(0)); + const nextIdx = fullfilledUpdateTime.findIndex((time) => time === undefined || time === 0 || time === null); + if (nextIdx !== -1) { + setTimeout(() => { loadGameCompletions(userid, nextIdx + 1); }, 5000); + return true; + } + return false; } - }); + return true; + }, + error: (e) => { console.log('loadGameCompletions error', e); }, + }); + }; - // 保存更新后的历史记录 - GM_setValue('personalGameCompletions', history); - // console.log(GM_getValue('personalGameCompletions')) - return true; + // 获取个人 ID + const myHomePage = document.querySelectorAll('ul.r li.dropdown ul li a')[0].href; + const myUserId = myHomePage.match(/\/psnid\/([A-Za-z0-9_-]+)/)[1] || '*'; + const myGamePageURLRegex = new RegExp(`psnid/${myUserId}/?(?:psngame(?:\\?page=(\\d+))?|$)`); + // match: https://psnine.com/psnid/kaikaiiiiiiiwu + // match: https://psnine.com/psnid/kaikaiiiiiiiwu/ + // match: https://psnine.com/psnid/kaikaiiiiiiiwu/psngame + // match: https://psnine.com/psnid/kaikaiiiiiiiwu/psngame?page=2 + // dismatch: https://psnine.com/psnid/kaikaiiiiiiiwu/comment + + // 后台更新频次控制 + const bgUpdateMyGameCompletion = () => { + const pagesUpdateTime = GM_getValue('personalGameCompletionsLastUpdate', []); + const now = new Date().getTime(); + if (pagesUpdateTime[0] === undefined || now - pagesUpdateTime[0] > 60 * 60 * 1000) { + loadGameCompletions(myUserId, 1); } - return false; }; - // 在个人页面或个人游戏列表页更新数据 - if ( - /psnid\/[A-Za-z0-9_-]+\/?$/.test(window.location.href) || /psnid\/[A-Za-z0-9_-]+\/psngame\/?/.test(window.location.href) - ) { - savePersonalGameCompletions(); + // 在用户浏览个人页面或个人游戏列表页时,无视 Interval 白嫖一次数据更新 + if (myGamePageURLRegex.test(window.location.href)) { + const pagesUpdateTime = GM_getValue('personalGameCompletionsLastUpdate', []); + const pageid = parseInt(window.location.href.match(myGamePageURLRegex)[1], 10) || 1; + + const { totalItems, thisPageCompletions } = parseCompletionPage(document); + const { totalRecords } = updateCompletions(thisPageCompletions); + + pagesUpdateTime[pageid - 1] = new Date().getTime(); + GM_setValue('personalGameCompletionsLastUpdate', pagesUpdateTime); + if (totalRecords < totalItems || totalItems === 0) { + const nextPageID = pageid === 1 ? 2 : 1; + loadGameCompletions(myUserId, nextPageID); + } + } else { + bgUpdateMyGameCompletion(); // 定时更新 } + /// ///////////////////////////////////////////////////////////////////////////// + if ( /psnid\/[A-Za-z0-9_-]+\/?$/.test(window.location.href) && $('tbody').length > 2 @@ -799,9 +901,6 @@ $('tbody > tr:last').after(nextGameContent); isbool2 = true; gamePageIndex += 1; - - // 同步更新个人游戏完成度 - savePersonalGameCompletions(); } else { $('#loadingMessage').text('没有更多游戏了...'); } @@ -1128,7 +1227,7 @@ .append(` 🔥热门 `); + };color: rgb(255, 255, 255);">🔥热门 `); } }); };