1
0

Enhance HUD functionality: implement shared index for cross-tab data management and improve persistence of member-only video visibility

This commit is contained in:
Chipperfluff 2026-01-20 05:53:21 +01:00
parent ab931e1264
commit cb062589fa

View File

@ -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);