diff --git a/lib/routes/12306/zxdt.ts b/lib/routes/12306/zxdt.ts index cf27ba013d..164159352f 100644 --- a/lib/routes/12306/zxdt.ts +++ b/lib/routes/12306/zxdt.ts @@ -1,7 +1,7 @@ import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; -const url = require('url'); +import * as url from 'node:url'; export default async (ctx) => { const id = ctx.req.param('id') || -1; diff --git a/lib/routes/163/dy2.ts b/lib/routes/163/dy2.ts index f249afb5f7..66ee621704 100644 --- a/lib/routes/163/dy2.ts +++ b/lib/routes/163/dy2.ts @@ -1,7 +1,7 @@ import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; -const iconv = require('iconv-lite'); +import iconv from 'iconv-lite'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import { parseDyArticle } from './utils'; diff --git a/lib/routes/163/news/rank.ts b/lib/routes/163/news/rank.ts index 7db04339a5..6cc37f65a2 100644 --- a/lib/routes/163/news/rank.ts +++ b/lib/routes/163/news/rank.ts @@ -1,7 +1,7 @@ import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; -const iconv = require('iconv-lite'); +import iconv from 'iconv-lite'; import { parseDate } from '@/utils/parse-date'; const rootUrl = 'https://news.163.com'; diff --git a/lib/routes/163/renjian.ts b/lib/routes/163/renjian.ts index f097f950ad..a50a5d2f28 100644 --- a/lib/routes/163/renjian.ts +++ b/lib/routes/163/renjian.ts @@ -1,7 +1,7 @@ import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; -const iconv = require('iconv-lite'); +import iconv from 'iconv-lite'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; diff --git a/lib/routes/163/utils.ts b/lib/routes/163/utils.ts index a1ab93de16..61fa5beae9 100644 --- a/lib/routes/163/utils.ts +++ b/lib/routes/163/utils.ts @@ -3,7 +3,7 @@ const __dirname = getCurrentPath(import.meta.url); import got from '@/utils/got'; import { load } from 'cheerio'; -const iconv = require('iconv-lite'); +import iconv from 'iconv-lite'; import { art } from '@/utils/render'; import * as path from 'node:path'; diff --git a/lib/routes/19lou/index.ts b/lib/routes/19lou/index.ts index 530fe4c846..97be42443b 100644 --- a/lib/routes/19lou/index.ts +++ b/lib/routes/19lou/index.ts @@ -3,7 +3,7 @@ import got from '@/utils/got'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; -const iconv = require('iconv-lite'); +import iconv from 'iconv-lite'; import { isValidHost } from '@/utils/valid-host'; const setCookie = function (cookieName, cookieValue, seconds, path, domain, secure) { diff --git a/lib/routes/1point3acres/utils.ts b/lib/routes/1point3acres/utils.ts index 07054a9b34..7c13fcaeac 100644 --- a/lib/routes/1point3acres/utils.ts +++ b/lib/routes/1point3acres/utils.ts @@ -5,7 +5,7 @@ import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -const bbcode = require('bbcodejs'); +import bbcode from 'bbcodejs'; const rootUrl = 'https://instant.1point3acres.com'; const apiRootUrl = 'https://api.1point3acres.com'; diff --git a/lib/routes/2cycd/index.ts b/lib/routes/2cycd/index.ts index 418ccc5d0b..d0d355ae7a 100644 --- a/lib/routes/2cycd/index.ts +++ b/lib/routes/2cycd/index.ts @@ -3,7 +3,7 @@ import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -const iconv = require('iconv-lite'); +import iconv from 'iconv-lite'; // http://www.2cycd.com/forum.php?mod=forumdisplay&fid=43&orderby=dateline diff --git a/lib/routes/36kr/utils.ts b/lib/routes/36kr/utils.ts index c17b8d6fe0..857713c0e6 100644 --- a/lib/routes/36kr/utils.ts +++ b/lib/routes/36kr/utils.ts @@ -1,6 +1,6 @@ import got from '@/utils/got'; import { load } from 'cheerio'; -const CryptoJS = require('crypto-js'); +import CryptoJS from 'crypto-js'; const rootUrl = 'https://www.36kr.com'; diff --git a/lib/routes/4ksj/forum.ts b/lib/routes/4ksj/forum.ts index 9003c5ea12..a54ff7eebb 100644 --- a/lib/routes/4ksj/forum.ts +++ b/lib/routes/4ksj/forum.ts @@ -8,7 +8,7 @@ import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -const iconv = require('iconv-lite'); +import iconv from 'iconv-lite'; export default async (ctx) => { const id = ctx.req.param('id') ?? '2-1'; diff --git a/lib/routes/56kog/util.ts b/lib/routes/56kog/util.ts index 16a2244a7a..e37c0133da 100644 --- a/lib/routes/56kog/util.ts +++ b/lib/routes/56kog/util.ts @@ -3,7 +3,7 @@ const __dirname = getCurrentPath(import.meta.url); import got from '@/utils/got'; import { load } from 'cheerio'; -const iconv = require('iconv-lite'); +import iconv from 'iconv-lite'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; diff --git a/lib/routes/591/list.ts b/lib/routes/591/list.ts index 337ede19a0..d97b6836ff 100644 --- a/lib/routes/591/list.ts +++ b/lib/routes/591/list.ts @@ -3,7 +3,7 @@ const __dirname = getCurrentPath(import.meta.url); import * as path from 'node:path'; -const { CookieJar } = require('tough-cookie'); +import { CookieJar } from 'tough-cookie'; import { load } from 'cheerio'; import got from '@/utils/got'; diff --git a/lib/routes/5eplay/index.ts b/lib/routes/5eplay/index.ts index 144764f7ab..86e81b6407 100644 --- a/lib/routes/5eplay/index.ts +++ b/lib/routes/5eplay/index.ts @@ -1,6 +1,6 @@ import cache from '@/utils/cache'; import { load } from 'cheerio'; -const zlib = require('zlib'); +import zlib from 'zlib'; import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { config } from '@/config'; diff --git a/lib/routes/6v123/utils.ts b/lib/routes/6v123/utils.ts index 8bbc501c15..72ccebc62b 100644 --- a/lib/routes/6v123/utils.ts +++ b/lib/routes/6v123/utils.ts @@ -1,6 +1,6 @@ import cache from '@/utils/cache'; import got from '@/utils/got'; -const iconv = require('iconv-lite'); +import iconv from 'iconv-lite'; import { load } from 'cheerio'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; diff --git a/lib/routes/8264/list.ts b/lib/routes/8264/list.ts index fe9a949bb6..339575922a 100644 --- a/lib/routes/8264/list.ts +++ b/lib/routes/8264/list.ts @@ -4,7 +4,7 @@ const __dirname = getCurrentPath(import.meta.url); import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; -const iconv = require('iconv-lite'); +import iconv from 'iconv-lite'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; diff --git a/lib/routes/ainvest/utils.ts b/lib/routes/ainvest/utils.ts index cef8b61140..9eb38ee53f 100644 --- a/lib/routes/ainvest/utils.ts +++ b/lib/routes/ainvest/utils.ts @@ -1,6 +1,6 @@ -const crypto = require('crypto'); -const CryptoJS = require('crypto-js'); -const { KJUR, KEYUTIL, hextob64 } = require('jsrsasign'); +import crypto from 'crypto'; +import CryptoJS from 'crypto-js'; +import { KJUR, KEYUTIL, hextob64 } from 'jsrsasign'; const publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCARnxLlrhTK28bEV7s2IROjT73KLSjfqpKIvV8L+Yhe4BrF0Ut4oOH728HZlbSF0C3N0vXZjLAFesoS4v1pYOjVCPXl920Lh2seCv82m0cK78WMGuqZTfA44Nv7JsQMHC3+J6IZm8YD53ft2d8mYBFgKektduucjx8sObe7eRyoQIDAQAB'; diff --git a/lib/routes/apiseven/blog.ts b/lib/routes/apiseven/blog.ts index 503b943cce..4cdbed77a3 100644 --- a/lib/routes/apiseven/blog.ts +++ b/lib/routes/apiseven/blog.ts @@ -3,7 +3,9 @@ import got from '@/utils/got'; import { load } from 'cheerio'; import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; -const md = require('markdown-it')({ +import MarkdownIt from 'markdown-it'; + +const md = MarkdownIt({ html: true, }); diff --git a/lib/routes/app-center/release.ts b/lib/routes/app-center/release.ts index 2c7b6b3f8d..d2b42376cc 100644 --- a/lib/routes/app-center/release.ts +++ b/lib/routes/app-center/release.ts @@ -6,7 +6,7 @@ import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; import * as path from 'node:path'; -const MarkdownIt = require('markdown-it'); +import MarkdownIt from 'markdown-it'; export default async (ctx) => { const user = ctx.req.param('user'); diff --git a/lib/routes/appstore/in-app-purchase.ts b/lib/routes/appstore/in-app-purchase.ts index 4111471413..1a293ae9de 100644 --- a/lib/routes/appstore/in-app-purchase.ts +++ b/lib/routes/appstore/in-app-purchase.ts @@ -1,5 +1,5 @@ import got from '@/utils/got'; -const url = require('url'); +import * as url from 'node:url'; import { load } from 'cheerio'; export default async (ctx) => { diff --git a/lib/routes/appstore/price.ts b/lib/routes/appstore/price.ts index 518f9c4f4e..785b5edc7e 100644 --- a/lib/routes/appstore/price.ts +++ b/lib/routes/appstore/price.ts @@ -1,5 +1,5 @@ import got from '@/utils/got'; -const currency = require('currency-symbol-map'); +import currency from 'currency-symbol-map'; export default async (ctx) => { const country = ctx.req.param('country'); const type = ctx.req.param('type').toLowerCase() === 'mac' ? 'macapps' : 'apps'; diff --git a/lib/routes/auto-stats/index.ts b/lib/routes/auto-stats/index.ts index edd7e1d8ce..d94907eac3 100644 --- a/lib/routes/auto-stats/index.ts +++ b/lib/routes/auto-stats/index.ts @@ -1,7 +1,7 @@ import cache from '@/utils/cache'; import got from '@/utils/got'; import { load } from 'cheerio'; -const iconv = require('iconv-lite'); +import iconv from 'iconv-lite'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; diff --git a/lib/routes/baidu/tieba/search.ts b/lib/routes/baidu/tieba/search.ts index 147c30480b..93367486fc 100644 --- a/lib/routes/baidu/tieba/search.ts +++ b/lib/routes/baidu/tieba/search.ts @@ -3,7 +3,7 @@ const __dirname = getCurrentPath(import.meta.url); import got from '@/utils/got'; import { load } from 'cheerio'; -const iconv = require('iconv-lite'); +import iconv from 'iconv-lite'; import timezone from '@/utils/timezone'; import { parseDate } from '@/utils/parse-date'; import { art } from '@/utils/render'; diff --git a/lib/routes/bdys/index.ts b/lib/routes/bdys/index.ts index b33d52a79c..43fb13f447 100644 --- a/lib/routes/bdys/index.ts +++ b/lib/routes/bdys/index.ts @@ -8,7 +8,7 @@ import { parseDate } from '@/utils/parse-date'; import timezone from '@/utils/timezone'; import { art } from '@/utils/render'; import * as path from 'node:path'; -const asyncPool = require('tiny-async-pool'); +import asyncPool from 'tiny-async-pool'; import { config } from '@/config'; // Visit https://www.bdys.me for the list of domains diff --git a/lib/routes/bigquant/collections.ts b/lib/routes/bigquant/collections.ts index e8f740afd8..304f50c851 100644 --- a/lib/routes/bigquant/collections.ts +++ b/lib/routes/bigquant/collections.ts @@ -1,6 +1,7 @@ import got from '@/utils/got'; import { parseDate } from '@/utils/parse-date'; -const md = require('markdown-it')({ +import MarkdownIt from 'markdown-it'; +const md = MarkdownIt({ html: true, }); diff --git a/lib/routes/bilibili/article.ts b/lib/routes/bilibili/article.ts index 26dcb3d567..e9bd984b0f 100644 --- a/lib/routes/bilibili/article.ts +++ b/lib/routes/bilibili/article.ts @@ -4,7 +4,7 @@ import cache from './cache'; import { parseDate } from '@/utils/parse-date'; export default async (ctx) => { const uid = ctx.req.param('uid'); - const name = await cache.getUsernameFromUID(ctx, uid); + const name = await cache.getUsernameFromUID(uid); const response = await got({ method: 'get', url: `https://api.bilibili.com/x/space/article?mid=${uid}&pn=1&ps=10&sort=publish_time&jsonp=jsonp`, @@ -18,7 +18,7 @@ export default async (ctx) => { const description = `${name} 的 bilibili 专栏`; const item = await Promise.all( data.articles.map(async (item) => { - const { url: art_url, description: eDescription } = await cache.getArticleDataFromCvid(ctx, item.id, uid); + const { url: art_url, description: eDescription } = await cache.getArticleDataFromCvid(item.id, uid); const publishDate = parseDate(item.publish_time * 1000); const single = { title: item.title, diff --git a/lib/routes/bilibili/cache.ts b/lib/routes/bilibili/cache.ts index 65a3357319..c5ced0529e 100644 --- a/lib/routes/bilibili/cache.ts +++ b/lib/routes/bilibili/cache.ts @@ -5,253 +5,273 @@ import { load } from 'cheerio'; import { config } from '@/config'; import logger from '@/utils/logger'; -export default { - getCookie: () => { - if (Object.keys(config.bilibili.cookies).length > 0) { - return config.bilibili.cookies[Object.keys(config.bilibili.cookies)[Math.floor(Math.random() * Object.keys(config.bilibili.cookies).length)]]; - } - const key = 'bili-cookie'; - return cache.tryGet(key, async () => { - // default Referer: https://www.bilibili.com is limited - // Bilibili return cookies with multiple set-cookie - // let response = await got('https://space.bilibili.com/1'); - // const setCookie = response.headers['set-cookie']; // should contain buvid3 and b_nut - // if (typeof setCookie === 'undefined') { - // return ''; - // } - // const cookie = setCookie.map((cookie) => cookie.split(';')[0]); - const cookie = []; - cookie.push(['b_lsid', utils.lsid()].join('='), ['_uuid', utils._uuid()].join('='), ['b_nut', Date.now().toString()].join('=')); - let response = await got('https://api.bilibili.com/x/frontend/finger/spi', { +const getCookie = () => { + if (Object.keys(config.bilibili.cookies).length > 0) { + return config.bilibili.cookies[Object.keys(config.bilibili.cookies)[Math.floor(Math.random() * Object.keys(config.bilibili.cookies).length)]]; + } + const key = 'bili-cookie'; + return cache.tryGet(key, async () => { + // default Referer: https://www.bilibili.com is limited + // Bilibili return cookies with multiple set-cookie + // let response = await got('https://space.bilibili.com/1'); + // const setCookie = response.headers['set-cookie']; // should contain buvid3 and b_nut + // if (typeof setCookie === 'undefined') { + // return ''; + // } + // const cookie = setCookie.map((cookie) => cookie.split(';')[0]); + const cookie = []; + cookie.push(['b_lsid', utils.lsid()].join('='), ['_uuid', utils._uuid()].join('='), ['b_nut', Date.now().toString()].join('=')); + let response = await got('https://api.bilibili.com/x/frontend/finger/spi', { + headers: { + Referer: 'https://www.bilibili.com/', + Cookie: cookie.join('; '), + }, + }); + cookie.push(['buvid3', encodeURIComponent(response.data.data.b_3)].join('='), ['bvuid4', encodeURIComponent(response.data.data.b_4)].join('=')); + const e = Math.floor(Date.now() / 1000); + const hexsign = utils.hexsign(e); + // await got('https://space.bilibili.com/1', { + // headers: { + // Referer: 'https://www.bilibili.com/', + // Cookie: cookie.join('; '), + // }, + // }); + try { + response = await got.post(`https://api.bilibili.com/bapis/bilibili.api.ticket.v1.Ticket/GenWebTicket?key_id=ec02&hexsign=${hexsign}&context[ts]=${e}&csrf=`, { headers: { - Referer: 'https://www.bilibili.com/', + Referer: 'https://space.bilibili.com/1', Cookie: cookie.join('; '), }, }); - cookie.push(['buvid3', encodeURIComponent(response.data.data.b_3)].join('='), ['bvuid4', encodeURIComponent(response.data.data.b_4)].join('=')); - const e = Math.floor(Date.now() / 1000); - const hexsign = utils.hexsign(e); - // await got('https://space.bilibili.com/1', { - // headers: { - // Referer: 'https://www.bilibili.com/', - // Cookie: cookie.join('; '), - // }, - // }); - try { - response = await got.post(`https://api.bilibili.com/bapis/bilibili.api.ticket.v1.Ticket/GenWebTicket?key_id=ec02&hexsign=${hexsign}&context[ts]=${e}&csrf=`, { + cookie.push(['bili_ticket', response.data.data.ticket].join('='), ['bili_ticket_expires', (Number.parseInt(response.data.data.created_at) + Number.parseInt(response.data.data.ttl)).toString()].join('=')); + } catch { + // HTTPError: Response code 429 (Too Many Requests) + } + + return cookie.join('; '); + }); +}; + +const getWbiVerifyString = () => { + const key = 'bili-wbi-verify-string'; + return cache.tryGet(key, async () => { + const cookie = await getCookie(); + const { data: navResponse } = await got('https://api.bilibili.com/x/web-interface/nav', { + headers: { + Referer: 'https://www.bilibili.com/', + Cookie: cookie, + }, + }); + const imgUrl = navResponse.data.wbi_img.img_url; + const subUrl = navResponse.data.wbi_img.sub_url; + const r = imgUrl.substring(imgUrl.lastIndexOf('/') + 1, imgUrl.length).split('.')[0] + subUrl.substring(subUrl.lastIndexOf('/') + 1, subUrl.length).split('.')[0]; + // const { body: spaceResponse } = await got('https://space.bilibili.com/1', { + // headers: { + // Referer: 'https://www.bilibili.com/', + // Cookie: cookie, + // }, + // }); + // const jsUrl = 'https:' + spaceResponse.match(/[^"]*9.space[^"]*/); + const jsUrl = 'https://s1.hdslb.com/bfs/seed/laputa-header/bili-header.umd.js'; + const { body: jsResponse } = await got(jsUrl, { + headers: { + Referer: 'https://space.bilibili.com/1', + }, + }); + // const array = [ + // 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, + // 62, 11, 36, 20, 34, 44, 52, + // ]; + const array = JSON.parse(jsResponse.match(/\[(?:\d+,){63}\d+]/)); + const o = []; + for (const t of array) { + r.charAt(t) && o.push(r.charAt(t)); + } + return o.join('').slice(0, 32); + }); +}; + +const getUsernameFromUID = (uid) => { + const key = 'bili-username-from-uid-' + uid; + return cache.tryGet(key, async () => { + const cookie = await getCookie(); + const wbiVerifyString = await getWbiVerifyString(); + // await got(`https://space.bilibili.com/${uid}/`, { + // headers: { + // Referer: 'https://www.bilibili.com/', + // Cookie: cookie, + // }, + // }); + const params = utils.addWbiVerifyInfo(`mid=${uid}&token=&platform=web&web_location=1550101`, wbiVerifyString); + const { data: nameResponse } = await got(`https://api.bilibili.com/x/space/wbi/acc/info?${params}`, { + headers: { + Referer: `https://space.bilibili.com/${uid}/`, + Cookie: cookie, + }, + }); + return nameResponse.data ? nameResponse.data.name : undefined; + }); +}; + +const getUsernameAndFaceFromUID = async (uid) => { + const nameKey = 'bili-username-from-uid-' + uid; + const faceKey = 'bili-userface-from-uid-' + uid; + let name = await cache.get(nameKey); + let face = await cache.get(faceKey); + if (!name || !face) { + const cookie = await getCookie(); + const wbiVerifyString = await getWbiVerifyString(); + // await got(`https://space.bilibili.com/${uid}/`, { + // headers: { + // Referer: `https://www.bilibili.com/`, + // Cookie: cookie, + // }, + // }); + const params = utils.addWbiVerifyInfo(`mid=${uid}&token=&platform=web&web_location=1550101`, wbiVerifyString); + const { data: nameResponse } = await got(`https://api.bilibili.com/x/space/wbi/acc/info?${params}`, { + headers: { + Referer: `https://space.bilibili.com/${uid}/`, + Cookie: cookie, + }, + }); + if (nameResponse.data.name) { + name = nameResponse.data.name; + face = nameResponse.data.face; + } else { + logger.error(`Error when visiting /x/space/wbi/acc/info: ${JSON.stringify(nameResponse)}`); + } + cache.set(nameKey, name); + cache.set(faceKey, face); + } + return [name, face]; +}; + +const getLiveIDFromShortID = (shortID) => { + const key = `bili-liveID-from-shortID-${shortID}`; + return cache.tryGet(key, async () => { + const { data: liveIDResponse } = await got(`https://api.live.bilibili.com/room/v1/Room/room_init?id=${shortID}`, { + headers: { + Referer: `https://live.bilibili.com/${shortID}`, + }, + }); + return liveIDResponse.data.room_id; + }); +}; + +const getUsernameFromLiveID = (liveID) => { + const key = `bili-username-from-liveID-${liveID}`; + return cache.tryGet(key, async () => { + const { data: nameResponse } = await got(`https://api.live.bilibili.com/live_user/v1/UserInfo/get_anchor_in_room?roomid=${liveID}`, { + headers: { + Referer: `https://live.bilibili.com/${liveID}`, + }, + }); + return nameResponse.data.info.uname; + }); +}; + +const getVideoNameFromId = (aid, bvid) => { + const key = `bili-videoname-from-id-${bvid || aid}`; + return cache.tryGet(key, async () => { + const { data } = await got(`https://api.bilibili.com/x/web-interface/view`, { + searchParams: { + aid: aid || undefined, + bvid: bvid || undefined, + }, + referer: `https://www.bilibili.com/video/${bvid || `av${aid}`}`, + }); + return data.data.title; + }); +}; + +const getCidFromId = (aid, pid, bvid) => { + const key = `bili-cid-from-id-${bvid || aid}-${pid}`; + return cache.tryGet(key, async () => { + const { data } = await got(`https://api.bilibili.com/x/web-interface/view?${bvid ? `bvid=${bvid}` : `aid=${aid}`}`, { + referer: `https://www.bilibili.com/video/${bvid || `av${aid}`}`, + }); + return data.data.pages[pid - 1].cid; + }); +}; + +const getAidFromBvid = async (bvid) => { + const key = `bili-cid-from-bvid-${bvid}`; + let aid = await cache.get(key); + if (!aid) { + const response = await got(`https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`, { + headers: { + Referer: `https://www.bilibili.com/video/${bvid}`, + }, + }); + if (response.data && response.data.data && response.data.data.aid) { + aid = response.data.data.aid; + } + cache.set(key, aid); + } + return aid; +}; + +const getArticleDataFromCvid = async (cvid, uid) => { + const url = `https://www.bilibili.com/read/cv${cvid}/`; + const data = await cache.tryGet( + url, + async () => + ( + await got({ + method: 'get', + url, headers: { - Referer: 'https://space.bilibili.com/1', - Cookie: cookie.join('; '), + Referer: `https://space.bilibili.com/${uid}/`, }, - }); - cookie.push(['bili_ticket', response.data.data.ticket].join('='), ['bili_ticket_expires', (Number.parseInt(response.data.data.created_at) + Number.parseInt(response.data.data.ttl)).toString()].join('=')); - } catch { - // HTTPError: Response code 429 (Too Many Requests) - } + }) + ).data + ); + const $ = load(data); + let description = $('#read-article-holder').html(); + if (!description) { + try { + const newFormatData = JSON.parse( + $('script:contains("window.__INITIAL_STATE__")') + .text() + .match(/window\.__INITIAL_STATE__\s*=\s*(.*?);\(/)[1] + ); - return cookie.join('; '); - }); - }, - getWbiVerifyString: (ctx) => { - const key = 'bili-wbi-verify-string'; - return cache.tryGet(key, async () => { - const cookie = await module.exports.getCookie(ctx); - const { data: navResponse } = await got('https://api.bilibili.com/x/web-interface/nav', { - headers: { - Referer: 'https://www.bilibili.com/', - Cookie: cookie, - }, - }); - const imgUrl = navResponse.data.wbi_img.img_url; - const subUrl = navResponse.data.wbi_img.sub_url; - const r = imgUrl.substring(imgUrl.lastIndexOf('/') + 1, imgUrl.length).split('.')[0] + subUrl.substring(subUrl.lastIndexOf('/') + 1, subUrl.length).split('.')[0]; - // const { body: spaceResponse } = await got('https://space.bilibili.com/1', { - // headers: { - // Referer: 'https://www.bilibili.com/', - // Cookie: cookie, - // }, - // }); - // const jsUrl = 'https:' + spaceResponse.match(/[^"]*9.space[^"]*/); - const jsUrl = 'https://s1.hdslb.com/bfs/seed/laputa-header/bili-header.umd.js'; - const { body: jsResponse } = await got(jsUrl, { - headers: { - Referer: 'https://space.bilibili.com/1', - }, - }); - // const array = [ - // 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, - // 62, 11, 36, 20, 34, 44, 52, - // ]; - const array = JSON.parse(jsResponse.match(/\[(?:\d+,){63}\d+]/)); - const o = []; - for (const t of array) { - r.charAt(t) && o.push(r.charAt(t)); - } - return o.join('').slice(0, 32); - }); - }, - getUsernameFromUID: (ctx, uid) => { - const key = 'bili-username-from-uid-' + uid; - return cache.tryGet(key, async () => { - const cookie = await module.exports.getCookie(ctx); - const wbiVerifyString = await module.exports.getWbiVerifyString(ctx); - // await got(`https://space.bilibili.com/${uid}/`, { - // headers: { - // Referer: 'https://www.bilibili.com/', - // Cookie: cookie, - // }, - // }); - const params = utils.addWbiVerifyInfo(`mid=${uid}&token=&platform=web&web_location=1550101`, wbiVerifyString); - const { data: nameResponse } = await got(`https://api.bilibili.com/x/space/wbi/acc/info?${params}`, { - headers: { - Referer: `https://space.bilibili.com/${uid}/`, - Cookie: cookie, - }, - }); - return nameResponse.data ? nameResponse.data.name : undefined; - }); - }, - getUsernameAndFaceFromUID: async (ctx, uid) => { - const nameKey = 'bili-username-from-uid-' + uid; - const faceKey = 'bili-userface-from-uid-' + uid; - let name = await cache.get(nameKey); - let face = await cache.get(faceKey); - if (!name || !face) { - const cookie = await module.exports.getCookie(ctx); - const wbiVerifyString = await module.exports.getWbiVerifyString(ctx); - // await got(`https://space.bilibili.com/${uid}/`, { - // headers: { - // Referer: `https://www.bilibili.com/`, - // Cookie: cookie, - // }, - // }); - const params = utils.addWbiVerifyInfo(`mid=${uid}&token=&platform=web&web_location=1550101`, wbiVerifyString); - const { data: nameResponse } = await got(`https://api.bilibili.com/x/space/wbi/acc/info?${params}`, { - headers: { - Referer: `https://space.bilibili.com/${uid}/`, - Cookie: cookie, - }, - }); - if (nameResponse.data.name) { - name = nameResponse.data.name; - face = nameResponse.data.face; - } else { - logger.error(`Error when visiting /x/space/wbi/acc/info: ${JSON.stringify(nameResponse)}`); - } - cache.set(nameKey, name); - cache.set(faceKey, face); - } - return [name, face]; - }, - getLiveIDFromShortID: (ctx, shortID) => { - const key = `bili-liveID-from-shortID-${shortID}`; - return cache.tryGet(key, async () => { - const { data: liveIDResponse } = await got(`https://api.live.bilibili.com/room/v1/Room/room_init?id=${shortID}`, { - headers: { - Referer: `https://live.bilibili.com/${shortID}`, - }, - }); - return liveIDResponse.data.room_id; - }); - }, - getUsernameFromLiveID: (ctx, liveID) => { - const key = `bili-username-from-liveID-${liveID}`; - return cache.tryGet(key, async () => { - const { data: nameResponse } = await got(`https://api.live.bilibili.com/live_user/v1/UserInfo/get_anchor_in_room?roomid=${liveID}`, { - headers: { - Referer: `https://live.bilibili.com/${liveID}`, - }, - }); - return nameResponse.data.info.uname; - }); - }, - getVideoNameFromId: (ctx, aid, bvid) => { - const key = `bili-videoname-from-id-${bvid || aid}`; - return cache.tryGet(key, async () => { - const { data } = await got(`https://api.bilibili.com/x/web-interface/view`, { - searchParams: { - aid: aid || undefined, - bvid: bvid || undefined, - }, - referer: `https://www.bilibili.com/video/${bvid || `av${aid}`}`, - }); - return data.data.title; - }); - }, - getCidFromId: (ctx, aid, pid, bvid) => { - const key = `bili-cid-from-id-${bvid || aid}-${pid}`; - return cache.tryGet(key, async () => { - const { data } = await got(`https://api.bilibili.com/x/web-interface/view?${bvid ? `bvid=${bvid}` : `aid=${aid}`}`, { - referer: `https://www.bilibili.com/video/${bvid || `av${aid}`}`, - }); - return data.data.pages[pid - 1].cid; - }); - }, - getAidFromBvid: async (ctx, bvid) => { - const key = `bili-cid-from-bvid-${bvid}`; - let aid = await cache.get(key); - if (!aid) { - const response = await got(`https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`, { - headers: { - Referer: `https://www.bilibili.com/video/${bvid}`, - }, - }); - if (response.data && response.data.data && response.data.data.aid) { - aid = response.data.data.aid; - } - cache.set(key, aid); - } - return aid; - }, - getArticleDataFromCvid: async (ctx, cvid, uid) => { - const url = `https://www.bilibili.com/read/cv${cvid}/`; - const data = await cache.tryGet( - url, - async () => - ( - await got({ - method: 'get', - url, - headers: { - Referer: `https://space.bilibili.com/${uid}/`, - }, - }) - ).data - ); - const $ = load(data); - let description = $('#read-article-holder').html(); - if (!description) { - try { - const newFormatData = JSON.parse( - $('script:contains("window.__INITIAL_STATE__")') - .text() - .match(/window\.__INITIAL_STATE__\s*=\s*(.*?);\(/)[1] - ); - - if (newFormatData?.readInfo?.opus?.content?.paragraphs) { - description = ''; - for (const element of newFormatData.readInfo.opus.content.paragraphs) { - if (element.para_type === 1) { - for (const text of element.text.nodes) { - if (text?.word?.words) { - description += `

${text.word.words}

`; - } + if (newFormatData?.readInfo?.opus?.content?.paragraphs) { + description = ''; + for (const element of newFormatData.readInfo.opus.content.paragraphs) { + if (element.para_type === 1) { + for (const text of element.text.nodes) { + if (text?.word?.words) { + description += `

${text.word.words}

`; } } - if (element.para_type === 2) { - for (const image of element.pic.pics) { - description += `

`; - } - } - if (element.para_type === 3 && element.line?.pic?.url) { - description += `
`; - } + } + if (element.para_type === 2) { + for (const image of element.pic.pics) { + description += `

`; + } + } + if (element.para_type === 3 && element.line?.pic?.url) { + description += `
`; } } - } catch { - /* empty */ } + } catch { + /* empty */ } - return { url, description }; - }, + } + return { url, description }; +}; + +export default { + getCookie, + getWbiVerifyString, + getUsernameFromUID, + getUsernameAndFaceFromUID, + getLiveIDFromShortID, + getUsernameFromLiveID, + getVideoNameFromId, + getCidFromId, + getAidFromBvid, + getArticleDataFromCvid, }; diff --git a/lib/routes/bilibili/coin.ts b/lib/routes/bilibili/coin.ts index 200f0d64f0..e79a0d810f 100644 --- a/lib/routes/bilibili/coin.ts +++ b/lib/routes/bilibili/coin.ts @@ -7,7 +7,7 @@ export default async (ctx) => { const uid = ctx.req.param('uid'); const disableEmbed = ctx.req.param('disableEmbed'); - const name = await cache.getUsernameFromUID(ctx, uid); + const name = await cache.getUsernameFromUID(uid); const response = await got({ url: `https://api.bilibili.com/x/space/coin/video?vmid=${uid}`, diff --git a/lib/routes/bilibili/danmaku.ts b/lib/routes/bilibili/danmaku.ts index bb4cab8c53..870dceb89d 100644 --- a/lib/routes/bilibili/danmaku.ts +++ b/lib/routes/bilibili/danmaku.ts @@ -1,7 +1,7 @@ import { load } from 'cheerio'; import cache from './cache'; import got from '@/utils/got'; -const zlib = require('zlib'); +import zlib from 'zlib'; const processFloatTime = (time) => { const totalSeconds = Number.parseInt(time); @@ -20,9 +20,9 @@ export default async (ctx) => { } const pid = Number(ctx.req.param('pid') || 1); const limit = 50; - const cid = await cache.getCidFromId(ctx, aid, pid, bvid); + const cid = await cache.getCidFromId(aid, pid, bvid); - const videoName = await cache.getVideoNameFromId(ctx, aid, bvid); + const videoName = await cache.getVideoNameFromId(aid, bvid); const link = `https://www.bilibili.com/video/${bvid || `av${aid}`}`; const danmakuResponse = await got.get(`https://comment.bilibili.com/${cid}.xml`, { diff --git a/lib/routes/bilibili/dynamic.ts b/lib/routes/bilibili/dynamic.ts index e7b06e95a7..a8015a8746 100644 --- a/lib/routes/bilibili/dynamic.ts +++ b/lib/routes/bilibili/dynamic.ts @@ -1,6 +1,6 @@ import cache from '@/utils/cache'; import got from '@/utils/got'; -const JSONbig = require('json-bigint'); +import JSONbig from 'json-bigint'; import utils from './utils'; import { parseDate } from '@/utils/parse-date'; import { fallback, queryToBoolean } from '@/utils/readable-social'; @@ -86,7 +86,7 @@ export default async (ctx) => { }); const cards = JSONbig.parse(response.body).data.cards; - const usernameAndFace = await cacheIn.getUsernameAndFaceFromUID(ctx, uid); + const usernameAndFace = await cacheIn.getUsernameAndFaceFromUID(uid); const author = usernameAndFace[0] ?? cards[0]?.desc?.user_profile?.info.uname; const face = usernameAndFace[1] ?? cards[0]?.desc?.user_profile?.info?.face; cache.set(`bili-username-from-uid-${uid}`, author); @@ -217,7 +217,7 @@ export default async (ctx) => { }; if (data.image_urls && displayArticle) { - data_content = (await cacheIn.getArticleDataFromCvid(ctx, data.id, uid)).description; + data_content = (await cacheIn.getArticleDataFromCvid(data.id, uid)).description; } return { diff --git a/lib/routes/bilibili/followers.ts b/lib/routes/bilibili/followers.ts index 7e33354b64..ba9ee195e8 100644 --- a/lib/routes/bilibili/followers.ts +++ b/lib/routes/bilibili/followers.ts @@ -11,7 +11,7 @@ export default async (ctx) => { throw new Error('缺少对应 loginUid 的 Bilibili 用户登录后的 Cookie 值 bilibili 用户关注动态系列路由'); } - const name = await cache.getUsernameFromUID(ctx, uid); + const name = await cache.getUsernameFromUID(uid); const countResponse = await got({ method: 'get', diff --git a/lib/routes/bilibili/followings-article.ts b/lib/routes/bilibili/followings-article.ts index d7039d2639..0d5969065b 100644 --- a/lib/routes/bilibili/followings-article.ts +++ b/lib/routes/bilibili/followings-article.ts @@ -4,7 +4,7 @@ import { config } from '@/config'; export default async (ctx) => { const uid = String(ctx.req.param('uid')); - const name = await cache.getUsernameFromUID(ctx, uid); + const name = await cache.getUsernameFromUID(uid); const cookie = config.bilibili.cookies[uid]; if (cookie === undefined) { @@ -27,7 +27,7 @@ export default async (ctx) => { const out = await Promise.all( cards.map(async (card) => { const card_data = JSON.parse(card.card); - const { url: link, description } = await cache.getArticleDataFromCvid(ctx, card_data.id, uid); + const { url: link, description } = await cache.getArticleDataFromCvid(card_data.id, uid); const item = { title: card_data.title, diff --git a/lib/routes/bilibili/followings-dynamic.ts b/lib/routes/bilibili/followings-dynamic.ts index 86b8a8868d..abb1b542fa 100644 --- a/lib/routes/bilibili/followings-dynamic.ts +++ b/lib/routes/bilibili/followings-dynamic.ts @@ -2,9 +2,9 @@ import got from '@/utils/got'; import cache from './cache'; import { config } from '@/config'; import utils from './utils'; -const JSONbig = require('json-bigint'); +import JSONbig from 'json-bigint'; import { fallback, queryToBoolean } from '@/utils/readable-social'; -const querystring = require('querystring'); +import querystring from 'querystring'; export default async (ctx) => { const uid = String(ctx.req.param('uid')); @@ -14,7 +14,7 @@ export default async (ctx) => { const disableEmbed = fallback(undefined, queryToBoolean(routeParams.disableEmbed), false); const displayArticle = fallback(undefined, queryToBoolean(routeParams.displayArticle), false); - const name = await cache.getUsernameFromUID(ctx, uid); + const name = await cache.getUsernameFromUID(uid); const cookie = config.bilibili.cookies[uid]; if (cookie === undefined) { @@ -123,7 +123,7 @@ export default async (ctx) => { } if (data.image_urls && displayArticle) { - data_content = (await cache.getArticleDataFromCvid(ctx, data.id, uid)).description; + data_content = (await cache.getArticleDataFromCvid(data.id, uid)).description; } return { diff --git a/lib/routes/bilibili/followings-video.ts b/lib/routes/bilibili/followings-video.ts index 5ea401fb70..8c6ef292c1 100644 --- a/lib/routes/bilibili/followings-video.ts +++ b/lib/routes/bilibili/followings-video.ts @@ -6,7 +6,7 @@ import utils from './utils'; export default async (ctx) => { const uid = String(ctx.req.param('uid')); const disableEmbed = ctx.req.param('disableEmbed'); - const name = await cache.getUsernameFromUID(ctx, uid); + const name = await cache.getUsernameFromUID(uid); const cookie = config.bilibili.cookies[uid]; if (cookie === undefined) { diff --git a/lib/routes/bilibili/followings.ts b/lib/routes/bilibili/followings.ts index 2041d06f2a..fdf5ac3b29 100644 --- a/lib/routes/bilibili/followings.ts +++ b/lib/routes/bilibili/followings.ts @@ -10,7 +10,7 @@ export default async (ctx) => { } const uid = ctx.req.param('uid'); - const name = await cache.getUsernameFromUID(ctx, uid); + const name = await cache.getUsernameFromUID(uid); const countResponse = await got({ method: 'get', diff --git a/lib/routes/bilibili/hot-search.ts b/lib/routes/bilibili/hot-search.ts index f54d36ea7d..9b05458bfc 100644 --- a/lib/routes/bilibili/hot-search.ts +++ b/lib/routes/bilibili/hot-search.ts @@ -3,7 +3,7 @@ import cache from './cache'; import utils from './utils'; export default async (ctx) => { - const wbiVerifyString = await cache.getWbiVerifyString(ctx); + const wbiVerifyString = await cache.getWbiVerifyString(); const params = utils.addWbiVerifyInfo('limit=10&platform=web', wbiVerifyString); const url = `https://api.bilibili.com/x/web-interface/wbi/search/square?${params}`; const response = await got({ diff --git a/lib/routes/bilibili/like.ts b/lib/routes/bilibili/like.ts index 0655ecdd2b..41f15ea7da 100644 --- a/lib/routes/bilibili/like.ts +++ b/lib/routes/bilibili/like.ts @@ -7,7 +7,7 @@ export default async (ctx) => { const uid = ctx.req.param('uid'); const disableEmbed = ctx.req.param('disableEmbed'); - const name = await cache.getUsernameFromUID(ctx, uid); + const name = await cache.getUsernameFromUID(uid); const response = await got({ url: `https://api.bilibili.com/x/space/like/video?vmid=${uid}`, diff --git a/lib/routes/bilibili/live-room.ts b/lib/routes/bilibili/live-room.ts index a6eccd58b1..2f21223c56 100644 --- a/lib/routes/bilibili/live-room.ts +++ b/lib/routes/bilibili/live-room.ts @@ -6,9 +6,9 @@ export default async (ctx) => { // 短号查询长号 if (Number.parseInt(roomID, 10) < 10000) { - roomID = await cache.getLiveIDFromShortID(ctx, roomID); + roomID = await cache.getLiveIDFromShortID(roomID); } - const name = await cache.getUsernameFromLiveID(ctx, roomID); + const name = await cache.getUsernameFromLiveID(roomID); const response = await got({ method: 'get', diff --git a/lib/routes/bilibili/live-search.ts b/lib/routes/bilibili/live-search.ts index 0f70a65e52..bb0ebc31f9 100644 --- a/lib/routes/bilibili/live-search.ts +++ b/lib/routes/bilibili/live-search.ts @@ -17,7 +17,7 @@ export default async (ctx) => { orderTitle = '人气直播'; break; } - const wbiVerifyString = await cache.getWbiVerifyString(ctx); + const wbiVerifyString = await cache.getWbiVerifyString(); let params = `__refresh__=true&_extra=&context=&page=1&page_size=42&order=${order}&duration=&from_source=&from_spmid=333.337&platform=pc&highlight=1&single_column=0&keyword=${urlEncodedKey}&ad_resource=&source_tag=3&gaia_vtoken=&category_id=&search_type=live&dynamic_offset=0&web_location=1430654`; params = utils.addWbiVerifyInfo(params, wbiVerifyString); @@ -26,7 +26,7 @@ export default async (ctx) => { url: `https://api.bilibili.com/x/web-interface/wbi/search/type?${params}`, headers: { Referer: `https://search.bilibili.com/live?keyword=${urlEncodedKey}&from_source=webtop_search&spm_id_from=444.7&search_source=3&search_type=live_room`, - Cookie: await cache.getCookie(ctx), + Cookie: await cache.getCookie(), }, }); const data = response.data.data.result.live_room; diff --git a/lib/routes/bilibili/manga-followings.ts b/lib/routes/bilibili/manga-followings.ts index f182586558..dad9e1d307 100644 --- a/lib/routes/bilibili/manga-followings.ts +++ b/lib/routes/bilibili/manga-followings.ts @@ -4,7 +4,7 @@ import { config } from '@/config'; export default async (ctx) => { const uid = String(ctx.req.param('uid')); - const name = await cache.getUsernameFromUID(ctx, uid); + const name = await cache.getUsernameFromUID(uid); const cookie = config.bilibili.cookies[uid]; if (cookie === undefined) { diff --git a/lib/routes/bilibili/reply.ts b/lib/routes/bilibili/reply.ts index 3d356ce932..24d965b529 100644 --- a/lib/routes/bilibili/reply.ts +++ b/lib/routes/bilibili/reply.ts @@ -8,9 +8,9 @@ export default async (ctx) => { aid = bvid; bvid = null; } - const name = await cache.getVideoNameFromId(ctx, aid, bvid); + const name = await cache.getVideoNameFromId(aid, bvid); if (!aid) { - aid = await cache.getAidFromBvid(ctx, bvid); + aid = await cache.getAidFromBvid(bvid); } const link = `https://www.bilibili.com/video/${bvid || `av${aid}`}`; diff --git a/lib/routes/bilibili/user-bangumi.ts b/lib/routes/bilibili/user-bangumi.ts index 335b0c63c9..c79727d162 100644 --- a/lib/routes/bilibili/user-bangumi.ts +++ b/lib/routes/bilibili/user-bangumi.ts @@ -5,7 +5,7 @@ export default async (ctx) => { const uid = ctx.req.param('uid'); const type = Number(ctx.req.param('type') || 1); const type_name = ((t) => ['', 'bangumi', 'cinema'][t])(type); - const name = await cache.getUsernameFromUID(ctx, uid); + const name = await cache.getUsernameFromUID(uid); const response = await got({ method: 'get', diff --git a/lib/routes/bilibili/user-channel.ts b/lib/routes/bilibili/user-channel.ts index ba31876af1..f35743696e 100644 --- a/lib/routes/bilibili/user-channel.ts +++ b/lib/routes/bilibili/user-channel.ts @@ -32,7 +32,7 @@ export default async (ctx) => { ctx.set('data', notFoundData); return; } - const [userName, face] = await cacheIn.getUsernameAndFaceFromUID(ctx, uid); + const [userName, face] = await cacheIn.getUsernameAndFaceFromUID(uid); const host = `https://api.bilibili.com/x/series/archives?mid=${uid}&series_id=${sid}&only_normal=true&sort=desc&pn=1&ps=${limit}`; const response = await got(host, { diff --git a/lib/routes/bilibili/user-collection.ts b/lib/routes/bilibili/user-collection.ts index affac3406f..497a3c163a 100644 --- a/lib/routes/bilibili/user-collection.ts +++ b/lib/routes/bilibili/user-collection.ts @@ -14,7 +14,7 @@ export default async (ctx) => { const limit = ctx.req.query('limit') ?? 25; const link = `https://space.bilibili.com/${uid}/channel/collectiondetail?sid=${sid}`; - const [userName, face] = await cache.getUsernameAndFaceFromUID(ctx, uid); + const [userName, face] = await cache.getUsernameAndFaceFromUID(uid); const host = `https://api.bilibili.com/x/polymer/space/seasons_archives_list?mid=${uid}&season_id=${sid}&sort_reverse=true&page_num=1&page_size=${limit}`; const response = await got(host, { diff --git a/lib/routes/bilibili/user-fav.ts b/lib/routes/bilibili/user-fav.ts index bc6bffbfa5..157827cafe 100644 --- a/lib/routes/bilibili/user-fav.ts +++ b/lib/routes/bilibili/user-fav.ts @@ -6,7 +6,7 @@ import { config } from '@/config'; export default async (ctx) => { const uid = ctx.req.param('uid'); const disableEmbed = ctx.req.param('disableEmbed'); - const name = await cache.getUsernameFromUID(ctx, uid); + const name = await cache.getUsernameFromUID(uid); const response = await got({ method: 'get', diff --git a/lib/routes/bilibili/utils.ts b/lib/routes/bilibili/utils.ts index 8b9fc5c762..613e3a153b 100644 --- a/lib/routes/bilibili/utils.ts +++ b/lib/routes/bilibili/utils.ts @@ -1,6 +1,6 @@ import { config } from '@/config'; import md5 from '@/utils/md5'; -const CryptoJS = require('crypto-js'); +import CryptoJS from 'crypto-js'; function iframe(aid, page, bvid) { return `