Enhance HUD functionality: implement shared index for cross-tab data management and improve persistence of member-only video visibility
This commit is contained in:
parent
ab931e1264
commit
cb062589fa
101
content.js
101
content.js
@ -4,6 +4,7 @@ let whitelistHandles = [];
|
|||||||
let debugEnabled = false;
|
let debugEnabled = false;
|
||||||
// In-memory index of known member-only videos.
|
// In-memory index of known member-only videos.
|
||||||
const memberOnlyIndex = new Map(); // id -> { channelKey, hidden }
|
const memberOnlyIndex = new Map(); // id -> { channelKey, hidden }
|
||||||
|
const sharedIndex = new Map(); // stableId -> meta for cross-tab HUD
|
||||||
// Data-* attribute prefix for DOM tags.
|
// Data-* attribute prefix for DOM tags.
|
||||||
const DATA_PREFIX = "chipperfluff-nobs"; // nobs = "no-bs" (member-only videos)
|
const DATA_PREFIX = "chipperfluff-nobs"; // nobs = "no-bs" (member-only videos)
|
||||||
const HUD_DOT_ID = `${DATA_PREFIX}-hud-dot`;
|
const HUD_DOT_ID = `${DATA_PREFIX}-hud-dot`;
|
||||||
@ -11,6 +12,10 @@ const HUD_PANEL_ID = `${DATA_PREFIX}-hud-panel`;
|
|||||||
let generatedIdCounter = 0;
|
let generatedIdCounter = 0;
|
||||||
let hudDirty = false;
|
let hudDirty = false;
|
||||||
let lastHudSignature = "";
|
let lastHudSignature = "";
|
||||||
|
const STORAGE_KEY = "memberOnlyHidden";
|
||||||
|
const MAX_GLOBAL_ITEMS = 200;
|
||||||
|
const MAX_PER_CHANNEL = 20;
|
||||||
|
let persistTimer = null;
|
||||||
|
|
||||||
/* ---------------- CSS injection ---------------- */
|
/* ---------------- CSS injection ---------------- */
|
||||||
|
|
||||||
@ -209,9 +214,20 @@ function loadSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
loadStoredIndex();
|
||||||
|
|
||||||
// Reload settings if popup changes them.
|
// Reload settings or shared HUD data if storage changes.
|
||||||
chrome.storage.onChanged.addListener(loadSettings);
|
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 ---------------- */
|
/* ---------------- detection logic ---------------- */
|
||||||
|
|
||||||
@ -243,6 +259,7 @@ function process(root = document) {
|
|||||||
const channelUrl = channelInfo.url || "";
|
const channelUrl = channelInfo.url || "";
|
||||||
const channelKey = channelInfo.key;
|
const channelKey = channelInfo.key;
|
||||||
const id = getOrCreateVideoId(video, url);
|
const id = getOrCreateVideoId(video, url);
|
||||||
|
const stableId = getStableId(video, url);
|
||||||
|
|
||||||
video.setAttribute(`data-${DATA_PREFIX}-member-only`, "true");
|
video.setAttribute(`data-${DATA_PREFIX}-member-only`, "true");
|
||||||
if (channelKey) video.setAttribute(`data-${DATA_PREFIX}-channel`, channelKey);
|
if (channelKey) video.setAttribute(`data-${DATA_PREFIX}-channel`, channelKey);
|
||||||
@ -255,6 +272,7 @@ function process(root = document) {
|
|||||||
channelKey,
|
channelKey,
|
||||||
channelLabel: channel,
|
channelLabel: channel,
|
||||||
channelUrl,
|
channelUrl,
|
||||||
|
stableId,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
title,
|
title,
|
||||||
url,
|
url,
|
||||||
@ -280,6 +298,7 @@ function process(root = document) {
|
|||||||
const meta = memberOnlyIndex.get(id);
|
const meta = memberOnlyIndex.get(id);
|
||||||
if (!meta.hidden) hudDirty = true;
|
if (!meta.hidden) hudDirty = true;
|
||||||
meta.hidden = true;
|
meta.hidden = true;
|
||||||
|
updateSharedIndex(meta, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
video.removeAttribute(`data-${DATA_PREFIX}-hidden`);
|
video.removeAttribute(`data-${DATA_PREFIX}-hidden`);
|
||||||
@ -287,6 +306,7 @@ function process(root = document) {
|
|||||||
const meta = memberOnlyIndex.get(id);
|
const meta = memberOnlyIndex.get(id);
|
||||||
if (meta.hidden) hudDirty = true;
|
if (meta.hidden) hudDirty = true;
|
||||||
meta.hidden = false;
|
meta.hidden = false;
|
||||||
|
updateSharedIndex(meta, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -310,10 +330,12 @@ function updateKnownVisibility() {
|
|||||||
video.removeAttribute(`data-${DATA_PREFIX}-hidden`);
|
video.removeAttribute(`data-${DATA_PREFIX}-hidden`);
|
||||||
if (meta.hidden) hudDirty = true;
|
if (meta.hidden) hudDirty = true;
|
||||||
meta.hidden = false;
|
meta.hidden = false;
|
||||||
|
updateSharedIndex(meta, false);
|
||||||
} else {
|
} else {
|
||||||
video.setAttribute(`data-${DATA_PREFIX}-hidden`, "true");
|
video.setAttribute(`data-${DATA_PREFIX}-hidden`, "true");
|
||||||
if (!meta.hidden) hudDirty = true;
|
if (!meta.hidden) hudDirty = true;
|
||||||
meta.hidden = true;
|
meta.hidden = true;
|
||||||
|
updateSharedIndex(meta, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateHudDot();
|
updateHudDot();
|
||||||
@ -352,6 +374,13 @@ function getOrCreateVideoId(video, url) {
|
|||||||
return generated;
|
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.
|
// Read the channel key from DOM or fallback to text.
|
||||||
function getChannelKey(video) {
|
function getChannelKey(video) {
|
||||||
const stored = video.getAttribute(`data-${DATA_PREFIX}-channel`);
|
const stored = video.getAttribute(`data-${DATA_PREFIX}-channel`);
|
||||||
@ -441,12 +470,8 @@ function ensureHudDot() {
|
|||||||
function updateHudDot() {
|
function updateHudDot() {
|
||||||
const dot = ensureHudDot();
|
const dot = ensureHudDot();
|
||||||
const panel = document.getElementById(HUD_PANEL_ID);
|
const panel = document.getElementById(HUD_PANEL_ID);
|
||||||
let total = 0;
|
const total = sharedIndex.size;
|
||||||
let hidden = 0;
|
const hidden = total;
|
||||||
for (const meta of memberOnlyIndex.values()) {
|
|
||||||
total += 1;
|
|
||||||
if (meta.hidden) hidden += 1;
|
|
||||||
}
|
|
||||||
const signature = `${total}:${hidden}`;
|
const signature = `${total}:${hidden}`;
|
||||||
dot.title = `Member-only videos: ${total} (${hidden} hidden)`;
|
dot.title = `Member-only videos: ${total} (${hidden} hidden)`;
|
||||||
dot.setAttribute(
|
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) {
|
function renderHudPanel(panel) {
|
||||||
panel.innerHTML = "";
|
panel.innerHTML = "";
|
||||||
const title = document.createElement("div");
|
const title = document.createElement("div");
|
||||||
@ -472,9 +550,12 @@ function renderHudPanel(panel) {
|
|||||||
panel.appendChild(title);
|
panel.appendChild(title);
|
||||||
|
|
||||||
const groups = new Map();
|
const groups = new Map();
|
||||||
for (const meta of memberOnlyIndex.values()) {
|
for (const meta of sharedIndex.values()) {
|
||||||
if (!meta.hidden) continue;
|
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) || [];
|
const list = groups.get(key) || [];
|
||||||
list.push(meta);
|
list.push(meta);
|
||||||
groups.set(key, list);
|
groups.set(key, list);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user