diff --git a/src/document/aidMap.ts b/src/document/aidMap.ts new file mode 100644 index 00000000..67fc2fd9 --- /dev/null +++ b/src/document/aidMap.ts @@ -0,0 +1,23 @@ +import { BiliVideoDetail } from "../requests/type/BilibiliRequestType"; +import { VideoID } from "../types"; +import { DataCache } from "../utils/cache"; + +const cache = new DataCache(); + +export function saveAidFromDetail(detail: BiliVideoDetail): void { + saveAid(detail.aid, detail.bvid); +} + +export function saveAid(aid: number | string, bvid: VideoID): void { + if (typeof aid === "string" && aid.startsWith("av")) { + aid = aid.replace("av", ""); + } + cache.set(aid.toString(), bvid); +} + +export function getBvid(aid: number | string): VideoID { + if (window?.__INITIAL_STATE__?.aid && window.__INITIAL_STATE__.bvid) { + saveAid(window.__INITIAL_STATE__.aid, window.__INITIAL_STATE__.bvid); + } + return cache.getFromCache(aid.toString()); +} diff --git a/src/document/document.ts b/src/document/document.ts index bf3dcde7..d88ce967 100644 --- a/src/document/document.ts +++ b/src/document/document.ts @@ -1,5 +1,6 @@ -import { BilibiliResponse, BiliPlayInfo } from "../requests/type/BilibiliRequestType"; +import { BilibiliResponse, BiliPlayInfo, BiliVideoDetail } from "../requests/type/BilibiliRequestType"; import { InjectedScriptMessageSend, sourceId } from "../utils/injectedScriptMessageUtils"; +import { getBvid, saveAidFromDetail } from "./aidMap"; import { getFrameRate, playUrlResponseToPlayInfo, savePlayInfo } from "./frameRateUtils"; const sendMessageToContent = (messageData: InjectedScriptMessageSend, payload): void => { @@ -54,6 +55,9 @@ function processURLRequest(url: URL, responseText: string): void { if (cid && response?.data?.dash?.video) { savePlayInfo(cid, playUrlResponseToPlayInfo(response.data)); } + } else if (url.pathname.includes("/x/player/wbi/v2")) { + const response = JSON.parse(responseText) as BilibiliResponse; + saveAidFromDetail(response.data); } } @@ -71,6 +75,8 @@ function windowMessageListener(message: MessageEvent) { sendMessageToContent(data, window?.__INITIAL_STATE__?.upData?.mid); } else if (data.type === "getDescription") { sendMessageToContent(data, window?.__INITIAL_STATE__?.videoData?.desc); + } else if (data.type === "convertAidToBvid") { + sendMessageToContent(data, getBvid(data.payload as string)); } } } diff --git a/src/globals.d.ts b/src/globals.d.ts index d3879e72..5a7b1375 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -1,5 +1,5 @@ import SBObject from "./config"; -import { BilibiliResponse, BiliPlayInfo } from "./requests/type/BilibiliRequestType"; +import { BilibiliResponse, BiliPlayInfo, BiliVideoDetail } from "./requests/type/BilibiliRequestType"; declare global { interface Window { SB: typeof SBObject; @@ -8,7 +8,7 @@ declare global { aid: number; cid: number; upData: { mid: string }; - videoData: { desc: string }; + videoData: BiliVideoDetail; }; __playinfo__?: BilibiliResponse; } diff --git a/src/requests/type/BilibiliRequestType.ts b/src/requests/type/BilibiliRequestType.ts index 6a685349..e737231a 100644 --- a/src/requests/type/BilibiliRequestType.ts +++ b/src/requests/type/BilibiliRequestType.ts @@ -5,7 +5,47 @@ export interface BilibiliResponse { data: T; } -export interface BiliVideoDetail {} +export interface BiliVideoDetail { + bvid: string; + aid: number; + cid: number; + owner: { + mid: number; + name: string; + face: string; + }; + + videos: number; + pages: BilibiliPagelistDetail[]; + + tid: number; + tname: string; + copyright: number; + pic: string; + title: string; + pubdate: number; + ctime: number; + desc: string; + desc_v2: { + raw_text: string; + type: number; + biz_id: number; + }[]; + state: number; + duration: number; +} + +export interface BilibiliPagelistDetail { + cid: number; + page: number; + from: string; + part: string; + duration: number; + vid: string | null; + weblink: string | null; + dimension: unknown | null; + first_frame: string | null; +} export interface BiliPlayInfo { quality: number; diff --git a/src/thumbnail-utils/thumbnails.ts b/src/thumbnail-utils/thumbnails.ts index 507131df..b96cda12 100644 --- a/src/thumbnail-utils/thumbnails.ts +++ b/src/thumbnail-utils/thumbnails.ts @@ -19,10 +19,9 @@ export async function labelThumbnail( // find all links in the thumbnail, reduce to only one video ID const links = Array.from(thumbnail.querySelectorAll("a[href]")) as Array; const videoIDs = new Set( - links - .filter((link) => link && link.href) - .map((link) => getBvIDFromURL(link.href)) - .filter((id) => id) + (await Promise.allSettled(links.filter((link) => link && link.href).map((link) => getBvIDFromURL(link.href)))) + .filter((result) => result.status === "fulfilled") + .map((result) => result.value) ); if (videoIDs.size !== 1) { // none or multiple video IDs found @@ -35,7 +34,7 @@ export async function labelThumbnail( const { overlay, text } = await createOrGetThumbnail(thumbnail); if (thumbnailOldHref) { - const oldVideoID = getBvIDFromURL(thumbnailOldHref); + const oldVideoID = await getBvIDFromURL(thumbnailOldHref); if (oldVideoID && oldVideoID == videoID) { return overlay; } diff --git a/src/utils/injectedScriptMessageUtils.ts b/src/utils/injectedScriptMessageUtils.ts index 0822977e..6614cba9 100644 --- a/src/utils/injectedScriptMessageUtils.ts +++ b/src/utils/injectedScriptMessageUtils.ts @@ -1,6 +1,8 @@ +import { VideoID } from "./video"; + export const sourceId = "biliSponsorBlock"; -export interface InjectedScriptMessageBase { +interface InjectedScriptMessageBase { source: string; id: string; type: string; @@ -8,19 +10,21 @@ export interface InjectedScriptMessageBase { export interface InjectedScriptMessageSend extends InjectedScriptMessageBase { responseType: string; + payload?: unknown; } export interface InjectedScriptMessageRecieve extends InjectedScriptMessageBase { data: unknown; } -export interface InjectedScriptMessageType { +interface InjectedScriptMessageType { sendType: string; responseType: string; } export async function getPropertyFromWindow( messageType: InjectedScriptMessageType, + payload?: unknown, timeout = 200 ): Promise { return new Promise((resolve) => { @@ -42,6 +46,7 @@ export async function getPropertyFromWindow( source: sourceId, type: messageType.sendType, responseType: messageType.responseType, + payload: payload, id: id, } as InjectedScriptMessageSend, "/" @@ -61,3 +66,13 @@ export async function getVideoDescriptionFromWindow(): Promise { responseType: "returnDescription", }); } + +export async function getBvidFromAidFromWindow(aid: string): Promise { + return getPropertyFromWindow( + { + sendType: "convertAidToBvid", + responseType: "returnAidToBvid", + }, + aid + ); +} diff --git a/src/utils/parseVideoID.ts b/src/utils/parseVideoID.ts index b43cf27d..032a2a68 100644 --- a/src/utils/parseVideoID.ts +++ b/src/utils/parseVideoID.ts @@ -1,6 +1,5 @@ -import { getBvIDfromAvIDBiliApi } from "../requests/bilibiliApi"; import { BILI_DOMAINS } from "./constants"; -import { getPropertyFromWindow } from "./injectedScriptMessageUtils"; +import { getBvidFromAidFromWindow, getPropertyFromWindow } from "./injectedScriptMessageUtils"; import { VideoID } from "./video"; export async function getBilibiliVideoID(url?: string): Promise { @@ -32,7 +31,7 @@ const BILIBILI_VIDEO_URL_REGEX = /^\/video\/((BV1[a-zA-Z0-9]{9})|(av\d+))\/?/; /** * Parse without side effects */ -export function getBvIDFromURL(url: string): VideoID | null { +export async function getBvIDFromURL(url: string): Promise { //Attempt to parse url let urlObject: URL | null = null; try { @@ -56,7 +55,7 @@ export function getBvIDFromURL(url: string): VideoID | null { return idMatch[2] as VideoID; } else if (idMatch && idMatch[3]) { // av id - return getBvIDFromCache(idMatch[3], "-1" as VideoID); + return await getBvidFromAidFromWindow(idMatch[3]); } } else if (urlObject.host == "www.bilibili.com" && urlObject.pathname.startsWith("/list/")) { const id = urlObject.searchParams.get("bvid"); @@ -66,23 +65,6 @@ export function getBvIDFromURL(url: string): VideoID | null { return null; } -const AvToBvMapCache = new Map(); -const AvToBvLoading = new Set(); -function getBvIDFromCache(avID: string, placeholder: null | VideoID = null): VideoID | null { - const bvID = AvToBvMapCache.get(avID); - if (bvID) return bvID; - - if (!AvToBvLoading.has(avID)) { - AvToBvLoading.add(avID); - getBvIDfromAvIDBiliApi(avID.replace("av", "")).then((bvID) => { - AvToBvMapCache.set(avID, bvID as VideoID); - AvToBvLoading.delete(avID); - }); - } - - return placeholder; -} - /** * Validates and sanitizes a YouTube video ID. */