Spaces:
Paused
Paused
| /* eslint-disable @next/next/no-img-element */ | |
| import { PlayCircle, PlayIcon, PlusIcon, VideoIcon } from 'lucide-react'; | |
| import { useRef, useState } from 'react'; | |
| import Lightbox, { GenericSlide, VideoSlide } from 'yet-another-react-lightbox'; | |
| import 'yet-another-react-lightbox/styles.css'; | |
| import { Message } from './ChatWindow'; | |
| type Video = { | |
| url: string; | |
| img_src: string; | |
| title: string; | |
| iframe_src: string; | |
| }; | |
| declare module 'yet-another-react-lightbox' { | |
| export interface VideoSlide extends GenericSlide { | |
| type: 'video-slide'; | |
| src: string; | |
| iframe_src: string; | |
| } | |
| interface SlideTypes { | |
| 'video-slide': VideoSlide; | |
| } | |
| } | |
| const Searchvideos = ({ | |
| query, | |
| chatHistory, | |
| }: { | |
| query: string; | |
| chatHistory: Message[]; | |
| }) => { | |
| const [videos, setVideos] = useState<Video[] | null>(null); | |
| const [loading, setLoading] = useState(false); | |
| const [open, setOpen] = useState(false); | |
| const [slides, setSlides] = useState<VideoSlide[]>([]); | |
| const [currentIndex, setCurrentIndex] = useState(0); | |
| const videoRefs = useRef<(HTMLIFrameElement | null)[]>([]); | |
| return ( | |
| <> | |
| {!loading && videos === null && ( | |
| <button | |
| onClick={async () => { | |
| setLoading(true); | |
| const chatModelProvider = localStorage.getItem('chatModelProvider'); | |
| const chatModel = localStorage.getItem('chatModel'); | |
| const customOpenAIBaseURL = localStorage.getItem('openAIBaseURL'); | |
| const customOpenAIKey = localStorage.getItem('openAIApiKey'); | |
| const res = await fetch( | |
| `${process.env.NEXT_PUBLIC_API_URL}/videos`, | |
| { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| query: query, | |
| chatHistory: chatHistory, | |
| chatModel: { | |
| provider: chatModelProvider, | |
| model: chatModel, | |
| ...(chatModelProvider === 'custom_openai' && { | |
| customOpenAIBaseURL: customOpenAIBaseURL, | |
| customOpenAIKey: customOpenAIKey, | |
| }), | |
| }, | |
| }), | |
| }, | |
| ); | |
| const data = await res.json(); | |
| const videos = data.videos ?? []; | |
| setVideos(videos); | |
| setSlides( | |
| videos.map((video: Video) => { | |
| return { | |
| type: 'video-slide', | |
| iframe_src: video.iframe_src, | |
| src: video.img_src, | |
| }; | |
| }), | |
| ); | |
| setLoading(false); | |
| }} | |
| className="border border-dashed border-light-200 dark:border-dark-200 hover:bg-light-200 dark:hover:bg-dark-200 active:scale-95 duration-200 transition px-4 py-2 flex flex-row items-center justify-between rounded-lg dark:text-white text-sm w-full" | |
| > | |
| <div className="flex flex-row items-center space-x-2"> | |
| <VideoIcon size={17} /> | |
| <p>Search videos</p> | |
| </div> | |
| <PlusIcon className="text-[#24A0ED]" size={17} /> | |
| </button> | |
| )} | |
| {loading && ( | |
| <div className="grid grid-cols-2 gap-2"> | |
| {[...Array(4)].map((_, i) => ( | |
| <div | |
| key={i} | |
| className="bg-light-secondary dark:bg-dark-secondary h-32 w-full rounded-lg animate-pulse aspect-video object-cover" | |
| /> | |
| ))} | |
| </div> | |
| )} | |
| {videos !== null && videos.length > 0 && ( | |
| <> | |
| <div className="grid grid-cols-2 gap-2"> | |
| {videos.length > 4 | |
| ? videos.slice(0, 3).map((video, i) => ( | |
| <div | |
| onClick={() => { | |
| setOpen(true); | |
| setSlides([ | |
| slides[i], | |
| ...slides.slice(0, i), | |
| ...slides.slice(i + 1), | |
| ]); | |
| }} | |
| className="relative transition duration-200 active:scale-95 hover:scale-[1.02] cursor-pointer" | |
| key={i} | |
| > | |
| <img | |
| src={video.img_src} | |
| alt={video.title} | |
| className="relative h-full w-full aspect-video object-cover rounded-lg" | |
| /> | |
| <div className="absolute bg-white/70 dark:bg-black/70 text-black/70 dark:text-white/70 px-2 py-1 flex flex-row items-center space-x-1 bottom-1 right-1 rounded-md"> | |
| <PlayCircle size={15} /> | |
| <p className="text-xs">Video</p> | |
| </div> | |
| </div> | |
| )) | |
| : videos.map((video, i) => ( | |
| <div | |
| onClick={() => { | |
| setOpen(true); | |
| setSlides([ | |
| slides[i], | |
| ...slides.slice(0, i), | |
| ...slides.slice(i + 1), | |
| ]); | |
| }} | |
| className="relative transition duration-200 active:scale-95 hover:scale-[1.02] cursor-pointer" | |
| key={i} | |
| > | |
| <img | |
| src={video.img_src} | |
| alt={video.title} | |
| className="relative h-full w-full aspect-video object-cover rounded-lg" | |
| /> | |
| <div className="absolute bg-white/70 dark:bg-black/70 text-black/70 dark:text-white/70 px-2 py-1 flex flex-row items-center space-x-1 bottom-1 right-1 rounded-md"> | |
| <PlayCircle size={15} /> | |
| <p className="text-xs">Video</p> | |
| </div> | |
| </div> | |
| ))} | |
| {videos.length > 4 && ( | |
| <button | |
| onClick={() => setOpen(true)} | |
| className="bg-light-100 hover:bg-light-200 dark:bg-dark-100 dark:hover:bg-dark-200 transition duration-200 active:scale-95 hover:scale-[1.02] h-auto w-full rounded-lg flex flex-col justify-between text-white p-2" | |
| > | |
| <div className="flex flex-row items-center space-x-1"> | |
| {videos.slice(3, 6).map((video, i) => ( | |
| <img | |
| key={i} | |
| src={video.img_src} | |
| alt={video.title} | |
| className="h-6 w-12 rounded-md lg:h-3 lg:w-6 lg:rounded-sm aspect-video object-cover" | |
| /> | |
| ))} | |
| </div> | |
| <p className="text-black/70 dark:text-white/70 text-xs"> | |
| View {videos.length - 3} more | |
| </p> | |
| </button> | |
| )} | |
| </div> | |
| <Lightbox | |
| open={open} | |
| close={() => setOpen(false)} | |
| slides={slides} | |
| index={currentIndex} | |
| on={{ | |
| view: ({ index }) => { | |
| const previousIframe = videoRefs.current[currentIndex]; | |
| if (previousIframe?.contentWindow) { | |
| previousIframe.contentWindow.postMessage( | |
| '{"event":"command","func":"pauseVideo","args":""}', | |
| '*', | |
| ); | |
| } | |
| setCurrentIndex(index); | |
| }, | |
| }} | |
| render={{ | |
| slide: ({ slide }) => { | |
| const index = slides.findIndex((s) => s === slide); | |
| return slide.type === 'video-slide' ? ( | |
| <div className="h-full w-full flex flex-row items-center justify-center"> | |
| <iframe | |
| src={`${slide.iframe_src}${slide.iframe_src.includes('?') ? '&' : '?'}enablejsapi=1`} | |
| ref={(el) => { | |
| if (el) { | |
| videoRefs.current[index] = el; | |
| } | |
| }} | |
| className="aspect-video max-h-[95vh] w-[95vw] rounded-2xl md:w-[80vw]" | |
| allowFullScreen | |
| allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" | |
| /> | |
| </div> | |
| ) : null; | |
| }, | |
| }} | |
| /> | |
| </> | |
| )} | |
| </> | |
| ); | |
| }; | |
| export default Searchvideos; | |