update: the close-milestone route to support eta table
This commit is contained in:
parent
82022231d1
commit
a102d6d771
@ -1,10 +1,16 @@
|
||||
import { dbMain } from "@core/drizzle";
|
||||
import { eta as etaTable } from "@core/drizzle/main/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { MINUTE, HOUR, getClosetMilestone } from "@core/lib";
|
||||
import { getLatestSnapshot, getClosestSnapshot } from "@core/db";
|
||||
|
||||
export const getMilestoneETA = async (aid: number, targetViews?: number): Promise<number> => {
|
||||
export const getGroundTruthMilestoneETA = async (
|
||||
aid: number,
|
||||
targetViews?: number
|
||||
): Promise<number> => {
|
||||
const DELTA = 1e-5;
|
||||
let minETAHours = Infinity;
|
||||
const timeIntervals = [3 * HOUR, 24 * HOUR, 96 * HOUR];
|
||||
const timeIntervals = [3 * MINUTE, 20 * MINUTE, HOUR, 3 * HOUR, 6 * HOUR, 72 * HOUR];
|
||||
const currentTimestamp = new Date().getTime();
|
||||
const latestSnapshot = await getLatestSnapshot(aid);
|
||||
const latestSnapshotTime = new Date(latestSnapshot.time).getTime();
|
||||
@ -26,3 +32,11 @@ export const getMilestoneETA = async (aid: number, targetViews?: number): Promis
|
||||
}
|
||||
return minETAHours;
|
||||
};
|
||||
|
||||
export const getMilestoneETA = async (aid: number) => {
|
||||
const data = await dbMain.select().from(etaTable).where(eq(etaTable.aid, aid)).limit(1);
|
||||
if (data.length > 0) {
|
||||
return data[0].eta;
|
||||
}
|
||||
return getGroundTruthMilestoneETA(aid);
|
||||
};
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
import { relations } from "drizzle-orm/relations";
|
||||
import {} from "./schema";
|
||||
import { } from "./schema";
|
||||
|
||||
|
||||
@ -1,334 +1,202 @@
|
||||
import {
|
||||
pgTable,
|
||||
index,
|
||||
uniqueIndex,
|
||||
bigserial,
|
||||
bigint,
|
||||
text,
|
||||
timestamp,
|
||||
integer,
|
||||
unique,
|
||||
serial,
|
||||
smallint,
|
||||
boolean,
|
||||
varchar,
|
||||
jsonb,
|
||||
pgSequence
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { sql } from "drizzle-orm";
|
||||
import { pgTable, uniqueIndex, index, bigint, real, integer, timestamp, bigserial, text, unique, serial, smallint, boolean, varchar, jsonb, pgSequence } from "drizzle-orm/pg-core"
|
||||
import { sql } from "drizzle-orm"
|
||||
|
||||
export const viewsIncrementRateIdSeq = pgSequence("views_increment_rate_id_seq", {
|
||||
startWith: "1",
|
||||
increment: "1",
|
||||
minValue: "1",
|
||||
maxValue: "9223372036854775807",
|
||||
cache: "1",
|
||||
cycle: false
|
||||
});
|
||||
export const allDataIdSeq = pgSequence("all_data_id_seq", {
|
||||
startWith: "1",
|
||||
increment: "1",
|
||||
minValue: "1",
|
||||
maxValue: "2147483647",
|
||||
cache: "1",
|
||||
cycle: false
|
||||
});
|
||||
export const labelingResultIdSeq = pgSequence("labeling_result_id_seq", {
|
||||
startWith: "1",
|
||||
increment: "1",
|
||||
minValue: "1",
|
||||
maxValue: "2147483647",
|
||||
cache: "1",
|
||||
cycle: false
|
||||
});
|
||||
export const songsIdSeq = pgSequence("songs_id_seq", {
|
||||
startWith: "1",
|
||||
increment: "1",
|
||||
minValue: "1",
|
||||
maxValue: "2147483647",
|
||||
cache: "1",
|
||||
cycle: false
|
||||
});
|
||||
export const videoSnapshotIdSeq = pgSequence("video_snapshot_id_seq", {
|
||||
startWith: "1",
|
||||
increment: "1",
|
||||
minValue: "1",
|
||||
maxValue: "2147483647",
|
||||
cache: "1",
|
||||
cycle: false
|
||||
});
|
||||
|
||||
export const snapshotSchedule = pgTable(
|
||||
"snapshot_schedule",
|
||||
{
|
||||
id: bigserial({ mode: "bigint" }).notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).notNull(),
|
||||
type: text(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
startedAt: timestamp("started_at", { withTimezone: true, mode: "string" }),
|
||||
finishedAt: timestamp("finished_at", { withTimezone: true, mode: "string" }),
|
||||
status: text().default("pending").notNull()
|
||||
},
|
||||
(table) => [
|
||||
index("idx_snapshot_schedule_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_snapshot_schedule_started_at").using(
|
||||
"btree",
|
||||
table.startedAt.asc().nullsLast().op("timestamptz_ops")
|
||||
),
|
||||
index("idx_snapshot_schedule_status").using("btree", table.status.asc().nullsLast().op("text_ops")),
|
||||
index("idx_snapshot_schedule_type").using("btree", table.type.asc().nullsLast().op("text_ops")),
|
||||
uniqueIndex("snapshot_schedule_pkey").using("btree", table.id.asc().nullsLast().op("int8_ops"))
|
||||
]
|
||||
);
|
||||
export const viewsIncrementRateIdSeq = pgSequence("views_increment_rate_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "9223372036854775807", cache: "1", cycle: false })
|
||||
export const allDataIdSeq = pgSequence("all_data_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "2147483647", cache: "1", cycle: false })
|
||||
export const labelingResultIdSeq = pgSequence("labeling_result_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "2147483647", cache: "1", cycle: false })
|
||||
export const songsIdSeq = pgSequence("songs_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "2147483647", cache: "1", cycle: false })
|
||||
export const videoSnapshotIdSeq = pgSequence("video_snapshot_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "2147483647", cache: "1", cycle: false })
|
||||
export const captchaDifficultySettingsIdSeq = pgSequence("captcha_difficulty_settings_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "2147483647", cache: "1", cycle: false })
|
||||
export const usersIdSeq = pgSequence("users_id_seq", { startWith: "1", increment: "1", minValue: "1", maxValue: "2147483647", cache: "1", cycle: false })
|
||||
|
||||
export const videoSnapshot = pgTable(
|
||||
"video_snapshot",
|
||||
{
|
||||
id: integer()
|
||||
.default(sql`nextval('video_snapshot_id_seq'::regclass)`)
|
||||
.notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
views: integer().notNull(),
|
||||
coins: integer().notNull(),
|
||||
likes: integer().notNull(),
|
||||
favorites: integer().notNull(),
|
||||
shares: integer().notNull(),
|
||||
danmakus: integer().notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).notNull(),
|
||||
replies: integer().notNull()
|
||||
},
|
||||
(table) => [
|
||||
index("idx_vid_snapshot_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_vid_snapshot_aid_created_at").using(
|
||||
"btree",
|
||||
table.aid.asc().nullsLast().op("int8_ops"),
|
||||
table.createdAt.asc().nullsLast().op("int8_ops")
|
||||
),
|
||||
index("idx_vid_snapshot_time").using("btree", table.createdAt.asc().nullsLast().op("timestamptz_ops")),
|
||||
index("idx_vid_snapshot_views").using("btree", table.views.asc().nullsLast().op("int4_ops")),
|
||||
uniqueIndex("video_snapshot_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops"))
|
||||
]
|
||||
);
|
||||
export const eta = pgTable("eta", {
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).notNull(),
|
||||
eta: real().notNull(),
|
||||
speed: real().notNull(),
|
||||
currentViews: integer("current_views").notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(),
|
||||
}, (table) => [
|
||||
uniqueIndex("eta_pkey").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_eta_eta").using("btree", table.eta.asc().nullsLast().op("float4_ops")),
|
||||
]);
|
||||
|
||||
export const bilibiliUser = pgTable(
|
||||
"bilibili_user",
|
||||
{
|
||||
id: serial().primaryKey().notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
uid: bigint({ mode: "number" }).notNull(),
|
||||
username: text().notNull(),
|
||||
desc: text().notNull(),
|
||||
fans: integer().notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "string" })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull()
|
||||
},
|
||||
(table) => [
|
||||
index("idx_bili-user_uid").using("btree", table.uid.asc().nullsLast().op("int8_ops")),
|
||||
unique("unq_bili-user_uid").on(table.uid)
|
||||
]
|
||||
);
|
||||
export const snapshotSchedule = pgTable("snapshot_schedule", {
|
||||
id: bigserial({ mode: "bigint" }).notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).notNull(),
|
||||
type: text(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
startedAt: timestamp("started_at", { withTimezone: true, mode: 'string' }),
|
||||
finishedAt: timestamp("finished_at", { withTimezone: true, mode: 'string' }),
|
||||
status: text().default('pending').notNull(),
|
||||
}, (table) => [
|
||||
index("idx_snapshot_schedule_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_snapshot_schedule_started_at").using("btree", table.startedAt.asc().nullsLast().op("timestamptz_ops")),
|
||||
index("idx_snapshot_schedule_status").using("btree", table.status.asc().nullsLast().op("text_ops")),
|
||||
index("idx_snapshot_schedule_type").using("btree", table.type.asc().nullsLast().op("text_ops")),
|
||||
uniqueIndex("snapshot_schedule_pkey").using("btree", table.id.asc().nullsLast().op("int8_ops")),
|
||||
]);
|
||||
|
||||
export const relations = pgTable(
|
||||
"relations",
|
||||
{
|
||||
id: serial().primaryKey().notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
sourceId: bigint("source_id", { mode: "number" }).notNull(),
|
||||
sourceType: text("source_type").notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
targetId: bigint("target_id", { mode: "number" }).notNull(),
|
||||
targetType: text("target_type").notNull(),
|
||||
relation: text().notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "string" })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull()
|
||||
},
|
||||
(table) => [
|
||||
index("idx_relations_source_id_source_type_relation").using(
|
||||
"btree",
|
||||
table.sourceId.asc().nullsLast().op("int8_ops"),
|
||||
table.sourceType.asc().nullsLast().op("int8_ops"),
|
||||
table.relation.asc().nullsLast().op("text_ops")
|
||||
),
|
||||
index("idx_relations_target_id_target_type_relation").using(
|
||||
"btree",
|
||||
table.targetId.asc().nullsLast().op("text_ops"),
|
||||
table.targetType.asc().nullsLast().op("text_ops"),
|
||||
table.relation.asc().nullsLast().op("text_ops")
|
||||
),
|
||||
unique("unq_relations").on(table.sourceId, table.sourceType, table.targetId, table.targetType, table.relation)
|
||||
]
|
||||
);
|
||||
export const videoSnapshot = pgTable("video_snapshot", {
|
||||
id: integer().default(sql`nextval('video_snapshot_id_seq'::regclass)`).notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
views: integer().notNull(),
|
||||
coins: integer().notNull(),
|
||||
likes: integer().notNull(),
|
||||
favorites: integer().notNull(),
|
||||
shares: integer().notNull(),
|
||||
danmakus: integer().notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).notNull(),
|
||||
replies: integer().notNull(),
|
||||
}, (table) => [
|
||||
index("idx_vid_snapshot_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_vid_snapshot_aid_created_at").using("btree", table.aid.asc().nullsLast().op("int8_ops"), table.createdAt.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_vid_snapshot_time").using("btree", table.createdAt.asc().nullsLast().op("timestamptz_ops")),
|
||||
index("idx_vid_snapshot_views").using("btree", table.views.asc().nullsLast().op("int4_ops")),
|
||||
uniqueIndex("video_snapshot_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
|
||||
]);
|
||||
|
||||
export const songs = pgTable(
|
||||
"songs",
|
||||
{
|
||||
id: integer()
|
||||
.default(sql`nextval('songs_id_seq'::regclass)`)
|
||||
.notNull(),
|
||||
name: text(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }),
|
||||
publishedAt: timestamp("published_at", { withTimezone: true, mode: "string" }),
|
||||
duration: integer(),
|
||||
type: smallint(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
neteaseId: bigint("netease_id", { mode: "number" }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: "string" })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
deleted: boolean().default(false).notNull(),
|
||||
image: text(),
|
||||
producer: text()
|
||||
},
|
||||
(table) => [
|
||||
index("idx_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_hash_songs_aid").using("hash", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_netease_id").using("btree", table.neteaseId.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_published_at").using("btree", table.publishedAt.asc().nullsLast().op("timestamptz_ops")),
|
||||
index("idx_type").using("btree", table.type.asc().nullsLast().op("int2_ops")),
|
||||
uniqueIndex("songs_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
|
||||
uniqueIndex("unq_songs_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
uniqueIndex("unq_songs_netease_id").using("btree", table.neteaseId.asc().nullsLast().op("int8_ops"))
|
||||
]
|
||||
);
|
||||
export const bilibiliUser = pgTable("bilibili_user", {
|
||||
id: serial().primaryKey().notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
uid: bigint({ mode: "number" }).notNull(),
|
||||
username: text().notNull(),
|
||||
desc: text().notNull(),
|
||||
fans: integer().notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
}, (table) => [
|
||||
index("idx_bili-user_uid").using("btree", table.uid.asc().nullsLast().op("int8_ops")),
|
||||
unique("unq_bili-user_uid").on(table.uid),
|
||||
]);
|
||||
|
||||
export const relations = pgTable("relations", {
|
||||
id: serial().primaryKey().notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
sourceId: bigint("source_id", { mode: "number" }).notNull(),
|
||||
sourceType: text("source_type").notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
targetId: bigint("target_id", { mode: "number" }).notNull(),
|
||||
targetType: text("target_type").notNull(),
|
||||
relation: text().notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
}, (table) => [
|
||||
index("idx_relations_source_id_source_type_relation").using("btree", table.sourceId.asc().nullsLast().op("int8_ops"), table.sourceType.asc().nullsLast().op("int8_ops"), table.relation.asc().nullsLast().op("text_ops")),
|
||||
index("idx_relations_target_id_target_type_relation").using("btree", table.targetId.asc().nullsLast().op("text_ops"), table.targetType.asc().nullsLast().op("text_ops"), table.relation.asc().nullsLast().op("text_ops")),
|
||||
unique("unq_relations").on(table.sourceId, table.sourceType, table.targetId, table.targetType, table.relation),
|
||||
]);
|
||||
|
||||
export const songs = pgTable("songs", {
|
||||
id: integer().default(sql`nextval('songs_id_seq'::regclass)`).notNull(),
|
||||
name: text(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }),
|
||||
publishedAt: timestamp("published_at", { withTimezone: true, mode: 'string' }),
|
||||
duration: integer(),
|
||||
type: smallint(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
neteaseId: bigint("netease_id", { mode: "number" }),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
updatedAt: timestamp("updated_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
deleted: boolean().default(false).notNull(),
|
||||
image: text(),
|
||||
producer: text(),
|
||||
}, (table) => [
|
||||
index("idx_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_hash_songs_aid").using("hash", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_netease_id").using("btree", table.neteaseId.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_published_at").using("btree", table.publishedAt.asc().nullsLast().op("timestamptz_ops")),
|
||||
index("idx_type").using("btree", table.type.asc().nullsLast().op("int2_ops")),
|
||||
uniqueIndex("songs_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
|
||||
uniqueIndex("unq_songs_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
uniqueIndex("unq_songs_netease_id").using("btree", table.neteaseId.asc().nullsLast().op("int8_ops")),
|
||||
]);
|
||||
|
||||
export const singer = pgTable("singer", {
|
||||
id: serial().primaryKey().notNull(),
|
||||
name: text().notNull()
|
||||
name: text().notNull(),
|
||||
});
|
||||
|
||||
export const bilibiliMetadata = pgTable(
|
||||
"bilibili_metadata",
|
||||
{
|
||||
id: integer()
|
||||
.default(sql`nextval('all_data_id_seq'::regclass)`)
|
||||
.notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).notNull(),
|
||||
bvid: varchar({ length: 12 }),
|
||||
description: text(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
uid: bigint({ mode: "number" }),
|
||||
tags: text(),
|
||||
title: text(),
|
||||
publishedAt: timestamp("published_at", { withTimezone: true, mode: "string" }),
|
||||
duration: integer(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" }).default(sql`CURRENT_TIMESTAMP`),
|
||||
status: integer().default(0).notNull(),
|
||||
coverUrl: text("cover_url")
|
||||
},
|
||||
(table) => [
|
||||
uniqueIndex("all_data_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
|
||||
index("idx_all-data_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_all-data_bvid").using("btree", table.bvid.asc().nullsLast().op("text_ops")),
|
||||
index("idx_all-data_uid").using("btree", table.uid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_bili-meta_status").using("btree", table.status.asc().nullsLast().op("int4_ops")),
|
||||
uniqueIndex("unq_all-data_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops"))
|
||||
]
|
||||
);
|
||||
export const bilibiliMetadata = pgTable("bilibili_metadata", {
|
||||
id: integer().default(sql`nextval('all_data_id_seq'::regclass)`).notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).notNull(),
|
||||
bvid: varchar({ length: 12 }),
|
||||
description: text(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
uid: bigint({ mode: "number" }),
|
||||
tags: text(),
|
||||
title: text(),
|
||||
publishedAt: timestamp("published_at", { withTimezone: true, mode: 'string' }),
|
||||
duration: integer(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`),
|
||||
status: integer().default(0).notNull(),
|
||||
coverUrl: text("cover_url"),
|
||||
}, (table) => [
|
||||
uniqueIndex("all_data_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
|
||||
index("idx_all-data_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_all-data_bvid").using("btree", table.bvid.asc().nullsLast().op("text_ops")),
|
||||
index("idx_all-data_uid").using("btree", table.uid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_bili-meta_status").using("btree", table.status.asc().nullsLast().op("int4_ops")),
|
||||
uniqueIndex("unq_all-data_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
]);
|
||||
|
||||
export const humanClassifiedLables = pgTable(
|
||||
"human_classified_lables",
|
||||
{
|
||||
id: serial().notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).notNull(),
|
||||
uid: integer().notNull(),
|
||||
label: smallint().notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull()
|
||||
},
|
||||
(table) => [
|
||||
index("idx_classified-labels-human_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_classified-labels-human_author").using("btree", table.uid.asc().nullsLast().op("int4_ops")),
|
||||
index("idx_classified-labels-human_created-at").using(
|
||||
"btree",
|
||||
table.createdAt.asc().nullsLast().op("timestamptz_ops")
|
||||
),
|
||||
index("idx_classified-labels-human_label").using("btree", table.label.asc().nullsLast().op("int2_ops"))
|
||||
]
|
||||
);
|
||||
export const humanClassifiedLables = pgTable("human_classified_lables", {
|
||||
id: serial().notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).notNull(),
|
||||
uid: integer().notNull(),
|
||||
label: smallint().notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
}, (table) => [
|
||||
index("idx_classified-labels-human_aid").using("btree", table.aid.asc().nullsLast().op("int8_ops")),
|
||||
index("idx_classified-labels-human_author").using("btree", table.uid.asc().nullsLast().op("int4_ops")),
|
||||
index("idx_classified-labels-human_created-at").using("btree", table.createdAt.asc().nullsLast().op("timestamptz_ops")),
|
||||
index("idx_classified-labels-human_label").using("btree", table.label.asc().nullsLast().op("int2_ops")),
|
||||
]);
|
||||
|
||||
export const history = pgTable("history", {
|
||||
id: serial().primaryKey().notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
objectId: bigint("object_id", { mode: "number" }).notNull(),
|
||||
changeType: text("change_type").notNull(),
|
||||
changedAt: timestamp("changed_at", { withTimezone: true, mode: "string" }).notNull(),
|
||||
changedAt: timestamp("changed_at", { withTimezone: true, mode: 'string' }).notNull(),
|
||||
changedBy: integer("changed_by").notNull(),
|
||||
data: jsonb().notNull()
|
||||
data: jsonb().notNull(),
|
||||
});
|
||||
|
||||
export const labellingResult = pgTable(
|
||||
"labelling_result",
|
||||
{
|
||||
id: integer()
|
||||
.default(sql`nextval('labeling_result_id_seq'::regclass)`)
|
||||
.notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).notNull(),
|
||||
label: smallint().notNull(),
|
||||
modelVersion: text("model_version").notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: "string" })
|
||||
.default(sql`CURRENT_TIMESTAMP`)
|
||||
.notNull(),
|
||||
logits: smallint().array()
|
||||
},
|
||||
(table) => [
|
||||
index("idx_labeling_label_model-version").using(
|
||||
"btree",
|
||||
table.label.asc().nullsLast().op("int2_ops"),
|
||||
table.modelVersion.asc().nullsLast().op("int2_ops")
|
||||
),
|
||||
index("idx_labeling_model-version").using("btree", table.modelVersion.asc().nullsLast().op("text_ops")),
|
||||
index("idx_labelling_aid-label").using(
|
||||
"btree",
|
||||
table.aid.asc().nullsLast().op("int2_ops"),
|
||||
table.label.asc().nullsLast().op("int2_ops")
|
||||
),
|
||||
uniqueIndex("labeling_result_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
|
||||
uniqueIndex("unq_labelling-result_aid_model-version").using(
|
||||
"btree",
|
||||
table.aid.asc().nullsLast().op("int8_ops"),
|
||||
table.modelVersion.asc().nullsLast().op("int8_ops")
|
||||
)
|
||||
]
|
||||
);
|
||||
export const labellingResult = pgTable("labelling_result", {
|
||||
id: integer().default(sql`nextval('labeling_result_id_seq'::regclass)`).notNull(),
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).notNull(),
|
||||
label: smallint().notNull(),
|
||||
modelVersion: text("model_version").notNull(),
|
||||
createdAt: timestamp("created_at", { withTimezone: true, mode: 'string' }).default(sql`CURRENT_TIMESTAMP`).notNull(),
|
||||
logits: smallint().array(),
|
||||
}, (table) => [
|
||||
index("idx_labeling_label_model-version").using("btree", table.label.asc().nullsLast().op("int2_ops"), table.modelVersion.asc().nullsLast().op("int2_ops")),
|
||||
index("idx_labeling_model-version").using("btree", table.modelVersion.asc().nullsLast().op("text_ops")),
|
||||
index("idx_labelling_aid-label").using("btree", table.aid.asc().nullsLast().op("int2_ops"), table.label.asc().nullsLast().op("int2_ops")),
|
||||
uniqueIndex("labeling_result_pkey").using("btree", table.id.asc().nullsLast().op("int4_ops")),
|
||||
uniqueIndex("unq_labelling-result_aid_model-version").using("btree", table.aid.asc().nullsLast().op("int8_ops"), table.modelVersion.asc().nullsLast().op("int8_ops")),
|
||||
]);
|
||||
|
||||
export const latestVideoSnapshot = pgTable(
|
||||
"latest_video_snapshot",
|
||||
{
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).primaryKey().notNull(),
|
||||
time: timestamp({ withTimezone: true, mode: "string" }).notNull(),
|
||||
views: integer().notNull(),
|
||||
coins: integer().notNull(),
|
||||
likes: integer().notNull(),
|
||||
favorites: integer().notNull(),
|
||||
replies: integer().notNull(),
|
||||
danmakus: integer().notNull(),
|
||||
shares: integer().notNull()
|
||||
},
|
||||
(table) => [
|
||||
index("idx_latest-video-snapshot_time").using("btree", table.time.asc().nullsLast().op("timestamptz_ops")),
|
||||
index("idx_latest-video-snapshot_views").using("btree", table.views.asc().nullsLast().op("int4_ops"))
|
||||
]
|
||||
);
|
||||
export const latestVideoSnapshot = pgTable("latest_video_snapshot", {
|
||||
// You can use { mode: "bigint" } if numbers are exceeding js number limitations
|
||||
aid: bigint({ mode: "number" }).primaryKey().notNull(),
|
||||
time: timestamp({ withTimezone: true, mode: 'string' }).notNull(),
|
||||
views: integer().notNull(),
|
||||
coins: integer().notNull(),
|
||||
likes: integer().notNull(),
|
||||
favorites: integer().notNull(),
|
||||
replies: integer().notNull(),
|
||||
danmakus: integer().notNull(),
|
||||
shares: integer().notNull(),
|
||||
}, (table) => [
|
||||
index("idx_latest-video-snapshot_time").using("btree", table.time.asc().nullsLast().op("timestamptz_ops")),
|
||||
index("idx_latest-video-snapshot_views").using("btree", table.views.asc().nullsLast().op("int4_ops")),
|
||||
]);
|
||||
|
||||
@ -1,51 +1,36 @@
|
||||
import { Elysia, t } from "elysia";
|
||||
import { dbMain } from "@core/drizzle";
|
||||
import { bilibiliMetadata, latestVideoSnapshot } from "@core/drizzle/main/schema";
|
||||
import { bilibiliMetadata, eta, latestVideoSnapshot } from "@core/drizzle/main/schema";
|
||||
import { eq, and, gte, lt, desc } from "drizzle-orm";
|
||||
import { getMilestoneETA } from "@core/db";
|
||||
import serverTiming from "@elysia/middlewares/timing";
|
||||
|
||||
type MileStoneType = "dendou" | "densetsu" | "shinwa";
|
||||
|
||||
const range = {
|
||||
dendou: [90000, 99999, 100000],
|
||||
densetsu: [900000, 999999, 1000000],
|
||||
shinwa: [5000000, 9999999, 10000000]
|
||||
dendou: [90000, 99999, 2160],
|
||||
densetsu: [900000, 999999, 8760],
|
||||
shinwa: [5000000, 9999999, 87600]
|
||||
};
|
||||
|
||||
export const closeMileStoneHandler = new Elysia({ prefix: "/song" }).use(serverTiming()).get(
|
||||
export const closeMileStoneHandler = new Elysia({ prefix: "/songs" }).use(serverTiming()).get(
|
||||
"/close-milestone/:type",
|
||||
async ({ params, timeLog }) => {
|
||||
timeLog.startTime("retrieveCandidates")
|
||||
timeLog.startTime("retrieveCandidates");
|
||||
const type = params.type;
|
||||
const min = range[type as MileStoneType][0];
|
||||
const max = range[type as MileStoneType][1];
|
||||
const data = await dbMain
|
||||
return dbMain
|
||||
.select()
|
||||
.from(bilibiliMetadata)
|
||||
.innerJoin(latestVideoSnapshot, eq(latestVideoSnapshot.aid, bilibiliMetadata.aid))
|
||||
.where(and(gte(latestVideoSnapshot.views, min), lt(latestVideoSnapshot.views, max)))
|
||||
.orderBy(desc(latestVideoSnapshot.views));
|
||||
type Row = (typeof data)[number];
|
||||
type Result = Row & {
|
||||
eta: number;
|
||||
};
|
||||
const result: Result[] = [];
|
||||
timeLog.endTime("retrieveCandidates")
|
||||
|
||||
timeLog.startTime("calculateETA");
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const aid = data[i].bilibili_metadata.aid;
|
||||
const eta = await getMilestoneETA(aid, range[type as MileStoneType][2]);
|
||||
result.push({
|
||||
...data[i],
|
||||
eta
|
||||
});
|
||||
}
|
||||
timeLog.endTime("calculateETA");
|
||||
|
||||
result.sort((a, b) => a.eta - b.eta);
|
||||
return result;
|
||||
.from(eta)
|
||||
.innerJoin(bilibiliMetadata, eq(bilibiliMetadata.aid, eta.aid))
|
||||
.where(
|
||||
and(
|
||||
gte(eta.currentViews, min),
|
||||
lt(eta.currentViews, max),
|
||||
lt(eta.eta, range[type as MileStoneType][2])
|
||||
)
|
||||
)
|
||||
.orderBy(eta.eta);
|
||||
},
|
||||
{
|
||||
response: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user