Refactor playtime data handling in NSLGameScanner

This commit is contained in:
Roy
2026-02-02 03:41:21 -08:00
committed by GitHub
parent 74ec8c9224
commit 784eac1d2a
+174 -169
View File
@@ -1371,196 +1371,201 @@ window.createShortcut = async function(data) {
"""
PLAYTIME_CODE = """
const STORAGE_KEY = "realPlaytimeData";
if (window.__REAL_PLAYTIME_LOADED__) {
} else {
window.__REAL_PLAYTIME_LOADED__ = true;
const STORAGE_KEY = "realPlaytimeData";
let memoryCache = null;
const appliedSessions = {};
let memoryCache = null;
const appliedSessions = {};
function isValidPlaytimeDataEntry(entry) {
return (
typeof entry === "object" &&
entry !== null &&
typeof entry.total === "number" &&
typeof entry.lastSessionEnd === "number"
);
}
function sanitizePlaytimeData(data) {
if (typeof data !== "object" || data === null) return {};
const cleaned = {};
for (const key in data) {
if (isValidPlaytimeDataEntry(data[key])) {
cleaned[key] = data[key];
}
function isValidPlaytimeDataEntry(entry) {
return (
typeof entry === "object" &&
entry !== null &&
typeof entry.total === "number" &&
typeof entry.lastSessionEnd === "number"
);
}
return cleaned;
}
function loadPlaytimeData() {
try {
if (memoryCache) return memoryCache;
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) {
function sanitizePlaytimeData(data) {
if (typeof data !== "object" || data === null) return {};
const cleaned = {};
for (const key in data) {
if (isValidPlaytimeDataEntry(data[key])) {
cleaned[key] = data[key];
}
}
return cleaned;
}
function loadPlaytimeData() {
try {
if (memoryCache) return memoryCache;
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) {
memoryCache = {};
return memoryCache;
}
const parsed = JSON.parse(raw);
const cleaned = sanitizePlaytimeData(parsed);
if (Object.keys(cleaned).length !== Object.keys(parsed || {}).length) {
savePlaytimeData(cleaned);
}
memoryCache = cleaned;
return memoryCache;
} catch {
memoryCache = {};
return memoryCache;
}
const parsed = JSON.parse(raw);
const cleaned = sanitizePlaytimeData(parsed);
if (Object.keys(cleaned).length !== Object.keys(parsed || {}).length) {
savePlaytimeData(cleaned);
}
memoryCache = cleaned;
return memoryCache;
} catch {
memoryCache = {};
return memoryCache;
}
}
function savePlaytimeData(data) {
try {
const latestFromStorage = JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}");
const merged = { ...latestFromStorage, ...data };
memoryCache = merged;
localStorage.setItem(STORAGE_KEY, JSON.stringify(merged));
} catch {
memoryCache = data;
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
}
}
function isEnvironmentReady() {
try {
localStorage.setItem("__rp_test__", "1");
localStorage.removeItem("__rp_test__");
if (!window.appStore || typeof window.appStore.GetAppOverviewByAppID !== "function") return false;
if (!window.appInfoStore) return false;
return true;
} catch {
return false;
}
}
function restoreSavedPlaytimes() {
const data = loadPlaytimeData();
if (!window.appStore?.GetAppOverviewByAppID) return;
let removedCount = 0;
for (const id in data) {
const entry = data[id];
const ov = appStore.GetAppOverviewByAppID(Number(id));
if (ov) {
ov.minutes_playtime_forever = Math.max(ov.minutes_playtime_forever || 0, entry.total);
ov.minutes_playtime_last_two_weeks = Math.max(ov.minutes_playtime_last_two_weeks || 0, entry.total);
ov.nPlaytimeForever = Math.max(ov.nPlaytimeForever || 0, entry.total);
ov.TriggerChange?.();
} else {
delete data[id];
removedCount++;
function savePlaytimeData(data) {
try {
const latestFromStorage = JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}");
const merged = { ...latestFromStorage, ...data };
memoryCache = merged;
localStorage.setItem(STORAGE_KEY, JSON.stringify(merged));
} catch {
memoryCache = data;
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
}
}
if (removedCount > 0) savePlaytimeData(data);
}
function applyRealSessionToOverview(appOverview) {
try {
if (!appOverview || appOverview.app_type !== 1073741824) return false;
const start = appOverview.rt_last_time_played;
const end = appOverview.rt_last_time_locally_played;
if (!start || !end || end <= start) return false;
const appId = String(appOverview.appid || (typeof appOverview.appid === "function" ? appOverview.appid() : appOverview.appId));
const sessionSeconds = end - start;
const sessionMinutes = Math.floor(sessionSeconds / 60);
if (sessionMinutes <= 0) return false;
function isEnvironmentReady() {
try {
localStorage.setItem("__rp_test__", "1");
localStorage.removeItem("__rp_test__");
if (!window.appStore || typeof window.appStore.GetAppOverviewByAppID !== "function") return false;
if (!window.appInfoStore) return false;
return true;
} catch {
return false;
}
}
function restoreSavedPlaytimes() {
const data = loadPlaytimeData();
const prevEntry = data[appId] || { total: 0, lastSessionEnd: 0 };
if (!window.appStore?.GetAppOverviewByAppID) return;
const effectiveEnd = Math.max(prevEntry.lastSessionEnd, end);
const addedMinutes = effectiveEnd > prevEntry.lastSessionEnd ? sessionMinutes : 0;
const newTotal = prevEntry.total + addedMinutes;
if (newTotal === prevEntry.total) return false;
data[appId] = { total: newTotal, lastSessionEnd: effectiveEnd };
savePlaytimeData(data);
appliedSessions[appId] = effectiveEnd;
appOverview.minutes_playtime_forever = newTotal;
appOverview.minutes_playtime_last_two_weeks = newTotal;
appOverview.nPlaytimeForever = newTotal;
appOverview.TriggerChange?.();
return true;
} catch {
return false;
}
}
function patchAppStore() {
if (!window.appStore?.m_mapApps) return;
if (appStore.m_mapApps._originalSet) return;
appStore.m_mapApps._originalSet = appStore.m_mapApps.set;
appStore.m_mapApps.set = function(appId, appOverview) {
const result = appStore.m_mapApps._originalSet.call(this, appId, appOverview);
restoreSavedPlaytimes();
applyRealSessionToOverview(appOverview);
return result;
};
}
function patchAppInfoStore() {
if (!window.appInfoStore) return;
if (appInfoStore._originalOnAppOverviewChange) return;
appInfoStore._originalOnAppOverviewChange = appInfoStore.OnAppOverviewChange;
appInfoStore.OnAppOverviewChange = function(apps) {
for (const a of apps || []) {
const id = typeof a?.appid === "function" ? a.appid() : a?.appid;
const overview = id && appStore?.GetAppOverviewByAppID ? appStore.GetAppOverviewByAppID(Number(id)) : a;
if (overview) {
restoreSavedPlaytimes();
applyRealSessionToOverview(overview);
}
}
return appInfoStore._originalOnAppOverviewChange.call(this, apps);
};
}
function manualPatch() {
try {
const m = location.pathname.match(/\\/library\\/app\\/(\\d+)/);
if (m) {
const id = Number(m[1]);
const ov = appStore.GetAppOverviewByAppID(id);
let removedCount = 0;
for (const id in data) {
const entry = data[id];
const ov = appStore.GetAppOverviewByAppID(Number(id));
if (ov) {
restoreSavedPlaytimes();
applyRealSessionToOverview(ov);
appInfoStore?.OnAppOverviewChange?.([ov]);
ov.minutes_playtime_forever = Math.max(ov.minutes_playtime_forever || 0, entry.total);
ov.minutes_playtime_last_two_weeks = Math.max(ov.minutes_playtime_last_two_weeks || 0, entry.total);
ov.nPlaytimeForever = Math.max(ov.nPlaytimeForever || 0, entry.total);
ov.TriggerChange?.();
} else {
delete data[id];
removedCount++;
}
}
} catch {}
}
(function initRealPlaytime(retryCount = 0) {
if (!isEnvironmentReady()) {
if (retryCount < 100) {
setTimeout(() => initRealPlaytime(retryCount + 1), 1000);
}
return;
if (removedCount > 0) savePlaytimeData(data);
}
try {
setTimeout(() => {
function applyRealSessionToOverview(appOverview) {
try {
if (!appOverview || appOverview.app_type !== 1073741824) return false;
const start = appOverview.rt_last_time_played;
const end = appOverview.rt_last_time_locally_played;
if (!start || !end || end <= start) return false;
const appId = String(appOverview.appid || (typeof appOverview.appid === "function" ? appOverview.appid() : appOverview.appId));
const sessionSeconds = end - start;
const sessionMinutes = Math.floor(sessionSeconds / 60);
if (sessionMinutes <= 0) return false;
const data = loadPlaytimeData();
const prevEntry = data[appId] || { total: 0, lastSessionEnd: 0 };
const effectiveEnd = Math.max(prevEntry.lastSessionEnd, end);
const addedMinutes = effectiveEnd > prevEntry.lastSessionEnd ? sessionMinutes : 0;
const newTotal = prevEntry.total + addedMinutes;
if (newTotal === prevEntry.total) return false;
data[appId] = { total: newTotal, lastSessionEnd: effectiveEnd };
savePlaytimeData(data);
appliedSessions[appId] = effectiveEnd;
appOverview.minutes_playtime_forever = newTotal;
appOverview.minutes_playtime_last_two_weeks = newTotal;
appOverview.nPlaytimeForever = newTotal;
appOverview.TriggerChange?.();
return true;
} catch {
return false;
}
}
function patchAppStore() {
if (!window.appStore?.m_mapApps) return;
if (appStore.m_mapApps._originalSet) return;
appStore.m_mapApps._originalSet = appStore.m_mapApps.set;
appStore.m_mapApps.set = function(appId, appOverview) {
const result = appStore.m_mapApps._originalSet.call(this, appId, appOverview);
restoreSavedPlaytimes();
patchAppStore();
patchAppInfoStore();
manualPatch();
}, 100);
} catch {}
})();
applyRealSessionToOverview(appOverview);
return result;
};
}
function patchAppInfoStore() {
if (!window.appInfoStore) return;
if (appInfoStore._originalOnAppOverviewChange) return;
appInfoStore._originalOnAppOverviewChange = appInfoStore.OnAppOverviewChange;
appInfoStore.OnAppOverviewChange = function(apps) {
for (const a of apps || []) {
const id = typeof a?.appid === "function" ? a.appid() : a?.appid;
const overview = id && appStore?.GetAppOverviewByAppID ? appStore.GetAppOverviewByAppID(Number(id)) : a;
if (overview) {
restoreSavedPlaytimes();
applyRealSessionToOverview(overview);
}
}
return appInfoStore._originalOnAppOverviewChange.call(this, apps);
};
}
function manualPatch() {
try {
const m = location.pathname.match(/\\/library\\/app\\/(\\d+)/);
if (m) {
const id = Number(m[1]);
const ov = appStore.GetAppOverviewByAppID(id);
if (ov) {
restoreSavedPlaytimes();
applyRealSessionToOverview(ov);
appInfoStore?.OnAppOverviewChange?.([ov]);
}
}
} catch {}
}
(function initRealPlaytime(retryCount = 0) {
if (!isEnvironmentReady()) {
if (retryCount < 100) {
setTimeout(() => initRealPlaytime(retryCount + 1), 1000);
}
return;
}
try {
setTimeout(() => {
restoreSavedPlaytimes();
patchAppStore();
patchAppInfoStore();
manualPatch();
}, 100);
} catch {}
})();
}
"""