您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Download/Copy HLTB Information for the HLTB Playnite Addon. Improved navigation support.
// ==UserScript== // @name How Long To Beat To Playnite // @namespace http://vers.works/ // @version 1.4 // @icon https://styles.redditmedia.com/t5_3koqm/styles/communityIcon_xc2zfag6beo81.png // @description Download/Copy HLTB Information for the HLTB Playnite Addon. Improved navigation support. // @author VERS // @match https://howlongtobeat.com/* // @grant none // @license MIT // ==/UserScript== // 1.4 - QUICK FIX A MISTAKE IN THE CODE. // 1.3 - FIXES MORE ISSUES, BUTTONS ARE NOW ADDED PROPERLY (NO NEED TO REFRESH) MADE BETTER UI - MORE IMPROVEMENTS TO COME. // 1.2 - FIXES ISSUES WHERE TIMES WERE NOT EXTRACTED PROPERLY. (function() { 'use strict'; function addCopyButton() { const statsListContainer = document.querySelector('.GameHeader_profile_details__oQTrK'); if (statsListContainer) { const statsList = statsListContainer.querySelector('ul'); if (statsList.querySelector('.list-item')) return; const copyButtonLi = document.createElement('li'); copyButtonLi.classList.add('list-item'); copyButtonLi.style.backgroundColor = '#2b7ab9'; copyButtonLi.style.color = '#ffffff'; copyButtonLi.style.padding = '4px'; copyButtonLi.style.borderRadius = '5px'; copyButtonLi.style.cursor = 'pointer'; copyButtonLi.style.textAlign = 'center'; copyButtonLi.style.fontWeight = 'bold'; const copyButtonText = document.createTextNode('COPY INFO'); copyButtonLi.appendChild(copyButtonText); statsList.appendChild(copyButtonLi); copyButtonLi.addEventListener('click', handleCopyButtonClick); } } function extractTime(category) { const liElement = [...document.querySelectorAll('li.GameStats_short__tSJ6I.time_00, li.GameStats_short__tSJ6I.time_10, li.GameStats_short__tSJ6I.time_20, li.GameStats_short__tSJ6I.time_30, li.GameStats_short__tSJ6I.time_40, li.GameStats_short__tSJ6I.time_50, li.GameStats_short__tSJ6I.time_60, li.GameStats_short__tSJ6I.time_70, li.GameStats_short__tSJ6I.time_80, li.GameStats_short__tSJ6I.time_90, li.GameStats_short__tSJ6I.time_100, li.GameStats_short__tSJ6I.time_110, li.GameStats_short__tSJ6I.time_120, li.GameStats_short__tSJ6I.time_130, li.GameStats_short__tSJ6I.time_140, li.GameStats_short__tSJ6I.time_150, li.GameStats_short__tSJ6I.time_160, li.GameStats_short__tSJ6I.time_170, li.GameStats_short__tSJ6I.time_180, li.GameStats_short__tSJ6I.time_190, li.GameStats_short__tSJ6I.time_200, li.GameStats_short__tSJ6I.time_210, li.GameStats_short__tSJ6I.time_220, li.GameStats_short__tSJ6I.time_230, li.GameStats_short__tSJ6I.time_240, li.GameStats_short__tSJ6I.time_250, li.GameStats_short__tSJ6I.time_260, li.GameStats_short__tSJ6I.time_270, li.GameStats_short__tSJ6I.time_280, li.GameStats_short__tSJ6I.time_290, li.GameStats_short__tSJ6I.time_300, li.GameStats_short__tSJ6I.time_310, li.GameStats_short__tSJ6I.time_320, li.GameStats_short__tSJ6I.time_330, li.GameStats_short__tSJ6I.time_340, li.GameStats_short__tSJ6I.time_350, li.GameStats_short__tSJ6I.time_360, li.GameStats_short__tSJ6I.time_370, li.GameStats_short__tSJ6I.time_380, li.GameStats_short__tSJ6I.time_390, li.GameStats_short__tSJ6I.time_400, li.GameStats_short__tSJ6I.time_410, li.GameStats_short__tSJ6I.time_420, li.GameStats_short__tSJ6I.time_430, li.GameStats_short__tSJ6I.time_440, li.GameStats_short__tSJ6I.time_450, li.GameStats_short__tSJ6I.time_460, li.GameStats_short__tSJ6I.time_470, li.GameStats_short__tSJ6I.time_480, li.GameStats_short__tSJ6I.time_490, li.GameStats_short__tSJ6I.time_500')].find(item => item.querySelector('h4')?.textContent.trim().includes(category) ); const timeString = liElement ? liElement.querySelector('h5')?.textContent.trim() : null; if (timeString) { return timeString.replace('½', '.5'); } return null; } function handleCopyButtonClick() { const gameNameElement = document.querySelector('.GameHeader_profile_header__q_PID.shadow_text'); const gameName = gameNameElement ? gameNameElement.textContent.trim() : "Unknown Game"; const gameId = window.location.pathname.split('/').pop(); const gameImageElement = document.querySelector('.GameSideBar_game_image__ozUTt.mobile_hide img'); const imageUrl = gameImageElement ? gameImageElement.src.split('?')[0] : ""; const platformElement = document.querySelector('.GameSummary_profile_info__HZFQu.GameSummary_medium___r_ia'); const platformsText = platformElement ? platformElement.textContent.trim().replace('Platforms:', '').trim() : ""; const platforms = platformsText ? platformsText : ""; const convertToSeconds = (timeString) => { let hours = 0; if (timeString) { timeString = timeString.replace(' Hours', '').trim(); timeString = timeString.replace('½', '.5'); const match = timeString.match(/(\d+(\.\d+)?)/); if (match) { hours = parseFloat(match[0]); } } return Math.round(hours * 3600); }; const mainStoryTime = extractTime("Main Story"); const mainExtraTime = extractTime("Main + Sides"); const completionistTime = extractTime("Completionist"); const mainStoryClassic = convertToSeconds(mainStoryTime); const mainExtraClassic = convertToSeconds(mainExtraTime); const completionistClassic = convertToSeconds(completionistTime); const currentDate = new Date(); const formattedDate = currentDate.toISOString(); const optionsModal = document.createElement('div'); optionsModal.style.position = 'fixed'; optionsModal.style.top = '50%'; optionsModal.style.left = '50%'; optionsModal.style.transform = 'translate(-50%, -50%)'; optionsModal.style.backgroundColor = '#242424'; optionsModal.style.padding = '20px'; optionsModal.style.borderRadius = '5px'; optionsModal.style.zIndex = '9999'; optionsModal.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.1)'; optionsModal.style.display = 'flex'; optionsModal.style.flexDirection = 'column'; optionsModal.style.gap = '10px'; optionsModal.style.width = '300px'; optionsModal.innerHTML = ` <label for="playniteId">DOWNLOAD HLTB DATA</label> <input type="text" id="playniteId" placeholder="Enter Playnite Database ID" style="padding: 5px; margin-bottom: 10px;"> <div style="display: flex; gap: 10px;"> <button id="copyToClipboard" style="flex: 1;">Copy to Clipboard</button> <button id="downloadJsonFile" style="flex: 1;">Download JSON File</button> </div> `; document.body.appendChild(optionsModal); const playniteIdInput = document.getElementById('playniteId'); const copyButton = document.getElementById('copyToClipboard'); const downloadButton = document.getElementById('downloadJsonFile'); const processJson = () => { const userId = playniteIdInput.value.trim(); if (!userId) { alert('ENTER DATABASE ID'); return null; } const jsonData = { "Items": [ { "Name": gameName, "Id": gameId, "UrlImg": imageUrl, "Url": window.location.href, "Platform": platforms, "GameType": 0, "GameHltbData": { "GameType": 0, "MainStoryClassic": mainStoryClassic, "MainStoryMedian": 0, "MainStoryAverage": 0, "MainStoryRushed": 0, "MainStoryLeisure": 0, "MainExtraClassic": mainExtraClassic, "MainExtraMedian": 0, "MainExtraAverage": 0, "MainExtraRushed": 0, "MainExtraLeisure": 0, "CompletionistClassic": completionistClassic, "CompletionistMedian": 0, "CompletionistAverage": 0, "CompletionistRushed": 0, "CompletionistLeisure": 0, "SoloClassic": 0, "SoloMedian": 0, "SoloAverage": 0, "SoloRushed": 0, "SoloLeisure": 0, "CoOpClassic": 0, "CoOpMedian": 0, "CoOpAverage": 0, "CoOpRushed": 0, "CoOpLeisure": 0, "VsClassic": 0, "VsMedian": 0, "VsAverage": 0, "VsRushed": 0, "VsLeisure": 0 }, "IsVndb": false } ], "DateLastRefresh": formattedDate, "Id": userId, "Name": gameName }; return jsonData; }; copyButton.addEventListener('click', function() { const jsonData = processJson(); if (jsonData) { const jsonString = JSON.stringify(jsonData, null, 4); navigator.clipboard.writeText(jsonString) .then(() => { document.body.removeChild(optionsModal); }) .catch(err => { console.error('Failed to copy: ', err); }); } }); downloadButton.addEventListener('click', function() { const jsonData = processJson(); if (jsonData) { const jsonString = JSON.stringify(jsonData, null, 4); const blob = new Blob([jsonString], { type: 'application/json' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = `${jsonData.Id}.json`; link.click(); document.body.removeChild(optionsModal); } }); } const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList') { if (window.location.pathname.includes('/game/') && document.querySelector('.GameHeader_profile_details__oQTrK') && !document.querySelector('.list-item')) { addCopyButton(); break; } } } }); window.addEventListener('load', function() { addCopyButton(); observer.observe(document.body, { childList: true, subtree: true }); }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址