Commit
β’
1cf03f7
1
Parent(s):
6eef442
switching to a cheaper interpolation engine
Browse files- .env +11 -2
- package-lock.json +0 -0
- public/images/models/sdxl-akira.jpg +0 -0
- public/images/models/sdxl-starfield.jpg +0 -0
- public/images/sign-in-with-huggingface-xl.svg +43 -0
- src/app/interface/auth-dialog/index.tsx +6 -0
- src/app/interface/generate/index.tsx +3 -2
- src/app/server/actions/animation.ts +4 -4
- src/app/server/actions/{generateWithGradioApi.ts β generateGradio.ts} +1 -1
- src/app/server/actions/{generateWithReplicateAPI.ts β generateReplicate.ts} +1 -1
- src/app/server/actions/interpolateGradio.ts +41 -0
- src/app/server/actions/{interpolate.ts β interpolateReplicate.ts} +1 -3
- src/app/server/actions/interpolation.ts +25 -0
- src/app/server/actions/models.ts +20 -0
- src/components/ui/dialog.tsx +1 -2
.env
CHANGED
@@ -1,13 +1,18 @@
|
|
1 |
AUTH_REPLICATE_API_TOKEN="<USE YOUR OWN>"
|
2 |
AUTH_HOTSHOT_XL_API_GRADIO_ACCESS_TOKEN="<USE YOUR OWN>"
|
|
|
3 |
|
4 |
-
# TODO:
|
5 |
# VIDEO_HOTSHOT_XL_API_OFFICIAL
|
6 |
-
# VIDEO_HOTSHOT_XL_API_NODE
|
7 |
# VIDEO_HOTSHOT_XL_API_GRADIO
|
8 |
# VIDEO_HOTSHOT_XL_API_REPLICATE
|
9 |
VIDEO_ENGINE="VIDEO_HOTSHOT_XL_API_GRADIO"
|
10 |
|
|
|
|
|
|
|
|
|
|
|
11 |
# the official API developed by the Hotshot-XL team
|
12 |
# note: it isn't released yet
|
13 |
VIDEO_HOTSHOT_XL_API_OFFICIAL=""
|
@@ -25,9 +30,13 @@ VIDEO_HOTSHOT_XL_API_GRADIO="https://jbilcke-hf-hotshot-xl-server-1.hf.space"
|
|
25 |
VIDEO_HOTSHOT_XL_API_REPLICATE_MODEL="cloneofsimo/hotshot-xl-lora-controlnet"
|
26 |
VIDEO_HOTSHOT_XL_API_REPLICATE_MODEL_VERSION="75e26ffd033a59a78954a3d675632f47f7f8470402aec51c255b9f9b7b62568b"
|
27 |
|
|
|
|
|
28 |
INTERPOLATION_API_REPLICATE_MODEL="zsxkib/st-mfnet"
|
29 |
INTERPOLATION_API_REPLICATE_MODEL_VERSION="faa7693430b0a4ac95d1b8e25165673c1d7a7263537a7c4bb9be82a3e2d130fb"
|
30 |
|
|
|
|
|
31 |
# ----------- RATE LIMIT -------
|
32 |
ENABLE_RATE_LIMIT=""
|
33 |
UPSTASH_REDIS_REST_URL="<USE YOUR OWN>"
|
|
|
1 |
AUTH_REPLICATE_API_TOKEN="<USE YOUR OWN>"
|
2 |
AUTH_HOTSHOT_XL_API_GRADIO_ACCESS_TOKEN="<USE YOUR OWN>"
|
3 |
+
AUTH_INTERPOLATION_API_GRADIO_TOKEN="<USE YOUR OWN>"
|
4 |
|
5 |
+
# TODO: support multiple backends
|
6 |
# VIDEO_HOTSHOT_XL_API_OFFICIAL
|
|
|
7 |
# VIDEO_HOTSHOT_XL_API_GRADIO
|
8 |
# VIDEO_HOTSHOT_XL_API_REPLICATE
|
9 |
VIDEO_ENGINE="VIDEO_HOTSHOT_XL_API_GRADIO"
|
10 |
|
11 |
+
# TODO: support multiple backends
|
12 |
+
# STMFNET_REPLICATE
|
13 |
+
# FILM_GRADIO
|
14 |
+
INTERPOLATION_ENGINE="STMFNET_REPLICATE"
|
15 |
+
|
16 |
# the official API developed by the Hotshot-XL team
|
17 |
# note: it isn't released yet
|
18 |
VIDEO_HOTSHOT_XL_API_OFFICIAL=""
|
|
|
30 |
VIDEO_HOTSHOT_XL_API_REPLICATE_MODEL="cloneofsimo/hotshot-xl-lora-controlnet"
|
31 |
VIDEO_HOTSHOT_XL_API_REPLICATE_MODEL_VERSION="75e26ffd033a59a78954a3d675632f47f7f8470402aec51c255b9f9b7b62568b"
|
32 |
|
33 |
+
# ----------- INTERPOLATION ------
|
34 |
+
|
35 |
INTERPOLATION_API_REPLICATE_MODEL="zsxkib/st-mfnet"
|
36 |
INTERPOLATION_API_REPLICATE_MODEL_VERSION="faa7693430b0a4ac95d1b8e25165673c1d7a7263537a7c4bb9be82a3e2d130fb"
|
37 |
|
38 |
+
INTERPOLATION_API_GRADIO_URL="https://jbilcke-hf-video-interpolation-server.hf.space"
|
39 |
+
|
40 |
# ----------- RATE LIMIT -------
|
41 |
ENABLE_RATE_LIMIT=""
|
42 |
UPSTASH_REDIS_REST_URL="<USE YOUR OWN>"
|
package-lock.json
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
public/images/models/sdxl-akira.jpg
ADDED
public/images/models/sdxl-starfield.jpg
ADDED
public/images/sign-in-with-huggingface-xl.svg
ADDED
src/app/interface/auth-dialog/index.tsx
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export function AuthDialog() {
|
2 |
+
return (
|
3 |
+
<>
|
4 |
+
</>
|
5 |
+
)
|
6 |
+
}
|
src/app/interface/generate/index.tsx
CHANGED
@@ -9,12 +9,13 @@ import { cn } from "@/lib/utils"
|
|
9 |
import { headingFont } from "@/app/interface/fonts"
|
10 |
import { useCharacterLimit } from "@/lib/useCharacterLimit"
|
11 |
import { generateAnimation } from "@/app/server/actions/animation"
|
|
|
12 |
import { getLatestPosts, getPost, postToCommunity } from "@/app/server/actions/community"
|
13 |
import { getSDXLModels } from "@/app/server/actions/models"
|
14 |
import { HotshotImageInferenceSize, Post, QualityLevel, QualityOption, SDXLModel } from "@/types"
|
15 |
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
|
16 |
import { TooltipProvider } from "@radix-ui/react-tooltip"
|
17 |
-
|
18 |
import { isRateLimitError } from "@/app/server/utils/isRateLimitError"
|
19 |
import { useCountdown } from "@/lib/useCountdown"
|
20 |
|
@@ -212,7 +213,7 @@ export function Generate() {
|
|
212 |
setRuns(runsRef.current + 1)
|
213 |
|
214 |
try {
|
215 |
-
assetUrl = await
|
216 |
|
217 |
if (!assetUrl) {
|
218 |
throw new Error("invalid interpolated asset url")
|
|
|
9 |
import { headingFont } from "@/app/interface/fonts"
|
10 |
import { useCharacterLimit } from "@/lib/useCharacterLimit"
|
11 |
import { generateAnimation } from "@/app/server/actions/animation"
|
12 |
+
import { interpolateVideo } from "@/app/server/actions/interpolation"
|
13 |
import { getLatestPosts, getPost, postToCommunity } from "@/app/server/actions/community"
|
14 |
import { getSDXLModels } from "@/app/server/actions/models"
|
15 |
import { HotshotImageInferenceSize, Post, QualityLevel, QualityOption, SDXLModel } from "@/types"
|
16 |
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
|
17 |
import { TooltipProvider } from "@radix-ui/react-tooltip"
|
18 |
+
|
19 |
import { isRateLimitError } from "@/app/server/utils/isRateLimitError"
|
20 |
import { useCountdown } from "@/lib/useCountdown"
|
21 |
|
|
|
213 |
setRuns(runsRef.current + 1)
|
214 |
|
215 |
try {
|
216 |
+
assetUrl = await interpolateVideo(rawAssetUrl)
|
217 |
|
218 |
if (!assetUrl) {
|
219 |
throw new Error("invalid interpolated asset url")
|
src/app/server/actions/animation.ts
CHANGED
@@ -5,8 +5,8 @@ import {Redis} from "@upstash/redis"
|
|
5 |
|
6 |
import { VideoOptions } from "@/types"
|
7 |
|
8 |
-
import {
|
9 |
-
import {
|
10 |
import { filterOutBadWords } from "./censorship"
|
11 |
|
12 |
const videoEngine = `${process.env.VIDEO_ENGINE || ""}`
|
@@ -102,7 +102,7 @@ export async function generateAnimation({
|
|
102 |
try {
|
103 |
|
104 |
if (videoEngine === "VIDEO_HOTSHOT_XL_API_REPLICATE") {
|
105 |
-
return
|
106 |
positivePrompt,
|
107 |
negativePrompt,
|
108 |
size,
|
@@ -144,7 +144,7 @@ export async function generateAnimation({
|
|
144 |
|
145 |
return content
|
146 |
} else if (videoEngine === "VIDEO_HOTSHOT_XL_API_GRADIO") {
|
147 |
-
return
|
148 |
positivePrompt,
|
149 |
negativePrompt,
|
150 |
size,
|
|
|
5 |
|
6 |
import { VideoOptions } from "@/types"
|
7 |
|
8 |
+
import { generateGradio } from "./generateGradio"
|
9 |
+
import { generateReplicate } from "./generateReplicate"
|
10 |
import { filterOutBadWords } from "./censorship"
|
11 |
|
12 |
const videoEngine = `${process.env.VIDEO_ENGINE || ""}`
|
|
|
102 |
try {
|
103 |
|
104 |
if (videoEngine === "VIDEO_HOTSHOT_XL_API_REPLICATE") {
|
105 |
+
return generateReplicate({
|
106 |
positivePrompt,
|
107 |
negativePrompt,
|
108 |
size,
|
|
|
144 |
|
145 |
return content
|
146 |
} else if (videoEngine === "VIDEO_HOTSHOT_XL_API_GRADIO") {
|
147 |
+
return generateGradio({
|
148 |
positivePrompt,
|
149 |
negativePrompt,
|
150 |
size,
|
src/app/server/actions/{generateWithGradioApi.ts β generateGradio.ts}
RENAMED
@@ -4,7 +4,7 @@ import { VideoOptions } from "@/types"
|
|
4 |
const gradioApi = `${process.env.VIDEO_HOTSHOT_XL_API_GRADIO || ""}`
|
5 |
const accessToken = `${process.env.AUTH_HOTSHOT_XL_API_GRADIO_ACCESS_TOKEN || ""}`
|
6 |
|
7 |
-
export async function
|
8 |
positivePrompt = "",
|
9 |
negativePrompt = "",
|
10 |
size = "512x512",
|
|
|
4 |
const gradioApi = `${process.env.VIDEO_HOTSHOT_XL_API_GRADIO || ""}`
|
5 |
const accessToken = `${process.env.AUTH_HOTSHOT_XL_API_GRADIO_ACCESS_TOKEN || ""}`
|
6 |
|
7 |
+
export async function generateGradio({
|
8 |
positivePrompt = "",
|
9 |
negativePrompt = "",
|
10 |
size = "512x512",
|
src/app/server/actions/{generateWithReplicateAPI.ts β generateReplicate.ts}
RENAMED
@@ -8,7 +8,7 @@ const replicateToken = `${process.env.AUTH_REPLICATE_API_TOKEN || ""}`
|
|
8 |
const replicateModel = `${process.env.VIDEO_HOTSHOT_XL_API_REPLICATE_MODEL || ""}`
|
9 |
const replicateModelVersion = `${process.env.VIDEO_HOTSHOT_XL_API_REPLICATE_MODEL_VERSION || ""}`
|
10 |
|
11 |
-
export async function
|
12 |
positivePrompt = "",
|
13 |
negativePrompt = "",
|
14 |
size = "512x512",
|
|
|
8 |
const replicateModel = `${process.env.VIDEO_HOTSHOT_XL_API_REPLICATE_MODEL || ""}`
|
9 |
const replicateModelVersion = `${process.env.VIDEO_HOTSHOT_XL_API_REPLICATE_MODEL_VERSION || ""}`
|
10 |
|
11 |
+
export async function generateReplicate({
|
12 |
positivePrompt = "",
|
13 |
negativePrompt = "",
|
14 |
size = "512x512",
|
src/app/server/actions/interpolateGradio.ts
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
const gradioApi = `${process.env.INTERPOLATION_API_GRADIO_URL || ""}`
|
3 |
+
const accessToken = `${process.env.AUTH_INTERPOLATION_API_GRADIO_TOKEN || ""}`
|
4 |
+
|
5 |
+
export async function interpolateGradio(assetUrl: string): Promise<string> {
|
6 |
+
// we need to remove this header perhaps
|
7 |
+
const videoInBase64 = assetUrl.split("data:video/mp4;base64,").pop()
|
8 |
+
|
9 |
+
const interpolationSteps = 2
|
10 |
+
const nbFramesPerSecond = 32
|
11 |
+
|
12 |
+
const res = await fetch(gradioApi + (gradioApi.endsWith("/") ? "" : "/") + "api/predict", {
|
13 |
+
method: "POST",
|
14 |
+
headers: {
|
15 |
+
"Content-Type": "application/json",
|
16 |
+
// Authorization: `Bearer ${token}`,
|
17 |
+
},
|
18 |
+
body: JSON.stringify({
|
19 |
+
fn_index: 0, // <- important!
|
20 |
+
data: [
|
21 |
+
accessToken,
|
22 |
+
videoInBase64,
|
23 |
+
interpolationSteps,
|
24 |
+
nbFramesPerSecond
|
25 |
+
],
|
26 |
+
}),
|
27 |
+
cache: "no-store",
|
28 |
+
// we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
|
29 |
+
// next: { revalidate: 1 }
|
30 |
+
})
|
31 |
+
|
32 |
+
const { data } = await res.json()
|
33 |
+
|
34 |
+
if (res.status !== 200 || !data[0]?.length) {
|
35 |
+
// This will activate the closest `error.js` Error Boundary
|
36 |
+
throw new Error(`Failed to fetch data (status: ${res.status})`)
|
37 |
+
}
|
38 |
+
|
39 |
+
return data[0]
|
40 |
+
}
|
41 |
+
|
src/app/server/actions/{interpolate.ts β interpolateReplicate.ts}
RENAMED
@@ -1,5 +1,3 @@
|
|
1 |
-
"use server"
|
2 |
-
|
3 |
import Replicate from "replicate"
|
4 |
|
5 |
import { sleep } from "@/lib/sleep"
|
@@ -8,7 +6,7 @@ const replicateToken = `${process.env.AUTH_REPLICATE_API_TOKEN || ""}`
|
|
8 |
const replicateModel = `${process.env.INTERPOLATION_API_REPLICATE_MODEL || ""}`
|
9 |
const replicateModelVersion = `${process.env.INTERPOLATION_API_REPLICATE_MODEL_VERSION || ""}`
|
10 |
|
11 |
-
export async function
|
12 |
if (!replicateToken) {
|
13 |
throw new Error(`you need to configure your AUTH_REPLICATE_API_TOKEN in order to use interpolation`)
|
14 |
}
|
|
|
|
|
|
|
1 |
import Replicate from "replicate"
|
2 |
|
3 |
import { sleep } from "@/lib/sleep"
|
|
|
6 |
const replicateModel = `${process.env.INTERPOLATION_API_REPLICATE_MODEL || ""}`
|
7 |
const replicateModelVersion = `${process.env.INTERPOLATION_API_REPLICATE_MODEL_VERSION || ""}`
|
8 |
|
9 |
+
export async function interpolateReplicate(input: string): Promise<string> {
|
10 |
if (!replicateToken) {
|
11 |
throw new Error(`you need to configure your AUTH_REPLICATE_API_TOKEN in order to use interpolation`)
|
12 |
}
|
src/app/server/actions/interpolation.ts
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use server"
|
2 |
+
|
3 |
+
import { interpolateGradio } from "./interpolateGradio"
|
4 |
+
import { interpolateReplicate } from "./interpolateReplicate"
|
5 |
+
|
6 |
+
const interpolationEngine = `${process.env.INTERPOLATION_ENGINE || ""}`
|
7 |
+
|
8 |
+
export async function interpolateVideo(inputVideo: string): Promise<string> {
|
9 |
+
if (!inputVideo?.length) {
|
10 |
+
throw new Error(`missing input video`)
|
11 |
+
}
|
12 |
+
|
13 |
+
try {
|
14 |
+
|
15 |
+
if (interpolationEngine === "STMFNET_REPLICATE") {
|
16 |
+
return interpolateReplicate(inputVideo)
|
17 |
+
} else if (interpolationEngine === "FILM_GRADIO") {
|
18 |
+
return interpolateGradio(inputVideo)
|
19 |
+
} else {
|
20 |
+
throw new Error(`unsupported interpolation engine "${interpolationEngine}"`)
|
21 |
+
}
|
22 |
+
} catch (err) {
|
23 |
+
throw new Error(`failed to interpolate the video ${err}`)
|
24 |
+
}
|
25 |
+
}
|
src/app/server/actions/models.ts
CHANGED
@@ -22,6 +22,26 @@ export async function getSDXLModels(): Promise<SDXLModel[]> {
|
|
22 |
const compatibleModels = content.filter(model => model.is_compatible)
|
23 |
|
24 |
const hardcoded: SDXLModel[] = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
{
|
26 |
"image": "https://jbilcke-hf-ai-clip-factory.hf.space/images/models/sdxl-cyberpunk-2077.jpg",
|
27 |
"title": "sdxl-cyberpunk-2077",
|
|
|
22 |
const compatibleModels = content.filter(model => model.is_compatible)
|
23 |
|
24 |
const hardcoded: SDXLModel[] = [
|
25 |
+
{
|
26 |
+
"image": "https://jbilcke-hf-ai-clip-factory.hf.space/images/models/sdxl-starfield.jpg",
|
27 |
+
"title": "sdxl-starfield",
|
28 |
+
"repo": "jbilcke-hf/sdxl-starfield",
|
29 |
+
"trigger_word": "starfield-style",
|
30 |
+
"weights": "pytorch_lora_weights.safetensors",
|
31 |
+
"is_compatible": true,
|
32 |
+
"likes": 0,
|
33 |
+
"downloads": 0
|
34 |
+
},
|
35 |
+
{
|
36 |
+
"image": "https://jbilcke-hf-ai-clip-factory.hf.space/images/models/sdxl-akira.jpg",
|
37 |
+
"title": "sdxl-akira",
|
38 |
+
"repo": "jbilcke-hf/sdxl-akira",
|
39 |
+
"trigger_word": "akira-style",
|
40 |
+
"weights": "pytorch_lora_weights.safetensors",
|
41 |
+
"is_compatible": true,
|
42 |
+
"likes": 0,
|
43 |
+
"downloads": 0
|
44 |
+
},
|
45 |
{
|
46 |
"image": "https://jbilcke-hf-ai-clip-factory.hf.space/images/models/sdxl-cyberpunk-2077.jpg",
|
47 |
"title": "sdxl-cyberpunk-2077",
|
src/components/ui/dialog.tsx
CHANGED
@@ -11,10 +11,9 @@ const Dialog = DialogPrimitive.Root
|
|
11 |
const DialogTrigger = DialogPrimitive.Trigger
|
12 |
|
13 |
const DialogPortal = ({
|
14 |
-
className,
|
15 |
...props
|
16 |
}: DialogPrimitive.DialogPortalProps) => (
|
17 |
-
<DialogPrimitive.Portal
|
18 |
)
|
19 |
DialogPortal.displayName = DialogPrimitive.Portal.displayName
|
20 |
|
|
|
11 |
const DialogTrigger = DialogPrimitive.Trigger
|
12 |
|
13 |
const DialogPortal = ({
|
|
|
14 |
...props
|
15 |
}: DialogPrimitive.DialogPortalProps) => (
|
16 |
+
<DialogPrimitive.Portal {...props} />
|
17 |
)
|
18 |
DialogPortal.displayName = DialogPrimitive.Portal.displayName
|
19 |
|