import { ComponentEntry } from '@/components/types' import { defineComponentMetadata } from '@/components/define' import type { RawWatchlaterItem } from '@/components/video/watchlater' const getBvidFromElement = (element: Element) => { const pic = element.querySelector('.av-pic, .bili-cover-card') as HTMLAnchorElement const bvid = pic.href.match(/bvid=([^&]+)/)?.[1] return bvid } const isRedirected = (element: Element) => { const pic = element.querySelector('.av-pic, .bili-cover-card') as HTMLAnchorElement return /video\/BV/.test(pic.href) } const redirect = (element: Element, watchlaterItem: RawWatchlaterItem) => { try { const { bvid, cid, pages } = watchlaterItem const page = pages.find(p => p.cid === cid)?.page ?? 1 const url = page > 1 ? `https://www.bilibili.com/video/${bvid}/?p=${page}` : `https://www.bilibili.com/video/${bvid}/` const pic = element.querySelector('.av-pic, .bili-cover-card') as HTMLAnchorElement pic.target = '_blank' pic.href = url const title = element.querySelector( '.av-about .t, .bili-video-card__title a, .video-card__right .title', ) as HTMLAnchorElement title.target = '_blank' title.href = url } catch (error) { console.error(`[watchlater redirect] error at ${watchlaterItem.bvid}`, element, error) } } const entry: ComponentEntry = async ({ settings }) => { if (!settings.options.page) { return } const { select } = await import('@/core/spin-query') const { childListSubtree } = await import('@/core/observer') const { getWatchlaterList } = await import('@/components/video/watchlater') const { useScopedConsole } = await import('@/core/utils/log') const console = useScopedConsole('稍后再看重定向') const list: RawWatchlaterItem[] = await getWatchlaterList(true) const listContainer = await select('.watch-later-list .list-box > span, .watchlater-list') if (!listContainer) { return } const tryRedirect = (element: Element) => { if (isRedirected(element)) { return } const bvid = getBvidFromElement(element) if (bvid === undefined) { console.warn('bvid not found for', element.outerHTML) return } const listItem = list.find(it => it.bvid === bvid) if (listItem === undefined) { console.warn('bvid no match for', bvid) return } console.log('redirect for', bvid) redirect(element, listItem) } const runRedirect = lodash.debounce(() => { const videoCards = listContainer.querySelectorAll('.av-item, .video-card') console.log('run redirect, length =', videoCards.length) videoCards.forEach(card => { tryRedirect(card) }) }, 200) runRedirect() childListSubtree(listContainer, async records => { records.forEach(r => { const hasVideoCardChange = [...r.addedNodes].some( node => node instanceof HTMLElement && ['bili-video-card__wrap', 'video-card', 'watchlater-list-container'].some(value => node.classList.contains(value), ), ) // console.log({ nodes: r.addedNodes, hasVideoCardChange }) if (hasVideoCardChange) { runRedirect() } }) }) } export const component = defineComponentMetadata({ name: 'watchlaterRedirect', displayName: '稍后再看重定向', entry, options: { page: { displayName: '重定向页面', defaultValue: true, }, navbar: { displayName: '重定向顶栏', defaultValue: true, }, }, urlInclude: [ 'https://www.bilibili.com/watchlater/#/list', 'https://www.bilibili.com/watchlater/list#/list', 'https://www.bilibili.com/watchlater/list', ], tags: [componentsTags.utils, componentsTags.video], })