Compare commits

..

No commits in common. "328c73c2099c44229c39667ce41cbbea6b371440" and "2cf5923b287d815871cb203614ec2dc04a2c4ca9" have entirely different histories.

12 changed files with 11 additions and 99 deletions

View File

@ -1,13 +0,0 @@
import { sql } from "@core/db/dbNew";
import type { LatestSnapshotType } from "@core/db/schema.d.ts";
export async function getVideosInViewsRange(minViews: number, maxViews: number) {
return sql<LatestSnapshotType[]>`
SELECT *
FROM latest_video_snapshot
WHERE views >= ${minViews}
AND views <= ${maxViews}
ORDER BY views DESC
LIMIT 5000
`;
}

View File

@ -1,7 +1,7 @@
{ {
"name": "@cvsa/backend", "name": "@cvsa/backend",
"private": false, "private": false,
"version": "0.6.0", "version": "0.5.3",
"scripts": { "scripts": {
"format": "prettier --write .", "format": "prettier --write .",
"dev": "NODE_ENV=development bun run --hot src/main.ts", "dev": "NODE_ENV=development bun run --hot src/main.ts",

View File

@ -1,65 +0,0 @@
import type { Context } from "hono";
import { createHandlers } from "src/utils.ts";
import type { BlankEnv, BlankInput } from "hono/types";
import { number, object, ValidationError } from "yup";
import { ErrorResponse } from "src/schema";
import { startTime, endTime } from "hono/timing";
import { getVideosInViewsRange } from "@/db/latestSnapshots";
const SnapshotQueryParamsSchema = object({
min_views: number().integer().optional().positive(),
max_views: number().integer().optional().positive()
});
type ContextType = Context<BlankEnv, "/videos", BlankInput>;
export const getVideosHanlder = createHandlers(async (c: ContextType) => {
startTime(c, "parse", "Parse the request");
try {
const queryParams = await SnapshotQueryParamsSchema.validate(c.req.query());
const { min_views, max_views } = queryParams;
if (!min_views && !max_views) {
const response: ErrorResponse<string> = {
code: "INVALID_QUERY_PARAMS",
message: "Invalid query parameters",
errors: ["Must provide one of these query parameters: min_views, max_views"]
};
return c.json<ErrorResponse<string>>(response, 400);
}
endTime(c, "parse");
startTime(c, "db", "Query the database");
const minViews = min_views ? min_views : 0;
const maxViews = max_views ? max_views : 2147483647;
const result = await getVideosInViewsRange(minViews, maxViews);
endTime(c, "db");
const rows = result.map((row) => ({
...row,
aid: Number(row.aid)
}));
return c.json(rows);
} catch (e: unknown) {
if (e instanceof ValidationError) {
const response: ErrorResponse<string> = {
code: "INVALID_QUERY_PARAMS",
message: "Invalid query parameters",
errors: e.errors
};
return c.json<ErrorResponse<string>>(response, 400);
} else {
const response: ErrorResponse<unknown> = {
code: "UNKNOWN_ERROR",
message: "Unhandled error",
errors: [e]
};
return c.json<ErrorResponse<unknown>>(response, 500);
}
}
});

View File

@ -1 +0,0 @@
export * from "./GET.ts";

View File

@ -15,4 +15,4 @@ configureRoutes(app);
await startServer(app); await startServer(app);
export const VERSION = "0.6.0"; export const VERSION = "0.5.3";

View File

@ -6,7 +6,6 @@ import { Hono } from "hono";
import { Variables } from "hono/types"; import { Variables } from "hono/types";
import { createCaptchaSessionHandler, verifyChallengeHandler } from "routes/captcha"; import { createCaptchaSessionHandler, verifyChallengeHandler } from "routes/captcha";
import { getCaptchaDifficultyHandler } from "routes/captcha/difficulty/GET.ts"; import { getCaptchaDifficultyHandler } from "routes/captcha/difficulty/GET.ts";
import { getVideosHanlder } from "@/routes/videos";
import { loginHandler } from "@/routes/login/session/POST"; import { loginHandler } from "@/routes/login/session/POST";
import { logoutHandler } from "@/routes/session"; import { logoutHandler } from "@/routes/session";
@ -14,8 +13,6 @@ export function configureRoutes(app: Hono<{ Variables: Variables }>) {
app.get("/", ...rootHandler); app.get("/", ...rootHandler);
app.all("/ping", ...pingHandler); app.all("/ping", ...pingHandler);
app.get("/videos", ...getVideosHanlder);
app.get("/video/:id/snapshots", ...getSnapshotsHanlder); app.get("/video/:id/snapshots", ...getSnapshotsHanlder);
app.get("/video/:id/info", ...videoInfoHandler); app.get("/video/:id/info", ...videoInfoHandler);

View File

@ -4,20 +4,20 @@ import type { Psql } from "@core/db/psql.d.ts";
export async function getVideosNearMilestone(sql: Psql) { export async function getVideosNearMilestone(sql: Psql) {
const queryResult = await sql<LatestSnapshotType[]>` const queryResult = await sql<LatestSnapshotType[]>`
SELECT ls.* SELECT ls.*
FROM latest_video_snapshot ls FROM latest_video_snapshot ls
RIGHT JOIN songs ON songs.aid = ls.aid RIGHT JOIN songs ON songs.aid = ls.aid
WHERE WHERE
(views >= 50000 AND views < 100000) OR (views >= 50000 AND views < 100000) OR
(views >= 900000 AND views < 1000000) OR (views >= 900000 AND views < 1000000) OR
(views >= CEIL(views::float/1000000::float)*1000000-100000 AND views < CEIL(views::float/1000000::float)*1000000) (views >= 9900000 AND views < 10000000)
UNION UNION
SELECT ls.* SELECT ls.*
FROM latest_video_snapshot ls FROM latest_video_snapshot ls
WHERE WHERE
(views >= 90000 AND views < 100000) OR (views >= 90000 AND views < 100000) OR
(views >= 900000 AND views < 1000000) OR (views >= 900000 AND views < 1000000) OR
(views >= CEIL(views::float/1000000::float)*1000000-100000 AND views < CEIL(views::float/1000000::float)*1000000) (views >= 9900000 AND views < 10000000)
`; `;
return queryResult.map((row) => { return queryResult.map((row) => {
return { return {

View File

@ -84,5 +84,5 @@ export const snapshotTickWorker = async (_job: Job) => {
export const closetMilestone = (views: number) => { export const closetMilestone = (views: number) => {
if (views < 100000) return 100000; if (views < 100000) return 100000;
if (views < 1000000) return 1000000; if (views < 1000000) return 1000000;
return Math.ceil(views / 1000000) * 1000000; return 10000000;
}; };

View File

@ -1,5 +1,5 @@
import { Job } from "bullmq"; import { Job } from "bullmq";
import { getLatestSnapshot, scheduleSnapshot, setSnapshotStatus, snapshotScheduleExists } from "db/snapshotSchedule.ts"; import { scheduleSnapshot, setSnapshotStatus, snapshotScheduleExists } from "db/snapshotSchedule.ts";
import logger from "@core/log/logger.ts"; import logger from "@core/log/logger.ts";
import { HOUR, MINUTE, SECOND } from "@core/const/time.ts"; import { HOUR, MINUTE, SECOND } from "@core/const/time.ts";
import { getBiliVideoStatus, setBiliVideoStatus } from "../../db/bilibili_metadata.ts"; import { getBiliVideoStatus, setBiliVideoStatus } from "../../db/bilibili_metadata.ts";
@ -8,7 +8,6 @@ import { getSongsPublihsedAt } from "db/songs.ts";
import { getAdjustedShortTermETA } from "mq/scheduling.ts"; import { getAdjustedShortTermETA } from "mq/scheduling.ts";
import { NetSchedulerError } from "@core/net/delegate.ts"; import { NetSchedulerError } from "@core/net/delegate.ts";
import { sql } from "@core/db/dbNew.ts"; import { sql } from "@core/db/dbNew.ts";
import { closetMilestone } from "./snapshotTick.ts";
const snapshotTypeToTaskMap: { [key: string]: string } = { const snapshotTypeToTaskMap: { [key: string]: string } = {
milestone: "snapshotMilestoneVideo", milestone: "snapshotMilestoneVideo",
@ -22,7 +21,6 @@ export const snapshotVideoWorker = async (job: Job): Promise<void> => {
const type = job.data.type; const type = job.data.type;
const task = snapshotTypeToTaskMap[type] ?? "snapshotVideo"; const task = snapshotTypeToTaskMap[type] ?? "snapshotVideo";
const retryInterval = type === "milestone" ? 5 * SECOND : 2 * MINUTE; const retryInterval = type === "milestone" ? 5 * SECOND : 2 * MINUTE;
const latestSnapshot = await getLatestSnapshot(sql, aid);
try { try {
const exists = await snapshotScheduleExists(sql, id); const exists = await snapshotScheduleExists(sql, id);
if (!exists) { if (!exists) {
@ -73,10 +71,6 @@ export const snapshotVideoWorker = async (job: Job): Promise<void> => {
await scheduleSnapshot(sql, aid, type, Date.now() + intervalMins * MINUTE, true); await scheduleSnapshot(sql, aid, type, Date.now() + intervalMins * MINUTE, true);
} }
if (type !== "milestone") return; if (type !== "milestone") return;
const alreadyAchievedMilestone = stat.views > closetMilestone(latestSnapshot.views);
if (alreadyAchievedMilestone) {
return;
}
const eta = await getAdjustedShortTermETA(sql, aid); const eta = await getAdjustedShortTermETA(sql, aid);
if (eta > 144) { if (eta > 144) {
const etaHoursString = eta.toFixed(2) + " hrs"; const etaHoursString = eta.toFixed(2) + " hrs";

View File

@ -1,6 +1,6 @@
{ {
"name": "crawler", "name": "crawler",
"version": "1.3.0", "version": "1.2.26",
"scripts": { "scripts": {
"test": "bun --env-file=.env.test run vitest", "test": "bun --env-file=.env.test run vitest",
"worker:main": "bun run ./src/worker.ts", "worker:main": "bun run ./src/worker.ts",

View File

@ -14,7 +14,7 @@ export const ErrorDialog: React.FC<ErrorDialogProps> = ({ children, closeDialog,
<> <>
<DialogHeadline>{errorCode ? t(errorCode) : "错误"}</DialogHeadline> <DialogHeadline>{errorCode ? t(errorCode) : "错误"}</DialogHeadline>
<DialogSupportingText>{children}</DialogSupportingText> <DialogSupportingText>{children}</DialogSupportingText>
<DialogButtonGroup close={closeDialog}> <DialogButtonGroup>
<DialogButton onClick={closeDialog}></DialogButton> <DialogButton onClick={closeDialog}></DialogButton>
</DialogButtonGroup> </DialogButtonGroup>
</> </>

View File

@ -1,6 +1,6 @@
{ {
"name": "next", "name": "next",
"version": "2.9.1", "version": "2.9.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev --turbopack -p 7400", "dev": "next dev --turbopack -p 7400",