Spaces:
Running
Running
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 |
}
|