Spaces:
Sleeping
Sleeping
Julian Bilcke commited on
Commit ·
9cea1bb
1
Parent(s): df83860
improve huggingface support
Browse files- src/app/interface/left-menu/index.tsx +6 -1
- src/app/main.tsx +17 -4
- src/app/page.tsx +9 -2
- src/app/server/actions/ai-tube-hf/getVideo.ts +24 -11
- src/app/state/useStore.ts +16 -0
- src/app/views/public-video-view/index.tsx +21 -1
- src/app/watch/page.tsx +6 -19
- src/types.ts +9 -0
src/app/interface/left-menu/index.tsx
CHANGED
|
@@ -16,6 +16,7 @@ export function LeftMenu() {
|
|
| 16 |
const setView = useStore(s => s.setView)
|
| 17 |
const menuMode = useStore(s => s.menuMode)
|
| 18 |
const setMenuMode = useStore(s => s.setMenuMode)
|
|
|
|
| 19 |
|
| 20 |
return (
|
| 21 |
<div className={cn(
|
|
@@ -27,11 +28,15 @@ export function LeftMenu() {
|
|
| 27 |
<div className={cn(
|
| 28 |
`flex flex-col w-full`,
|
| 29 |
)}>
|
| 30 |
-
<Link href=
|
|
|
|
|
|
|
|
|
|
| 31 |
<MenuItem
|
| 32 |
icon={<RiHome8Line className="h-6 w-6" />}
|
| 33 |
selected={view === "home"}
|
| 34 |
// </Link>onClick={() => setView("home")}
|
|
|
|
| 35 |
>
|
| 36 |
Discover
|
| 37 |
</MenuItem>
|
|
|
|
| 16 |
const setView = useStore(s => s.setView)
|
| 17 |
const menuMode = useStore(s => s.menuMode)
|
| 18 |
const setMenuMode = useStore(s => s.setMenuMode)
|
| 19 |
+
const setCurrentVideo = useStore(s => s.setCurrentVideo)
|
| 20 |
|
| 21 |
return (
|
| 22 |
<div className={cn(
|
|
|
|
| 28 |
<div className={cn(
|
| 29 |
`flex flex-col w-full`,
|
| 30 |
)}>
|
| 31 |
+
<Link href={{
|
| 32 |
+
pathname: '/',
|
| 33 |
+
query: { v: undefined },
|
| 34 |
+
}}>
|
| 35 |
<MenuItem
|
| 36 |
icon={<RiHome8Line className="h-6 w-6" />}
|
| 37 |
selected={view === "home"}
|
| 38 |
// </Link>onClick={() => setView("home")}
|
| 39 |
+
// onClick={() => setCurrentVideo(undefiend)}
|
| 40 |
>
|
| 41 |
Discover
|
| 42 |
</MenuItem>
|
src/app/main.tsx
CHANGED
|
@@ -11,7 +11,7 @@ import { UserAccountView } from "./views/user-account-view"
|
|
| 11 |
import { NotFoundView } from "./views/not-found-view"
|
| 12 |
import { VideoInfo } from "@/types"
|
| 13 |
import { useEffect } from "react"
|
| 14 |
-
import { usePathname } from "next/navigation"
|
| 15 |
import { TubeLayout } from "./interface/tube-layout"
|
| 16 |
|
| 17 |
// this is where we transition from the server-side space
|
|
@@ -29,16 +29,29 @@ export function Main({
|
|
| 29 |
video?: VideoInfo
|
| 30 |
}) {
|
| 31 |
const pathname = usePathname()
|
|
|
|
| 32 |
|
| 33 |
const setCurrentVideo = useStore(s => s.setCurrentVideo)
|
| 34 |
const setView = useStore(s => s.setView)
|
| 35 |
const setPathname = useStore(s => s.setPathname)
|
| 36 |
|
|
|
|
|
|
|
|
|
|
| 37 |
useEffect(() => {
|
| 38 |
-
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
}
|
| 41 |
-
}, [
|
| 42 |
|
| 43 |
|
| 44 |
useEffect(() => {
|
|
|
|
| 11 |
import { NotFoundView } from "./views/not-found-view"
|
| 12 |
import { VideoInfo } from "@/types"
|
| 13 |
import { useEffect } from "react"
|
| 14 |
+
import { usePathname, useRouter } from "next/navigation"
|
| 15 |
import { TubeLayout } from "./interface/tube-layout"
|
| 16 |
|
| 17 |
// this is where we transition from the server-side space
|
|
|
|
| 29 |
video?: VideoInfo
|
| 30 |
}) {
|
| 31 |
const pathname = usePathname()
|
| 32 |
+
const router = useRouter()
|
| 33 |
|
| 34 |
const setCurrentVideo = useStore(s => s.setCurrentVideo)
|
| 35 |
const setView = useStore(s => s.setView)
|
| 36 |
const setPathname = useStore(s => s.setPathname)
|
| 37 |
|
| 38 |
+
const videoId = `${video?.id || ""}`
|
| 39 |
+
// console.log("Main video= "+ videoId)
|
| 40 |
+
|
| 41 |
useEffect(() => {
|
| 42 |
+
// note: it is important to ALWAYS set the current video to videoId
|
| 43 |
+
// even if it's undefined
|
| 44 |
+
setCurrentVideo(video)
|
| 45 |
+
|
| 46 |
+
if (videoId) {
|
| 47 |
+
// this is a hack for hugging face:
|
| 48 |
+
// we allow the ?v=<id> param on the root of the domain
|
| 49 |
+
if (pathname !== "/watch") {
|
| 50 |
+
// console.log("we are on huggingface apparently!")
|
| 51 |
+
router.replace(`/watch?v=${videoId}`)
|
| 52 |
+
}
|
| 53 |
}
|
| 54 |
+
}, [videoId])
|
| 55 |
|
| 56 |
|
| 57 |
useEffect(() => {
|
src/app/page.tsx
CHANGED
|
@@ -1,8 +1,15 @@
|
|
| 1 |
|
|
|
|
|
|
|
| 2 |
import { Main } from "./main"
|
|
|
|
| 3 |
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
return (
|
| 6 |
-
<Main />
|
| 7 |
)
|
| 8 |
}
|
|
|
|
| 1 |
|
| 2 |
+
import { AppQueryProps } from "@/types"
|
| 3 |
+
|
| 4 |
import { Main } from "./main"
|
| 5 |
+
import { getVideo } from "./server/actions/ai-tube-hf/getVideo"
|
| 6 |
|
| 7 |
+
// we have routes but on Hugging Face we don't see them
|
| 8 |
+
// so.. let's use the work around
|
| 9 |
+
export default async function Page({ searchParams: { v: videoId } }: AppQueryProps) {
|
| 10 |
+
const video = await getVideo({ videoId, neverThrow: true })
|
| 11 |
+
// console.log("Root page: videoId ----> ", video?.id)
|
| 12 |
return (
|
| 13 |
+
<Main video={video} />
|
| 14 |
)
|
| 15 |
}
|
src/app/server/actions/ai-tube-hf/getVideo.ts
CHANGED
|
@@ -4,19 +4,32 @@ import { VideoInfo } from "@/types"
|
|
| 4 |
|
| 5 |
import { getIndex } from "./getIndex"
|
| 6 |
|
| 7 |
-
export async function getVideo(
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
|
| 15 |
-
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
}
|
|
|
|
| 4 |
|
| 5 |
import { getIndex } from "./getIndex"
|
| 6 |
|
| 7 |
+
export async function getVideo({
|
| 8 |
+
videoId,
|
| 9 |
+
neverThrow,
|
| 10 |
+
}: {
|
| 11 |
+
videoId?: string | string[] | null,
|
| 12 |
+
neverThrow?: boolean
|
| 13 |
+
}): Promise<VideoInfo | undefined> {
|
| 14 |
+
try {
|
| 15 |
+
const id = `${videoId || ""}`
|
| 16 |
|
| 17 |
+
if (!id) {
|
| 18 |
+
throw new Error(`cannot get the video, invalid id: "${id}"`)
|
| 19 |
+
}
|
| 20 |
+
const published = await getIndex({ status: "published" })
|
| 21 |
|
| 22 |
+
const video = published[id] || undefined
|
| 23 |
|
| 24 |
+
if (!video) {
|
| 25 |
+
throw new Error(`cannot get the video, nothing found for id "${id}"`)
|
| 26 |
+
}
|
| 27 |
|
| 28 |
+
return video
|
| 29 |
+
} catch (err) {
|
| 30 |
+
if (neverThrow) {
|
| 31 |
+
return undefined
|
| 32 |
+
}
|
| 33 |
+
throw err
|
| 34 |
+
}
|
| 35 |
}
|
src/app/state/useStore.ts
CHANGED
|
@@ -31,6 +31,12 @@ export const useStore = create<{
|
|
| 31 |
currentTags: string[]
|
| 32 |
setCurrentTags: (currentTags?: string[]) => void
|
| 33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
currentVideos: VideoInfo[]
|
| 35 |
setCurrentVideos: (currentVideos: VideoInfo[]) => void
|
| 36 |
|
|
@@ -93,6 +99,16 @@ export const useStore = create<{
|
|
| 93 |
set({ currentTags })
|
| 94 |
},
|
| 95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
currentVideos: [],
|
| 97 |
setCurrentVideos: (currentVideos: VideoInfo[] = []) => {
|
| 98 |
set({
|
|
|
|
| 31 |
currentTags: string[]
|
| 32 |
setCurrentTags: (currentTags?: string[]) => void
|
| 33 |
|
| 34 |
+
currentModels: string[]
|
| 35 |
+
setCurrentModels: (currentModels?: string[]) => void
|
| 36 |
+
|
| 37 |
+
currentModel?: string
|
| 38 |
+
setCurrentModel: (currentModel?: string) => void
|
| 39 |
+
|
| 40 |
currentVideos: VideoInfo[]
|
| 41 |
setCurrentVideos: (currentVideos: VideoInfo[]) => void
|
| 42 |
|
|
|
|
| 99 |
set({ currentTags })
|
| 100 |
},
|
| 101 |
|
| 102 |
+
currentModels: [],
|
| 103 |
+
setCurrentModels: (currentModels?: string[]) => {
|
| 104 |
+
set({ currentModels })
|
| 105 |
+
},
|
| 106 |
+
|
| 107 |
+
currentModel: undefined,
|
| 108 |
+
setCurrentModel: (currentModel?: string) => {
|
| 109 |
+
set({ currentModel })
|
| 110 |
+
},
|
| 111 |
+
|
| 112 |
currentVideos: [],
|
| 113 |
setCurrentVideos: (currentVideos: VideoInfo[] = []) => {
|
| 114 |
set({
|
src/app/views/public-video-view/index.tsx
CHANGED
|
@@ -12,8 +12,28 @@ import { VideoInfo } from "@/types"
|
|
| 12 |
export function PublicVideoView() {
|
| 13 |
const video = useStore(s => s.currentVideo)
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
if (!video) { return null }
|
| 16 |
-
|
| 17 |
return (
|
| 18 |
<div className={cn(
|
| 19 |
`w-full`,
|
|
|
|
| 12 |
export function PublicVideoView() {
|
| 13 |
const video = useStore(s => s.currentVideo)
|
| 14 |
|
| 15 |
+
const videoId = `${video?.id || ""}`
|
| 16 |
+
|
| 17 |
+
// we inject the current videoId in the URL, if it's not already present
|
| 18 |
+
// this is a hack for Hugging Face iframes
|
| 19 |
+
useEffect(() => {
|
| 20 |
+
const queryString = new URL(location.href).search
|
| 21 |
+
const searchParams = new URLSearchParams(queryString)
|
| 22 |
+
if (videoId) {
|
| 23 |
+
if (searchParams.get("v") !== videoId) {
|
| 24 |
+
console.log(`current videoId "${videoId}" isn't set in the URL query params.. TODO we should set it`)
|
| 25 |
+
|
| 26 |
+
// searchParams.set("v", videoId)
|
| 27 |
+
// location.search = searchParams.toString()
|
| 28 |
+
}
|
| 29 |
+
} else {
|
| 30 |
+
// searchParams.delete("v")
|
| 31 |
+
// location.search = searchParams.toString()
|
| 32 |
+
}
|
| 33 |
+
}, [videoId])
|
| 34 |
+
|
| 35 |
if (!video) { return null }
|
| 36 |
+
|
| 37 |
return (
|
| 38 |
<div className={cn(
|
| 39 |
`w-full`,
|
src/app/watch/page.tsx
CHANGED
|
@@ -3,22 +3,15 @@ import Head from "next/head"
|
|
| 3 |
import Script from "next/script"
|
| 4 |
import { Metadata, ResolvingMetadata } from "next"
|
| 5 |
|
|
|
|
| 6 |
|
| 7 |
import { Main } from "../main"
|
| 8 |
-
|
| 9 |
import { getVideo } from "../server/actions/ai-tube-hf/getVideo"
|
| 10 |
|
| 11 |
-
type Props = {
|
| 12 |
-
params: { id: string }
|
| 13 |
-
searchParams: {
|
| 14 |
-
v?: string | string[],
|
| 15 |
-
[key: string]: string | string[] | undefined
|
| 16 |
-
}
|
| 17 |
-
}
|
| 18 |
|
| 19 |
// https://nextjs.org/docs/pages/building-your-application/optimizing/fonts
|
| 20 |
export async function generateMetadata(
|
| 21 |
-
{ params, searchParams: { v: videoId } }:
|
| 22 |
parent: ResolvingMetadata
|
| 23 |
): Promise<Metadata> {
|
| 24 |
// read route params
|
|
@@ -26,7 +19,7 @@ export async function generateMetadata(
|
|
| 26 |
const metadataBase = new URL('https://huggingface.co/spaces/jbilcke-hf/ai-tube')
|
| 27 |
|
| 28 |
try {
|
| 29 |
-
const video = await getVideo(videoId)
|
| 30 |
|
| 31 |
if (!video) {
|
| 32 |
throw new Error("Video not found")
|
|
@@ -69,15 +62,9 @@ export async function generateMetadata(
|
|
| 69 |
}
|
| 70 |
|
| 71 |
|
| 72 |
-
export default async function WatchPage({ searchParams: { v: videoId } }:
|
| 73 |
-
|
| 74 |
-
//
|
| 75 |
-
// const setCurrentVideo = useStore(s => s.setCurrentVideo)
|
| 76 |
-
const id = `${videoId || ""}`
|
| 77 |
-
|
| 78 |
-
const video = await getVideo(videoId)
|
| 79 |
-
|
| 80 |
-
// console.log("got video:", video.id)
|
| 81 |
return (
|
| 82 |
<Main video={video} />
|
| 83 |
)
|
|
|
|
| 3 |
import Script from "next/script"
|
| 4 |
import { Metadata, ResolvingMetadata } from "next"
|
| 5 |
|
| 6 |
+
import { AppQueryProps } from "@/types"
|
| 7 |
|
| 8 |
import { Main } from "../main"
|
|
|
|
| 9 |
import { getVideo } from "../server/actions/ai-tube-hf/getVideo"
|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
// https://nextjs.org/docs/pages/building-your-application/optimizing/fonts
|
| 13 |
export async function generateMetadata(
|
| 14 |
+
{ params, searchParams: { v: videoId } }: AppQueryProps,
|
| 15 |
parent: ResolvingMetadata
|
| 16 |
): Promise<Metadata> {
|
| 17 |
// read route params
|
|
|
|
| 19 |
const metadataBase = new URL('https://huggingface.co/spaces/jbilcke-hf/ai-tube')
|
| 20 |
|
| 21 |
try {
|
| 22 |
+
const video = await getVideo({ videoId, neverThrow: true })
|
| 23 |
|
| 24 |
if (!video) {
|
| 25 |
throw new Error("Video not found")
|
|
|
|
| 62 |
}
|
| 63 |
|
| 64 |
|
| 65 |
+
export default async function WatchPage({ searchParams: { v: videoId } }: AppQueryProps) {
|
| 66 |
+
const video = await getVideo({ videoId, neverThrow: true })
|
| 67 |
+
// console.log("WatchPage: --> " + video?.id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
return (
|
| 69 |
<Main video={video} />
|
| 70 |
)
|
src/types.ts
CHANGED
|
@@ -410,4 +410,13 @@ export type UpdateQueueRequest = {
|
|
| 410 |
export type UpdateQueueResponse = {
|
| 411 |
error?: string
|
| 412 |
nbUpdated: number
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 413 |
}
|
|
|
|
| 410 |
export type UpdateQueueResponse = {
|
| 411 |
error?: string
|
| 412 |
nbUpdated: number
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
export type AppQueryProps = {
|
| 417 |
+
params: { id: string }
|
| 418 |
+
searchParams: {
|
| 419 |
+
v?: string | string[],
|
| 420 |
+
[key: string]: string | string[] | undefined
|
| 421 |
+
}
|
| 422 |
}
|