Initial commit
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
import { Database } from "bun:sqlite";
|
||||
import { mkdirSync } from "fs";
|
||||
import { dirname } from "path";
|
||||
import type { VideoEntry, VideoListItem, PreviousPB } from "../types/video";
|
||||
|
||||
const DB_PATH = process.env.DB_PATH || "./data/surf.db";
|
||||
|
||||
let db: Database | null = null;
|
||||
|
||||
export function initDb(): void {
|
||||
if (db) return;
|
||||
|
||||
mkdirSync(dirname(DB_PATH), { recursive: true });
|
||||
|
||||
db = new Database(DB_PATH, { create: true });
|
||||
db.exec("PRAGMA journal_mode = WAL");
|
||||
db.exec("PRAGMA strict_mode = ON");
|
||||
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS videos (
|
||||
id TEXT PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
map_name TEXT NOT NULL,
|
||||
player_name TEXT NOT NULL,
|
||||
steam_id TEXT NOT NULL,
|
||||
run_time REAL NOT NULL,
|
||||
total_ticks INTEGER NOT NULL,
|
||||
tick_interval REAL NOT NULL,
|
||||
video_key TEXT NOT NULL,
|
||||
mtv_key TEXT NOT NULL,
|
||||
thumbnail_key TEXT,
|
||||
tier INTEGER,
|
||||
json_stats TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_videos_map ON videos(map_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_videos_created ON videos(created_at DESC);
|
||||
`);
|
||||
|
||||
const columns = db.prepare("PRAGMA table_info(videos)").all() as {
|
||||
name: string;
|
||||
}[];
|
||||
if (!columns.some((col) => col.name === "tier")) {
|
||||
db.exec("ALTER TABLE videos ADD COLUMN tier INTEGER");
|
||||
}
|
||||
if (!columns.some((col) => col.name === "map_id")) {
|
||||
db.exec("ALTER TABLE videos ADD COLUMN map_id INTEGER");
|
||||
}
|
||||
if (!columns.some((col) => col.name === "rank")) {
|
||||
db.exec("ALTER TABLE videos ADD COLUMN rank INTEGER");
|
||||
}
|
||||
if (!columns.some((col) => col.name === "rank_updated_at")) {
|
||||
db.exec("ALTER TABLE videos ADD COLUMN rank_updated_at TEXT");
|
||||
}
|
||||
if (!columns.some((col) => col.name === "previous_pbs")) {
|
||||
db.exec("ALTER TABLE videos ADD COLUMN previous_pbs TEXT");
|
||||
}
|
||||
}
|
||||
|
||||
function getDb(): Database {
|
||||
if (!db) initDb();
|
||||
return db!;
|
||||
}
|
||||
|
||||
export function insertVideo(entry: VideoEntry): void {
|
||||
const d = getDb();
|
||||
d.prepare(
|
||||
`INSERT INTO videos (id, title, description, map_name, player_name, steam_id, run_time, total_ticks, tick_interval, video_key, mtv_key, thumbnail_key, tier, map_id, json_stats, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
).run(
|
||||
entry.id,
|
||||
entry.title,
|
||||
entry.description,
|
||||
entry.mapName,
|
||||
entry.playerName,
|
||||
entry.steamId,
|
||||
entry.runTime,
|
||||
entry.totalTicks,
|
||||
entry.tickInterval,
|
||||
entry.videoKey,
|
||||
entry.mtvKey,
|
||||
entry.thumbnailKey ?? null,
|
||||
entry.tier ?? null,
|
||||
entry.mapId ?? null,
|
||||
entry.jsonStats ?? null,
|
||||
entry.createdAt,
|
||||
);
|
||||
}
|
||||
|
||||
export function getAllVideos(): VideoListItem[] {
|
||||
const d = getDb();
|
||||
return d
|
||||
.prepare(
|
||||
`SELECT id, title, map_name, player_name, run_time, tier, thumbnail_key, created_at
|
||||
FROM videos ORDER BY created_at DESC`,
|
||||
)
|
||||
.all()
|
||||
.map(rowToListItem);
|
||||
}
|
||||
|
||||
export function getVideoById(id: string): VideoEntry | null {
|
||||
const d = getDb();
|
||||
const row = d.prepare(`SELECT * FROM videos WHERE id = ?`).get(id);
|
||||
return row ? rowToEntry(row as any) : null;
|
||||
}
|
||||
|
||||
export function deleteVideoById(id: string): boolean {
|
||||
const d = getDb();
|
||||
const result = d.prepare(`DELETE FROM videos WHERE id = ?`).run(id);
|
||||
return result.changes > 0;
|
||||
}
|
||||
|
||||
function rowToListItem(row: any): VideoListItem {
|
||||
return {
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
mapName: row.map_name,
|
||||
playerName: row.player_name,
|
||||
runTime: row.run_time,
|
||||
tier: row.tier ?? undefined,
|
||||
thumbnailUrl: row.thumbnail_key || undefined,
|
||||
createdAt: row.created_at,
|
||||
previousPbs: row.previous_pbs
|
||||
? JSON.parse(row.previous_pbs)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function rowToEntry(row: any): VideoEntry {
|
||||
return {
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
description: row.description,
|
||||
mapName: row.map_name,
|
||||
playerName: row.player_name,
|
||||
steamId: row.steam_id,
|
||||
runTime: row.run_time,
|
||||
totalTicks: row.total_ticks,
|
||||
tickInterval: row.tick_interval,
|
||||
videoKey: row.video_key,
|
||||
mtvKey: row.mtv_key,
|
||||
thumbnailKey: row.thumbnail_key ?? undefined,
|
||||
tier: row.tier ?? undefined,
|
||||
mapId: row.map_id ?? undefined,
|
||||
jsonStats: row.json_stats ?? undefined,
|
||||
createdAt: row.created_at,
|
||||
previousPbs: row.previous_pbs
|
||||
? JSON.parse(row.previous_pbs)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateThumbnailKey(
|
||||
id: string,
|
||||
thumbnailKey: string | null,
|
||||
): boolean {
|
||||
const d = getDb();
|
||||
const result = d
|
||||
.prepare("UPDATE videos SET thumbnail_key = ? WHERE id = ?")
|
||||
.run(thumbnailKey, id);
|
||||
return result.changes > 0;
|
||||
}
|
||||
|
||||
export function getVideoByMapAndPlayer(
|
||||
mapName: string,
|
||||
steamId: string,
|
||||
): VideoEntry | null {
|
||||
const d = getDb();
|
||||
const row = d
|
||||
.prepare(`SELECT * FROM videos WHERE map_name = ? AND steam_id = ?`)
|
||||
.get(mapName, steamId);
|
||||
return row ? rowToEntry(row as any) : null;
|
||||
}
|
||||
|
||||
export function updateVideoPB(
|
||||
id: string,
|
||||
entry: VideoEntry,
|
||||
previousPbs: PreviousPB[],
|
||||
): void {
|
||||
const d = getDb();
|
||||
d.prepare(
|
||||
`UPDATE videos SET
|
||||
title = ?,
|
||||
description = ?,
|
||||
map_name = ?,
|
||||
player_name = ?,
|
||||
steam_id = ?,
|
||||
run_time = ?,
|
||||
total_ticks = ?,
|
||||
tick_interval = ?,
|
||||
video_key = ?,
|
||||
mtv_key = ?,
|
||||
thumbnail_key = ?,
|
||||
tier = ?,
|
||||
map_id = ?,
|
||||
json_stats = ?,
|
||||
created_at = ?,
|
||||
previous_pbs = ?
|
||||
WHERE id = ?`,
|
||||
).run(
|
||||
entry.title,
|
||||
entry.description,
|
||||
entry.mapName,
|
||||
entry.playerName,
|
||||
entry.steamId,
|
||||
entry.runTime,
|
||||
entry.totalTicks,
|
||||
entry.tickInterval,
|
||||
entry.videoKey,
|
||||
entry.mtvKey,
|
||||
entry.thumbnailKey ?? null,
|
||||
entry.tier ?? null,
|
||||
entry.mapId ?? null,
|
||||
entry.jsonStats ?? null,
|
||||
entry.createdAt,
|
||||
JSON.stringify(previousPbs),
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
export function updateTier(id: string, tier: number | null): boolean {
|
||||
const d = getDb();
|
||||
const result = d
|
||||
.prepare("UPDATE videos SET tier = ? WHERE id = ?")
|
||||
.run(tier, id);
|
||||
return result.changes > 0;
|
||||
}
|
||||
Reference in New Issue
Block a user