jbilcke-hf HF staff commited on
Commit
1185ec1
1 Parent(s): e70dbb1

just a lil bit of plumbing

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. src/app/channel/page.tsx +1 -1
  2. src/app/config.ts +1 -1
  3. src/app/interface/channel-card/index.tsx +1 -1
  4. src/app/interface/channel-list/index.tsx +1 -1
  5. src/app/interface/collection-card/index.tsx +1 -1
  6. src/app/interface/collection-list/index.tsx +1 -1
  7. src/app/interface/comment-card/index.tsx +1 -1
  8. src/app/interface/comment-list/index.tsx +1 -1
  9. src/app/interface/equirectangular-video-player/index.tsx +1 -1
  10. src/app/interface/equirectangular-video-player/viewer.tsx +1 -1
  11. src/app/interface/like-button/index.tsx +1 -1
  12. src/app/interface/media-list/index.tsx +1 -1
  13. src/app/interface/pending-video-card/index.tsx +1 -1
  14. src/app/interface/pending-video-list/index.tsx +1 -1
  15. src/app/interface/playlist-control/index.tsx +1 -1
  16. src/app/interface/recommended-videos/index.tsx +1 -1
  17. src/app/interface/track-card/index.tsx +1 -1
  18. src/app/interface/video-card/index.tsx +1 -1
  19. src/app/interface/video-player/cartesian.tsx +1 -1
  20. src/app/interface/video-player/equirectangular.tsx +1 -1
  21. src/app/interface/video-player/index.tsx +1 -1
  22. src/app/main.tsx +1 -1
  23. src/app/music/page.tsx +1 -1
  24. src/app/page.tsx +1 -1
  25. src/app/playlist/page.tsx +1 -1
  26. src/app/server/actions/ai-tube-hf/deleteVideoRequest.ts +1 -1
  27. src/app/server/actions/ai-tube-hf/downloadClapProject.ts +99 -0
  28. src/app/server/actions/ai-tube-hf/downloadFileAsBlob.ts +63 -0
  29. src/app/server/actions/ai-tube-hf/downloadPlainText.ts +13 -0
  30. src/app/server/actions/ai-tube-hf/extendVideosWithStats.ts +1 -1
  31. src/app/server/actions/ai-tube-hf/getChannel.ts +1 -1
  32. src/app/server/actions/ai-tube-hf/getChannelVideos.ts +13 -10
  33. src/app/server/actions/ai-tube-hf/getChannels.ts +1 -1
  34. src/app/server/actions/ai-tube-hf/getPrivateChannels.ts +1 -1
  35. src/app/server/actions/ai-tube-hf/getVideo.ts +1 -1
  36. src/app/server/actions/ai-tube-hf/getVideoIndex.ts +1 -1
  37. src/app/server/actions/ai-tube-hf/getVideoRequestsFromChannel.ts +108 -81
  38. src/app/server/actions/ai-tube-hf/getVideos.ts +1 -1
  39. src/app/server/actions/ai-tube-hf/parseChannel.ts +1 -1
  40. src/app/server/actions/ai-tube-hf/uploadVideoRequestToDataset.ts +10 -1
  41. src/app/server/actions/ai-tube-robot/updateQueue.ts +1 -1
  42. src/app/server/actions/comments.ts +1 -1
  43. src/app/server/actions/stats.ts +1 -1
  44. src/app/server/actions/submitVideoRequest.ts +1 -1
  45. src/app/server/actions/users.ts +1 -1
  46. src/app/server/actions/utils/computeOrientationProjectionWidthHeight.ts +1 -1
  47. src/app/server/actions/utils/isAntisocial.ts +1 -1
  48. src/app/server/actions/utils/isHighQuality.ts +1 -1
  49. src/app/server/actions/utils/parseClap.ts +91 -0
  50. src/app/server/actions/utils/parseDatasetPrompt.ts +1 -1
src/app/channel/page.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { AppQueryProps } from "@/types"
2
 
3
  import { Main } from "../main"
4
  import { getChannel } from "../server/actions/ai-tube-hf/getChannel"
 
1
+ import { AppQueryProps } from "@/types/general"
2
 
3
  import { Main } from "../main"
4
  import { getChannel } from "../server/actions/ai-tube-hf/getChannel"
src/app/config.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { VideoGenerationModel, VideoOrientation } from "@/types"
2
 
