mirror of
https://github.com/yusufipk/YouTubeOutlierBadge.git
synced 2026-09-11 10:56:14 +00:00
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.
146 lines
4.4 KiB
JavaScript
146 lines
4.4 KiB
JavaScript
/* 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
|
||
};
|
||
})();
|