shiveshnavin commited on
Commit
bd2ef17
·
unverified ·
1 Parent(s): 6325d36

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +271 -0
app.py ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Glitch Video Generator — Hugging Face Space
2
+
3
+ This Space wraps your existing `scripts/glitch.py` CLI so users can upload an image **or** pass an image URL, choose a duration and optional parameters, and receive a downloadable video URL (served by Gradio). Built for **FFmpeg/ffprobe v7**.
4
+
5
+ ---
6
+
7
+ ## ✅ Features
8
+
9
+ * Accept **image URL** or **direct image upload** (PNG/JPG/GIF).
10
+ * Required: `duration` (seconds). Optional: all your CLI params `--fps, --base, --glitch2_secs, --wobble_main, --wobble_jitter, --wobble_f1, --wobble_f2, --sigma`.
11
+ * Outputs a hosted **file URL** to the generated video.
12
+ * Accessible via **web UI** and **programmatic API**.
13
+
14
+ ---
15
+
16
+ ## 📂 Repository Layout
17
+
18
+ ```
19
+ .
20
+ ├─ app.py # Gradio UI + API wrapper around scripts/glitch.py
21
+ ├─ scripts/
22
+ │ └─ glitch.py # Your existing script (copied as-is)
23
+ ├─ requirements.txt # Python deps (Gradio, Pillow, glitch-this, requests)
24
+ ├─ Dockerfile # Ensures FFmpeg/ffprobe v7 is present
25
+ └─ README.md # (optional) reuse this document
26
+ ```
27
+
28
+ > **Note:** Replace `scripts/glitch.py` contents with your real script; this Space just calls it.
29
+
30
+ ---
31
+
32
+ ## 🐳 Dockerfile (FFmpeg/ffprobe v7)
33
+
34
+ ```dockerfile
35
+ FROM python:3.10-slim
36
+
37
+ ENV DEBIAN_FRONTEND=noninteractive \
38
+ PIP_NO_CACHE_DIR=1 \
39
+ PYTHONDONTWRITEBYTECODE=1 \
40
+ PYTHONUNBUFFERED=1
41
+
42
+ RUN apt-get update && apt-get install -y --no-install-recommends \
43
+ wget xz-utils ca-certificates && \
44
+ rm -rf /var/lib/apt/lists/*
45
+
46
+ RUN wget -O /tmp/ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz && \
47
+ mkdir -p /opt/ffmpeg && \
48
+ tar -xJf /tmp/ffmpeg.tar.xz -C /opt/ffmpeg --strip-components=1 && \
49
+ ln -s /opt/ffmpeg/ffmpeg /usr/local/bin/ffmpeg && \
50
+ ln -s /opt/ffmpeg/ffprobe /usr/local/bin/ffprobe && \
51
+ ffmpeg -version && ffprobe -version
52
+
53
+ WORKDIR /app
54
+ COPY requirements.txt ./
55
+ RUN pip install -r requirements.txt
56
+
57
+ COPY scripts ./scripts
58
+ COPY app.py ./app.py
59
+
60
+ EXPOSE 7860
61
+ CMD ["python", "app.py"]
62
+ ```
63
+
64
+ ---
65
+
66
+ ## 📦 requirements.txt
67
+
68
+ ```txt
69
+ gradio>=4.44.0
70
+ pillow>=9.5.0
71
+ glitch-this>=1.0.3
72
+ requests>=2.31.0
73
+ ```
74
+
75
+ ---
76
+
77
+ ## 🐍 app.py (Gradio UI + API)
78
+
79
+ ````python
80
+ import os
81
+ import tempfile
82
+ import subprocess
83
+ import shlex
84
+ from pathlib import Path
85
+ from typing import Optional
86
+
87
+ import gradio as gr
88
+ import requests
89
+ from PIL import Image
90
+
91
+ GLITCH_SCRIPT = Path("scripts/glitch.py").resolve()
92
+ assert GLITCH_SCRIPT.exists(), f"glitch.py not found at {GLITCH_SCRIPT}"
93
+
94
+ def _download_image(url: str, dst_dir: Path) -> Path:
95
+ r = requests.get(url, timeout=20)
96
+ r.raise_for_status()
97
+ ctype = r.headers.get("content-type", "").lower()
98
+ ext = ".jpg"
99
+ if "png" in ctype:
100
+ ext = ".png"
101
+ elif "jpeg" in ctype:
102
+ ext = ".jpg"
103
+ elif "gif" in ctype:
104
+ ext = ".gif"
105
+ img_path = dst_dir / f"input{ext}"
106
+ with open(img_path, "wb") as f:
107
+ f.write(r.content)
108
+ return img_path
109
+
110
+
111
+ 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]):
112
+ if not duration or duration <= 0:
113
+ raise gr.Error("Duration must be > 0 seconds")
114
+
115
+ with tempfile.TemporaryDirectory() as td:
116
+ tdir = Path(td)
117
+ src_path: Optional[Path] = None
118
+ if image_file is not None:
119
+ src_path = Path(image_file)
120
+ elif image_url and image_url.strip():
121
+ src_path = _download_image(image_url.strip(), tdir)
122
+ else:
123
+ raise gr.Error("Provide either an image URL or upload a file")
124
+
125
+ out_path = tdir / "glitched.mp4"
126
+
127
+ cmd = ["python", str(GLITCH_SCRIPT), str(src_path), str(duration), "--out", str(out_path)]
128
+ if fps is not None: cmd += ["--fps", str(fps)]
129
+ if base is not None: cmd += ["--base", str(base)]
130
+ if glitch2_secs is not None: cmd += ["--glitch2_secs", str(glitch2_secs)]
131
+ if wobble_main is not None: cmd += ["--wobble_main", str(wobble_main)]
132
+ if wobble_jitter is not None: cmd += ["--wobble_jitter", str(wobble_jitter)]
133
+ if wobble_f1 is not None: cmd += ["--wobble_f1", str(wobble_f1)]
134
+ if wobble_f2 is not None: cmd += ["--wobble_f2", str(wobble_f2)]
135
+ if sigma is not None: cmd += ["--sigma", str(sigma)]
136
+
137
+ subprocess.run(cmd, check=True)
138
+ if not out_path.exists():
139
+ raise gr.Error("Output file not produced")
140
+ return str(out_path)
141
+
142
+
143
+ def build_ui():
144
+ with gr.Blocks(title="Glitch Video Generator") as demo:
145
+ with gr.Row():
146
+ image_url = gr.Textbox(label="Image URL")
147
+ image_file = gr.Image(label="Upload Image", type="filepath")
148
+ duration = gr.Number(label="Duration (seconds)", value=5, precision=2)
149
+ with gr.Accordion("Optional Parameters", open=False):
150
+ fps = gr.Slider(1, 120, value=30, step=1, label="fps")
151
+ base = gr.Slider(1, 100, value=20, step=1, label="base")
152
+ glitch2_secs = gr.Number(value=0.0, precision=2, label="glitch2_secs")
153
+ wobble_main = gr.Number(value=0.0, precision=2, label="wobble_main")
154
+ wobble_jitter = gr.Number(value=0.0, precision=2, label="wobble_jitter")
155
+ wobble_f1 = gr.Number(value=0.0, precision=2, label="wobble_f1")
156
+ wobble_f2 = gr.Number(value=0.0, precision=2, label="wobble_f2")
157
+ sigma = gr.Number(value=0.0, precision=2, label="sigma")
158
+
159
+ run_btn = gr.Button("Generate")
160
+ output_file = gr.File(label="Output video")
161
+ url_box = gr.Textbox(label="Output URL", interactive=False)
162
+
163
+ def _wrap(*args):
164
+ path = run_glitch(*args)
165
+ return path, path
166
+
167
+ 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])
168
+
169
+ gr.Markdown("""
170
+ ## API Usage
171
+ Programmatic access:
172
+ ```bash
173
+ curl -X POST -H "Content-Type: application/json" \\
174
+ -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]}' \\
175
+ https://<your-space>.hf.space/run/predict
176
+ ```
177
+ The response includes the hosted file URL.
178
+ """)
179
+
180
+ return demo
181
+
182
+ demo = build_ui()
183
+
184
+ if __name__ == "__main__":
185
+ demo.launch(server_name="0.0.0.0", server_port=7860)
186
+ ````
187
+
188
+ ---
189
+
190
+ ## 📝 README.md
191
+
192
+ ````md
193
+ # Glitch Video Generator (Hugging Face Space)
194
+
195
+ Accessible both via **UI** and **API**. Wraps `scripts/glitch.py` to convert an image (URL or upload) into a glitched video.
196
+
197
+ ### Local run
198
+ ```bash
199
+ python -m venv .venv && source .venv/bin/activate
200
+ pip install -r requirements.txt
201
+ python app.py
202
+ ````
203
+
204
+ ### Deploy to Spaces
205
+
206
+ Choose **Docker** SDK, push repo. The app provides UI and API endpoints automatically.
207
+
208
+ ```
209
+
210
+
211
+ ---
212
+
213
+ ## 🔌 API Access (UI + Programmatic)
214
+
215
+ This Space is accessible via the Gradio UI **and** a JSON/multipart API. The model function is exposed at `/run/predict` and the payload order matches the UI inputs:
216
+
217
+ ```
218
+
219
+ \[ image\_url\:str | null,
220
+ image\_file\:path | "file" | null,
221
+ duration\:number,
222
+ fps\:number,
223
+ base\:number,
224
+ glitch2\_secs\:number,
225
+ wobble\_main\:number,
226
+ wobble\_jitter\:number,
227
+ wobble\_f1\:number,
228
+ wobble\_f2\:number,
229
+ sigma\:number ]
230
+
231
+ ````
232
+
233
+ ### JSON request (using image URL)
234
+ ```bash
235
+ curl -X POST \
236
+ -H "Content-Type: application/json" \
237
+ -d '{
238
+ "data": [
239
+ "https://picsum.photos/seed/abc/800/600", # image_url
240
+ null, # image_file (null when using URL)
241
+ 5, # duration
242
+ 30, 20, 0, 0, 0, 0, 0, 0 # fps, base, glitch2_secs, wobble_main, wobble_jitter, wobble_f1, wobble_f2, sigma
243
+ ]
244
+ }' \
245
+ https://<your-username>-glitch-video.hf.space/run/predict
246
+ ````
247
+
248
+ Response:
249
+
250
+ ```json
251
+ {"data": ["https://<space-hosted-file-url>/file=glitched.mp4"]}
252
+ ```
253
+
254
+ ### Multipart request (file upload)
255
+
256
+ ```bash
257
+ curl -X POST \
258
+ -F "data=@-;type=application/json" \
259
+ -F "files[]=@/path/to/local_image.jpg" \
260
+ https://<your-username>-glitch-video.hf.space/run/predict <<'JSON'
261
+ {"data": [null, "file", 5, 30, 20, 0, 0, 0, 0, 0, 0]}
262
+ JSON
263
+ ```
264
+
265
+ Notes:
266
+
267
+ * Set **second** element to the literal string `"file"` when uploading via `files[]`.
268
+ * The endpoint returns a hosted URL (same one shown in the UI) which you can store or forward.
269
+ * CORS is allowed by default on Spaces; if needed you can proxy requests via your backend.
270
+
271
+ If you want a custom path like `/api/glitch`, we can swap Gradio for a **FastAPI** app serving the same UI and an OpenAPI schema—say the word and I’ll provide that variant.