Steam 家庭库已有游戏标记

能够自动扫描你的家庭库库存,并在Steam游戏页面标记,并支持一键安装游戏

目前为 2024-04-06 提交的版本。查看 最新版本

// ==UserScript==
// @name         Steam 家庭库已有游戏标记
// @namespace    http://tampermonkey.net/
// @version      2024-04-06
// @description  能够自动扫描你的家庭库库存,并在Steam游戏页面标记,并支持一键安装游戏
// @author       Cliencer Goge
// @match        https://store.steampowered.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=steampowered.com
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @license      GPLv3
// ==/UserScript==

var dialog,appid
var saves = readstorage()
const url = window.location.pathname;

(function() {
    'use strict';
    init()
    if((url=='/account/familymanagement' || url=='/account/familymanagement/?tab=library') && saves.isStartDump){
        observer_1();
    }
    if(url.startsWith('/app/')&&g_AccountID != 0){
        //addBanner(document.querySelector('div.block.game_media_and_summary_ctn'))
        if(g_ServerTime-saves.lastupDateTime>2592000){
            let innerText
            if(saves.familyGameList.length == 0){
                innerText="您似乎没有家庭库的游戏记录,是否现在扫描家庭库游戏并记录呢?"
            }else{
                innerText="您已经超过1个月没有更新家庭库的游戏记录了,是否现在去扫描?"
            }
            ShowConfirmDialog('脚本提示',innerText,'扫描家庭库','取消').done(scan).fail(()=>{
                ShowAlertDialog('脚本提示','如果需要手动扫描,可以在Steam主页右上角进入进行扫描','好的')
            })
        }
        observer_2();
    }


    function init(){

        let setting_btn = document.createElement('span');
        setting_btn.id = "setting_btn"
        setting_btn.style = "position:relative;background:linear-gradient(to right, rgb(6 207 199 / 60%) 0%, rgb(33 105 106 / 60%) 100%)"
        setting_btn.innerHTML = `<a href="javascript:void(0)" style = "color:#06cfb5">家庭游戏标记 脚本设置</a></span>`
        setting_btn.onclick = btnonclick
        plug();





        function plug(){
            let headding = document.getElementById('global_action_menu')
            if(headding){
                headding.insertBefore(setting_btn, headding.firstChild);
            }else{
                setTimeout(plug,200)
            }
        }
        function btnonclick(){
            ShowConfirmDialog('脚本设置',`目前你的家庭库一共记录了${saves.familyGameList.length}个游戏。\n上次扫描时间:${timestampToTime(saves.lastupDateTime)}`,'扫描家庭库',null,'清空库记录').done(function(arg){
                if(arg == 'SECONDARY'){
                    ShowConfirmDialog('再次确认','你即将清空当前保存的家庭库列表,该行为无法撤销!','好的','算了').done(() =>{
                        saves = {
                            isStartDump:false,
                            familyGameList:[],
                            lastupDateTime:0
                        }
                        savestorage()
                        ShowAlertDialog('完成','已经清除所有的缓存','好的')
                    })
                }else{
                    scan()
                }
            })
        }




    }
    function scan(){
        ShowAlertDialog('提示','即将打开家庭库页面进行扫描,请确认已加入一个有效的家庭组,否则脚本可能会出错,扫描期间不要关闭浏览器,耐心等待进度条结束!','好的,开始扫描').done(()=>{

            var windowFeatures = "width=1000,height=700,resizable=no,scrollbars=yes,status=yes";
            var newWindow = window.open('https://store.steampowered.com/account/familymanagement?tab=library', '家庭库扫描窗口---不要关闭!', windowFeatures);
            saves.isStartDump=true;
            savestorage()
            // 检查窗口是否成功打开
            if (newWindow) {
                newWindow.focus();
            } else {
                // 弹窗被阻止的情况
                alert("打开新窗口被阻止,请在浏览器设置中允许打开新窗口!");
            }
        })
    }
    function observer_2(){
        let block = document.querySelector('div.block.game_media_and_summary_ctn')
        if(block){
            appid = Number(url.split('/')[2])
            if(saves.familyGameList.includes(appid)){
                addBanner(block)
            }
        }else{
            setTimeout(observer_2,200)
        }
    }

    function addBanner(block){
        let appname = appHubAppName.innerText
        let owned = false
        if(block.querySelector('div.game_area_already_owned.page_content')){
            owned = true
        }
        if(owned == false){
            var headplug = document.createElement('div');
            var targetElement = block.querySelector('div.queue_overflow_ctn');
            headplug.style = "background:linear-gradient(to right, rgb(6 207 199 / 60%) 0%, rgb(33 105 106 / 60%) 100%);color:#06cfb5"
            headplug.className = "game_area_already_owned page_content"
            headplug.innerHTML =`<div class="game_area_already_owned_ctn" >
				                   <div class="ds_owned_flag ds_flag" style="background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAKCAYAAABi8KSDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUNDNzBFNTUyMUM0MTFFNDk1REVFODRBNUU5RjA2MUYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUNDNzBFNTYyMUM0MTFFNDk1REVFODRBNUU5RjA2MUYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5Q0M3MEU1MzIxQzQxMUU0OTVERUU4NEE1RTlGMDYxRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5Q0M3MEU1NDIxQzQxMUU0OTVERUU4NEE1RTlGMDYxRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv3vUKAAAAAlSURBVHjaYvz//z8DsYARpFhISAivjnfv3jGSp3jUGeQ4AyDAADZHNe2nyOBrAAAAAElFTkSuQmCC) no-repeat 4px 4px #06cfbe">在家庭库中&nbsp;&nbsp;</div>
				                   <div class="already_in_library" >您的 Steam 家庭库中已有《${appname}》</div>
		                     </div>`

            targetElement.parentNode.insertBefore(headplug, targetElement.nextSibling);

            var endplug = document.createElement('div');
            targetElement = block.querySelector('div.purchase_options_content');
            endplug.className = "game_area_play_stats"
            endplug.innerHTML = `<div class="already_owned_actions">
								       <div class="game_area_already_owned_btn">
									         <a class="btnv6_lightblue_blue btnv6_border_2px btn_medium" href="https://store.steampowered.com/about/?snr=1_5_9__owned-game">
										        <span>安装 Steam</span>
									         </a>
								       </div>
									   <div class="game_area_already_owned_btn">
										     <a class="btnv6_lightblue_blue btnv6_border_2px btn_medium" href="steam://launch/${appid}/Dialog">
										        <span>马上开玩</span>
									         </a>
								       </div>
							     </div>
					             <div style="clear:left;"></div>`
            targetElement.parentNode.insertBefore(endplug, targetElement);

        }

    }

    function observer_1(){
        let button_list = document.querySelectorAll('button._2UOyb8dGbKlL6QDQiqYFoc.DialogButton._DialogLayout.Secondary.Focusable')
        if(button_list.length == 6){
            button_list[1].click();
            console.log('点击显示全部')
            let loadinghtml=`<div id="progress-bar-container" style="height: 20px; width: 100%; border-radius: 10px;position: relative;">
            <div class="progress-filled" style=" height: 100%;width: 0%; background:linear-gradient(to bottom, #a4d007 5%, #536904 95%);border-radius: 10px;z-index: 2;position: absolute;transition: width 0.3s ease;"></div>
            <div id="progress-unfilled" style=" height: 100%;width: 100%;background: rgb(30, 30, 31);position: relative;border-radius: 10px"></div>
            </div>`
            dialog = ShowBlockingWaitDialog('正在扫描家庭组库存...',loadinghtml)
            waitfordialog()
            function waitfordialog(){
                if(dialog.m_$Content.length==0){
                    setTimeout(waitfordialog,500)
                }else{
                    setTimeout(startDump,500)
                }
            }
        }else{
            setTimeout(observer_1,1000)
        }
    }
    function startDump(){
        dialog.m_$Content[0].querySelector('div.waiting_dialog_throbber').remove()
        let bar = dialog.m_$Content[0].querySelector('.progress-filled')
        let containGames_panel = document.querySelectorAll('div._1o7lKXffOJjZ_CpH1bHfY-')[0]
        let totalGames = getGameCounts(containGames_panel)//游戏总数
        let gameList = []
        let dumped_panel_index = -1
        scroll_page();
        function scroll_page(){
            var i = 0
            let panelList = containGames_panel.querySelectorAll('div._3yp3JSnseRSpM7H2FnoqG9.Panel.Focusable')

            for(i = 0;i<panelList.length;i++){

                let panel_index = Number(panelList[i].parentElement.getAttribute('data-index')) //当前行的index
                if(panel_index <= dumped_panel_index) {
                    i += dumped_panel_index-panel_index
                    continue;
                }
                if(panel_index - dumped_panel_index > 1){console.log(`有跳过行数在index=${panel_index}`)}
                for(var games_box of panelList[i].children){
                    gameList.push(getGameAppid(games_box))

                }

                dumped_panel_index = panel_index
            }
            if(gameList.length < totalGames){
                updateProgressBar(gameList.length/totalGames)
                panelList[i-1].scrollIntoView({ behavior: 'auto', block: 'start' });
                setTimeout(scroll_page,100)

            }else{
                dialog.Dismiss()
                saves.familyGameList = gameList
                saves.lastupDateTime = g_ServerTime
                saves.isStartDump = false
                savestorage()
                ShowAlertDialog('完成',`你家庭组中的${totalGames}个游戏已全部记录,现在可以关闭该页面,回到主页后刷新生效!`,'知道啦,屑屑你',{strSubTitle:null,bExplicitDismissalOnly:false})
            }

        }
        function updateProgressBar(progress) {
            bar.style.width = progress*100 + '%';
        }

    }
    function getGameAppid(element){
        return Number(element.firstChild.firstChild.getAttribute('src').split('/')[5])
    }
    function getGameCounts(containGames_panel){
        return Number(containGames_panel.querySelector('div.LP9H7bBiPB8N8jFzCQumL').lastChild.innerText.match(/\d*/)[0])
    }
})();



function readstorage(){
    var saves = GM_getValue('saves')
    if(saves) return saves
    saves = {
        isStartDump:false,
        familyGameList:[],
        lastupDateTime:0
    }
    return saves
}
function savestorage(){
    GM_setValue('saves',saves)
}
function timestampToTime(timestamp) {
    if(timestamp == 0){return '无记录'}
    timestamp = timestamp ? timestamp : null;
    timestamp *= 1000
    let date = new Date(timestamp);//时间戳为10位需*1000,时间戳为13位的话不需乘1000
    let Y = date.getFullYear() + '-';
    let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
    let D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ';
    let h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
    let m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':';
    let s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
    return Y + M + D + h + m + s;
}

QingJ © 2025

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