From cb062589fac8f2ade5b1e6be580b1d8660dc0183 Mon Sep 17 00:00:00 2001 From: lordlogo2002 Date: Tue, 20 Jan 2026 05:53:21 +0100 Subject: [PATCH] Enhance HUD functionality: implement shared index for cross-tab data management and improve persistence of member-only video visibility --- content.js | 101 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 91 insertions(+), 10 deletions(-) diff --git a/content.js b/content.js index 5f331b8..9a1d7b2 100644 --- a/content.js +++ b/content.js @@ -4,6 +4,7 @@ let whitelistHandles = []; let debugEnabled = false; // In-memory index of known member-only videos. const memberOnlyIndex = new Map(); // id -> { channelKey, hidden } +const sharedIndex = new Map(); // stableId -> meta for cross-tab HUD // Data-* attribute prefix for DOM tags. const DATA_PREFIX = "chipperfluff-nobs"; // nobs = "no-bs" (member-only videos) const HUD_DOT_ID = `${DATA_PREFIX}-hud-dot`; @@ -11,6 +12,10 @@ const HUD_PANEL_ID = `${DATA_PREFIX}-hud-panel`; let generatedIdCounter = 0; let hudDirty = false; let lastHudSignature = ""; +const STORAGE_KEY = "memberOnlyHidden"; +const MAX_GLOBAL_ITEMS = 200; +const MAX_PER_CHANNEL = 20; +let persistTimer = null; /* ---------------- CSS injection ---------------- */ @@ -209,9 +214,20 @@ function loadSettings() { } loadSettings(); +loadStoredIndex(); -// Reload settings if popup changes them. -chrome.storage.onChanged.addListener(loadSettings); +// Reload settings or shared HUD data if storage changes. +chrome.storage.onChanged.addListener(changes => { + if (changes[STORAGE_KEY]) { + const items = changes[STORAGE_KEY].newValue || []; + sharedIndex.clear(); + items.forEach(item => sharedIndex.set(item.id, item)); + updateHudDot(); + } + if (changes.whitelist || changes.debug) { + loadSettings(); + } +}); /* ---------------- detection logic ---------------- */ @@ -243,6 +259,7 @@ function process(root = document) { const channelUrl = channelInfo.url || ""; const channelKey = channelInfo.key; const id = getOrCreateVideoId(video, url); + const stableId = getStableId(video, url); video.setAttribute(`data-${DATA_PREFIX}-member-only`, "true"); if (channelKey) video.setAttribute(`data-${DATA_PREFIX}-channel`, channelKey); @@ -255,6 +272,7 @@ function process(root = document) { channelKey, channelLabel: channel, channelUrl, + stableId, hidden: false, title, url, @@ -280,6 +298,7 @@ function process(root = document) { const meta = memberOnlyIndex.get(id); if (!meta.hidden) hudDirty = true; meta.hidden = true; + updateSharedIndex(meta, true); } } else { video.removeAttribute(`data-${DATA_PREFIX}-hidden`); @@ -287,6 +306,7 @@ function process(root = document) { const meta = memberOnlyIndex.get(id); if (meta.hidden) hudDirty = true; meta.hidden = false; + updateSharedIndex(meta, false); } } }); @@ -310,10 +330,12 @@ function updateKnownVisibility() { video.removeAttribute(`data-${DATA_PREFIX}-hidden`); if (meta.hidden) hudDirty = true; meta.hidden = false; + updateSharedIndex(meta, false); } else { video.setAttribute(`data-${DATA_PREFIX}-hidden`, "true"); if (!meta.hidden) hudDirty = true; meta.hidden = true; + updateSharedIndex(meta, true); } } updateHudDot(); @@ -352,6 +374,13 @@ function getOrCreateVideoId(video, url) { return generated; } +function getStableId(video, url) { + const dataId = video.getAttribute("data-video-id"); + if (dataId) return dataId; + if (url) return url; + return ""; +} + // Read the channel key from DOM or fallback to text. function getChannelKey(video) { const stored = video.getAttribute(`data-${DATA_PREFIX}-channel`); @@ -441,12 +470,8 @@ function ensureHudDot() { function updateHudDot() { const dot = ensureHudDot(); const panel = document.getElementById(HUD_PANEL_ID); - let total = 0; - let hidden = 0; - for (const meta of memberOnlyIndex.values()) { - total += 1; - if (meta.hidden) hidden += 1; - } + const total = sharedIndex.size; + const hidden = total; const signature = `${total}:${hidden}`; dot.title = `Member-only videos: ${total} (${hidden} hidden)`; dot.setAttribute( @@ -464,6 +489,59 @@ function updateHudDot() { } } +function updateSharedIndex(meta, hidden) { + if (!meta.stableId) return; + if (hidden) { + sharedIndex.set(meta.stableId, { + id: meta.stableId, + channelKey: meta.channelKey, + channelLabel: meta.channelLabel, + channelUrl: meta.channelUrl, + title: meta.title, + url: meta.url, + thumb: meta.thumb, + hidden: true + }); + } else { + sharedIndex.delete(meta.stableId); + } + schedulePersistSharedIndex(); +} + +function schedulePersistSharedIndex() { + if (persistTimer) return; + persistTimer = setTimeout(() => { + persistTimer = null; + persistSharedIndex(); + }, 200); +} + +function persistSharedIndex() { + const items = Array.from(sharedIndex.values()); + items.sort((a, b) => a.id.localeCompare(b.id)); + + const perChannelCounts = new Map(); + const pruned = []; + for (const item of items) { + const key = item.channelKey || item.channelLabel || "(unknown)"; + const count = perChannelCounts.get(key) || 0; + if (count >= MAX_PER_CHANNEL) continue; + if (pruned.length >= MAX_GLOBAL_ITEMS) break; + perChannelCounts.set(key, count + 1); + pruned.push(item); + } + + chrome.storage.local.set({ [STORAGE_KEY]: pruned }); +} + +function loadStoredIndex() { + chrome.storage.local.get({ [STORAGE_KEY]: [] }, data => { + sharedIndex.clear(); + data[STORAGE_KEY].forEach(item => sharedIndex.set(item.id, item)); + updateHudDot(); + }); +} + function renderHudPanel(panel) { panel.innerHTML = ""; const title = document.createElement("div"); @@ -472,9 +550,12 @@ function renderHudPanel(panel) { panel.appendChild(title); const groups = new Map(); - for (const meta of memberOnlyIndex.values()) { + for (const meta of sharedIndex.values()) { if (!meta.hidden) continue; - const key = meta.channelKey || getChannelKeyFromData(meta.channelLabel, meta.channelUrl) || "(unknown)"; + const key = + meta.channelKey || + getChannelKeyFromData(meta.channelLabel, meta.channelUrl) || + "(unknown)"; const list = groups.get(key) || []; list.push(meta); groups.set(key, list);