shiveshnavin commited on
Commit
e1f5f9a
·
1 Parent(s): c219a4c
Files changed (1) hide show
  1. app.py +211 -84
app.py CHANGED
@@ -1,91 +1,218 @@
1
  import os
2
-
3
-
4
- def run_glitch(image_url: Optional[str], image_file: Optional[Path], duration: float, fps: Optional[int], base: Optional[int], glitch2_secs: Optional[float], wobble_main: Optional[float], wobble_jitter: Optional[float], wobble_f1: Optional[float], wobble_f2: Optional[float], sigma: Optional[float]):
5
- if not duration or duration <= 0:
6
- raise gr.Error("Duration must be > 0 seconds")
7
-
8
-
9
- with tempfile.TemporaryDirectory() as td:
10
- tdir = Path(td)
11
- src_path: Optional[Path] = None
12
- if image_file is not None:
13
- src_path = Path(image_file)
14
- elif image_url and image_url.strip():
15
- src_path = _download_image(image_url.strip(), tdir)
16
- else:
17
- raise gr.Error("Provide either an image URL or upload a file")
18
-
19
-
20
- out_path = tdir / "glitched.mp4"
21
-
22
-
23
- cmd = ["python", str(GLITCH_SCRIPT), str(src_path), str(duration), "--out", str(out_path)]
24
- if fps is not None: cmd += ["--fps", str(fps)]
25
- if base is not None: cmd += ["--base", str(base)]
26
- if glitch2_secs is not None: cmd += ["--glitch2_secs", str(glitch2_secs)]
27
- if wobble_main is not None: cmd += ["--wobble_main", str(wobble_main)]
28
- if wobble_jitter is not None: cmd += ["--wobble_jitter", str(wobble_jitter)]
29
- if wobble_f1 is not None: cmd += ["--wobble_f1", str(wobble_f1)]
30
- if wobble_f2 is not None: cmd += ["--wobble_f2", str(wobble_f2)]
31
- if sigma is not None: cmd += ["--sigma", str(sigma)]
32
-
33
-
34
- subprocess.run(cmd, check=True)
35
- if not out_path.exists():
36
- raise gr.Error("Output file not produced")
37
- return str(out_path)
38
-
39
-
40
-
41
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def build_ui():
43
- with gr.Blocks(title="Glitch Video Generator") as demo:
44
- with gr.Row():
45
- image_url = gr.Textbox(label="Image URL")
46
- image_file = gr.Image(label="Upload Image", type="filepath")
47
- duration = gr.Number(label="Duration (seconds)", value=5, precision=2)
48
- with gr.Accordion("Optional Parameters", open=False):
49
- fps = gr.Slider(1, 120, value=30, step=1, label="fps")
50
- base = gr.Slider(1, 100, value=20, step=1, label="base")
51
- glitch2_secs = gr.Number(value=0.0, precision=2, label="glitch2_secs")
52
- wobble_main = gr.Number(value=0.0, precision=2, label="wobble_main")
53
- wobble_jitter = gr.Number(value=0.0, precision=2, label="wobble_jitter")
54
- wobble_f1 = gr.Number(value=0.0, precision=2, label="wobble_f1")
55
- wobble_f2 = gr.Number(value=0.0, precision=2, label="wobble_f2")
56
- sigma = gr.Number(value=0.0, precision=2, label="sigma")
57
-
58
-
59
- run_btn = gr.Button("Generate")
60
- output_file = gr.File(label="Output video")
61
- url_box = gr.Textbox(label="Output URL", interactive=False)
62
-
63
-
64
- def _wrap(*args):
65
- path = run_glitch(*args)
66
- return path, path
67
-
68
-
69
- run_btn.click(fn=_wrap, inputs=[image_url, image_file, duration, fps, base, glitch2_secs, wobble_main, wobble_jitter, wobble_f1, wobble_f2, sigma], outputs=[output_file, url_box])
70
-
71
-
72
- gr.Markdown("""
73
- ## API Usage
74
- Programmatic access:
75
- ```bash
76
- curl -X POST -H "Content-Type: application/json" \\
77
- -d '{"data": ["https://picsum.photos/seed/abc/800/600", null, 5, 30, 20, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]}' \\
78
- https://<your-space>.hf.space/run/predict
79
- ```
80
- The response includes the hosted file URL.
81
- """)
82
-
83
-
84
- return demo
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
 
