From 50f90a08fb9db5b51742181febc12082581dc057 Mon Sep 17 00:00:00 2001 From: tydolla00 <90355178+tydolla00@users.noreply.github.com> Date: Sun, 10 Nov 2024 11:59:59 -0500 Subject: [PATCH] check in YouTube api validation(#8) * begin work on validating videos utilizing yt api. * check in youtube form validation. --- next.config.mjs | 6 +- .../(routes)/submission/_components/form.tsx | 42 +++++++-- src/app/_actions/action.ts | 89 +++++++++++++++++++ src/lib/config.ts | 4 + 4 files changed, 134 insertions(+), 7 deletions(-) diff --git a/next.config.mjs b/next.config.mjs index 4678774..797364f 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,4 +1,8 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + images: { + remotePatterns: [{ protocol: "https", hostname: "i.ytimg.com" }], + }, +}; export default nextConfig; diff --git a/src/app/(routes)/submission/_components/form.tsx b/src/app/(routes)/submission/_components/form.tsx index 0c112cb..612e29d 100644 --- a/src/app/(routes)/submission/_components/form.tsx +++ b/src/app/(routes)/submission/_components/form.tsx @@ -24,7 +24,7 @@ import { FormLabel, FormMessage, } from "@/components/ui/form"; -import { submitUpdates } from "@/app/_actions/action"; +import { getYTVid, submitUpdates } from "@/app/_actions/action"; import { Input } from "@/components/ui/input"; import { Select, @@ -35,6 +35,8 @@ import { } from "@/components/ui/select"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { toast } from "sonner"; +import { useState } from "react"; +import Image from "next/image"; const formSchema = z.object({ video: z @@ -43,7 +45,9 @@ const formSchema = z.object({ .startsWith( "https://www.youtube.com", "Please paste in a valid youtube url.", - ), + ) + .max(100) + .includes("v="), stat: z .object({ member: z @@ -60,6 +64,9 @@ const formSchema = z.object({ }); export const SubmissionForm = () => { + const [session, setSession] = useState< + Awaited> | undefined + >(undefined); const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { video: "", stat: [{ member: [{ name: "" }] }] }, @@ -76,9 +83,24 @@ export const SubmissionForm = () => { console.log("Got here"); submitUpdates(data); }; - const nextButtonClicked = () => { - if (!isTouched) toast("Please submit a valid url."); - if (isTouched && !invalid) toast("Changes saved"); + const saveBtnClicked = async () => { + if (!isTouched || invalid) { + toast("Please submit a valid url."); + return; + } + + let id = form.getValues().video.split("=")[1]; + const trimEnd = id.indexOf("&"); + + if (trimEnd !== -1) id = id.slice(0, trimEnd); + const video = await getYTVid(id); + if (!video) { + form.reset(undefined, { keepIsValid: true }); + toast("Please upload a video by RDC Live"); + } else { + if (isTouched && !invalid) toast("Changes saved"); + setSession(video); + } }; return (
@@ -101,6 +123,14 @@ export const SubmissionForm = () => { + {session && ( + Youtube Video + )} { - diff --git a/src/app/_actions/action.ts b/src/app/_actions/action.ts index acbcfc8..95c5aa0 100644 --- a/src/app/_actions/action.ts +++ b/src/app/_actions/action.ts @@ -1,4 +1,93 @@ "use server"; + +import { PrismaClient } from "@prisma/client"; +import config from "@/lib/config"; + export const submitUpdates = async (props: any) => { console.log(props); }; + +export const getYTVid = async (videoId: string) => { + const prisma = new PrismaClient(); + const sessions = await prisma.session.findMany(); + const sessionURL = sessions.find( + (session) => session.sessionUrl.split("=")[1] === videoId, + ); + console.log(sessionURL); + if (!sessionURL) { + const YTvideo = await fetch( + `https://youtube.googleapis.com/youtube/v3/videos?part=snippet&part=player&id=${videoId}&key=${config.YOUTUBE_LOCAL_API_KEY}`, + ); + console.log( + `https://youtube.googleapis.com/youtube/v3/videos?part=snippet&part=player&id=${videoId}&key=${config.YOUTUBE_LOCAL_API_KEY}`, + ); + console.log(YTvideo, videoId); + !config.YOUTUBE_LOCAL_API_KEY && + console.log("YOUTUBE API KEY NOT CONFIGURED"); + + if (!YTvideo.ok) return undefined; + + const json = (await YTvideo.json()) as YouTubeVideoListResponse; + console.log(json); + const video = json.items[0]; + + if (video.snippet.channelTitle !== "RDC Live") return undefined; + + const session = { + sessionUrl: `https://youtube.com/watch?v=${video.id}`, + date: video.snippet.publishedAt, + sessionName: video.snippet.title, + thumbnail: + video.snippet.thumbnails.maxres || video.snippet.thumbnails.high, + }; + return session; + } + await prisma.$disconnect(); + + return undefined; +}; + +type YouTubeVideoListResponse = { + kind: "youtube#videoListResponse"; + etag: string; + items: { + kind: "youtube#video"; + etag: string; + id: string; + snippet: { + publishedAt: string; + channelId: string; + title: string; + description: string; + thumbnails: { + default: Thumbnail; + medium: Thumbnail; + high: Thumbnail; + standard?: Thumbnail; + maxres?: Thumbnail; + }; + channelTitle: string; + tags?: string[]; + categoryId: string; + liveBroadcastContent: string; + localized: { + title: string; + description: string; + }; + defaultAudioLanguage?: string; + }; + player: { + embedHtml: string; + }; + }[]; + pageInfo: { + totalResults: number; + resultsPerPage: number; + }; +}; + +type Thumbnail = { + url: string; + width: number; + height: number; +}; diff --git a/src/lib/config.ts b/src/lib/config.ts index 4731c8c..e914d6a 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -1,10 +1,14 @@ interface Config { + YOUTUBE_API_KEY: string | undefined; + YOUTUBE_LOCAL_API_KEY: string | undefined; NEXT_PUBLIC_POSTHOG_KEY: string | undefined; NEXT_PUBLIC_POSTHOG_HOST: string | undefined; } const getConfig = (): Config => { return { + YOUTUBE_API_KEY: process.env.YOUTUBE_API_KEY, + YOUTUBE_LOCAL_API_KEY: process.env.YOUTUBE_LOCAL_API_KEY, NEXT_PUBLIC_POSTHOG_KEY: process.env.NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_HOST: process.env.NEXT_PUBLIC_POSTHOG_HOST, };