import { Category, CategorySkipOption, VideoID } from "../types"; import { getHash } from "../../maze-utils/src/hash"; import { logWarn } from "./logger"; import { asyncRequestToServer } from "./requests"; import { getCategorySelection } from "./skipRule"; import { FetchResponse, logRequest } from "../../maze-utils/src/background-request-proxy"; export interface VideoLabelsCacheData { category: Category; hasStartSegment: boolean; } export interface LabelCacheEntry { timestamp: number; videos: Record; } const labelCache: Record = {}; const cacheLimit = 1000; async function getLabelHashBlock(hashPrefix: string): Promise { // Check cache const cachedEntry = labelCache[hashPrefix]; if (cachedEntry) { return cachedEntry; } let response: FetchResponse; try { response = await asyncRequestToServer("GET", `/api/videoLabels/${hashPrefix}?hasStartSegment=true`); } catch (e) { console.error("[SB] Caught error while fetching video labels", e) return null; } if (response.status !== 200) { logRequest(response, "SB", "video labels"); // No video labels or server down labelCache[hashPrefix] = { timestamp: Date.now(), videos: {}, }; return null; } try { const data = JSON.parse(response.responseText); const newEntry: LabelCacheEntry = { timestamp: Date.now(), videos: Object.fromEntries(data.map(video => [video.videoID, { category: video.segments[0]?.category, hasStartSegment: video.hasStartSegment }])), }; labelCache[hashPrefix] = newEntry; if (Object.keys(labelCache).length > cacheLimit) { // Remove oldest entry const oldestEntry = Object.entries(labelCache).reduce((a, b) => a[1].timestamp < b[1].timestamp ? a : b); delete labelCache[oldestEntry[0]]; } return newEntry; } catch (e) { logWarn(`Error parsing video labels: ${e}`); return null; } } export async function getVideoLabel(videoID: VideoID): Promise { const prefix = (await getHash(videoID, 1)).slice(0, 4); const result = await getLabelHashBlock(prefix); if (result) { const category = result.videos[videoID]?.category; if (category && getCategorySelection(result.videos[videoID]).option !== CategorySkipOption.Disabled) { return category; } else { return null; } } return null; } export async function getHasStartSegment(videoID: VideoID): Promise { const prefix = (await getHash(videoID, 1)).slice(0, 4); const result = await getLabelHashBlock(prefix); if (result) { return result?.videos[videoID]?.hasStartSegment ?? false; } return null; }