Skip to content

Commit

Permalink
feat: add switch button for shouldShowTimestamp
Browse files Browse the repository at this point in the history
  • Loading branch information
JimmyLv committed Mar 6, 2023
1 parent 7b463fa commit ec1c8a3
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 22 deletions.
23 changes: 23 additions & 0 deletions components/ui/label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use client"

import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"

import { cn } from "@/lib/utils"

const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
className
)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName

export { Label }
29 changes: 29 additions & 0 deletions components/ui/switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client"

import * as React from "react"
import * as SwitchPrimitives from "@radix-ui/react-switch"

import { cn } from "@/lib/utils"

const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
>(({ className, ...props }, ref) => (
<SwitchPrimitives.Root
className={cn(
"peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=unchecked]:bg-slate-200 data-[state=checked]:bg-slate-900 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900 dark:data-[state=unchecked]:bg-slate-700 dark:data-[state=checked]:bg-slate-400",
className
)}
{...props}
ref={ref}
>
<SwitchPrimitives.Thumb
className={cn(
"pointer-events-none block h-5 w-5 rounded-full bg-white shadow-lg ring-0 transition-transform data-[state=unchecked]:translate-x-0 data-[state=checked]:translate-x-5"
)}
/>
</SwitchPrimitives.Root>
))
Switch.displayName = SwitchPrimitives.Root.displayName

export { Switch }
11 changes: 9 additions & 2 deletions hooks/useSummarize.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from "react";
import { useToast } from "~/hooks/use-toast";
import { UserConfig } from "~/lib/types";
import { RATE_LIMIT_COUNT } from "~/utils/constants";

export function useSummarize() {
Expand All @@ -11,7 +12,10 @@ export function useSummarize() {
setSummary("");
};

const summarize = async (bvId: string, apiKey: string | undefined) => {
const summarize = async (
bvId: string,
{ userKey, shouldShowTimestamp }: UserConfig
) => {
setSummary("");
setLoading(true);

Expand All @@ -22,7 +26,10 @@ export function useSummarize() {
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ bvId, apiKey }),
body: JSON.stringify({
bvId,
userConfig: { userKey, shouldShowTimestamp },
}),
});