87
  demo = build_ui()
88
 
89
-
90
  if __name__ == "__main__":
91
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import os
2
+ import tempfile
3
+ import subprocess
4
+ import shlex
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+ import gradio as gr
9
+ import requests
10
+ from PIL import Image
11
+
12
+ # --- Config ---
13
+ GLITCH_SCRIPT = Path("scripts/glitch.py").resolve()
14
+ assert GLITCH_SCRIPT.exists(), f"glitch.py not found at {GLITCH_SCRIPT}"
15
+
16
+ # Verify ffmpeg/ffprobe presence (v7+ preferred)
17
+ def _check_binaries():
18
+ def ver(cmd):
19
+ try:
20
+ out = subprocess.check_output([cmd, "-version"], text=True)
21
+ return out.splitlines()[0]
22
+ except Exception:
23
+ return "NOT FOUND"
24
+ return ver("ffmpeg"), ver("ffprobe")
25
+
26
+ print("FFmpeg:", _check_binaries()[0])
27
+ print("FFprobe:", _check_binaries()[1])
28
+
29
+ # --- Helpers ---
30
+ def _download_image(url: str, dst_dir: Path) -> Path:
31
+ r = requests.get(url, timeout=20)
32
+ r.raise_for_status()
33
+ # Attempt to infer extension
34
+ ctype = r.headers.get("content-type", "").lower()
35
+ ext = ".jpg"
36
+ if "png" in ctype:
37
+ ext = ".png"
38
+ elif "jpeg" in ctype:
39
+ ext = ".jpg"
40
+ elif "gif" in ctype:
41
+ ext = ".gif"
42
+ img_path = dst_dir / f"input{ext}"
43
+ with open(img_path, "wb") as f:
44
+ f.write(r.content)
45
+ return img_path
46
+
47
+
48
+ def _ensure_image(path: Path) -> Path:
49
+ # Simple open-validate cycle (also normalizes/transcodes weird formats)
50
+ with Image.open(path) as im:
51
+ im.verify() # quick integrity check
52
+ return path
53
+
54
+
55
+ def run_glitch(
56
+ image_url: Optional[str],
57
+ image_file: Optional[Path],
58
+ duration: float,
59
+ fps: Optional[int],
60
+ base: Optional[int],
61
+ glitch2_secs: Optional[float],
62
+ wobble_main: Optional[float],
63
+ wobble_jitter: Optional[float],
64
+ wobble_f1: Optional[float],
65
+ wobble_f2: Optional[float],
66
+ sigma: Optional[float],
67
+ ):
68
+ if not duration or duration <= 0:
69
+ raise gr.Error("Duration must be > 0 seconds")
70
+
71
+ with tempfile.TemporaryDirectory() as td:
72
+ tdir = Path(td)
73
+ # Source image handling
74
+ src_path: Optional[Path] = None
75
+ if image_file is not None:
76
+ src_path = Path(image_file)
77
+ elif image_url and image_url.strip():
78
+ src_path = _download_image(image_url.strip(), tdir)
79
+ else:
80
+ raise gr.Error("Provide either an image URL or upload a file")
81
+
82
+ _ensure_image(src_path)
83
+
84
+ # Output path
85
+ out_path = tdir / "glitched.mp4"
86
+
87
+ # Build CLI
88
+ cmd = [
89
+ "python", str(GLITCH_SCRIPT),
90
+ str(src_path),
91
+ str(duration),
92
+ "--out", str(out_path),
93
+ ]
94
+ if fps is not None:
95
+ cmd += ["--fps", str(fps)]
96
+ if base is not None:
97
+ cmd += ["--base", str(base)]
98
+ if glitch2_secs is not None:
99
+ cmd += ["--glitch2_secs", str(glitch2_secs)]
100
+ if wobble_main is not None:
101
+ cmd += ["--wobble_main", str(wobble_main)]
102
+ if wobble_jitter is not None:
103
+ cmd += ["--wobble_jitter", str(wobble_jitter)]
104
+ if wobble_f1 is not None:
105
+ cmd += ["--wobble_f1", str(wobble_f1)]
106
+ if wobble_f2 is not None:
107
+ cmd += ["--wobble_f2", str(wobble_f2)]
108
+ if sigma is not None:
109
+ cmd += ["--sigma", str(sigma)]
110
+
111
+ # Run
112
+ print("Running:", shlex.join(cmd))
113
+ try:
114
+ subprocess.run(cmd, check=True)
115
+ except subprocess.CalledProcessError as e:
116
+ raise gr.Error(f"glitch.py failed: {e}")
117
+
118
+ if not out_path.exists():
119
+ raise gr.Error("Output file not produced by glitch.py")
120
+
121
+ # Return file path; Gradio will host and provide a URL.
122
+ return str(out_path)
123
+
124
+
125
+ # --- Gradio UI ---
126
  def build_ui():
