Files
YouTubeOutlierBadge/src/store.js
T
yusufipek 66e6fec778 Make the distribution chart readable
Bars are HTML now instead of SVG: the SVG <title> tooltip barely showed up in
Firefox and thin bars were hard to hit. Each bar sits in a full-height hover
target, hovering shows the video's title, views and age, clicking opens it. A
legend explains the median line and the highlighted bar, and baselines now
cache titles for those tooltips.
2026-08-15 10:45:30 +03:00

146 lines
4.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Kalıcı depo: ayarlar, kanal baseline önbelleği ve handle -> kanal kimliği
* eşleşmesi.
*
* Baseline önbelleği olmadan ana sayfada aşağı her kaydırmada onlarca kanal
* için yeniden istek atılırdı. Sayfa ömrü boyunca ayrıca bellekte tutulur,
* aynı kanal için aynı oturumda depoya bir kez gidilir.
*/
var OBStore = (function () {
"use strict";
var api = (typeof browser !== "undefined" ? browser : chrome);
var local = api.storage.local;
/* 2: baseline videolarına başlık eklendi (panelin grafik ipuçları için). */
var CACHE_VERSION = 2;
var MAX_CHANNELS = 400; /* aşılınca en eski baseline'lar silinir */
var DEFAULTS = {
enabled: true,
baselineSize: 30, /* kanalın son kaç videosu medyanı oluşturur */
ttlHours: 24,
minScore: 0, /* altındaki skorlar rozet basmaz (0 = hepsi) */
scoreShorts: true,
showPanel: true,
language: "auto" /* auto | tr | en */
};
var mem = {}; /* anahtar -> değer, sayfa ömrü boyunca */
var settingsCache = null;
function get(keys) {
return local.get(keys);
}
function set(obj) {
return local.set(obj);
}
function settings() {
if (settingsCache) return Promise.resolve(settingsCache);
return get("settings").then(function (r) {
settingsCache = Object.assign({}, DEFAULTS, r.settings || {});
return settingsCache;
});
}
function saveSettings(patch) {
return settings().then(function (s) {
settingsCache = Object.assign({}, s, patch);
return set({ settings: settingsCache });
});
}
/* --- Kanal baseline'ı ------------------------------------------------ */
function baselineKey(channelId) { return "bl:" + channelId; }
function readBaseline(channelId, size, ttlHours) {
var key = baselineKey(channelId);
var fresh = function (blob) {
if (!blob || blob.v !== CACHE_VERSION || blob.size !== size) return null;
if (Date.now() - blob.fetchedAt > ttlHours * 3600 * 1000) return null;
return blob;
};
if (mem[key] !== undefined) return Promise.resolve(fresh(mem[key]));
return get(key).then(function (r) {
mem[key] = r[key] || null;
return fresh(mem[key]);
});
}
function writeBaseline(channelId, blob) {
var key = baselineKey(channelId);
mem[key] = blob;
var payload = {};
payload[key] = blob;
return set(payload).then(function () { return touchIndex(channelId); });
}
/* Silinecekleri seçebilmek için kanal başına son yazma zamanı tutulur.
* storage.local'ın tamamını okuyup taramak yerine tek bir dizin anahtarı. */
function touchIndex(channelId) {
return get("blIndex").then(function (r) {
var index = r.blIndex || {};
index[channelId] = Date.now();
var ids = Object.keys(index);
if (ids.length > MAX_CHANNELS) {
ids.sort(function (a, b) { return index[a] - index[b]; });
var drop = ids.slice(0, ids.length - MAX_CHANNELS + 40);
for (var i = 0; i < drop.length; i++) {
delete index[drop[i]];
delete mem[baselineKey(drop[i])];
}
local.remove(drop.map(baselineKey));
}
return set({ blIndex: index });
});
}
/* --- handle -> kanal kimliği ----------------------------------------- */
function readChannelId(handle) {
var key = "ch:" + handle.toLowerCase();
if (mem[key] !== undefined) return Promise.resolve(mem[key]);
return get(key).then(function (r) {
mem[key] = r[key] || null;
return mem[key];
});
}
function writeChannelId(handle, channelId) {
var key = "ch:" + handle.toLowerCase();
mem[key] = channelId;
var payload = {};
payload[key] = channelId;
return set(payload);
}
function clearBaselines() {
mem = {};
return get("blIndex").then(function (r) {
var ids = Object.keys(r.blIndex || {});
return local.remove(ids.map(baselineKey).concat(["blIndex"]));
});
}
function stats() {
return get("blIndex").then(function (r) {
return { channels: Object.keys(r.blIndex || {}).length };
});
}
return {
DEFAULTS: DEFAULTS,
CACHE_VERSION: CACHE_VERSION,
settings: settings,
saveSettings: saveSettings,
readBaseline: readBaseline,
writeBaseline: writeBaseline,
readChannelId: readChannelId,
writeChannelId: writeChannelId,
clearBaselines: clearBaselines,
stats: stats
};
})();