Skip to content

Commit f84ee2d

Browse files
Image to Video Example (#1088)
* Adding image-to-video example * Adding example video * Updating web_endpoint -> fastapi_endpoint * Updating autoscale params * Adding defaults to CLI (for testing) * removing prompt from frontmatter * Failing CI because of an extra line: ruff * minor text fixes --------- Co-authored-by: Charles Frye <[email protected]>
1 parent 0f55fae commit f84ee2d

File tree

2 files changed

+476
-0
lines changed

2 files changed

+476
-0
lines changed

Diff for: 06_gpu_and_ml/image-to-video/frontend/index.html

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<html>
2+
<head>
3+
<script
4+
defer
5+
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"
6+
></script>
7+
<script src="https://cdn.tailwindcss.com"></script>
8+
<meta name="viewport" content="width=device-width, initial-scale=1" />
9+
<title>{{ model_name }} — Modal</title>
10+
</head>
11+
<body x-data="state()">
12+
<div class="max-w-3xl mx-auto pt-4 pb-8 px-10 sm:py-12 sm:px-6 lg:px-8">
13+
<h2 class="text-3xl font-medium text-center mb-10">
14+
{{ model_name }} on Modal
15+
</h2>
16+
17+
<form
18+
@submit.prevent="submitPrompt"
19+
class="flex flex-col items-center justify-center gap-x-4 gap-y-2 w-full mx-auto mb-4"
20+
>
21+
<textarea
22+
x-data
23+
x-model="prompt"
24+
x-init="$nextTick(() => { $el.focus(); });"
25+
rows="2"
26+
class="w-full px-3 py-3 mb-3 text-md bg-white border rounded-md border-neutral-300 ring-offset-background placeholder:text-neutral-500 focus:border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-400 disabled:cursor-not-allowed disabled:opacity-50 text-center"
27+
></textarea>
28+
<div class="flex w-full justify-between">
29+
<input
30+
type="file"
31+
accept="image/*"
32+
@change="previewImage"
33+
@click="$event.target.value = null;"
34+
class="text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-neutral-950 file:text-white hover:file:bg-neutral-900"
35+
/>
36+
<button
37+
type="submit"
38+
class="px-4 py-2 text-sm font-semibold tracking-wide text-white transition-colors duration-200 rounded-md bg-neutral-950 hover:bg-neutral-900 focus:ring-2 focus:ring-offset-2 focus:ring-neutral-900 focus:shadow-outline focus:outline-none"
39+
:disabled="loading"
40+
>
41+
<span x-show="!loading">Submit</span>
42+
<div class="animate-spin w-6 h-6 mx-3" x-show="loading">
43+
<svg
44+
xmlns="http://www.w3.org/2000/svg"
45+
viewBox="0 0 24 24"
46+
fill="none"
47+
stroke="currentColor"
48+
stroke-width="2"
49+
stroke-linecap="round"
50+
stroke-linejoin="round"
51+
class="lucide lucide-loader-2"
52+
>
53+
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
54+
</svg>
55+
</div>
56+
</button>
57+
</div>
58+
</form>
59+
60+
<div class="mx-auto w-full max-w-[768px] relative grid">
61+
<div
62+
style="padding-top: 100%"
63+
x-show="loading"
64+
class="absolute w-full h-full animate-pulse bg-neutral-100 rounded-md"
65+
></div>
66+
<img
67+
x-show="imageURL && !videoURL"
68+
class="rounded-md self-center justify-self-center"
69+
:src="imageURL"
70+
/>
71+
<video
72+
x-bind:src="videoURL"
73+
x-show="videoURL"
74+
controls
75+
class="w-full rounded-md"
76+
></video>
77+
</div>
78+
</div>
79+
80+
<script>
81+
function state() {
82+
return {
83+
prompt: "{{ default_prompt }}",
84+
submitted: "",
85+
loading: false,
86+
imageURL: "",
87+
videoURL: "",
88+
selectedFile: null,
89+
previewImage(event) {
90+
const file = event.target.files[0];
91+
if (file) {
92+
this.selectedFile = file;
93+
this.imageURL = URL.createObjectURL(file);
94+
this.videoURL = "";
95+
}
96+
},
97+
async submitPrompt() {
98+
if (!this.prompt || !this.selectedFile) return;
99+
this.submitted = this.prompt;
100+
this.loading = true;
101+
102+
try {
103+
const formData = new FormData();
104+
formData.append("image_bytes", this.selectedFile);
105+
106+
url = `{{ inference_url }}?prompt=${this.prompt}`;
107+
const res = await fetch(url, {
108+
method: "POST",
109+
headers: {
110+
accept: "application/json",
111+
},
112+
body: formData,
113+
});
114+
115+
if (!res.ok) {
116+
throw new Error("Inference failed");
117+
}
118+
119+
const blob = await res.blob();
120+
this.videoURL = URL.createObjectURL(blob);
121+
this.imageURL = "";
122+
} catch (error) {
123+
console.error("Fetch failed:", error);
124+
alert("There was an error generating the video.");
125+
} finally {
126+
this.loading = false;
127+
}
128+
},
129+
};
130+
}
131+
</script>
132+
</body>
133+
</html>

0 commit comments

Comments
 (0)