3
  export const showBetaFeatures = `${
4
  process.env.NEXT_PUBLIC_SHOW_BETA_FEATURES || ""
 
1
+ import { VideoGenerationModel, VideoOrientation } from "@/types/general"
2
 
3
  export const showBetaFeatures = `${
4
  process.env.NEXT_PUBLIC_SHOW_BETA_FEATURES || ""
src/app/interface/channel-card/index.tsx CHANGED
@@ -4,7 +4,7 @@ import { RiCheckboxCircleFill } from "react-icons/ri"
4
  import { IoAdd } from "react-icons/io5"
5
 
6
  import { cn } from "@/lib/utils"
7
- import { ChannelInfo } from "@/types"
8
  import { isCertifiedUser } from "@/app/certification"
9
  import { DefaultAvatar } from "../default-avatar"
10
  import { formatLargeNumber } from "@/lib/formatLargeNumber"
 
4
  import { IoAdd } from "react-icons/io5"
5
 
6
  import { cn } from "@/lib/utils"
7
+ import { ChannelInfo } from "@/types/general"
8
  import { isCertifiedUser } from "@/app/certification"
9
  import { DefaultAvatar } from "../default-avatar"
10
  import { formatLargeNumber } from "@/lib/formatLargeNumber"
src/app/interface/channel-list/index.tsx CHANGED
@@ -1,5 +1,5 @@
1
  import { cn } from "@/lib/utils"
2
- import { ChannelInfo } from "@/types"
3
 
4
  import { ChannelCard } from "../channel-card"
5
 
 
1
  import { cn } from "@/lib/utils"
2
+ import { ChannelInfo } from "@/types/general"
3
 
4
  import { ChannelCard } from "../channel-card"
5
 
src/app/interface/collection-card/index.tsx CHANGED
@@ -5,7 +5,7 @@ import Link from "next/link"
5
  import { RiCheckboxCircleFill } from "react-icons/ri"
6
 
7
  import { cn } from "@/lib/utils"
8
- import { CollectionInfo } from "@/types"
9
  import { formatDuration } from "@/lib/formatDuration"
10
  import { formatTimeAgo } from "@/lib/formatTimeAgo"
11
  import { isCertifiedUser } from "@/app/certification"
 
5
  import { RiCheckboxCircleFill } from "react-icons/ri"
6
 
7
  import { cn } from "@/lib/utils"
8
+ import { CollectionInfo } from "@/types/general"
9
  import { formatDuration } from "@/lib/formatDuration"
10
  import { formatTimeAgo } from "@/lib/formatTimeAgo"
11
  import { isCertifiedUser } from "@/app/certification"
src/app/interface/collection-list/index.tsx CHANGED
@@ -1,5 +1,5 @@
1
  import { cn } from "@/lib/utils"
2
- import { CollectionInfo } from "@/types"
3
 
4
  import { CollectionCard } from "../collection-card"
5
 
 
1
  import { cn } from "@/lib/utils"
2
+ import { CollectionInfo } from "@/types/general"
3
 
4
  import { CollectionCard } from "../collection-card"
5
 
src/app/interface/comment-card/index.tsx CHANGED
@@ -1,5 +1,5 @@
1
  import { cn } from "@/lib/utils"
2
- import { CommentInfo } from "@/types"
3
  import { useEffect, useState } from "react"
4
  import { DefaultAvatar } from "../default-avatar"
5
  import { formatTimeAgo } from "@/lib/formatTimeAgo"
 
1
  import { cn } from "@/lib/utils"
2
+ import { CommentInfo } from "@/types/general"
3
  import { useEffect, useState } from "react"
4
  import { DefaultAvatar } from "../default-avatar"
5
  import { formatTimeAgo } from "@/lib/formatTimeAgo"
src/app/interface/comment-list/index.tsx CHANGED
@@ -1,7 +1,7 @@
1
  "use client"
2
 
3
  import { cn } from "@/lib/utils"
4
- import { CommentInfo } from "@/types"
5
  import { CommentCard } from "../comment-card"
6
 
7
  export function CommentList({
 
1
  "use client"
2
 
3
  import { cn } from "@/lib/utils"
4
+ import { CommentInfo } from "@/types/general"
5
  import { CommentCard } from "../comment-card"
6
 
7
  export function CommentList({
src/app/interface/equirectangular-video-player/index.tsx CHANGED
@@ -3,7 +3,7 @@
3
  import AutoSizer from "react-virtualized-auto-sizer"
4
 
5
  import { cn } from "@/lib/utils"
6
- import { VideoInfo } from "@/types"
7
 
8
  import { VideoSphereViewer } from "./viewer"
9
 
 
3
  import AutoSizer from "react-virtualized-auto-sizer"
4
 
5
  import { cn } from "@/lib/utils"
6
+ import { VideoInfo } from "@/types/general"
7
 
8
  import { VideoSphereViewer } from "./viewer"
9
 
src/app/interface/equirectangular-video-player/viewer.tsx CHANGED
@@ -6,7 +6,7 @@ import { PanoramaPosition, PluginConstructor, Point, Position, SphericalPosition
6
  import { EquirectangularVideoAdapter, LensflarePlugin, ReactPhotoSphereViewer, ResolutionPlugin, SettingsPlugin, VideoPlugin } from "react-photo-sphere-viewer"
7
 
8
  import { cn } from "@/lib/utils"
9
- import { VideoInfo } from "@/types"
10
 
11
  type PhotoSpherePlugin = (PluginConstructor | [PluginConstructor, any])
12
 
 
6
  import { EquirectangularVideoAdapter, LensflarePlugin, ReactPhotoSphereViewer, ResolutionPlugin, SettingsPlugin, VideoPlugin } from "react-photo-sphere-viewer"
7
 
8
  import { cn } from "@/lib/utils"
9
+ import { VideoInfo } from "@/types/general"
10
 
11
  type PhotoSpherePlugin = (PluginConstructor | [PluginConstructor, any])
12
 
src/app/interface/like-button/index.tsx CHANGED
@@ -1,5 +1,5 @@
1
  import { useEffect, useState, useTransition } from "react"
2
- import { VideoInfo, VideoRating } from "@/types"
3
 
4
  import { GenericLikeButton } from "./generic"
5
  import { getVideoRating, rateVideo } from "@/app/server/actions/stats"
 
1
  import { useEffect, useState, useTransition } from "react"
2
+ import { VideoInfo, VideoRating } from "@/types/general"
3
 
4
  import { GenericLikeButton } from "./generic"
5
  import { getVideoRating, rateVideo } from "@/app/server/actions/stats"
src/app/interface/media-list/index.tsx CHANGED
@@ -1,5 +1,5 @@
1
  import { cn } from "@/lib/utils"
2
- import { MediaDisplayLayout, VideoInfo } from "@/types"
3
  import { TrackCard } from "../track-card"
4
  import { VideoCard } from "../video-card"
5
 
 
1
  import { cn } from "@/lib/utils"
2
+ import { MediaDisplayLayout, VideoInfo } from "@/types/general"
3
  import { TrackCard } from "../track-card"
4
  import { VideoCard } from "../video-card"
5
 
src/app/interface/pending-video-card/index.tsx CHANGED
@@ -3,7 +3,7 @@ import { PiTrashBold } from "react-icons/pi"
3
  import { TableCell, TableRow } from "@/components/ui/table"
4
  import { cn } from "@/lib/utils"
5
  import { MdLockClock } from "react-icons/md"
6
- import { VideoInfo } from "@/types"
7
  import { truncate } from "./truncate"
8
 
9
  export function PendingVideoCard({
 
3
  import { TableCell, TableRow } from "@/components/ui/table"
4
  import { cn } from "@/lib/utils"
5
  import { MdLockClock } from "react-icons/md"
6
+ import { VideoInfo } from "@/types/general"
7
  import { truncate } from "./truncate"
8
 
9
  export function PendingVideoCard({
src/app/interface/pending-video-list/index.tsx CHANGED
@@ -1,5 +1,5 @@
1
  import { cn } from "@/lib/utils"
2
- import { VideoInfo } from "@/types"
3
  import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
4
 
5
  import { PendingVideoCard } from "../pending-video-card"
 
1
  import { cn } from "@/lib/utils"
2
+ import { VideoInfo } from "@/types/general"
3
  import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
4
 
5
  import { PendingVideoCard } from "../pending-video-card"
src/app/interface/playlist-control/index.tsx CHANGED
@@ -3,7 +3,7 @@ import { IoIosPause } from "react-icons/io"
3
 
4
  import { cn } from "@/lib/utils"
5
  import { usePlaylist } from "@/lib/usePlaylist"
6
- import { VideoInfo } from "@/types"
7
 
8
  export function PlaylistControl() {
9
  const playlist = usePlaylist()
 
3
 
4
  import { cn } from "@/lib/utils"
5
  import { usePlaylist } from "@/lib/usePlaylist"
6
+ import { VideoInfo } from "@/types/general"
7
 
8
  export function PlaylistControl() {
9
  const playlist = usePlaylist()
src/app/interface/recommended-videos/index.tsx CHANGED
@@ -3,7 +3,7 @@ import { useEffect, useTransition } from "react"
3
 
4
  import { useStore } from "@/app/state/useStore"
5
  import { cn } from "@/lib/utils"
6
- import { VideoInfo } from "@/types"
7
 
8
  import { VideoList } from "../video-list"
9
  import { getVideos } from "@/app/server/actions/ai-tube-hf/getVideos"
 
3
 
4
  import { useStore } from "@/app/state/useStore"
5
  import { cn } from "@/lib/utils"
6
+ import { VideoInfo } from "@/types/general"
7
 
8
  import { VideoList } from "../video-list"
9
  import { getVideos } from "@/app/server/actions/ai-tube-hf/getVideos"
src/app/interface/track-card/index.tsx CHANGED
@@ -5,7 +5,7 @@ import Link from "next/link"
5
  import { RiCheckboxCircleFill } from "react-icons/ri"
6
 
7
  import { cn } from "@/lib/utils"
8
- import { MediaDisplayLayout, VideoInfo } from "@/types"
9
  import { formatDuration } from "@/lib/formatDuration"
10
  import { formatTimeAgo } from "@/lib/formatTimeAgo"
11
  import { isCertifiedUser } from "@/app/certification"
 
5
  import { RiCheckboxCircleFill } from "react-icons/ri"
6
 
7
  import { cn } from "@/lib/utils"
8
+ import { MediaDisplayLayout, VideoInfo } from "@/types/general"
9
  import { formatDuration } from "@/lib/formatDuration"
10
  import { formatTimeAgo } from "@/lib/formatTimeAgo"
11
  import { isCertifiedUser } from "@/app/certification"
src/app/interface/video-card/index.tsx CHANGED
@@ -5,7 +5,7 @@ import Link from "next/link"
5
  import { RiCheckboxCircleFill } from "react-icons/ri"
6
 
7
  import { cn } from "@/lib/utils"
8
- import { MediaDisplayLayout, VideoInfo } from "@/types"
9
  import { formatDuration } from "@/lib/formatDuration"
10
  import { formatTimeAgo } from "@/lib/formatTimeAgo"
11
  import { isCertifiedUser } from "@/app/certification"
 
5
  import { RiCheckboxCircleFill } from "react-icons/ri"
6
 
7
  import { cn } from "@/lib/utils"
8
+ import { MediaDisplayLayout, VideoInfo } from "@/types/general"
9
  import { formatDuration } from "@/lib/formatDuration"
10
  import { formatTimeAgo } from "@/lib/formatTimeAgo"
11
  import { isCertifiedUser } from "@/app/certification"
src/app/interface/video-player/cartesian.tsx CHANGED
@@ -4,7 +4,7 @@ import { Player } from "react-tuby"
4
  import "react-tuby/css/main.css"
5
 
6
  import { cn } from "@/lib/utils"
7
- import { VideoInfo } from "@/types"
8
 
9
  export function CartesianVideoPlayer({
10
  video,
 
4
  import "react-tuby/css/main.css"
5
 
6
  import { cn } from "@/lib/utils"
7
+ import { VideoInfo } from "@/types/general"
8
 
9
  export function CartesianVideoPlayer({
10
  video,
src/app/interface/video-player/equirectangular.tsx CHANGED
@@ -6,7 +6,7 @@ import { PanoramaPosition, PluginConstructor, Point, Position, SphericalPosition
6
  import { EquirectangularVideoAdapter, LensflarePlugin, ReactPhotoSphereViewer, ResolutionPlugin, SettingsPlugin, VideoPlugin } from "react-photo-sphere-viewer"
7
 
8
  import { cn } from "@/lib/utils"
9
- import { VideoInfo } from "@/types"
10
 
11
  type PhotoSpherePlugin = (PluginConstructor | [PluginConstructor, any])
12
 
 
6
  import { EquirectangularVideoAdapter, LensflarePlugin, ReactPhotoSphereViewer, ResolutionPlugin, SettingsPlugin, VideoPlugin } from "react-photo-sphere-viewer"
7
 
8
  import { cn } from "@/lib/utils"
9
+ import { VideoInfo } from "@/types/general"
10
 
11
  type PhotoSpherePlugin = (PluginConstructor | [PluginConstructor, any])
12
 
src/app/interface/video-player/index.tsx CHANGED
@@ -3,7 +3,7 @@
3
  import AutoSizer from "react-virtualized-auto-sizer"
4
 
5
  import { cn } from "@/lib/utils"
6
- import { VideoInfo } from "@/types"
7
  import { parseProjectionFromLoRA } from "@/app/server/actions/utils/parseProjectionFromLoRA"
8
 
9
  import { EquirectangularVideoPlayer } from "./equirectangular"
 
3
  import AutoSizer from "react-virtualized-auto-sizer"
4
 
5
  import { cn } from "@/lib/utils"
6
+ import { VideoInfo } from "@/types/general"
7
  import { parseProjectionFromLoRA } from "@/app/server/actions/utils/parseProjectionFromLoRA"
8
 
9
  import { EquirectangularVideoPlayer } from "./equirectangular"
src/app/main.tsx CHANGED
@@ -8,7 +8,7 @@ import { UserChannelView } from "./views/user-channel-view"
8
  import { PublicVideoView } from "./views/public-video-view"
9
  import { UserAccountView } from "./views/user-account-view"
10
  import { NotFoundView } from "./views/not-found-view"
11
- import { ChannelInfo, InterfaceView, VideoInfo } from "@/types"
12
  import { useEffect } from "react"
13
  import { usePathname, useRouter } from "next/navigation"
14
  import { TubeLayout } from "./interface/tube-layout"
 
8
  import { PublicVideoView } from "./views/public-video-view"
9
  import { UserAccountView } from "./views/user-account-view"
10
  import { NotFoundView } from "./views/not-found-view"
11
+ import { ChannelInfo, InterfaceView, VideoInfo } from "@/types/general"
12
  import { useEffect } from "react"
13
  import { usePathname, useRouter } from "next/navigation"
14
  import { TubeLayout } from "./interface/tube-layout"
src/app/music/page.tsx CHANGED
@@ -1,5 +1,5 @@
1
 
2
- import { AppQueryProps } from "@/types"
3
  import { Main } from "../main"
4
  import { getVideos } from "../server/actions/ai-tube-hf/getVideos"
5
  import { getVideo } from "../server/actions/ai-tube-hf/getVideo"
 
1
 
2
+ import { AppQueryProps } from "@/types/general"
3
  import { Main } from "../main"
4
  import { getVideos } from "../server/actions/ai-tube-hf/getVideos"
5
  import { getVideo } from "../server/actions/ai-tube-hf/getVideo"
src/app/page.tsx CHANGED
@@ -1,5 +1,5 @@
1
 
2
- import { AppQueryProps } from "@/types"
3
 
4
  import { Main } from "./main"
5
  import { getVideo } from "./server/actions/ai-tube-hf/getVideo"
 
1
 
2
+ import { AppQueryProps } from "@/types/general"
3
 
4
  import { Main } from "./main"
5
  import { getVideo } from "./server/actions/ai-tube-hf/getVideo"
src/app/playlist/page.tsx CHANGED
@@ -1,5 +1,5 @@
1
 
2
- import { AppQueryProps } from "@/types"
3
  import { Main } from "../main"
4
  import { getVideos } from "../server/actions/ai-tube-hf/getVideos"
5
  import { getVideo } from "../server/actions/ai-tube-hf/getVideo"
 
1
 
2
+ import { AppQueryProps } from "@/types/general"
3
  import { Main } from "../main"
4
  import { getVideos } from "../server/actions/ai-tube-hf/getVideos"
5
  import { getVideo } from "../server/actions/ai-tube-hf/getVideo"
src/app/server/actions/ai-tube-hf/deleteVideoRequest.ts CHANGED
@@ -1,5 +1,5 @@
1
 
2
- import { VideoInfo } from "@/types"
3
 
4
  import { deleteFileFromDataset } from "./deleteFileFromDataset"
5
  import { formatPromptFileName } from "../utils/formatPromptFileName"
 
1
 
2
+ import { VideoInfo } from "@/types/general"
3
 
4
  import { deleteFileFromDataset } from "./deleteFileFromDataset"
5
  import { formatPromptFileName } from "../utils/formatPromptFileName"
src/app/server/actions/ai-tube-hf/downloadClapProject.ts ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { v4 as uuidv4 } from "uuid"
2
+
3
+ import { ClapProject } from "@/types/clap"
4
+ import { ChannelInfo, VideoInfo, VideoRequest } from "@/types/general"
5
+ import { defaultVideoModel } from "@/app/config"
6
+
7
+ import { parseClap } from "../utils/parseClap"
8
+ import { parseVideoModelName } from "../utils/parseVideoModelName"
9
+ import { computeOrientationProjectionWidthHeight } from "../utils/computeOrientationProjectionWidthHeight"
10
+
11
+ import { downloadFileAsText } from "./downloadFileAsText"
12
+ import { downloadFileAsBlob } from "./downloadFileAsBlob"
13
+
14
+ export async function downloadClapProject({
15
+ path,
16
+ apiKey,
17
+ channel
18
+ }: {
19
+ path: string
20
+ apiKey?: string
21
+ channel: ChannelInfo
22
+ }): Promise<{
23
+ videoRequest: VideoRequest
24
+ videoInfo: VideoInfo
25
+ clapProject: ClapProject
26
+ }> {
27
+ // we recover the repo from the cnannel info
28
+ const repo = `datasets/${channel.datasetUser}/${channel.datasetName}`
29
+
30
+ // we download the clap file (which might be in a private repo)
31
+ const clapString = await downloadFileAsBlob({
32
+ repo,
33
+ path,
34
+ apiKey,
35
+ expectedMimeType: "application/gzip"
36
+ })
37
+
38
+ const clapProject = await parseClap(clapString)
39
+
40
+ const id = uuidv4()
41
+
42
+ const videoRequest: VideoRequest = {
43
+ id,
44
+ label: clapProject.meta.title || "Untitled",
45
+ description: clapProject.meta.description || "",
46
+ prompt: "", // there is no prompt - instead we use segments
47
+ model: parseVideoModelName(clapProject.meta.defaultVideoModel, channel.model),
48
+ style: channel.style,
49
+ lora: channel.lora,
50
+ voice: channel.voice,
51
+ music: channel.music,
52
+ thumbnailUrl: "",
53
+ clapUrl: `https://huggingface.co/${repo}/resolve/main/${path}`,
54
+ updatedAt: new Date().toISOString(),
55
+ tags: channel.tags,
56
+ channel,
57
+ duration: 0, // will be computed automatically
58
+ ...computeOrientationProjectionWidthHeight({
59
+ lora: "",
60
+ orientation: clapProject.meta.orientation,
61
+ // projection, // <- will be extrapolated from the LoRA for now
62
+ }),
63
+ }
64
+
65
+ const videoInfo: VideoInfo = {
66
+ id,
67
+ status: "submitted",
68
+ label: videoRequest.label || "",
69
+ description: videoRequest.description || "",
70
+ prompt: videoRequest.prompt || "",
71
+ model: videoRequest.model || defaultVideoModel,
72
+ style: videoRequest.style || "",
73
+ lora: videoRequest.lora || "",
74
+ voice: videoRequest.voice || "",
75
+ music: videoRequest.music || "",
76
+ thumbnailUrl: videoRequest.thumbnailUrl || "", // will be generated in async
77
+ clapUrl: videoRequest.clapUrl || "",
78
+ assetUrl: "", // will be generated in async
79
+ assetUrlHd: "",
80
+ numberOfViews: 0,
81
+ numberOfLikes: 0,
82
+ numberOfDislikes: 0,
83
+ updatedAt: new Date().toISOString(),
84
+ tags: videoRequest.tags,
85
+ channel,
86
+ duration: videoRequest.duration || 0,
87
+ ...computeOrientationProjectionWidthHeight({
88
+ lora: videoRequest.lora,
89
+ orientation: videoRequest.orientation,
90
+ // projection, // <- will be extrapolated from the LoRA for now
91
+ }),
92
+ }
93
+
94
+ return {
95
+ videoRequest,
96
+ videoInfo,
97
+ clapProject
98
+ }
99
+ }
src/app/server/actions/ai-tube-hf/downloadFileAsBlob.ts ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { downloadFile } from "@/huggingface/hub/src"
2
+ import { getCredentials } from "./getCredentials"
3
+
4
+ export async function downloadFileAsBlob({
5
+ repo,
6
+ path,
7
+ apiKey,
8
+ expectedMimeType = "text/plain",
9
+ renewCache = false,
10
+ neverThrow = false
11
+ }: {
12
+ repo: string
13
+
14
+ path: string
15
+
16
+ apiKey?: string
17
+
18
+ expectedMimeType?: string
19
+
20
+ /**
21
+ * Force renewing the cache
22
+ *
23
+ * False by default
24
+ */
25
+ renewCache?: boolean
26
+
27
+ /**
28
+ * If set to true, this function will never throw an exception
29
+ * this is useful in workflow where we don't care about what happened
30
+ *
31
+ * False by default
32
+ */
33
+ neverThrow?: boolean
34
+ }): Promise<Blob> {
35
+ try {
36
+ const { credentials } = await getCredentials(apiKey)
37
+
38
+ const response = await downloadFile({
39
+ repo,
40
+ path,
41
+ credentials,
42
+ requestInit: renewCache
43
+ ? { cache: "no-cache" }
44
+ : undefined
45
+ })
46
+
47
+ if (!response) {
48
+ throw new Error("missing response")
49
+ }
50
+ const blob = await response.blob()
51
+
52
+ return blob
53
+ } catch (err) {
54
+ if (neverThrow) {
55
+ console.error(`downloadFileAsBlob():`, err)
56
+
57
+ const blobResult = new Blob([""], { type: expectedMimeType })
58
+ return blobResult
59
+ } else {
60
+ throw err
61
+ }
62
+ }
63
+ }
src/app/server/actions/ai-tube-hf/downloadPlainText.ts ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ export async function downloadPlainText(url: string): Promise<string> {
3
+ // Fetch the plain text file
4
+ const response = await fetch(url)
5
+
6
+ if (!response.ok) {
7
+ throw new Error(`Failed to download the plain/text file: ${response.statusText}`)
8
+ }
9
+
10
+ const plainText = await response.text()
11
+
12
+ return plainText
13
+ }
src/app/server/actions/ai-tube-hf/extendVideosWithStats.ts CHANGED
@@ -1,6 +1,6 @@
1
  "use server"
2
 
3
- import { VideoInfo } from "@/types"
4
 
5
  import { getStatsForVideos } from "../stats"
6
 
 
1
  "use server"
2
 
3
+ import { VideoInfo } from "@/types/general"
4
 
5
  import { getStatsForVideos } from "../stats"
6
 
src/app/server/actions/ai-tube-hf/getChannel.ts CHANGED
@@ -1,7 +1,7 @@
1
  "use server"
2
 
3
 
4
- import { ChannelInfo } from "@/types"
5
 
6
  import { getChannels } from "./getChannels"
7
 
 
1
  "use server"
2
 
3
 
4
+ import { ChannelInfo } from "@/types/general"
5
 
6
  import { getChannels } from "./getChannels"
7
 
src/app/server/actions/ai-tube-hf/getChannelVideos.ts CHANGED
@@ -1,12 +1,13 @@
1
  "use server"
2
 
3
- import { ChannelInfo, VideoInfo, VideoStatus } from "@/types"
4
 
5
  import { getVideoRequestsFromChannel } from "./getVideoRequestsFromChannel"
6
  import { adminApiKey } from "../config"
7
  import { getVideoIndex } from "./getVideoIndex"
8
  import { extendVideosWithStats } from "./extendVideosWithStats"
9
  import { computeOrientationProjectionWidthHeight } from "../utils/computeOrientationProjectionWidthHeight"
 
10
 
11
  // return
12
  export async function getChannelVideos({
@@ -40,16 +41,18 @@ export async function getChannelVideos({
40
  let video: VideoInfo = {
41
  id: v.id,
42
  status: "submitted",
43
- label: v.label,
44
- description: v.description,
45
- prompt: v.prompt,
46
- thumbnailUrl: v.thumbnailUrl,
47
- model: v.model,
48
- lora: v.lora,
49
- style: v.style,
50
- voice: v.voice,
51
- music: v.music,
 
52
  assetUrl: "",
 
53
  numberOfViews: 0,
54
  numberOfLikes: 0,
55
  numberOfDislikes: 0,
 
1
  "use server"
2
 
3
+ import { ChannelInfo, VideoInfo, VideoStatus } from "@/types/general"
4
 
5
  import { getVideoRequestsFromChannel } from "./getVideoRequestsFromChannel"
6
  import { adminApiKey } from "../config"
7
  import { getVideoIndex } from "./getVideoIndex"
8
  import { extendVideosWithStats } from "./extendVideosWithStats"
9
  import { computeOrientationProjectionWidthHeight } from "../utils/computeOrientationProjectionWidthHeight"
10
+ import { defaultVideoModel } from "@/app/config"
11
 
12
  // return
13
  export async function getChannelVideos({
 
41
  let video: VideoInfo = {
42
  id: v.id,
43
  status: "submitted",
44
+ label: v.label || "",
45
+ description: v.description || "",
46
+ prompt: v.prompt || "",
47
+ thumbnailUrl: v.thumbnailUrl || "",
48
+ clapUrl: v.clapUrl || "",
49
+ model: v.model || defaultVideoModel,
50
+ lora: v.lora || "",
51
+ style: v.style || "",
52
+ voice: v.voice || "",
53
+ music: v.music || "",
54
  assetUrl: "",
55
+ assetUrlHd: "",
56
  numberOfViews: 0,
57
  numberOfLikes: 0,
58
  numberOfDislikes: 0,
src/app/server/actions/ai-tube-hf/getChannels.ts CHANGED
@@ -1,6 +1,6 @@
1
  "use server"
2
 
3
- import { ChannelInfo } from "@/types"
4
 
5
  import { adminUsername } from "../config"
6
 
 
1
  "use server"
2
 
3
+ import { ChannelInfo } from "@/types/general"
4
 
5
  import { adminUsername } from "../config"
6
 
src/app/server/actions/ai-tube-hf/getPrivateChannels.ts CHANGED
@@ -1,7 +1,7 @@
1
  "use server"
2
 
3
  import { Credentials, listDatasets, whoAmI } from "@/huggingface/hub/src"
4
- import { ChannelInfo } from "@/types"
5
 
6
  import { adminCredentials } from "../config"
7
  import { parseChannel } from "./parseChannel"
 
1
  "use server"
2
 
3
  import { Credentials, listDatasets, whoAmI } from "@/huggingface/hub/src"
4
+ import { ChannelInfo } from "@/types/general"
5
 
6
  import { adminCredentials } from "../config"
7
  import { parseChannel } from "./parseChannel"
src/app/server/actions/ai-tube-hf/getVideo.ts CHANGED
@@ -1,6 +1,6 @@
1
  "use server"
2
 
3
- import { VideoInfo } from "@/types"
4
 
5
  import { getVideoIndex } from "./getVideoIndex"
6
  import { getStatsForVideos } from "../stats"
 
1
  "use server"
2
 
3
+ import { VideoInfo } from "@/types/general"
4
 
5
  import { getVideoIndex } from "./getVideoIndex"
6
  import { getStatsForVideos } from "../stats"
src/app/server/actions/ai-tube-hf/getVideoIndex.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { VideoInfo, VideoStatus } from "@/types"
2
 
3
  import { adminUsername } from "../config"
4
 
 
1
+ import { VideoInfo, VideoStatus } from "@/types/general"
2
 
3
  import { adminUsername } from "../config"
4
 
src/app/server/actions/ai-tube-hf/getVideoRequestsFromChannel.ts CHANGED
@@ -1,12 +1,13 @@
1
  "use server"
2
 
3
- import { ChannelInfo, VideoRequest } from "@/types"
4
  import { getCredentials } from "./getCredentials"
5
  import { listFiles } from "@/huggingface/hub/src"
6
  import { parsePromptFileName } from "../utils/parsePromptFileName"
7
  import { downloadFileAsText } from "./downloadFileAsText"
8
  import { parseDatasetPrompt } from "../utils/parseDatasetPrompt"
9
  import { computeOrientationProjectionWidthHeight } from "../utils/computeOrientationProjectionWidthHeight"
 
10
 
11
  /**
12
  * Return all the videos requests created by a user on their channel
@@ -42,89 +43,115 @@ export async function getVideoRequestsFromChannel({
42
  ? { cache: "no-cache" }
43
  : undefined
44
  })) {
45
-
46
- // TODO we should add some safety mechanisms here:
47
- // skip lists of files that are too long
48
- // skip files that are too big
49
- // skip files with file.security.safe !== true
50
-
51
- // console.log("file.path:", file.path)
52
- /// { type, oid, size, path }
53
- if (file.path === "README.md") {
54
- // console.log("found the README")
55
- // TODO: read this readme
56
- } else if (file.path.startsWith("prompt_") && file.path.endsWith(".md")) {
57
-
58
- const id = parsePromptFileName(file.path)
59
-
60
- if (!id) { continue }
61
-
62
- const rawMarkdown = await downloadFileAsText({
63
- repo,
64
- path: file.path,
65
- apiKey,
66
- renewCache,
67
- neverThrow: true,
68
- })
69
-
70
- if (!rawMarkdown) {
71
- // console.log(`markdown file is empty, skipping`)
72
- continue
73
- }
74
-
75
- const {
76
- title,
77
- description,
78
- tags,
79
- prompt,
80
- thumbnail,
81
- model,
82
- lora,
83
- style,
84
- music,
85
- voice,
86
- orientation,
87
- } = parseDatasetPrompt(rawMarkdown, channel)
88
-
89
- if (!title || !description || !prompt) {
90
- // console.log("dataset prompt is incomplete or unparseable")
91
- continue
92
- }
93
- // console.log("prompt parsed markdown:", { title, description, tags })
94
- let thumbnailUrl =
95
- thumbnail.startsWith("http")
96
- ? thumbnail
97
- : (thumbnail.endsWith(".jpg") || thumbnail.endsWith(".jpeg"))
98
- ? `https://huggingface.co/${repo}/resolve/main/${thumbnail}`
99
- : ""
100
-
101
-
102
- const video: VideoRequest = {
103
- id,
104
- label: title,
105
- description,
106
- prompt,
107
- thumbnailUrl,
108
- model,
109
- lora,
110
- style,
111
- voice,
112
- music,
113
- updatedAt: file.lastCommit?.date || new Date().toISOString(),
114
- tags: Array.isArray(tags) && tags.length ? tags : channel.tags,
115
- channel,
116
- duration: 0,
117
- ...computeOrientationProjectionWidthHeight({
118
  lora,
 
 
 
119
  orientation,
120
- // projection, // <- will be extrapolated from the LoRA for now
121
- }),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  }
123
-
124
- videos[id] = video
125
-
126
- } else if (file.path.endsWith(".mp4")) {
127
- // console.log("found a video:", file.path)
128
  }
129
  }
130
 
 
1
  "use server"
2
 
3
+ import { ChannelInfo, VideoRequest } from "@/types/general"
4
  import { getCredentials } from "./getCredentials"
5
  import { listFiles } from "@/huggingface/hub/src"
6
  import { parsePromptFileName } from "../utils/parsePromptFileName"
7
  import { downloadFileAsText } from "./downloadFileAsText"
8
  import { parseDatasetPrompt } from "../utils/parseDatasetPrompt"
9
  import { computeOrientationProjectionWidthHeight } from "../utils/computeOrientationProjectionWidthHeight"
10
+ import { downloadClapProject } from "./downloadClapProject"
11
 
12
  /**
13
  * Return all the videos requests created by a user on their channel
 
43
  ? { cache: "no-cache" }
44
  : undefined
45
  })) {
46
+ try {
47
+ const filePath = file.path.toLowerCase().trim()
48
+ // TODO we should add some safety mechanisms here:
49
+ // skip lists of files that are too long
50
+ // skip files that are too big
51
+ // skip files with file.security.safe !== true
52
+
53
+ // console.log("file.path:", file.path)
54
+ /// { type, oid, size, path }
55
+ if (filePath === "readme.md") {
56
+ // console.log("found the README")
57
+ // TODO: read this readme
58
+ } else if (filePath.endsWith(".clap")) {
59
+ const clap = await downloadClapProject({
60
+ path: file.path,
61
+ channel,
62
+ apiKey
63
+ })
64
+ console.log("got a clap file:", clap.clapProject.meta)
65
+
66
+ // in the frontend UI we want to display everything,
67
+ // we don't filter stuff even if they are incomplete
68
+
69
+ videos[clap.videoRequest.id] = clap.videoRequest
70
+ } else if (filePath.startsWith("prompt_") && filePath.endsWith(".md")) {
71
+
72
+ const id = parsePromptFileName(filePath)
73
+
74
+ if (!id) { continue }
75
+
76
+ const rawMarkdown = await downloadFileAsText({
77
+ repo,
78
+ path: file.path, // be sure to use the original file.path (with capitalization if any) and not filePath
79
+ apiKey,
80
+ renewCache,
81
+ neverThrow: true,
82
+ })
83
+
84
+ if (!rawMarkdown) {
85
+ // console.log(`markdown file is empty, skipping`)
86
+ continue
87
+ }
88
+
89
+ const {
90
+ title,
91
+ description,
92
+ tags,
93
+ prompt,
94
+ thumbnail,
95
+ model,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  lora,
97
+ style,
98
+ music,
99
+ voice,
100
  orientation,
101
+ } = parseDatasetPrompt(rawMarkdown, channel)
102
+
103
+ /*
104
+ on ai-tube side (not the ai-tube robot) we are okay with partial video requests,
105
+ ie. drafts
106
+ if (!title || !description || !prompt) {
107
+ // console.log("dataset prompt is incomplete or unparseable")
108
+ // continue
109
+ }
110
+ */
111
+
112
+ // console.log("prompt parsed markdown:", { title, description, tags })
113
+ let thumbnailUrl =
114
+ thumbnail.startsWith("http")
115
+ ? thumbnail
116
+ : (thumbnail.endsWith(".webp") || thumbnail.endsWith(".jpg") || thumbnail.endsWith(".jpeg"))
117
+ ? `https://huggingface.co/${repo}/resolve/main/${thumbnail}`
118
+ : ""
119
+
120
+ // TODO: the clap file is empty if
121
+ // the video is prompted using Markdown
122
+ const clapUrl = ""
123
+
124
+ const video: VideoRequest = {
125
+ id,
126
+ label: title,
127
+ description,
128
+ prompt,
129
+ thumbnailUrl,
130
+ clapUrl,
131
+ model,
132
+ lora,
133
+ style,
134
+ voice,
135
+ music,
136
+ updatedAt: file.lastCommit?.date || new Date().toISOString(),
137
+ tags: Array.isArray(tags) && tags.length ? tags : channel.tags,
138
+ channel,
139
+ duration: 0,
140
+ ...computeOrientationProjectionWidthHeight({
141
+ lora,
142
+ orientation,
143
+ // projection, // <- will be extrapolated from the LoRA for now
144
+ }),
145
+ }
146
+
147
+ videos[id] = video
148
+
149
+ } else if (filePath.endsWith(".mp4")) {
150
+ // console.log("found a video:", file.path)
151
  }
152
+ } catch (err) {
153
+ console.error("error while processing a dataset file:")
154
+ console.error(err)
 
 
155
  }
156
  }
157
 
src/app/server/actions/ai-tube-hf/getVideos.ts CHANGED
@@ -1,6 +1,6 @@
1
  "use server"
2
 
3
- import { VideoInfo } from "@/types"
4
 
5
  import { getVideoIndex } from "./getVideoIndex"
6
  import { extendVideosWithStats } from "./extendVideosWithStats"
 
1
  "use server"
2
 
3
+ import { VideoInfo } from "@/types/general"
4
 
5
  import { getVideoIndex } from "./getVideoIndex"
6
  import { extendVideosWithStats } from "./extendVideosWithStats"
src/app/server/actions/ai-tube-hf/parseChannel.ts CHANGED
@@ -2,7 +2,7 @@
2
 
3
  import { Credentials, downloadFile, whoAmI } from "@/huggingface/hub/src"
4
  import { parseDatasetReadme } from "@/app/server/actions/utils/parseDatasetReadme"
5
- import { ChannelInfo, VideoGenerationModel, VideoOrientation } from "@/types"
6
 
7
  import { adminCredentials } from "../config"
8
  import { defaultVideoModel, defaultVideoOrientation } from "@/app/config"
 
2
 
3
  import { Credentials, downloadFile, whoAmI } from "@/huggingface/hub/src"
4
  import { parseDatasetReadme } from "@/app/server/actions/utils/parseDatasetReadme"
5
+ import { ChannelInfo, VideoGenerationModel, VideoOrientation } from "@/types/general"
6
 
7
  import { adminCredentials } from "../config"
8
  import { defaultVideoModel, defaultVideoOrientation } from "@/app/config"
src/app/server/actions/ai-tube-hf/uploadVideoRequestToDataset.ts CHANGED
@@ -3,7 +3,7 @@
3
  import { Blob } from "buffer"
4
 
5
  import { Credentials, uploadFile, whoAmI } from "@/huggingface/hub/src"
6
- import { ChannelInfo, VideoGenerationModel, VideoInfo, VideoOrientation, VideoRequest } from "@/types"
7
  import { formatPromptFileName } from "../utils/formatPromptFileName"
8
  import { computeOrientationProjectionWidthHeight } from "../utils/computeOrientationProjectionWidthHeight"
9
 
@@ -126,6 +126,10 @@ ${prompt}
126
  voice,
127
  music,
128
  thumbnailUrl: channel.thumbnail,
 
 
 
 
129
  updatedAt: new Date().toISOString(),
130
  tags,
131
  channel,
@@ -149,7 +153,12 @@ ${prompt}
149
  voice,
150
  music,
151
  thumbnailUrl: channel.thumbnail, // will be generated in async
 
 
 
 
152
  assetUrl: "", // will be generated in async
 
153
  numberOfViews: 0,
154
  numberOfLikes: 0,
155
  numberOfDislikes: 0,
 
3
  import { Blob } from "buffer"
4
 
5
  import { Credentials, uploadFile, whoAmI } from "@/huggingface/hub/src"
6
+ import { ChannelInfo, VideoGenerationModel, VideoInfo, VideoOrientation, VideoRequest } from "@/types/general"
7
  import { formatPromptFileName } from "../utils/formatPromptFileName"
8
  import { computeOrientationProjectionWidthHeight } from "../utils/computeOrientationProjectionWidthHeight"
9
 
 
126
  voice,
127
  music,
128
  thumbnailUrl: channel.thumbnail,
129
+
130
+ // for now AI Tube doesn't support upload of clap files
131
+ clapUrl: "",
132
+
133
  updatedAt: new Date().toISOString(),
134
  tags,
135
  channel,
 
153
  voice,
154
  music,
155
  thumbnailUrl: channel.thumbnail, // will be generated in async
156
+
157
+ // for now AI Tube doesn't support upload of clap files
158
+ clapUrl: "",
159
+
160
  assetUrl: "", // will be generated in async
161
+ assetUrlHd: "",
162
  numberOfViews: 0,
163
  numberOfLikes: 0,
164
  numberOfDislikes: 0,
src/app/server/actions/ai-tube-robot/updateQueue.ts CHANGED
@@ -1,6 +1,6 @@
1
  "use server"
2
 
3
- import { ChannelInfo, UpdateQueueResponse } from "@/types"
4
 
5
  import { aiTubeRobotApi } from "../config"
6
 
 
1
  "use server"
2
 
3
+ import { ChannelInfo, UpdateQueueResponse } from "@/types/general"
4
 
5
  import { aiTubeRobotApi } from "../config"
6
 
src/app/server/actions/comments.ts CHANGED
@@ -2,7 +2,7 @@
2
 
3
  import { v4 as uuidv4 } from "uuid"
4
 
5
- import { CommentInfo, StoredCommentInfo } from "@/types"
6
  import { stripHtml } from "@/lib/stripHtml"
7
  import { getCurrentUser, getUsers } from "./users"
8
  import { redis } from "./redis"
 
2
 
3
  import { v4 as uuidv4 } from "uuid"
4
 
5
+ import { CommentInfo, StoredCommentInfo } from "@/types/general"
6
  import { stripHtml } from "@/lib/stripHtml"
7
  import { getCurrentUser, getUsers } from "./users"
8
  import { redis } from "./redis"
src/app/server/actions/stats.ts CHANGED
@@ -2,7 +2,7 @@
2
 
3
  import { developerMode } from "@/app/config"
4
  import { WhoAmIUser, whoAmI } from "@/huggingface/hub/src"
5
- import { VideoRating } from "@/types"
6
  import { redis } from "./redis";
7
 
8
  export async function getStatsForVideos(videoIds: string[]): Promise<Record<string, { numberOfViews: number; numberOfLikes: number; numberOfDislikes: number}>> {
 
2
 
3
  import { developerMode } from "@/app/config"
4
  import { WhoAmIUser, whoAmI } from "@/huggingface/hub/src"
5
+ import { VideoRating } from "@/types/general"
6
  import { redis } from "./redis";
7
 
8
  export async function getStatsForVideos(videoIds: string[]): Promise<Record<string, { numberOfViews: number; numberOfLikes: number; numberOfDislikes: number}>> {
src/app/server/actions/submitVideoRequest.ts CHANGED
@@ -1,6 +1,6 @@
1
  "use server"
2
 
3
- import { ChannelInfo, VideoGenerationModel, VideoInfo, VideoOrientation } from "@/types"
4
 
5
  import { uploadVideoRequestToDataset } from "./ai-tube-hf/uploadVideoRequestToDataset"
6
 
 
1
  "use server"
2
 
3
+ import { ChannelInfo, VideoGenerationModel, VideoInfo, VideoOrientation } from "@/types/general"
4
 
5
  import { uploadVideoRequestToDataset } from "./ai-tube-hf/uploadVideoRequestToDataset"
6
 
src/app/server/actions/users.ts CHANGED
@@ -1,7 +1,7 @@
1
  "use server"
2
 
3
  import { WhoAmIUser, whoAmI } from "@/huggingface/hub/src"
4
- import { UserInfo } from "@/types"
5
  import { adminApiKey } from "./config"
6
  import { redis } from "./redis"
7
 
 
1
  "use server"
2
 
3
  import { WhoAmIUser, whoAmI } from "@/huggingface/hub/src"
4
+ import { UserInfo } from "@/types/general"
5
  import { adminApiKey } from "./config"
6
  import { redis } from "./redis"
7
 
src/app/server/actions/utils/computeOrientationProjectionWidthHeight.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { VideoOrientation, VideoProjection } from "@/types"
2
 
3
  import { parseVideoOrientation } from "./parseVideoOrientation"
4
  import { parseProjectionFromLoRA } from "./parseProjectionFromLoRA"
 
1
+ import { VideoOrientation, VideoProjection } from "@/types/general"
2
 
3
  import { parseVideoOrientation } from "./parseVideoOrientation"
4
  import { parseProjectionFromLoRA } from "./parseProjectionFromLoRA"
src/app/server/actions/utils/isAntisocial.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { VideoInfo } from "@/types"
2
 
3
  const winners = new Set(`${process.env.WINNERS || ""}`.toLowerCase().split(",").map(x => x.trim()).filter(x => x))
4
 
 
1
+ import { VideoInfo } from "@/types/general"
2
 
3
  const winners = new Set(`${process.env.WINNERS || ""}`.toLowerCase().split(",").map(x => x.trim()).filter(x => x))
4
 
src/app/server/actions/utils/isHighQuality.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { VideoInfo } from "@/types"
2
 
3
  export function isHighQuality(video: VideoInfo) {
4
  const numberOfViews = Math.abs(Math.max(0, video.numberOfViews))
 
1
+ import { VideoInfo } from "@/types/general"
2
 
3
  export function isHighQuality(video: VideoInfo) {
4
  const numberOfViews = Math.abs(Math.max(0, video.numberOfViews))
src/app/server/actions/utils/parseClap.ts ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import YAML from "yaml"
2
+ import { v4 as uuidv4 } from "uuid"
3
+
4
+ import { ClapHeader, ClapMeta, ClapProject, ClapSegment } from "@/types/clap"
5
+ import { getValidNumber } from "@/lib/getValidNumber"
6
+
7
+ /**
8
+ * import a Clap file (from a plain text string)
9
+ *
10
+ * note: it is not really async, because for some reason YAML.parse is a blocking call like for JSON,
11
+ * they is no async version although we are now in the 20s not 90s
12
+ */
13
+ export async function parseClap(inputStringOrBlob: string | Blob): Promise<ClapProject> {
14
+
15
+ // Decompress the input blob using gzip
16
+ const decompressor = new DecompressionStream('gzip');
17
+
18
+ const inputBlob =
19
+ typeof inputStringOrBlob === "string"
20
+ ? new Blob([inputStringOrBlob], { type: "application/x-yaml" })
21
+ : inputStringOrBlob;
22
+
23
+ const decompressedStream = inputBlob.stream().pipeThrough(decompressor);
24
+
25
+ // Convert the stream to text using a Response object
26
+ const text = await new Response(decompressedStream).text();
27
+
28
+ // Parse YAML string to raw data
29
+ const rawData = YAML.parse(text);
30
+
31
+ if (!Array.isArray(rawData) || rawData.length < 2) {
32
+ throw new Error("invalid clap file (need a clap format header block and project metadata block)")
33
+ }
34
+
35
+ const maybeClapHeader = rawData[0] as ClapHeader
36
+
37
+ if (maybeClapHeader.format !== "clap-0") {
38
+ throw new Error("invalid clap file (sorry, but you can't make up version numbers like that)")
39
+ }
40
+
41
+ const maybeClapMeta = rawData[1] as ClapMeta
42
+
43
+ const clapMeta: ClapMeta = {
44
+ id: typeof maybeClapMeta.title === "string" ? maybeClapMeta.id : uuidv4(),
45
+ title: typeof maybeClapMeta.title === "string" ? maybeClapMeta.title : "",
46
+ description: typeof maybeClapMeta.description === "string" ? maybeClapMeta.description : "",
47
+ licence: typeof maybeClapMeta.licence === "string" ? maybeClapMeta.licence : "",
48
+ orientation: maybeClapMeta.orientation === "portrait" ? "portrait" : maybeClapMeta.orientation === "square" ? "square" : "landscape",
49
+ width: getValidNumber(maybeClapMeta.width, 256, 4096, 1024),
50
+ height: getValidNumber(maybeClapMeta.height, 256, 4096, 1024),
51
+ defaultVideoModel: typeof maybeClapMeta.defaultVideoModel === "string" ? maybeClapMeta.defaultVideoModel : "SVD",
52
+ }
53
+
54
+ const maybeSegments = rawData.slice(2) as ClapSegment[]
55
+
56
+ const clapSegments: ClapSegment[] = Array.isArray(maybeSegments) ? maybeSegments.map(({
57
+ id,
58
+ track,
59
+ startTimeInMs,
60
+ endTimeInMs,
61
+ category,
62
+ modelId,
63
+ prompt,
64
+ outputType,
65
+ renderId,
66
+ status,
67
+ assetUrl,
68
+ outputGain,
69
+ seed,
70
+ }) => ({
71
+ // TODO: we should verify each of those, probably
72
+ id,
73
+ track,
74
+ startTimeInMs,
75
+ endTimeInMs,
76
+ category,
77
+ modelId,
78
+ prompt,
79
+ outputType,
80
+ renderId,
81
+ status,
82
+ assetUrl,
83
+ outputGain,
84
+ seed,
85
+ })) : []
86
+
87
+ return {
88
+ meta: clapMeta,
89
+ segments: clapSegments
90
+ }
91
+ }
src/app/server/actions/utils/parseDatasetPrompt.ts CHANGED
@@ -1,5 +1,5 @@
1
 
2
- import { ChannelInfo, ParsedDatasetPrompt } from "@/types"
3
  import { parseVideoModelName } from "./parseVideoModelName"
4
  import { parseVideoOrientation } from "./parseVideoOrientation"
5
  import { defaultVideoModel, defaultVideoOrientation } from "@/app/config"
 
1
 
2
+ import { ChannelInfo, ParsedDatasetPrompt } from "@/types/general"
3
  import { parseVideoModelName } from "./parseVideoModelName"
4
  import { parseVideoOrientation } from "./parseVideoOrientation"
5
  import { defaultVideoModel, defaultVideoOrientation } from "@/app/config"