jbilcke-hf HF staff commited on
Commit
c90a662
β€’
1 Parent(s): 40e9c34

added preview

Browse files
src/app/studio/[ownerId]/main.tsx CHANGED
@@ -1,18 +1,26 @@
1
- "use server"
 
 
2
 
3
- import { getTasks } from "@/server"
4
  import { VideoTasksQueue } from "@/components/business/tasks/video-tasks-queue"
5
  import { RefreshStudio } from "@/components/business/refresh"
6
- import { VideoForm } from "@/components/business/videoForm"
 
 
7
 
8
- export default async function Main({ ownerId }: { ownerId: string }) {
9
- const tasks = await getTasks(ownerId)
10
 
11
  return (
12
- <div className="h-full flex flex-col space-y-4 max-w-4xl w-full px-4 py-8">
13
- <VideoForm />
14
- <VideoTasksQueue tasks={tasks} />
15
- <RefreshStudio />
 
 
 
 
 
16
  </div>
17
  )
18
  }
 
1
+ "use client"
2
+
3
+ import { useState } from "react"
4
 
 
5
  import { VideoTasksQueue } from "@/components/business/tasks/video-tasks-queue"
6
  import { RefreshStudio } from "@/components/business/refresh"
7
+ import { VideoForm } from "@/components/business/video-form"
8
+ import { VideoTask } from "@/app/types"
9
+ import { VideoPlayer } from "@/components/business/video-player"
10
 
11
+ export default function Main({ videoTasks }: { videoTasks: VideoTask[] }) {
12
+ const [selectedVideo, selectVideo] = useState<VideoTask>()
13
 
14
  return (
15
+ <div className="flex flex-col md:flex-row">
16
+ <div className="h-full flex flex-col space-y-4 w-full md:w-[600px] px-4 py-8">
17
+ <VideoForm />
18
+ <VideoTasksQueue videoTasks={videoTasks} onSelectVideo={selectVideo} />
19
+ <RefreshStudio />
20
+ </div>
21
+ <div className="flex flex-col w-auto">
22
+ <VideoPlayer video={selectedVideo} />
23
+ </div>
24
  </div>
25
  )
26
  }
src/app/studio/[ownerId]/page.tsx CHANGED
@@ -1,15 +1,20 @@
 
 
1
  import Head from "next/head"
2
 
3
  import Main from "./main"
 
4
 
