add: video info page in frontend
This commit is contained in:
parent
c82a95d0bc
commit
980dd542ee
@ -1,17 +1,21 @@
|
||||
const requiredEnvVars = ["DB_HOST", "DB_NAME", "DB_USER", "DB_PASSWORD", "DB_PORT", "DB_NAME_CRED"];
|
||||
|
||||
const unsetVars = requiredEnvVars.filter((key) => process.env[key] === undefined);
|
||||
const getEnvVar = (key: string) => {
|
||||
return process.env[key] || import.meta.env[key];
|
||||
}
|
||||
|
||||
const unsetVars = requiredEnvVars.filter((key) => getEnvVar(key) === undefined);
|
||||
|
||||
if (unsetVars.length > 0) {
|
||||
throw new Error(`Missing required environment variables: ${unsetVars.join(", ")}`);
|
||||
}
|
||||
|
||||
const databaseHost = process.env["DB_HOST"]!;
|
||||
const databaseName = process.env["DB_NAME"];
|
||||
const databaseNameCred = process.env["DB_NAME_CRED"]!;
|
||||
const databaseUser = process.env["DB_USER"]!;
|
||||
const databasePassword = process.env["DB_PASSWORD"]!;
|
||||
const databasePort = process.env["DB_PORT"]!;
|
||||
const databaseHost = getEnvVar("DB_HOST")!;
|
||||
const databaseName = getEnvVar("DB_NAME");
|
||||
const databaseNameCred = getEnvVar("DB_NAME_CRED")!;
|
||||
const databaseUser = getEnvVar("DB_USER")!;
|
||||
const databasePassword = getEnvVar("DB_PASSWORD")!;
|
||||
const databasePort = getEnvVar("DB_PORT")!;
|
||||
|
||||
export const postgresConfig = {
|
||||
hostname: databaseHost,
|
||||
|
@ -3,6 +3,6 @@ const { title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<tr>
|
||||
<td class="max-w-14 min-w-14 md:max-w-none md:min-w-none border dark:border-zinc-500 px-2 md:px-4 py-2 font-semibold">{title}</td>
|
||||
<td class="max-w-14 min-w-14 md:max-w-24 md:min-w-24 border dark:border-zinc-500 px-2 md:px-3 py-2 font-semibold">{title}</td>
|
||||
<td class="break-all max-w-[calc(100vw-4.5rem)] border dark:border-zinc-500 px-4 py-2">{description}</td>
|
||||
</tr>
|
@ -7,7 +7,7 @@
|
||||
};
|
||||
|
||||
export function changeFocusState(target: boolean) {
|
||||
if (!inputElement) return; // 使用 inputElement 而不是 inputBox
|
||||
if (!inputElement) return;
|
||||
if (target) {
|
||||
inputElement.focus();
|
||||
} else {
|
||||
@ -22,13 +22,13 @@
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
const value = inputValue.trim(); // 使用绑定的变量
|
||||
const value = inputValue.trim();
|
||||
if (!value) return;
|
||||
search(value);
|
||||
}
|
||||
}
|
||||
|
||||
let inputElement: HTMLInputElement; // 引用 input 元素
|
||||
let inputElement: HTMLInputElement;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
12
packages/frontend/src/components/VideoInfoPage/StatRow.astro
Normal file
12
packages/frontend/src/components/VideoInfoPage/StatRow.astro
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
const { title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="flex justify-between w-36">
|
||||
<span>
|
||||
{title}
|
||||
</span>
|
||||
<span>
|
||||
{description}
|
||||
</span>
|
||||
</div>
|
@ -9,22 +9,6 @@ import { getAidFromBV } from "src/db/bilibili_metadata/getAidFromBV";
|
||||
import { getVideoMetadata } from "src/db/bilibili_metadata/getVideoMetadata";
|
||||
import { aidExists as idExists } from "src/db/bilibili_metadata/aidExists";
|
||||
|
||||
const databaseHost = import.meta.env.DB_HOST;
|
||||
const databaseName = import.meta.env.DB_NAME;
|
||||
const databaseUser = import.meta.env.DB_USER;
|
||||
const databasePassword = import.meta.env.DB_PASSWORD;
|
||||
const databasePort = import.meta.env.DB_PORT;
|
||||
|
||||
const postgresConfig = {
|
||||
hostname: databaseHost,
|
||||
port: parseInt(databasePort!),
|
||||
database: databaseName,
|
||||
user: databaseUser,
|
||||
password: databasePassword,
|
||||
};
|
||||
|
||||
console.log(postgresConfig);
|
||||
|
||||
const { id } = Astro.params;
|
||||
|
||||
async function getVideoAid(id: string) {
|
||||
@ -52,18 +36,6 @@ if (!aidExists) {
|
||||
}
|
||||
const videoInfo = await getVideoMetadata(aid);
|
||||
const snapshots = await getAllSnapshots(aid);
|
||||
|
||||
interface Snapshot {
|
||||
created_at: Date;
|
||||
views: number;
|
||||
danmakus: number;
|
||||
replies: number;
|
||||
coins: number;
|
||||
likes: number;
|
||||
favorites: number;
|
||||
shares: number;
|
||||
id: number;
|
||||
}
|
||||
---
|
||||
|
||||
<Layout>
|
||||
@ -79,9 +51,9 @@ interface Snapshot {
|
||||
<div class="overflow-x-auto max-w-full px-2">
|
||||
<table class="table-fixed">
|
||||
<tbody>
|
||||
<MetadataRow title={id} description={videoInfo?.id} />
|
||||
<MetadataRow title={videoInfo?.aid} description={videoInfo?.aid} />
|
||||
<MetadataRow title={videoInfo?.bvid} description={videoInfo?.bvid} />
|
||||
<MetadataRow title="ID" description={videoInfo?.id} />
|
||||
<MetadataRow title="av 号" description={videoInfo?.aid} />
|
||||
<MetadataRow title="BV 号" description={videoInfo?.bvid} />
|
||||
<MetadataRow title="标题" description={videoInfo?.title} />
|
||||
<MetadataRow title="描述" description={videoInfo?.description} />
|
||||
<MetadataRow title="UID" description={videoInfo?.uid} />
|
||||
@ -124,7 +96,7 @@ interface Snapshot {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{snapshots.map((snapshot: Snapshot) => (
|
||||
{snapshots.map((snapshot) => (
|
||||
<tr>
|
||||
<td class="border dark:border-zinc-500 px-4 py-2">
|
||||
{format(new Date(snapshot.created_at), "yyyy-MM-dd HH:mm:ss", {
|
||||
|
68
packages/frontend/src/pages/video/[id]/info.astro
Normal file
68
packages/frontend/src/pages/video/[id]/info.astro
Normal file
@ -0,0 +1,68 @@
|
||||
---
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import TitleBar from "@components/TitleBar.astro";
|
||||
import MetadataRow from "@components/InfoPage/MetadataRow.astro";
|
||||
import { format } from "date-fns";
|
||||
import { zhCN } from "date-fns/locale";
|
||||
import { getAllSnapshots } from "src/db/snapshots/getAllSnapshots";
|
||||
import { getAidFromBV } from "src/db/bilibili_metadata/getAidFromBV";
|
||||
import { getVideoMetadata } from "src/db/bilibili_metadata/getVideoMetadata";
|
||||
import { aidExists as idExists } from "src/db/bilibili_metadata/aidExists";
|
||||
import StatRow from "@components/VideoInfoPage/StatRow.astro";
|
||||
|
||||
const { id } = Astro.params;
|
||||
|
||||
if (!id) {
|
||||
Astro.response.status = 404;
|
||||
return new Response(null, { status: 404 });
|
||||
}
|
||||
|
||||
const backendURL = import.meta.env.BACKEND_URL;
|
||||
const res = await fetch(backendURL + `video/${id}/info`);
|
||||
const data = await res.json();
|
||||
---
|
||||
|
||||
<Layout title={`${data.title ?? data.bvid} - 视频信息`}>
|
||||
<TitleBar />
|
||||
<main class="flex flex-col items-center min-h-screen gap-8 mt-10 md:mt-6 relative z-0 overflow-x-auto pb-8">
|
||||
<div class="w-full lg:max-w-4xl lg:mx-auto lg:p-6 px-4">
|
||||
<h2 class="text-lg md:text-2xl mb-2">
|
||||
<a href={`https://www.bilibili.com/video/${data.bvid}`}>{data.title}</a>
|
||||
</h2>
|
||||
<p
|
||||
class="text-sm md:text-base font-normal text-on-surface-variant
|
||||
dark:text-dark-on-surface-variant mb-4"
|
||||
>
|
||||
<span>{data.bvid} · av{data.aid}</span><br />
|
||||
<span>
|
||||
发布于
|
||||
{format(new Date(data.pubdate * 1000), "yyyy-MM-dd HH:mm:ss")}
|
||||
</span><br />
|
||||
<span>播放:{(data.stat?.view ?? 0).toLocaleString()}</span> ·
|
||||
<span>弹幕:{(data.stat?.danmaku ?? 0).toLocaleString()}</span>
|
||||
<br/>
|
||||
<span>分区: {data.tname}, tid{data.tid} · v2: {data.tname_v2}, tid{data.tid_v2}</span>
|
||||
</p>
|
||||
<img src={data.pic} referrerpolicy="no-referrer" class="rounded-lg" />
|
||||
|
||||
<h3 class="font-medium text-lg mt-6 mb-1">简介</h3>
|
||||
<pre
|
||||
class="max-w-full wrap-anywhere break-all text-on-surface-variant
|
||||
text-sm md:text-base whitespace-pre-wrap dark:text-dark-on-surface-variant
|
||||
font-zh">{data.desc}</pre>
|
||||
|
||||
<div class="mb-6 mt-4">
|
||||
<h2 class="mb-2 text-xl font-medium">统计数据</h2>
|
||||
<div class="flex flex-col gap-1">
|
||||
<StatRow title="播放" description={data.stat?.view} />
|
||||
<StatRow title="点赞" description={data.stat?.like} />
|
||||
<StatRow title="收藏" description={data.stat?.favorite} />
|
||||
<StatRow title="硬币" description={data.stat?.coin} />
|
||||
<StatRow title="评论" description={data.stat?.reply} />
|
||||
<StatRow title="弹幕" description={data.stat?.danmaku} />
|
||||
<StatRow title="分享" description={data.stat?.share} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
@ -102,6 +102,8 @@
|
||||
--color-dark-surface-container-low: rgb(35 25 24);
|
||||
--color-surface-container-highest: rgb(241 223 220);
|
||||
--color-dark-surface-container-highest: rgb(61 50 48);
|
||||
|
||||
--font-zh: "InterVariable", "MiSans VF", sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
|
Loading…
Reference in New Issue
Block a user