update: song page
This commit is contained in:
parent
4fe266ce82
commit
8b17f8177c
@ -14,6 +14,7 @@
|
||||
},
|
||||
"imports": {
|
||||
"@astrojs/node": "npm:@astrojs/node@^9.1.3",
|
||||
"@astrojs/svelte": "npm:@astrojs/svelte@^7.0.8"
|
||||
"@astrojs/svelte": "npm:@astrojs/svelte@^7.0.8",
|
||||
"@core/db/": "./packages/core/db/"
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ if (unsetVars.length > 0) {
|
||||
|
||||
const databaseHost = Deno.env.get("DB_HOST")!;
|
||||
const databaseName = Deno.env.get("DB_NAME")!;
|
||||
const databaseNameCred = Deno.env.get("DB_NAME_CRED")!;
|
||||
const databaseUser = Deno.env.get("DB_USER")!;
|
||||
const databasePassword = Deno.env.get("DB_PASSWORD")!;
|
||||
const databasePort = Deno.env.get("DB_PORT")!;
|
||||
@ -19,3 +20,11 @@ export const postgresConfig = {
|
||||
user: databaseUser,
|
||||
password: databasePassword,
|
||||
};
|
||||
|
||||
export const postgresConfigCred = {
|
||||
hostname: databaseHost,
|
||||
port: parseInt(databasePort),
|
||||
database: databaseNameCred,
|
||||
user: databaseUser,
|
||||
password: databasePassword,
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { Pool } from "https://deno.land/x/postgres@v0.19.3/mod.ts";
|
||||
import { postgresConfig } from "db/pgConfig.ts";
|
||||
import { postgresConfig } from "@core/db/pgConfig.ts";
|
||||
|
||||
const pool = new Pool(postgresConfig, 12);
|
||||
|
||||
|
@ -12,8 +12,12 @@
|
||||
"@astrojs/tailwind": "^6.0.2",
|
||||
"astro": "^5.5.5",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"pg": "^8.11.11",
|
||||
"postcss": "^8.5.3",
|
||||
"tailwindcss": "^3.0.24",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/pg": "^8.11.11"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,14 @@
|
||||
<script lang="ts">
|
||||
export let autoFocus = false;
|
||||
let inputBox: HTMLInputElement | null = null;
|
||||
|
||||
export function changeFocusState(target: boolean) {
|
||||
if (!inputBox) return;
|
||||
if (target) {
|
||||
inputBox.focus();
|
||||
} else {
|
||||
inputBox.blur();
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === "Enter") {
|
||||
@ -8,7 +17,7 @@
|
||||
const value: string = input.value.trim();
|
||||
if (!value) return;
|
||||
if (value.startsWith("av")) {
|
||||
window.location.href = `/song/${value.slice(2)}/info`;
|
||||
window.location.href = `/song/${value}/info`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,9 +26,9 @@
|
||||
<!-- svelte-ignore a11y_autofocus -->
|
||||
<div
|
||||
class="absolute left-0 md:left-96 ml-4 w-[calc(100%-5rem)] md:w-[calc(100%-40rem)] 2xl:max-w-[50rem] 2xl:left-1/2 2xl:-translate-x-1/2 inline-flex items-center h-full"
|
||||
autofocus={autoFocus}
|
||||
>
|
||||
<input
|
||||
bind:this={inputBox}
|
||||
type="search"
|
||||
placeholder="搜索"
|
||||
class="top-0 w-full h-10 px-4 rounded-lg bg-white/80 dark:bg-zinc-800/70
|
||||
|
@ -7,14 +7,19 @@
|
||||
import DarkModeImage from "./DarkModeImage.svelte";
|
||||
import CloseIcon from "./CloseIcon.svelte";
|
||||
|
||||
let searchBox: SearchBox | null = null;
|
||||
let showSearchBox = false;
|
||||
|
||||
$: if (showSearchBox && searchBox) {
|
||||
searchBox.changeFocusState(true);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="md:hidden fixed top-0 left-0 w-full h-16 bg-white/80 dark:bg-zinc-800/70 backdrop-blur-lg z-100">
|
||||
{#if !showSearchBox}
|
||||
<div class="inline-block ml-4 mt-4 text-white">
|
||||
<button class="inline-block ml-4 mt-4 text-white">
|
||||
<MenuIcon />
|
||||
</div>
|
||||
</button>
|
||||
<div class="ml-8 inline-flex h-full items-center">
|
||||
<a href="/">
|
||||
<DarkModeImage
|
||||
@ -27,7 +32,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if showSearchBox}
|
||||
<SearchBox autoFocus={true} />
|
||||
<SearchBox bind:this={searchBox} />
|
||||
{/if}
|
||||
<button
|
||||
class="inline-flex absolute right-0 h-full items-center mr-4"
|
||||
|
10
packages/frontend/src/pages/404.astro
Normal file
10
packages/frontend/src/pages/404.astro
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
import Layout from '@layouts/Layout.astro';
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<main class="flex flex-col items-center justify-center min-h-screen gap-8">
|
||||
<h1 class="text-9xl font-thin">404</h1>
|
||||
<p class="text-xl font-medium">咦……页面去哪里了(゚Д゚≡゚д゚)!?</p>
|
||||
</main>
|
||||
</Layout>
|
@ -1,40 +0,0 @@
|
||||
---
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
|
||||
// 路由参数
|
||||
const { aid } = Astro.params;
|
||||
const videoAid = Number(aid);
|
||||
|
||||
// 数据库查询函数
|
||||
async function getVideoMetadata(aid: number) {
|
||||
// TODO: 实现bilibili_metadata表查询
|
||||
return {};
|
||||
}
|
||||
|
||||
async function getVideoSnapshots(aid: number) {
|
||||
// TODO: 实现video_snapshot表查询,按created_at排序,限制100条
|
||||
return [];
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
const videoInfo = await getVideoMetadata(videoAid);
|
||||
const snapshots = await getVideoSnapshots(videoAid);
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div class="max-w-4xl mx-auto rounded-lg shadow p-6">
|
||||
<h1 class="text-2xl font-bold mb-4">视频信息: {videoAid}</h1>
|
||||
|
||||
<!-- 视频基本信息 -->
|
||||
<div class="mb-6 p-4 rounded-lg">
|
||||
<h2 class="text-xl font-semibold mb-2">基本信息</h2>
|
||||
<pre class="bg-gray-50 dark:bg-zinc-700 p-2 rounded">{JSON.stringify(videoInfo, null, 2)}</pre>
|
||||
</div>
|
||||
|
||||
<!-- 视频快照数据 -->
|
||||
<div class="p-4 rounded-lg">
|
||||
<h2 class="text-xl font-semibold mb-2">历史数据 (最新100条)</h2>
|
||||
<pre class="bg-gray-50 dark:bg-zinc-700 p-2 rounded">{JSON.stringify(snapshots, null, 2)}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
88
packages/frontend/src/pages/song/[id]/info.astro
Normal file
88
packages/frontend/src/pages/song/[id]/info.astro
Normal file
@ -0,0 +1,88 @@
|
||||
---
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import pg from 'pg'
|
||||
import { postgresConfig } from "@core/db/pgConfig.ts";
|
||||
|
||||
// 路由参数
|
||||
const { id } = Astro.params;
|
||||
const { Client } = pg
|
||||
const client = new Client(postgresConfig);
|
||||
await client.connect();
|
||||
|
||||
// 数据库查询函数
|
||||
async function getVideoMetadata(aid: number) {
|
||||
const res = await client.query('SELECT * FROM bilibili_metadata WHERE aid = $1', [aid]);
|
||||
if (res.rows.length <= 0) {
|
||||
return null
|
||||
}
|
||||
const row = res.rows[0];
|
||||
if (row) {
|
||||
return row;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
async function getVideoSnapshots(aid: number) {
|
||||
// TODO: 实现video_snapshot表查询,按created_at排序,限制100条
|
||||
const res = await client.query('SELECT * FROM video_snapshot WHERE aid = $1 ORDER BY created_at DESC LIMIT 100', [aid]);
|
||||
if (res.rows.length <= 0) {
|
||||
return null
|
||||
}
|
||||
return res.rows;
|
||||
}
|
||||
|
||||
async function getAidFromBV(bv: string) {
|
||||
const res = await client.query('SELECT aid FROM bilibili_metadata WHERE bvid = $1', [bv]);
|
||||
if (res.rows.length <= 0) {
|
||||
return null
|
||||
}
|
||||
const row = res.rows[0];
|
||||
if (row && row.aid) {
|
||||
return Number(row.aid);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function getVideoAid(id: string) {
|
||||
if (id.startsWith("av")) {
|
||||
return parseInt(id.slice(2));
|
||||
} else if (id.startsWith("BV")) {
|
||||
return getAidFromBV(id);
|
||||
}
|
||||
return parseInt(id);
|
||||
}
|
||||
|
||||
// 获取数据
|
||||
if (!id) {
|
||||
Astro.response.status = 404;
|
||||
client.end();
|
||||
return new Response(null, { status: 404 });
|
||||
}
|
||||
const aid = await getVideoAid(id);
|
||||
if (!aid || isNaN(aid)) {
|
||||
Astro.response.status = 404;
|
||||
client.end();
|
||||
return new Response(null, { status: 404 });
|
||||
}
|
||||
const videoInfo = await getVideoMetadata(aid);
|
||||
const snapshots = await getVideoSnapshots(aid);
|
||||
client.end();
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<div class="max-w-4xl mx-auto rounded-lg shadow p-6">
|
||||
<h1 class="text-2xl font-bold mb-4">视频信息: {aid}</h1>
|
||||
|
||||
<!-- 视频基本信息 -->
|
||||
<div class="mb-6 p-4 rounded-lg">
|
||||
<h2 class="text-xl font-semibold mb-2">基本信息</h2>
|
||||
<pre class="bg-gray-50 dark:bg-zinc-700 p-2 rounded text-wrap">{JSON.stringify(videoInfo, null, 2)}</pre>
|
||||
</div>
|
||||
|
||||
<!-- 视频快照数据 -->
|
||||
<div class="p-4 rounded-lg">
|
||||
<h2 class="text-xl font-semibold mb-2">快照历史数据 (最新100条)</h2>
|
||||
<pre class="bg-gray-50 dark:bg-zinc-700 p-2 rounded">{JSON.stringify(snapshots, null, 2)}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
@ -9,7 +9,8 @@
|
||||
"@layouts/*": ["src/layouts/*"],
|
||||
"@utils/*": ["src/utils/*"],
|
||||
"@assets/*": ["src/assets/*"],
|
||||
"@styles": ["src/styles/*"]
|
||||
"@styles": ["src/styles/*"],
|
||||
"@core/*": ["../core/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user