// ==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">在家庭库中 </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;
}