if (response.redirected) {
Expand Down
7 changes: 7 additions & 0 deletions lib/lemon.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { isDev } from "~/utils/env";

export async function activateLicenseKey(licenseKey: string, bvId?: string) {
// not active when dev
if (isDev) {
return true;
}

// https://docs.lemonsqueezy.com/help/licensing/license-api
const response = await fetch(
`https://api.lemonsqueezy.com/v1/licenses/activate`,
Expand Down
3 changes: 2 additions & 1 deletion lib/openai/prompt.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

export function getSummaryPrompt(title: string, transcript: any, shouldShowTimestamp?: boolean) {
console.log('========shouldShowTimestamp========', shouldShowTimestamp);
const betterPrompt = `我希望你是一名专业的视频内容编辑,帮我总结视频的内容精华。请你将视频字幕文本进行总结,然后以无序列表的方式返回,不要超过5条。记得不要重复句子,确保所有的句子都足够精简,清晰完整,祝你好运!`
const promptWithTimestamp = `我希望你是一名专业的视频内容编辑,帮我总结视频的内容精华。请先用一句简短的话总结视频梗概。然后再请你将视频字幕文本进行总结,在每句话的最前面加上开始的时间戳,然后以无序列表的方式返回。请注意不要超过5条哦,确保所有的句子都足够精简,清晰完整,祝你好运!`;
const promptWithTimestamp = `我希望你是一名专业的视频内容编辑,帮我总结视频的内容精华。请先用一句简短的话总结视频梗概。然后再请你将视频字幕文本进行总结,在每句话的最前面加上时间戳(类似 10:24),每句话开头只需要一个开始时间。请你以无序列表的方式返回,请注意不要超过5条哦,确保所有的句子都足够精简,清晰完整,祝你好运!`;

return `标题: "${title
.replace(/\n+/g, " ")
Expand Down
8 changes: 8 additions & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type SummarizeParams = {
bvId: string;
userConfig: UserConfig;
};
export type UserConfig = {
userKey?: string;
shouldShowTimestamp?: boolean;
};
12 changes: 7 additions & 5 deletions middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createMiddlewareSupabaseClient } from "@supabase/auth-helpers-nextjs";
import { Redis } from "@upstash/redis";
import type { NextFetchEvent, NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { SummarizeParams } from "~/lib/types";
import { validateLicenseKey } from "./lib/lemon";
import { checkOpenaiApiKeys } from "./lib/openai/openai";
import { ratelimit } from "./lib/upstash";
Expand All @@ -10,7 +11,8 @@ import { isDev } from "./utils/env";
const redis = Redis.fromEnv();

export async function middleware(req: NextRequest, context: NextFetchEvent) {
const { apiKey, bvId } = await req.json();
const { userConfig, bvId } = (await req.json()) as SummarizeParams;
const { userKey } = userConfig;

function redirectAuth() {
// return NextResponse.redirect(new URL("/shop", req.url));
Expand All @@ -23,18 +25,18 @@ export async function middleware(req: NextRequest, context: NextFetchEvent) {
}

// licenseKeys
if (apiKey) {
if (checkOpenaiApiKeys(apiKey)) {
if (userKey) {
if (checkOpenaiApiKeys(userKey)) {
return NextResponse.next();
}

// 3. something-invalid-sdalkjfasncs-key
if (!(await validateLicenseKey(apiKey, bvId))) {
if (!(await validateLicenseKey(userKey, bvId))) {
return redirectAuth();
}
}

if (!apiKey) {
if (!userKey) {
const identifier = req.ip ?? "127.0.0.7";
const { success, remaining } = await ratelimit.limit(identifier);
console.log(`======== ip ${identifier}, remaining: ${remaining} ========`);
Expand Down
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"dependencies": {
"@next/font": "^13.1.5",
"@radix-ui/react-dialog": "^1.0.2",
"@radix-ui/react-label": "^2.0.0",
"@radix-ui/react-popover": "^1.0.4",
"@radix-ui/react-switch": "^1.0.1",
"@radix-ui/react-toast": "^1.1.2",
"@radix-ui/react-tooltip": "^1.0.4",
"@supabase/auth-helpers-nextjs": "^0.5.5",
Expand Down
27 changes: 22 additions & 5 deletions pages/[...slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import type { NextPage } from "next";
import Image from "next/image";
import { useSearchParams } from "next/navigation";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import React, { useEffect, useState } from "react";
import { TypeAnimation } from "react-type-animation";
import { useLocalStorage } from "react-use";
import { Label } from "~/components/ui/label";
import { Switch } from "~/components/ui/switch";
import { useToast } from "~/hooks/use-toast";
import { useSummarize } from "~/hooks/useSummarize";
import { CHECKOUT_URL, RATE_LIMIT_COUNT } from "~/utils/constants";
Expand All @@ -25,6 +27,8 @@ export const Home: NextPage = () => {
const searchParams = useSearchParams();
const licenseKey = searchParams.get("license_key");
const [curVideo, setCurVideo] = useState<string>("");
const [shouldShowTimestamp, setShouldShowTimestamp] =
useLocalStorage<boolean>("should-show-timestamp", false);
const [currentBvId, setCurrentBvId] = useState<string>("");
const [userKey, setUserKey, remove] =
useLocalStorage<string>("user-openai-apikey");
Expand Down Expand Up @@ -87,12 +91,13 @@ export const Home: NextPage = () => {
const bvId = matchResult[1];
setCurrentBvId(matchResult[1]);

await summarize(bvId, userKey);
await summarize(bvId, { userKey, shouldShowTimestamp });
setTimeout(() => {
window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" });
}, 10);
};
const onFormSubmit = async (e: any) => {
console.log("========e========", e.target.value);
e.preventDefault();
await generateSummary();
};
Expand Down Expand Up @@ -129,6 +134,11 @@ export const Home: NextPage = () => {
setUserKey(e.target.value);
};

function handleShowTimestamp(checked: boolean) {
console.log("================", checked);
setShouldShowTimestamp(checked);
}

return (
<div className="mt-10 w-full sm:mt-40">
<a
Expand Down Expand Up @@ -259,15 +269,14 @@ export const Home: NextPage = () => {
className="mx-auto mt-10 w-full appearance-none rounded-lg rounded-md border bg-transparent py-2 pl-2 text-sm leading-6 text-slate-900 shadow-sm ring-1 ring-slate-200 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder={"输入 bilibili.com 视频链接,按下「回车」"}
/>
{!loading && (
{!loading ? (
<button
className="z-10 mx-auto mt-7 w-3/4 rounded-2xl border-gray-500 bg-sky-400 p-3 text-lg font-medium text-white transition hover:bg-sky-500 sm:mt-10 sm:w-1/3"
type="submit"
>
一键总结
</button>
)}
{loading && (
) : (
<button
className="z-10 mx-auto mt-7 w-3/4 cursor-not-allowed rounded-2xl border-gray-500 bg-sky-400 p-3 text-lg font-medium transition hover:bg-sky-500 sm:mt-10 sm:w-1/3"
disabled
Expand All @@ -282,6 +291,14 @@ export const Home: NextPage = () => {
</div>
</button>
)}
<div className="mt-6 flex items-center justify-end space-x-2">
<Switch
id="timestamp-mode"
checked={shouldShowTimestamp}
onCheckedChange={handleShowTimestamp}
/>
<Label htmlFor="timestamp-mode">是否显示时间戳</Label>
</div>
</form>
{summary && (
<div className="mb-8 px-4">
Expand Down
20 changes: 11 additions & 9 deletions pages/api/summarize.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Redis } from "@upstash/redis";
import type { NextFetchEvent, NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { selectApiKeyAndActivatedLicenseKey } from "~/lib/openai/selectApiKeyAndActivatedLicenseKey";
import { fetchSubtitle } from "~/lib/bilibili";
import { OpenAIResult } from "~/lib/openai/OpenAIResult";
import { getChunckedTranscripts, getSummaryPrompt } from "~/lib/openai/prompt";
import { selectApiKeyAndActivatedLicenseKey } from "~/lib/openai/selectApiKeyAndActivatedLicenseKey";
import { SummarizeParams, UserConfig } from "~/lib/types";
import { isDev } from "~/utils/env";

export const config = {
Expand All @@ -19,10 +20,8 @@ export default async function handler(
req: NextRequest,
context: NextFetchEvent
) {
const { bvId, apiKey } = (await req.json()) as {
bvId: string;
apiKey?: string;
};
const { bvId, userConfig } = (await req.json()) as SummarizeParams;
const { userKey, shouldShowTimestamp } = userConfig;

if (!bvId) {
return new Response("No bvid in the request", { status: 500 });
Expand All @@ -41,10 +40,10 @@ export default async function handler(
});
// console.log("========transcripts========", transcripts);
const text = getChunckedTranscripts(transcripts, transcripts);
const prompt = getSummaryPrompt(title, text, true);
const prompt = getSummaryPrompt(title, text, shouldShowTimestamp);

try {
apiKey && console.log("========use user apiKey========");
userKey && console.log("========use user apiKey========");
isDev && console.log("prompt", prompt);
const payload = {
model: "gpt-3.5-turbo",
Expand All @@ -53,13 +52,16 @@ export default async function handler(
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
max_tokens: apiKey ? 400 : 300,
max_tokens: userKey ? 400 : 300,
stream: false,
n: 1,
};

// TODO: need refactor
const openaiApiKey = await selectApiKeyAndActivatedLicenseKey(apiKey, bvId);
const openaiApiKey = await selectApiKeyAndActivatedLicenseKey(
userKey,
bvId
);
const result = await OpenAIResult(payload, openaiApiKey);
// TODO: add better logging when dev or prod
console.log("result", result);
Expand Down

1 comment on commit ec1c8a3

@vercel
Copy link

@vercel vercel bot commented on ec1c8a3 Mar 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.