5
  export default async function StudioPage({ params: { ownerId } }: { params: { ownerId: string }}) {
 
 
6
  return (
7
  <div>
8
  <Head>
9
  <meta name="viewport" content="width=device-width, initial-scale=0.86, maximum-scale=5.0, minimum-scale=0.86" />
10
  </Head>
11
  <main className="dark fixed inset-0 flex flex-col items-center bg-stone-900 text-stone-10 overflow-y-scroll">
12
- <Main ownerId={`${ownerId || ""}`} />
13
  </main>
14
  </div>
15
  )
 
1
+ "use server"
2
+
3
  import Head from "next/head"
4
 
5
  import Main from "./main"
6
+ import { getTasks } from "@/server"
7
 
8
  export default async function StudioPage({ params: { ownerId } }: { params: { ownerId: string }}) {
9
+ const videoTasks = await getTasks(ownerId)
10
+
11
  return (
12
  <div>
13
  <Head>
14
  <meta name="viewport" content="width=device-width, initial-scale=0.86, maximum-scale=5.0, minimum-scale=0.86" />
15
  </Head>
16
  <main className="dark fixed inset-0 flex flex-col items-center bg-stone-900 text-stone-10 overflow-y-scroll">
17
+ <Main videoTasks={videoTasks} />
18
  </main>
19
  </div>
20
  )
src/components/business/refresh.tsx CHANGED
@@ -9,6 +9,7 @@ export function RefreshStudio() {
9
  const pathname = usePathname()
10
  const [isPending, startTransition] = useTransition()
11
 
 
12
  useEffect(() => {
13
  const slug = `${pathname.split("/").pop()}`
14
  setInterval(() => {
@@ -22,6 +23,7 @@ export function RefreshStudio() {
22
  })
23
  }, 2000)
24
  }, [])
 
25
 
26
  // TODO we could display a spinner here
27
  return <></>
 
9
  const pathname = usePathname()
10
  const [isPending, startTransition] = useTransition()
11
 
12
+ /*
13
  useEffect(() => {
14
  const slug = `${pathname.split("/").pop()}`
15
  setInterval(() => {
 
23
  })
24
  }, 2000)
25
  }, [])
26
+ */
27
 
28
  // TODO we could display a spinner here
29
  return <></>
src/components/business/tasks/columns.tsx CHANGED
@@ -32,12 +32,10 @@ export const columns: ColumnDef<VideoTask>[] = [
32
  },
33
  {
34
  accessorKey: "id",
35
- header: ({ column }) => (
36
- <DataTableColumnHeader column={column} title="Video ID" />
37
- ),
38
- cell: ({ row }) => <div className="w-[80px]">{`${row.getValue("id") || ''}`.split("-")[0]}..</div>,
39
  enableSorting: false,
40
- enableHiding: false,
41
  },
42
  {
43
  accessorKey: "videoPrompt",
@@ -63,7 +61,7 @@ export const columns: ColumnDef<VideoTask>[] = [
63
  const progress = Number(row.getValue("progressPercent") || 0)
64
 
65
  return (
66
- <div className="flex w-[100px] items-center">
67
  <span>{progress}%</span>
68
  </div>
69
  )
@@ -77,12 +75,12 @@ export const columns: ColumnDef<VideoTask>[] = [
77
  header: ({ column }) => (
78
  null // no header
79
  ),
80
- cell: ({ row }) => <div className="w-[200px]">
81
  <a
82
  className="hover:underline cursor-pointer"
83
  target="_blank"
84
  href={`${process.env.NEXT_PUBLIC_DOWNLOAD_URL}/${row.getValue("fileName")}`}>
85
- <video src={`${process.env.NEXT_PUBLIC_DOWNLOAD_URL}/${row.getValue("fileName")}?progress=${row.getValue("progressPercent") || 0}`} muted autoPlay loop />
86
  </a>
87
  </div>,
88
  enableSorting: false,
 
32
  },
33
  {
34
  accessorKey: "id",
35
+ header: ({ column }) => null,
36
+ cell: ({ row }) => null,
 
 
37
  enableSorting: false,
38
+ enableHiding: true,
39
  },
40
  {
41
  accessorKey: "videoPrompt",
 
61
  const progress = Number(row.getValue("progressPercent") || 0)
62
 
63
  return (
64
+ <div className="flex w-[30px] items-center">
65
  <span>{progress}%</span>
66
  </div>
67
  )
 
75
  header: ({ column }) => (
76
  null // no header
77
  ),
78
+ cell: ({ row }) => <div className="w-[120px]">
79
  <a
80
  className="hover:underline cursor-pointer"
81
  target="_blank"
82
  href={`${process.env.NEXT_PUBLIC_DOWNLOAD_URL}/${row.getValue("fileName")}`}>
83
+ <video src={`${process.env.NEXT_PUBLIC_DOWNLOAD_URL}/${row.getValue("fileName")}?progress=${row.getValue("progressPercent") || 0}`} muted />
84
  </a>
85
  </div>,
86
  enableSorting: false,
src/components/business/tasks/video-tasks-queue.tsx CHANGED
@@ -29,9 +29,11 @@ import { VideoTask } from "@/app/types"
29
  import { useState } from "react"
30
 
31
  export function VideoTasksQueue({
32
- tasks
 
33
  }: {
34
- tasks: VideoTask[]
 
35
  }) {
36
  const [rowSelection, setRowSelection] = useState({})
37
  const [columnVisibility, setColumnVisibility] =
@@ -42,7 +44,7 @@ export function VideoTasksQueue({
42
  const [sorting, setSorting] = useState<SortingState>([])
43
 
44
  const table = useReactTable({
45
- data: tasks,
46
  columns: columns as ColumnDef<VideoTask, any>[],
47
  state: {
48
  sorting,
@@ -92,7 +94,14 @@ export function VideoTasksQueue({
92
  data-state={row.getIsSelected() && "selected"}
93
  >
94
  {row.getVisibleCells().map((cell) => (
95
- <TableCell key={cell.id}>
 
 
 
 
 
 
 
96
  {flexRender(
97
  cell.column.columnDef.cell,
98
  cell.getContext()
 
29
  import { useState } from "react"
30
 
31
  export function VideoTasksQueue({
32
+ videoTasks = [],
33
+ onSelectVideo,
34
  }: {
35
+ videoTasks: VideoTask[]
36
+ onSelectVideo: (task: VideoTask) => void
37
  }) {
38
  const [rowSelection, setRowSelection] = useState({})
39
  const [columnVisibility, setColumnVisibility] =
 
44
  const [sorting, setSorting] = useState<SortingState>([])
45
 
46
  const table = useReactTable({
47
+ data: videoTasks,
48
  columns: columns as ColumnDef<VideoTask, any>[],
49
  state: {
50
  sorting,
 
94
  data-state={row.getIsSelected() && "selected"}
95
  >
96
  {row.getVisibleCells().map((cell) => (
97
+ <TableCell key={cell.id}
98
+ onClick={() => {
99
+ const videoId = `${row.getValue("id") || ""}`
100
+ const video = videoTasks.find(({ id }) => id === videoId)
101
+ if (video) {
102
+ onSelectVideo(video)
103
+ }
104
+ }}>
105
  {flexRender(
106
  cell.column.columnDef.cell,
107
  cell.getContext()
src/components/business/{videoForm.tsx β†’ video-form.tsx} RENAMED
File without changes
src/components/business/video-player.tsx ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import { VideoTask } from "@/app/types"
4
+
5
+ export const VideoPlayer = ({ video }: { video?: VideoTask }) => {
6
+
7
+ if (typeof video === "undefined") {
8
+ return <p>No video to display</p>
9
+ }
10
+
11
+ return (
12
+ <div className="w-full py-8 px-2">
13
+ <video
14
+ src={`${
15
+ process.env.NEXT_PUBLIC_DOWNLOAD_URL
16
+ }/${
17
+ video.fileName
18
+ }?progress=${
19
+ video.progressPercent
20
+ }`}
21
+ muted
22
+ autoPlay
23
+ loop
24
+ controls
25
+ className="w-full rounded-md overflow-hidden"
26
+ />
27
+ </div>
28
+ )
29
+ }
src/server/actions.ts CHANGED
@@ -6,11 +6,12 @@ import { submitNewTask } from "."
6
  export async function formSubmit(formData: FormData) {
7
 
8
  const ownerId = `${formData.get("ownerId") || ""}`
 
9
  await submitNewTask({
10
  prompt: `${formData.get("prompt") || ""}`,
11
  ownerId,
12
  })
13
-
14
  // for doc see https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions
15
  revalidatePath(`/studio/${ownerId}`)
16
  }
 
6
  export async function formSubmit(formData: FormData) {
7
 
8
  const ownerId = `${formData.get("ownerId") || ""}`
9
+ console.log('submitting to ', ownerId)
10
  await submitNewTask({
11
  prompt: `${formData.get("prompt") || ""}`,
12
  ownerId,
13
  })
14
+ console.log('calling revalidate', ownerId)
15
  // for doc see https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions
16
  revalidatePath(`/studio/${ownerId}`)
17
  }
src/server/base.ts CHANGED
@@ -45,9 +45,9 @@ export const post = async <S, T>(path: string = '', payload: S, defaultValue: T)
45
  Authorization: `Bearer ${process.env.VC_SECRET_ACCESS_TOKEN}`,
46
  },
47
  body: JSON.stringify(payload),
48
- cache: 'no-store',
49
  // we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
50
- // next: { revalidate: 10 }
51
  })
52
  // The return value is *not* serialized
53
  // You can return Date, Map, Set, etc.
 
45
  Authorization: `Bearer ${process.env.VC_SECRET_ACCESS_TOKEN}`,
46
  },
47
  body: JSON.stringify(payload),
48
+ // cache: 'no-store',
49
  // we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
50
+ next: { revalidate: 1 }
51
  })
52
  // The return value is *not* serialized
53
  // You can return Date, Map, Set, etc.