127
+ with gr.Blocks(title="Glitch Video Generator", analytics_enabled=False) as demo:
128
+ gr.Markdown(
129
+ """
130
+ # 🔧 Glitch Video Generator
131
+ Convert an image into a glitched video. Provide a URL **or** upload an image, set the duration, and tweak optional parameters.
132
+ """
133
+ )
134
+
135
+ with gr.Row():
136
+ image_url = gr.Textbox(label="Image URL", placeholder="https://example.com/pic.jpg")
137
+ image_file = gr.Image(label="Upload Image", type="filepath")
138
+
139
+ duration = gr.Number(label="Duration (seconds)", value=5, precision=2)
140
+ with gr.Accordion("Optional Parameters", open=False):
141
+ fps = gr.Slider(1, 120, value=30, step=1, label="fps (frames per second)")
142
+ base = gr.Slider(1, 100, value=20, step=1, label="base")
143
+ glitch2_secs = gr.Number(value=0.0, precision=2, label="glitch2_secs")
144
+ wobble_main = gr.Number(value=0.0, precision=2, label="wobble_main")
145
+ wobble_jitter = gr.Number(value=0.0, precision=2, label="wobble_jitter")
146
+ wobble_f1 = gr.Number(value=0.0, precision=2, label="wobble_f1")
147
+ wobble_f2 = gr.Number(value=0.0, precision=2, label="wobble_f2")
148
+ sigma = gr.Number(value=0.0, precision=2, label="sigma")
149
+
150
+ run_btn = gr.Button("Generate")
151
+ output_file = gr.File(label="Output video")
152
+ url_box = gr.Textbox(label="Output URL", interactive=False)
153
+
154
+ def _wrap(*args):
155
+ path = run_glitch(*args)
156
+ # Gradio returns a hosted URL when a File component is used. We emit both.
157
+ return path, path
158
+
159
+ run_btn.click(
160
+ fn=_wrap,
161
+ inputs=[
162
+ image_url,
163
+ image_file,
164
+ duration,
165
+ fps,
166
+ base,
167
+ glitch2_secs,
168
+ wobble_main,
169
+ wobble_jitter,
170
+ wobble_f1,
171
+ wobble_f2,
172
+ sigma,
173
+ ],
174
+ outputs=[output_file, url_box],
175
+ )
176
+
177
+ # ---- Lightweight API doc in UI ----
178
+ gr.Markdown(
179
+ """
180
+ ### API Usage
181
+ This Space exposes a **predict API** at `/run/predict`.
182
+
183
+ **JSON (image URL)**
184
+ ```bash
185
+ curl -X POST \
186
+ -H "Content-Type: application/json" \
187
+ -d '{
188
+ "data": [
189
+ "https://picsum.photos/seed/abc/800/600", # image_url
190
+ null, # image_file (null when using URL)
191
+ 5, # duration
192
+ 30, 20, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 # optional params
193
+ ]
194
+ }' \
195
+ https://<your-username>-glitch-video.hf.space/run/predict
196
+ ```
197
+ The response contains `data[0]` → hosted file URL.
198
+
199
+ **Multipart (file upload)**
200
+ ```bash
201
+ curl -X POST \
202
+ -F "data=@-;type=application/json" \
203
+ -F "files[]=@/path/to/local_image.jpg" \
204
+ https://<your-username>-glitch-video.hf.space/run/predict <<'JSON'
205
+ {"data": [null, "file", 5, 30, 20, 0, 0, 0, 0, 0, 0]}
206
+ JSON
207
+ ```
208
+ In multipart mode, set the **second** item to the string `"file"` and attach the image in `files[]`.
209
+ """
210
+ )
211
+
212
+ return demo
213
 
214
 
215
  demo = build_ui()
216
 
 
217
  if __name__ == "__main__":
218
+ demo.launch(server_name="0.0.0.0", server_port=7860)