improve: more accurate snapshot timestamp
This commit is contained in:
parent
22a450e94f
commit
cb6b70faf4
@ -16,6 +16,7 @@ import { Readable } from "stream";
|
||||
interface FCResponse {
|
||||
statusCode: number;
|
||||
body: string;
|
||||
serverTime: number;
|
||||
}
|
||||
|
||||
interface Proxy {
|
||||
@ -167,7 +168,10 @@ class NetworkDelegate {
|
||||
* - The alicloud-fc threw an error: with error code `ALICLOUD_FC_ERROR`
|
||||
* - The proxy type is not supported: with error code `NOT_IMPLEMENTED`
|
||||
*/
|
||||
async request<R>(url: string, task: string): Promise<R> {
|
||||
async request<R>(url: string, task: string): Promise<{
|
||||
data: R;
|
||||
time: number;
|
||||
}> {
|
||||
// find a available proxy
|
||||
const proxiesNames = this.getTaskProxies(task);
|
||||
for (const proxyName of shuffleArray(proxiesNames)) {
|
||||
@ -203,7 +207,10 @@ class NetworkDelegate {
|
||||
proxyName: string,
|
||||
task: string,
|
||||
force: boolean = false
|
||||
): Promise<R> {
|
||||
): Promise<{
|
||||
data: R;
|
||||
time: number;
|
||||
}> {
|
||||
const proxy = this.proxies[proxyName];
|
||||
if (!proxy) {
|
||||
throw new NetSchedulerError(`Proxy "${proxyName}" not found`, "PROXY_NOT_FOUND");
|
||||
@ -214,7 +221,10 @@ class NetworkDelegate {
|
||||
return result;
|
||||
}
|
||||
|
||||
private async makeRequest<R>(url: string, proxy: Proxy): Promise<R> {
|
||||
private async makeRequest<R>(url: string, proxy: Proxy): Promise<{
|
||||
data: R;
|
||||
time: number;
|
||||
}> {
|
||||
switch (proxy.type) {
|
||||
case "native":
|
||||
return await this.nativeRequest<R>(url);
|
||||
@ -228,7 +238,10 @@ class NetworkDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private async nativeRequest<R>(url: string): Promise<R> {
|
||||
private async nativeRequest<R>(url: string): Promise<{
|
||||
data: R;
|
||||
time: number;
|
||||
}> {
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 10 * SECOND);
|
||||
@ -239,13 +252,26 @@ class NetworkDelegate {
|
||||
|
||||
clearTimeout(timeout);
|
||||
|
||||
return (await response.json()) as R;
|
||||
const start = Date.now();
|
||||
const data = await response.json();
|
||||
const end = Date.now();
|
||||
const serverTime = start + (end - start) / 2;
|
||||
return {
|
||||
data: data as R,
|
||||
time: serverTime
|
||||
};
|
||||
} catch (e) {
|
||||
throw new NetSchedulerError("Fetch error", "FETCH_ERROR", e);
|
||||
}
|
||||
}
|
||||
|
||||
private async alicloudFcRequest<R>(url: string, region: string): Promise<R> {
|
||||
private async alicloudFcRequest<R>(
|
||||
url: string,
|
||||
region: string
|
||||
): Promise<{
|
||||
data: R;
|
||||
time: number;
|
||||
}> {
|
||||
try {
|
||||
const client = getAlicloudClient(region);
|
||||
const bodyStream = Stream.readFromString(JSON.stringify({ url: url }));
|
||||
@ -275,7 +301,10 @@ class NetworkDelegate {
|
||||
"ALICLOUD_PROXY_ERR"
|
||||
);
|
||||
} else {
|
||||
return JSON.parse(rawData.body) as R;
|
||||
return {
|
||||
data: JSON.parse(rawData.body) as R,
|
||||
time: rawData.serverTime
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(e as Error, "net", "fn:alicloudFcRequest");
|
||||
|
||||
@ -15,15 +15,27 @@ import logger from "@core/log";
|
||||
* - The native `fetch` function threw an error: with error code `FETCH_ERROR`
|
||||
* - The alicloud-fc threw an error: with error code `ALICLOUD_FC_ERROR`
|
||||
*/
|
||||
export async function getVideoInfo(aid: number, task: string): Promise<VideoInfoData | number> {
|
||||
export async function getVideoInfo(
|
||||
aid: number,
|
||||
task: string
|
||||
): Promise<
|
||||
| {
|
||||
data: VideoInfoData;
|
||||
time: number;
|
||||
}
|
||||
| number
|
||||
> {
|
||||
const url = `https://api.bilibili.com/x/web-interface/view?aid=${aid}`;
|
||||
const data = await networkDelegate.request<VideoInfoResponse>(url, task);
|
||||
const { data, time } = await networkDelegate.request<VideoInfoResponse>(url, task);
|
||||
const errMessage = `Error fetching metadata for ${aid}:`;
|
||||
if (data.code !== 0) {
|
||||
logger.error(errMessage + data.code + "-" + data.message, "net", "fn:getVideoInfo");
|
||||
return data.code;
|
||||
}
|
||||
return data.data;
|
||||
return {
|
||||
data: data.data,
|
||||
time: time
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
@ -39,9 +51,12 @@ export async function getVideoInfo(aid: number, task: string): Promise<VideoInfo
|
||||
* - The native `fetch` function threw an error: with error code `FETCH_ERROR`
|
||||
* - The alicloud-fc threw an error: with error code `ALICLOUD_FC_ERROR`
|
||||
*/
|
||||
export async function getVideoInfoByBV(bvid: string, task: string): Promise<VideoInfoData | number> {
|
||||
export async function getVideoInfoByBV(
|
||||
bvid: string,
|
||||
task: string
|
||||
): Promise<VideoInfoData | number> {
|
||||
const url = `https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`;
|
||||
const data = await networkDelegate.request<VideoInfoResponse>(url, task);
|
||||
const { data } = await networkDelegate.request<VideoInfoResponse>(url, task);
|
||||
const errMessage = `Error fetching metadata for ${bvid}:`;
|
||||
if (data.code !== 0) {
|
||||
logger.error(errMessage + data.code + "-" + data.message, "net", "fn:getVideoInfoByBV");
|
||||
|
||||
@ -3,7 +3,7 @@ import { test, expect, describe } from "bun:test";
|
||||
|
||||
describe("proxying requests", () => {
|
||||
test("Alibaba Cloud FC", async () => {
|
||||
const res = await networkDelegate.request("https://postman-echo.com/get", "test") as any;
|
||||
const { res } = await networkDelegate.request("https://postman-echo.com/get", "test") as any;
|
||||
expect(res.headers.referer).toBe('https://www.bilibili.com/');
|
||||
});
|
||||
});
|
||||
|
||||
@ -30,12 +30,13 @@ export const takeBulkSnapshotForVideosWorker = async (job: Job) => {
|
||||
}
|
||||
aidsToFetch.push(aid);
|
||||
}
|
||||
const data = await bulkGetVideoStats(aidsToFetch);
|
||||
if (typeof data === "number") {
|
||||
const r = await bulkGetVideoStats(aidsToFetch);
|
||||
if (typeof r === "number") {
|
||||
await bulkSetSnapshotStatus(sql, ids, "failed");
|
||||
await bulkScheduleSnapshot(sql, aidsToFetch, "normal", Date.now() + 15 * SECOND);
|
||||
return `GET_BILI_STATUS_${data}`;
|
||||
return `GET_BILI_STATUS_${r}`;
|
||||
}
|
||||
const { data, time } = r;
|
||||
for (const video of data) {
|
||||
const aid = video.id;
|
||||
const stat = video.cnt_info;
|
||||
@ -58,7 +59,7 @@ export const takeBulkSnapshotForVideosWorker = async (job: Job) => {
|
||||
await updateETA(sql, aid, eta, speed, views);
|
||||
}
|
||||
await sql`
|
||||
INSERT INTO video_snapshot (aid, views, danmakus, replies, likes, coins, shares, favorites)
|
||||
INSERT INTO video_snapshot (aid, views, danmakus, replies, likes, coins, shares, favorites, created_at)
|
||||
VALUES (
|
||||
${aid},
|
||||
${views},
|
||||
@ -67,7 +68,8 @@ export const takeBulkSnapshotForVideosWorker = async (job: Job) => {
|
||||
${likes},
|
||||
${coins},
|
||||
${shares},
|
||||
${favorites}
|
||||
${favorites},
|
||||
${new Date(time).toUTCString()}
|
||||
)
|
||||
`;
|
||||
|
||||
|
||||
@ -24,12 +24,16 @@ export interface SnapshotNumber {
|
||||
* - The native `fetch` function threw an error: with error code `FETCH_ERROR`
|
||||
* - The alicloud-fc threw an error: with error code `ALICLOUD_FC_ERROR`
|
||||
*/
|
||||
export async function insertVideoSnapshot(sql: Psql, aid: number, task: string): Promise<number | SnapshotNumber> {
|
||||
const data = await getVideoInfo(aid, task);
|
||||
if (typeof data == "number") {
|
||||
return data;
|
||||
export async function insertVideoSnapshot(
|
||||
sql: Psql,
|
||||
aid: number,
|
||||
task: string
|
||||
): Promise<number | SnapshotNumber> {
|
||||
const r = await getVideoInfo(aid, task);
|
||||
if (typeof r == "number") {
|
||||
return r;
|
||||
}
|
||||
const time = new Date().getTime();
|
||||
const { data, time } = r;
|
||||
const views = data.stat.view;
|
||||
const danmakus = data.stat.danmaku;
|
||||
const replies = data.stat.reply;
|
||||
@ -39,8 +43,8 @@ export async function insertVideoSnapshot(sql: Psql, aid: number, task: string):
|
||||
const favorites = data.stat.favorite;
|
||||
|
||||
await sql`
|
||||
INSERT INTO video_snapshot (aid, views, danmakus, replies, likes, coins, shares, favorites)
|
||||
VALUES (${aid}, ${views}, ${danmakus}, ${replies}, ${likes}, ${coins}, ${shares}, ${favorites})
|
||||
INSERT INTO video_snapshot (aid, views, danmakus, replies, likes, coins, shares, favorites, created_at)
|
||||
VALUES (${aid}, ${views}, ${danmakus}, ${replies}, ${likes}, ${coins}, ${shares}, ${favorites}, ${new Date(time).toUTCString()})
|
||||
`;
|
||||
|
||||
logger.log(`Taken snapshot for video ${aid}.`, "net", "fn:insertVideoSnapshot");
|
||||
|
||||
@ -11,17 +11,29 @@ import logger from "@core/log";
|
||||
* - The native `fetch` function threw an error: with error code `FETCH_ERROR`
|
||||
* - The alicloud-fc threw an error: with error code `ALICLOUD_FC_ERROR`
|
||||
*/
|
||||
export async function bulkGetVideoStats(aids: number[]): Promise<MediaListInfoData | number> {
|
||||
export async function bulkGetVideoStats(aids: number[]): Promise<
|
||||
| {
|
||||
data: MediaListInfoData;
|
||||
time: number;
|
||||
}
|
||||
| number
|
||||
> {
|
||||
// TODO: https://api.bilibili.com/x/v3/fav/resource/infos?resources=20:2
|
||||
let url = `https://api.bilibili.com/medialist/gateway/base/resource/infos?resources=`;
|
||||
for (const aid of aids) {
|
||||
url += `${aid}:2,`;
|
||||
}
|
||||
const data = await networkDelegate.request<MediaListInfoResponse>(url, "bulkSnapshot");
|
||||
const { data, time } = await networkDelegate.request<MediaListInfoResponse>(
|
||||
url,
|
||||
"bulkSnapshot"
|
||||
);
|
||||
const errMessage = `Error fetching metadata for aid list: ${aids.join(",")}:`;
|
||||
if (data.code !== 0) {
|
||||
logger.error(errMessage + data.code + "-" + data.message, "net", "fn:getVideoInfo");
|
||||
return data.code;
|
||||
}
|
||||
return data.data;
|
||||
return {
|
||||
data: data.data,
|
||||
time: time
|
||||
};
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ export async function getLatestVideoAids(page: number = 1, pageSize: number = 10
|
||||
const range = `${startFrom}-${endTo}`;
|
||||
const errMessage = `Error fetching latest aid for ${range}:`;
|
||||
const url = `https://api.bilibili.com/x/web-interface/newlist?rid=30&ps=${pageSize}&pn=${page}`;
|
||||
const data = await networkDelegate.request<VideoListResponse>(url, "getLatestVideos");
|
||||
const { data } = await networkDelegate.request<VideoListResponse>(url, "getLatestVideos");
|
||||
if (data.code != 0) {
|
||||
logger.error(errMessage + data.message, "net", "getLastestVideos");
|
||||
return [];
|
||||
|
||||
@ -4,7 +4,7 @@ import logger from "@core/log";
|
||||
|
||||
export async function getVideoDetails(aid: number, archive: boolean = false): Promise<VideoDetailsData | null> {
|
||||
const url = `https://api.bilibili.com/x/web-interface/view/detail?aid=${aid}`;
|
||||
const data = await networkDelegate.request<VideoDetailsResponse>(url, archive ? "" : "getVideoInfo");
|
||||
const { data } = await networkDelegate.request<VideoDetailsResponse>(url, archive ? "" : "getVideoInfo");
|
||||
const errMessage = `Error fetching metadata for ${aid}:`;
|
||||
if (data.code !== 0) {
|
||||
logger.error(errMessage + data.code + "-" + data.message, "net", "fn:getVideoInfo");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user