From 2abd89ef295d1a82468d17fda4255dc2088ab8d6 Mon Sep 17 00:00:00 2001 From: hanyd Date: Mon, 13 Jan 2025 17:01:36 +0800 Subject: [PATCH] Add type for different selectors --- src/content.ts | 4 +-- src/requests/videoLabels.ts | 12 ++++++--- src/thumbnail-utils/thumbnail-selectors.ts | 29 ++++++++++++++++++---- src/thumbnail-utils/thumbnailManagement.ts | 22 ++++++++-------- src/thumbnail-utils/thumbnails.ts | 28 +++++++++++---------- 5 files changed, 59 insertions(+), 36 deletions(-) diff --git a/src/content.ts b/src/content.ts index 026e7877..63cf3969 100644 --- a/src/content.ts +++ b/src/content.ts @@ -17,7 +17,7 @@ import { getPortVideoByHash, postPortVideo, postPortVideoVote, updatePortedSegme import { asyncRequestToServer } from "./requests/requests"; import { getSegmentsByHash } from "./requests/segments"; import { getVideoLabel } from "./requests/videoLabels"; -import { setupThumbnailListener, updateAll } from "./thumbnail-utils/thumbnailManagement"; +import { checkPageForNewThumbnails, setupThumbnailListener } from "./thumbnail-utils/thumbnailManagement"; import { ActionType, Category, @@ -436,7 +436,7 @@ function contentConfigUpdateListener(changes: StorageChangesObject) { break; case "fullVideoSegments": case "fullVideoLabelsOnThumbnails": - updateAll(); + checkPageForNewThumbnails(); break; } } diff --git a/src/requests/videoLabels.ts b/src/requests/videoLabels.ts index 1e074eba..5e25385f 100644 --- a/src/requests/videoLabels.ts +++ b/src/requests/videoLabels.ts @@ -15,10 +15,14 @@ const labelCache: Record = {}; const cacheLimit = 1000; async function getLabelHashBlock(hashPrefix: string, refreshCache: boolean = false): Promise { - // Check cache - const cachedEntry = labelCache[hashPrefix]; - if (cachedEntry && !refreshCache) { - return cachedEntry; + if (refreshCache) { + delete labelCache[hashPrefix]; + } else { + // Check cache + const cachedEntry = labelCache[hashPrefix]; + if (cachedEntry) { + return cachedEntry; + } } const response = await asyncRequestToServer("GET", `/api/videoLabels/${hashPrefix}`, {}, refreshCache); diff --git a/src/thumbnail-utils/thumbnail-selectors.ts b/src/thumbnail-utils/thumbnail-selectors.ts index cd656494..5cb76040 100644 --- a/src/thumbnail-utils/thumbnail-selectors.ts +++ b/src/thumbnail-utils/thumbnail-selectors.ts @@ -4,6 +4,7 @@ interface ThumbnailSelector { containerSelector: string; thumbnailSelector: string; customLinkSelector?: string; + customLinkAttribute?: string; } // TODO: support customLinkSelector @@ -22,7 +23,6 @@ const thumbnailSelectors: { [key: string]: ThumbnailSelector } = { // 历史视频弹出框 containerSelector: ".bili-header .right-entry .v-popover-wrap:nth-of-type(5) div.v-popover-content", thumbnailSelector: "a.header-history-card", - customLinkSelector: "", }, "mainPageRecommendation": { // 主页 @@ -110,6 +110,14 @@ const pageTypeSepecialSelector: { [key in PageType]: string[] } = { [PageType.Embed]: [], }; +const combinedPageTypeSelectors = {}; +for (const pageType in pageTypeSepecialSelector) { + combinedPageTypeSelectors[pageType as PageType] = [ + ...commonSelector, + ...pageTypeSepecialSelector[pageType as PageType], + ]; +} + const pageTypeSelector: { [key in PageType]?: ThumbnailSelector } = {}; const thumbnailElementSelectors: { [key in PageType]?: string[] } = {}; const thumbnailContainerSelectors: { [key in PageType]?: string[] } = {}; @@ -122,14 +130,25 @@ for (const [key, value] of Object.entries(pageTypeSepecialSelector)) { thumbnailContainerSelectors[key] = combinedSelector.map((s) => thumbnailSelectors[s].containerSelector); } -export function getThumbnailContainerElements(pageType: PageType) { - return thumbnailContainerSelectors[pageType]; +export function getThumbnailContainerElements(pageType: PageType): { containerType: string; selector: string }[] { + return combinedPageTypeSelectors[pageType].map((type: string) => ({ + containerType: type, + selector: thumbnailSelectors[type].containerSelector, + })); } export function getThumbnailLink(thumbnail: HTMLElement): HTMLElement | null { return thumbnail.querySelector("a"); } -export function getThumbnailSelectors(pageType: PageType) { - return thumbnailElementSelectors[pageType].join(", "); +export function getThumbnailSelectors(containerType: string) { + return thumbnailSelectors[containerType].thumbnailSelector; +} + +export function getLinkSelectors(containerType: string) { + return thumbnailSelectors[containerType].customLinkSelector ?? "a[href]"; +} + +export function getLinkAttribute(containerType: string) { + return thumbnailSelectors[containerType].customLinkAttribute ?? "href"; } diff --git a/src/thumbnail-utils/thumbnailManagement.ts b/src/thumbnail-utils/thumbnailManagement.ts index 30c420fc..9867fb66 100644 --- a/src/thumbnail-utils/thumbnailManagement.ts +++ b/src/thumbnail-utils/thumbnailManagement.ts @@ -3,7 +3,7 @@ import { waitFor } from "../utils/"; import { addCleanupListener } from "../utils/cleanup"; import { getPageType } from "../utils/video"; import { getThumbnailContainerElements, getThumbnailSelectors } from "./thumbnail-selectors"; -import { insertSBIconDefinition, labelThumbnail, labelThumbnails } from "./thumbnails"; +import { insertSBIconDefinition, labelThumbnail } from "./thumbnails"; export type ThumbnailListener = (newThumbnails: HTMLElement[]) => void; @@ -15,10 +15,11 @@ export function setupThumbnailListener(): void { onInitialLoad(); // listen to container child changes - getThumbnailContainerElements(getPageType()).forEach((selector) => { + getThumbnailContainerElements(getPageType()).forEach(({ containerType, selector }) => { + console.log(containerType, selector); void waitFor(() => document.querySelector(selector), 10000) .then((thumbnailContainer) => { - labelNewThumbnails(thumbnailContainer); // fire thumbnail check once when the container is loaded + labelNewThumbnails(thumbnailContainer, containerType); // fire thumbnail check once when the container is loaded if (!thumbnailContainer) return; thumbnailContainerObserver ??= new MutationObserver(() => checkPageForNewThumbnails()); thumbnailContainerObserver?.observe(thumbnailContainer, { childList: true, subtree: true }); @@ -70,19 +71,16 @@ export function checkPageForNewThumbnails() { } lastThumbnailCheck = performance.now(); - for (const selector of getThumbnailContainerElements(getPageType())) { + for (const { containerType, selector } of getThumbnailContainerElements(getPageType())) { waitFor(() => document.querySelector(selector), 10000) - .then((thumbnailContainer) => labelNewThumbnails(thumbnailContainer)) + .then((thumbnailContainer) => labelNewThumbnails(thumbnailContainer, containerType)) .catch((err) => console.log(err)); } } -async function labelNewThumbnails(container: Element) { +async function labelNewThumbnails(container: Element, containerType: string) { if (!container || !document.body.contains(container)) return; - const thumbnails = container.querySelectorAll(getThumbnailSelectors(getPageType())) as NodeListOf; - thumbnails.forEach((t) => labelThumbnail(t as HTMLImageElement)); -} - -export function updateAll(): void { - labelThumbnails([...handledThumbnailsObserverMap.keys()]); + const thumbnails = container.querySelectorAll(getThumbnailSelectors(containerType)) as NodeListOf; + console.log(thumbnails); + thumbnails.forEach((t) => labelThumbnail(t as HTMLImageElement, containerType)); } diff --git a/src/thumbnail-utils/thumbnails.ts b/src/thumbnail-utils/thumbnails.ts index e4005f35..c9365304 100644 --- a/src/thumbnail-utils/thumbnails.ts +++ b/src/thumbnail-utils/thumbnails.ts @@ -2,22 +2,31 @@ import Config from "../config"; import { getVideoLabel } from "../requests/videoLabels"; import { waitFor } from "../utils/"; import { getBvIDFromURL } from "../utils/parseVideoID"; +import { getLinkAttribute, getLinkSelectors } from "./thumbnail-selectors"; -export async function labelThumbnails(thumbnails: HTMLElement[]): Promise { - await Promise.all(thumbnails.map((t) => labelThumbnail(t as HTMLElement))); +export async function labelThumbnails(thumbnails: HTMLElement[], containerType: string): Promise { + await Promise.all(thumbnails.map((t) => labelThumbnail(t as HTMLElement, containerType))); } -export async function labelThumbnail(thumbnail: HTMLElement, thumbnailOldHref?: string): Promise { +export async function labelThumbnail(thumbnail: HTMLElement, containerType: string): Promise { if (!Config.config?.fullVideoSegments || !Config.config?.fullVideoLabelsOnThumbnails) { hideThumbnailLabel(thumbnail); return null; } // find all links in the thumbnail, reduce to only one video ID - const links: HTMLAnchorElement[] = thumbnail.matches("a[href]") ? [thumbnail as HTMLAnchorElement] : []; - links.push(...Array.from(thumbnail.querySelectorAll("a[href]") as NodeListOf)); + const links: HTMLAnchorElement[] = thumbnail.matches(getLinkSelectors(containerType)) + ? [thumbnail as HTMLAnchorElement] + : []; + links.push( + ...Array.from(thumbnail.querySelectorAll(getLinkSelectors(containerType)) as NodeListOf) + ); const videoIDs = new Set( - (await Promise.allSettled(links.filter((link) => link && link.href).map((link) => getBvIDFromURL(link.href)))) + ( + await Promise.allSettled( + links.map((link) => getBvIDFromURL(link.getAttribute(getLinkAttribute(containerType)))) + ) + ) .filter((result) => result.status === "fulfilled") .map((result) => result.value) .filter((id) => !!id) @@ -32,13 +41,6 @@ export async function labelThumbnail(thumbnail: HTMLElement, thumbnailOldHref?: // 获取或创建缩略图标签 const { overlay, text } = await createOrGetThumbnail(thumbnail); - if (thumbnailOldHref) { - const oldVideoID = await getBvIDFromURL(thumbnailOldHref); - if (oldVideoID && oldVideoID == videoID) { - return overlay; - } - } - const category = await getVideoLabel(videoID); if (!category) { hideThumbnailLabel(thumbnail);