diff --git a/components/Sentence.tsx b/components/Sentence.tsx new file mode 100644 index 00000000..71bdc7af --- /dev/null +++ b/components/Sentence.tsx @@ -0,0 +1,41 @@ +export default function Sentence({ + bvId, + sentence, +}: { + bvId: string; + sentence: string; +}) { + const baseUrl = `https://www.bilibili.com/video/${bvId}`; + + const matchResult = sentence.match(/\s*(\d+\.\d+)(.*)/); + let timestamp: string | undefined; + if (matchResult) { + console.log("========matchResult========", matchResult); + const seconds = Number(matchResult[1]); + const hours = Math.floor(seconds / 3600); + const remainingSeconds = Math.floor(seconds % 3600); + const minutes = Math.floor(remainingSeconds / 60); + const remainingMinutes = Math.floor(remainingSeconds % 60); + if (hours > 0) { + timestamp = `${hours}:${minutes + .toString() + .padStart(2, "0")}:${remainingMinutes.toString().padStart(2, "0")}`; + } else { + timestamp = `${minutes}:${remainingMinutes.toString().padStart(2, "0")}`; + } + + const content = matchResult[2]; + return ( +
  • + + {timestamp} + + {`${content?.startsWith(":") ? content.substring(1) : content}`} +
  • + ); + } + return
  • {sentence}
  • ; +} diff --git a/middleware.ts b/middleware.ts index 53db2070..a704ba90 100644 --- a/middleware.ts +++ b/middleware.ts @@ -2,6 +2,7 @@ import { NextResponse } from "next/server"; import type { NextFetchEvent, NextRequest } from "next/server"; import { Redis } from "@upstash/redis"; import { Ratelimit } from "@upstash/ratelimit"; +import { isDev } from "./utils/env"; const redis = Redis.fromEnv(); @@ -16,6 +17,10 @@ const ratelimit = new Ratelimit({ }); export async function middleware(req: NextRequest, ev: NextFetchEvent) { + if (isDev) { + return NextResponse.next(); + } + const { bvId, apiKey } = await req.json(); const result = await redis.get(bvId); if (result) { diff --git a/pages/[...slug].tsx b/pages/[...slug].tsx index 5f52cf12..39909c0e 100644 --- a/pages/[...slug].tsx +++ b/pages/[...slug].tsx @@ -4,6 +4,7 @@ import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { toast, Toaster } from "react-hot-toast"; import { useLocalStorage } from "react-use"; +import Sentence from "../components/Sentence"; import SquigglyLines from "../components/SquigglyLines"; import { useSummarize } from "../hooks/useSummarize"; @@ -77,6 +78,19 @@ export const Home: NextPage = () => { await generateSummary(); }; + const handleCopy = () => { + if (!isSecureContext) { + toast("复制错误", { + icon: "❌", + }); + return; + } + navigator.clipboard.writeText(summary + "\n\n via #BiliGPT b.jimmylv.cn"); + toast("复制成功", { + icon: "✂️", + }); + }; + return ( <> {
    { - if (!isSecureContext) { - toast("复制错误", { - icon: "❌", - }); - return; - } - navigator.clipboard.writeText( - summary + "\n\n via #BiliGPT b.jimmylv.cn" - ); - toast("复制成功", { - icon: "✂️", - }); - }} + className="mx-auto mt-6 max-w-3xl rounded-xl border bg-white p-4 text-lg leading-7 shadow-md transition hover:bg-gray-50" + // onClick={handleCopy} > {summary.split("- ").map((sentence, index) => (
    {sentence.length > 0 && ( -
  • {sentence}
  • + )}
    ))} diff --git a/pages/api/summarize.ts b/pages/api/summarize.ts index b614de54..1ccc2e0c 100644 --- a/pages/api/summarize.ts +++ b/pages/api/summarize.ts @@ -1,6 +1,7 @@ import { NextResponse } from "next/server"; import { Redis } from "@upstash/redis"; import type { NextFetchEvent, NextRequest } from "next/server"; +import { isDev } from "../../utils/env"; import { OpenAIResult } from "../../utils/OpenAIResult"; import { getChunckedTranscripts, getSummaryPrompt } from "../../utils/prompt"; @@ -47,9 +48,8 @@ export default async function handler( // @ts-ignore const transcripts = subtitles.body.map((item, index) => { return { - text: item.content, + text: `${item.from}: ${item.content}`, index, - timestamp: item.from, }; }); // console.log("========transcripts========", transcripts); @@ -58,6 +58,7 @@ export default async function handler( try { apiKey && console.log("========use user key========"); + isDev && console.log("prompt", prompt); const payload = { model: "gpt-3.5-turbo", messages: [{ role: "user" as const, content: prompt }], @@ -71,6 +72,7 @@ export default async function handler( }; const result = await OpenAIResult(payload, apiKey); + // TODO: add better logging when dev or prod console.log("result", result); const redis = Redis.fromEnv(); const data = await redis.set(bvId, result); diff --git a/utils/env.ts b/utils/env.ts new file mode 100644 index 00000000..f793ce58 --- /dev/null +++ b/utils/env.ts @@ -0,0 +1 @@ +export const isDev = process.env.NODE_ENV === "development"; diff --git a/utils/prompt.ts b/utils/prompt.ts index e28a4f05..eae74ad1 100644 --- a/utils/prompt.ts +++ b/utils/prompt.ts @@ -4,7 +4,7 @@ export function getSummaryPrompt(title: string,transcript: any) { .replace(/\n+/g, " ") .trim()}"\n视频字幕: "${truncateTranscript(transcript) .replace(/\n+/g, " ") - .trim()}"\n我希望你是一名专业的视频内容编辑,帮我总结视频的内容精华。请你将视频字幕文本进行总结,然后以无序列表的方式返回,不要超过5条。记得不要重复句子,确保所有的句子都足够精简,清晰完整,祝你好运!`; + .trim()}"\n我希望你是一名专业的视频内容编辑,帮我总结视频的内容精华。请先用一句简短的话总结视频梗概。然后再请你将视频字幕文本进行总结,在每句话的最前面加上时间戳,然后以无序列表的方式返回,不要超过5条。记得不要重复句子,确保所有的句子都足够精简,清晰完整,祝你好运!`; } // Seems like 15,000 bytes is the limit for the prompt