Spaces:
Running
Running
add cloudflare upload and base64 for video output response to gradio
Browse files- README.md +80 -0
- app.py +209 -38
- example_usage.py +186 -0
README.md
CHANGED
@@ -11,3 +11,83 @@ license: apache-2.0
|
|
11 |
---
|
12 |
|
13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
---
|
12 |
|
13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
14 |
+
|
15 |
+
# AI-SL API
|
16 |
+
|
17 |
+
Convert text documents to American Sign Language (ASL) videos using AI.
|
18 |
+
|
19 |
+
## Video Output Options
|
20 |
+
|
21 |
+
The Gradio interface provides multiple ways for users to receive and download the generated ASL videos:
|
22 |
+
|
23 |
+
### 1. R2 Cloud Storage (Recommended)
|
24 |
+
- Videos are automatically uploaded to Cloudflare R2 storage
|
25 |
+
- Returns a public URL that users can download directly
|
26 |
+
- Videos persist and can be shared via URL
|
27 |
+
- Includes a styled download button in the interface
|
28 |
+
|
29 |
+
### 2. Base64 Encoding (Alternative)
|
30 |
+
- Videos are embedded as base64 data directly in the response
|
31 |
+
- No external storage required
|
32 |
+
- Good for smaller videos or when you want to avoid cloud storage
|
33 |
+
- Can be downloaded directly from the interface
|
34 |
+
|
35 |
+
### 3. Programmatic Access
|
36 |
+
Users can access the video output programmatically using:
|
37 |
+
|
38 |
+
```python
|
39 |
+
from gradio_client import Client
|
40 |
+
|
41 |
+
# Connect to the running interface
|
42 |
+
client = Client("http://localhost:7860")
|
43 |
+
|
44 |
+
# Upload a document and get results
|
45 |
+
result = client.predict(
|
46 |
+
"path/to/document.pdf",
|
47 |
+
api_name="/predict"
|
48 |
+
)
|
49 |
+
|
50 |
+
# The result contains: (json_data, video_output, download_html)
|
51 |
+
json_data, video_url, download_html = result
|
52 |
+
|
53 |
+
# Download the video
|
54 |
+
import requests
|
55 |
+
response = requests.get(video_url)
|
56 |
+
with open("asl_video.mp4", "wb") as f:
|
57 |
+
f.write(response.content)
|
58 |
+
```
|
59 |
+
|
60 |
+
### 4. Direct Download from Interface
|
61 |
+
- The interface includes a styled download button
|
62 |
+
- Users can right-click and "Save As" if automatic download doesn't work
|
63 |
+
- Video files are named `asl_video.mp4` by default
|
64 |
+
|
65 |
+
## Example Usage
|
66 |
+
|
67 |
+
See `example_usage.py` for complete examples of how to:
|
68 |
+
- Download videos from URLs
|
69 |
+
- Process base64 video data
|
70 |
+
- Use the interface programmatically
|
71 |
+
- Perform further video processing
|
72 |
+
|
73 |
+
## Requirements
|
74 |
+
|
75 |
+
- Python 3.7+
|
76 |
+
- Required packages listed in `requirements.txt`
|
77 |
+
- Cloudflare R2 credentials (for cloud storage option)
|
78 |
+
- Supabase credentials for video database
|
79 |
+
|
80 |
+
## Setup
|
81 |
+
|
82 |
+
1. Install dependencies: `pip install -r requirements.txt`
|
83 |
+
2. Set up environment variables in `.env` file
|
84 |
+
3. Run the interface: `python app.py`
|
85 |
+
|
86 |
+
## Video Processing
|
87 |
+
|
88 |
+
Once you have the video file, you can:
|
89 |
+
- Upload to YouTube, Google Drive, or other services
|
90 |
+
- Analyze with OpenCV for computer vision tasks
|
91 |
+
- Convert to different formats
|
92 |
+
- Extract frames for further processing
|
93 |
+
- Add subtitles or overlays
|
app.py
CHANGED
@@ -10,6 +10,8 @@ from botocore.config import Config
|
|
10 |
from dotenv import load_dotenv
|
11 |
import requests
|
12 |
import tempfile
|
|
|
|
|
13 |
|
14 |
# Load environment variables from .env file
|
15 |
load_dotenv()
|
@@ -30,7 +32,8 @@ article = ("<p style='text-align: center'><a href='https://github.com/deenasun'
|
|
30 |
inputs = gr.File(label="Upload Document (pdf, txt, docx, or epub)")
|
31 |
outputs = [
|
32 |
gr.JSON(label="Processing Results"),
|
33 |
-
gr.Video(label="ASL Video Output")
|
|
|
34 |
]
|
35 |
|
36 |
asl_converter = DocumentToASLConverter()
|
@@ -57,6 +60,88 @@ def clean_gloss_token(token):
|
|
57 |
cleaned = cleaned.lower()
|
58 |
return cleaned
|
59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
async def parse_vectorize_and_search(file):
|
61 |
print(file)
|
62 |
gloss = asl_converter.convert_document(file)
|
@@ -109,61 +194,147 @@ async def parse_vectorize_and_search(file):
|
|
109 |
# If only one video, just use it directly
|
110 |
stitched_video_path = video_files[0]
|
111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
# Clean up individual video files after stitching
|
113 |
for video_file in video_files:
|
114 |
if video_file != stitched_video_path: # Don't delete the final output
|
115 |
cleanup_temp_video(video_file)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
|
117 |
return {
|
118 |
"status": "success",
|
119 |
"videos": videos,
|
120 |
"video_count": len(videos),
|
121 |
"gloss": gloss,
|
122 |
-
"cleaned_tokens": cleaned_tokens
|
123 |
-
|
|
|
124 |
|
125 |
# Create a synchronous wrapper for Gradio
|
126 |
def parse_vectorize_and_search_sync(file):
|
127 |
return asyncio.run(parse_vectorize_and_search(file))
|
128 |
|
129 |
-
def download_video_from_url(video_url):
|
130 |
-
"""
|
131 |
-
Download a video from a public R2 URL
|
132 |
-
Returns the local file path where the video is saved
|
133 |
-
"""
|
134 |
-
try:
|
135 |
-
# Create a temporary file with .mp4 extension
|
136 |
-
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
|
137 |
-
temp_path = temp_file.name
|
138 |
-
temp_file.close()
|
139 |
-
|
140 |
-
# Download the video
|
141 |
-
print(f"Downloading video from: {video_url}")
|
142 |
-
response = requests.get(video_url, stream=True)
|
143 |
-
response.raise_for_status()
|
144 |
-
|
145 |
-
# Save to temporary file
|
146 |
-
with open(temp_path, 'wb') as f:
|
147 |
-
for chunk in response.iter_content(chunk_size=8192):
|
148 |
-
f.write(chunk)
|
149 |
-
|
150 |
-
print(f"Video downloaded to: {temp_path}")
|
151 |
-
return temp_path
|
152 |
-
|
153 |
-
except Exception as e:
|
154 |
-
print(f"Error downloading video: {e}")
|
155 |
-
return None
|
156 |
|
157 |
-
def
|
158 |
"""
|
159 |
-
|
160 |
"""
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
167 |
|
168 |
intf = gr.Interface(
|
169 |
fn=parse_vectorize_and_search_sync,
|
|
|
10 |
from dotenv import load_dotenv
|
11 |
import requests
|
12 |
import tempfile
|
13 |
+
import uuid
|
14 |
+
import base64
|
15 |
|
16 |
# Load environment variables from .env file
|
17 |
load_dotenv()
|
|
|
32 |
inputs = gr.File(label="Upload Document (pdf, txt, docx, or epub)")
|
33 |
outputs = [
|
34 |
gr.JSON(label="Processing Results"),
|
35 |
+
gr.Video(label="ASL Video Output"),
|
36 |
+
gr.HTML(label="Download Link")
|
37 |
]
|
38 |
|
39 |
asl_converter = DocumentToASLConverter()
|
|
|
60 |
cleaned = cleaned.lower()
|
61 |
return cleaned
|
62 |
|
63 |
+
|
64 |
+
def upload_video_to_r2(video_path, bucket_name="ai-sl-videos"):
|
65 |
+
"""
|
66 |
+
Upload a video file to R2 and return a public URL
|
67 |
+
"""
|
68 |
+
try:
|
69 |
+
# Generate a unique filename
|
70 |
+
file_extension = os.path.splitext(video_path)[1]
|
71 |
+
unique_filename = f"{uuid.uuid4()}{file_extension}"
|
72 |
+
|
73 |
+
# Upload to R2
|
74 |
+
with open(video_path, 'rb') as video_file:
|
75 |
+
s3.upload_fileobj(
|
76 |
+
video_file,
|
77 |
+
bucket_name,
|
78 |
+
unique_filename,
|
79 |
+
ExtraArgs={'ACL': 'public-read'}
|
80 |
+
)
|
81 |
+
|
82 |
+
# Generate the public URL
|
83 |
+
video_url = f"{R2_ENDPOINT}/{bucket_name}/{unique_filename}"
|
84 |
+
print(f"Video uploaded to R2: {video_url}")
|
85 |
+
return video_url
|
86 |
+
|
87 |
+
except Exception as e:
|
88 |
+
print(f"Error uploading video to R2: {e}")
|
89 |
+
return None
|
90 |
+
|
91 |
+
def video_to_base64(video_path):
|
92 |
+
"""
|
93 |
+
Convert a video file to base64 string for direct download
|
94 |
+
"""
|
95 |
+
try:
|
96 |
+
with open(video_path, 'rb') as video_file:
|
97 |
+
video_data = video_file.read()
|
98 |
+
base64_data = base64.b64encode(video_data).decode('utf-8')
|
99 |
+
return f"data:video/mp4;base64,{base64_data}"
|
100 |
+
except Exception as e:
|
101 |
+
print(f"Error converting video to base64: {e}")
|
102 |
+
return None
|
103 |
+
|
104 |
+
def download_video_from_url(video_url):
|
105 |
+
"""
|
106 |
+
Download a video from a public R2 URL
|
107 |
+
Returns the local file path where the video is saved
|
108 |
+
"""
|
109 |
+
try:
|
110 |
+
# Create a temporary file with .mp4 extension
|
111 |
+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4')
|
112 |
+
temp_path = temp_file.name
|
113 |
+
temp_file.close()
|
114 |
+
|
115 |
+
# Download the video
|
116 |
+
print(f"Downloading video from: {video_url}")
|
117 |
+
response = requests.get(video_url, stream=True)
|
118 |
+
response.raise_for_status()
|
119 |
+
|
120 |
+
# Save to temporary file
|
121 |
+
with open(temp_path, 'wb') as f:
|
122 |
+
for chunk in response.iter_content(chunk_size=8192):
|
123 |
+
f.write(chunk)
|
124 |
+
|
125 |
+
print(f"Video downloaded to: {temp_path}")
|
126 |
+
return temp_path
|
127 |
+
|
128 |
+
except Exception as e:
|
129 |
+
print(f"Error downloading video: {e}")
|
130 |
+
return None
|
131 |
+
|
132 |
+
|
133 |
+
def cleanup_temp_video(file_path):
|
134 |
+
"""
|
135 |
+
Clean up temporary video file
|
136 |
+
"""
|
137 |
+
try:
|
138 |
+
if file_path and os.path.exists(file_path):
|
139 |
+
os.unlink(file_path)
|
140 |
+
print(f"Cleaned up: {file_path}")
|
141 |
+
except Exception as e:
|
142 |
+
print(f"Error cleaning up file: {e}")
|
143 |
+
|
144 |
+
|
145 |
async def parse_vectorize_and_search(file):
|
146 |
print(file)
|
147 |
gloss = asl_converter.convert_document(file)
|
|
|
194 |
# If only one video, just use it directly
|
195 |
stitched_video_path = video_files[0]
|
196 |
|
197 |
+
# Upload final video to R2 and get public URL
|
198 |
+
final_video_url = None
|
199 |
+
if stitched_video_path:
|
200 |
+
final_video_url = upload_video_to_r2(stitched_video_path)
|
201 |
+
# Clean up the local file after upload
|
202 |
+
cleanup_temp_video(stitched_video_path)
|
203 |
+
|
204 |
# Clean up individual video files after stitching
|
205 |
for video_file in video_files:
|
206 |
if video_file != stitched_video_path: # Don't delete the final output
|
207 |
cleanup_temp_video(video_file)
|
208 |
+
|
209 |
+
# Create download link HTML
|
210 |
+
download_html = ""
|
211 |
+
if final_video_url:
|
212 |
+
download_html = f"""
|
213 |
+
<div style="text-align: center; padding: 20px;">
|
214 |
+
<h3>Download Your ASL Video</h3>
|
215 |
+
<a href="{final_video_url}" download="asl_video.mp4"
|
216 |
+
style="background-color: #4CAF50; color: white;
|
217 |
+
padding: 12px 24px; text-decoration: none;
|
218 |
+
border-radius: 4px; display: inline-block;">
|
219 |
+
Download Video
|
220 |
+
</a>
|
221 |
+
<p style="margin-top: 10px; color: #666;">
|
222 |
+
<small>Right-click and "Save As" if the download doesn't
|
223 |
+
start automatically</small>
|
224 |
+
</p>
|
225 |
+
</div>
|
226 |
+
"""
|
227 |
|
228 |
return {
|
229 |
"status": "success",
|
230 |
"videos": videos,
|
231 |
"video_count": len(videos),
|
232 |
"gloss": gloss,
|
233 |
+
"cleaned_tokens": cleaned_tokens,
|
234 |
+
"final_video_url": final_video_url
|
235 |
+
}, final_video_url, download_html
|
236 |
|
237 |
# Create a synchronous wrapper for Gradio
|
238 |
def parse_vectorize_and_search_sync(file):
|
239 |
return asyncio.run(parse_vectorize_and_search(file))
|
240 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
|
242 |
+
async def parse_vectorize_and_search_base64(file):
|
243 |
"""
|
244 |
+
Alternative version that returns video as base64 data instead of uploading to R2
|
245 |
"""
|
246 |
+
print(file)
|
247 |
+
gloss = asl_converter.convert_document(file)
|
248 |
+
print("ASL", gloss)
|
249 |
+
|
250 |
+
# Split by spaces and clean each token
|
251 |
+
gloss_tokens = gloss.split()
|
252 |
+
cleaned_tokens = []
|
253 |
+
|
254 |
+
for token in gloss_tokens:
|
255 |
+
cleaned = clean_gloss_token(token)
|
256 |
+
if cleaned: # Only add non-empty tokens
|
257 |
+
cleaned_tokens.append(cleaned)
|
258 |
+
|
259 |
+
print("Cleaned tokens:", cleaned_tokens)
|
260 |
+
|
261 |
+
videos = []
|
262 |
+
video_files = [] # Store local file paths for stitching
|
263 |
+
|
264 |
+
for g in cleaned_tokens:
|
265 |
+
print(f"Processing {g}")
|
266 |
+
try:
|
267 |
+
result = await vectorizer.vector_query_from_supabase(query=g)
|
268 |
+
print("result", result)
|
269 |
+
if result.get("match", False):
|
270 |
+
video_url = result["video_url"]
|
271 |
+
videos.append(video_url)
|
272 |
+
|
273 |
+
# Download the video
|
274 |
+
local_path = download_video_from_url(video_url)
|
275 |
+
if local_path:
|
276 |
+
video_files.append(local_path)
|
277 |
+
|
278 |
+
except Exception as e:
|
279 |
+
print(f"Error processing {g}: {e}")
|
280 |
+
continue
|
281 |
+
|
282 |
+
# Create stitched video if we have multiple videos
|
283 |
+
stitched_video_path = None
|
284 |
+
if len(video_files) > 1:
|
285 |
+
try:
|
286 |
+
print(f"Creating stitched video from {len(video_files)} videos...")
|
287 |
+
stitched_video_path = tempfile.NamedTemporaryFile(delete=False, suffix='.mp4').name
|
288 |
+
create_multi_stitched_video(video_files, stitched_video_path)
|
289 |
+
print(f"Stitched video created: {stitched_video_path}")
|
290 |
+
except Exception as e:
|
291 |
+
print(f"Error creating stitched video: {e}")
|
292 |
+
stitched_video_path = None
|
293 |
+
elif len(video_files) == 1:
|
294 |
+
# If only one video, just use it directly
|
295 |
+
stitched_video_path = video_files[0]
|
296 |
+
|
297 |
+
# Convert final video to base64
|
298 |
+
final_video_base64 = None
|
299 |
+
if stitched_video_path:
|
300 |
+
final_video_base64 = video_to_base64(stitched_video_path)
|
301 |
+
# Clean up the local file after conversion
|
302 |
+
cleanup_temp_video(stitched_video_path)
|
303 |
+
|
304 |
+
# Clean up individual video files after stitching
|
305 |
+
for video_file in video_files:
|
306 |
+
if video_file != stitched_video_path: # Don't delete the final output
|
307 |
+
cleanup_temp_video(video_file)
|
308 |
+
|
309 |
+
# Create download link HTML for base64
|
310 |
+
download_html = ""
|
311 |
+
if final_video_base64:
|
312 |
+
download_html = f"""
|
313 |
+
<div style="text-align: center; padding: 20px;">
|
314 |
+
<h3>Download Your ASL Video</h3>
|
315 |
+
<a href="{final_video_base64}" download="asl_video.mp4"
|
316 |
+
style="background-color: #4CAF50; color: white;
|
317 |
+
padding: 12px 24px; text-decoration: none;
|
318 |
+
border-radius: 4px; display: inline-block;">
|
319 |
+
Download Video
|
320 |
+
</a>
|
321 |
+
<p style="margin-top: 10px; color: #666;">
|
322 |
+
<small>Video is embedded directly - click to download</small>
|
323 |
+
</p>
|
324 |
+
</div>
|
325 |
+
"""
|
326 |
+
|
327 |
+
return {
|
328 |
+
"status": "success",
|
329 |
+
"videos": videos,
|
330 |
+
"video_count": len(videos),
|
331 |
+
"gloss": gloss,
|
332 |
+
"cleaned_tokens": cleaned_tokens,
|
333 |
+
"video_format": "base64"
|
334 |
+
}, final_video_base64, download_html
|
335 |
+
|
336 |
+
def parse_vectorize_and_search_base64_sync(file):
|
337 |
+
return asyncio.run(parse_vectorize_and_search_base64(file))
|
338 |
|
339 |
intf = gr.Interface(
|
340 |
fn=parse_vectorize_and_search_sync,
|
example_usage.py
ADDED
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Example: How to programmatically access video output from the AI-SL API
|
3 |
+
|
4 |
+
This file demonstrates different ways users can receive and process video
|
5 |
+
output from the Gradio interface.
|
6 |
+
"""
|
7 |
+
|
8 |
+
import requests
|
9 |
+
import base64
|
10 |
+
from pathlib import Path
|
11 |
+
|
12 |
+
|
13 |
+
def download_video_from_url(video_url, output_path="downloaded_video.mp4"):
|
14 |
+
"""
|
15 |
+
Download a video from a public URL
|
16 |
+
"""
|
17 |
+
try:
|
18 |
+
response = requests.get(video_url, stream=True)
|
19 |
+
response.raise_for_status()
|
20 |
+
|
21 |
+
with open(output_path, 'wb') as f:
|
22 |
+
for chunk in response.iter_content(chunk_size=8192):
|
23 |
+
f.write(chunk)
|
24 |
+
|
25 |
+
print(f"Video downloaded to: {output_path}")
|
26 |
+
return output_path
|
27 |
+
except Exception as e:
|
28 |
+
print(f"Error downloading video: {e}")
|
29 |
+
return None
|
30 |
+
|
31 |
+
|
32 |
+
def save_base64_video(base64_data, output_path="video_from_base64.mp4"):
|
33 |
+
"""
|
34 |
+
Save a base64-encoded video to a file
|
35 |
+
"""
|
36 |
+
try:
|
37 |
+
# Remove the data URL prefix if present
|
38 |
+
if base64_data.startswith('data:video/mp4;base64,'):
|
39 |
+
base64_data = base64_data.split(',')[1]
|
40 |
+
|
41 |
+
# Decode and save
|
42 |
+
video_data = base64.b64decode(base64_data)
|
43 |
+
with open(output_path, 'wb') as f:
|
44 |
+
f.write(video_data)
|
45 |
+
|
46 |
+
print(f"Video saved from base64 to: {output_path}")
|
47 |
+
return output_path
|
48 |
+
except Exception as e:
|
49 |
+
print(f"Error saving base64 video: {e}")
|
50 |
+
return None
|
51 |
+
|
52 |
+
|
53 |
+
def process_gradio_output(gradio_result):
|
54 |
+
"""
|
55 |
+
Process the output from the Gradio interface
|
56 |
+
|
57 |
+
gradio_result should be a tuple: (json_data, video_output, download_html)
|
58 |
+
"""
|
59 |
+
json_data, video_output, download_html = gradio_result
|
60 |
+
|
61 |
+
print("Processing Results:")
|
62 |
+
print(f"Status: {json_data['status']}")
|
63 |
+
print(f"Video Count: {json_data['video_count']}")
|
64 |
+
print(f"Gloss: {json_data['gloss']}")
|
65 |
+
|
66 |
+
# Handle video output based on format
|
67 |
+
if json_data.get('video_format') == 'base64':
|
68 |
+
# Video is base64 encoded
|
69 |
+
print("Video format: Base64")
|
70 |
+
video_path = save_base64_video(video_output, "asl_output.mp4")
|
71 |
+
else:
|
72 |
+
# Video is a URL (from R2 upload)
|
73 |
+
print("Video format: URL")
|
74 |
+
video_path = download_video_from_url(video_output, "asl_output.mp4")
|
75 |
+
|
76 |
+
return video_path
|
77 |
+
|
78 |
+
|
79 |
+
# Example usage scenarios:
|
80 |
+
|
81 |
+
def example_1_direct_download():
|
82 |
+
"""
|
83 |
+
Example 1: Direct download from R2 URL
|
84 |
+
"""
|
85 |
+
print("=== Example 1: Direct Download ===")
|
86 |
+
|
87 |
+
# Simulate getting a video URL from the interface
|
88 |
+
video_url = "https://your-r2-endpoint.com/bucket/video.mp4"
|
89 |
+
|
90 |
+
# Download the video
|
91 |
+
video_path = download_video_from_url(video_url)
|
92 |
+
|
93 |
+
if video_path:
|
94 |
+
print(f"Video ready for processing: {video_path}")
|
95 |
+
# Now you can use the video file for further processing
|
96 |
+
# e.g., upload to another service, analyze with OpenCV, etc.
|
97 |
+
|
98 |
+
|
99 |
+
def example_2_base64_processing():
|
100 |
+
"""
|
101 |
+
Example 2: Processing base64 video data
|
102 |
+
"""
|
103 |
+
print("=== Example 2: Base64 Processing ===")
|
104 |
+
|
105 |
+
# Simulate getting base64 data from the interface
|
106 |
+
base64_video = ("data:video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAA...") # noqa: E501
|
107 |
+
|
108 |
+
# Save the video
|
109 |
+
video_path = save_base64_video(base64_video)
|
110 |
+
|
111 |
+
if video_path:
|
112 |
+
print(f"Video ready for processing: {video_path}")
|
113 |
+
|
114 |
+
|
115 |
+
def example_3_programmatic_interface():
|
116 |
+
"""
|
117 |
+
Example 3: Using the Gradio interface programmatically
|
118 |
+
"""
|
119 |
+
print("=== Example 3: Programmatic Interface ===")
|
120 |
+
|
121 |
+
# If you want to call the Gradio interface programmatically
|
122 |
+
# You can use the gradio_client library
|
123 |
+
|
124 |
+
try:
|
125 |
+
from gradio_client import Client
|
126 |
+
|
127 |
+
# Connect to your running Gradio interface
|
128 |
+
client = Client("http://localhost:7860") # Adjust URL as needed
|
129 |
+
|
130 |
+
# Upload a document and get results
|
131 |
+
result = client.predict(
|
132 |
+
"path/to/your/document.pdf", # File path
|
133 |
+
api_name="/predict" # Adjust based on your interface
|
134 |
+
)
|
135 |
+
|
136 |
+
# Process the results
|
137 |
+
video_path = process_gradio_output(result)
|
138 |
+
print(f"Processed video: {video_path}")
|
139 |
+
|
140 |
+
except ImportError:
|
141 |
+
print("gradio_client not installed. Install with: "
|
142 |
+
"pip install gradio_client")
|
143 |
+
except Exception as e:
|
144 |
+
print(f"Error calling Gradio interface: {e}")
|
145 |
+
|
146 |
+
|
147 |
+
def example_4_video_processing():
|
148 |
+
"""
|
149 |
+
Example 4: Further video processing
|
150 |
+
"""
|
151 |
+
print("=== Example 4: Video Processing ===")
|
152 |
+
|
153 |
+
# Once you have the video file, you can process it further
|
154 |
+
video_path = "asl_output.mp4"
|
155 |
+
|
156 |
+
if Path(video_path).exists():
|
157 |
+
print(f"Processing video: {video_path}")
|
158 |
+
|
159 |
+
# Example: Get video information
|
160 |
+
try:
|
161 |
+
import cv2
|
162 |
+
cap = cv2.VideoCapture(video_path)
|
163 |
+
fps = cap.get(cv2.CAP_PROP_FPS)
|
164 |
+
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
165 |
+
duration = frame_count / fps
|
166 |
+
cap.release()
|
167 |
+
|
168 |
+
print(f"Video info: {duration:.2f} seconds, {fps} FPS, "
|
169 |
+
f"{frame_count} frames")
|
170 |
+
|
171 |
+
except ImportError:
|
172 |
+
print("OpenCV not installed. Install with: "
|
173 |
+
"pip install opencv-python")
|
174 |
+
|
175 |
+
# Example: Upload to another service
|
176 |
+
# upload_to_youtube(video_path)
|
177 |
+
# upload_to_drive(video_path)
|
178 |
+
# etc.
|
179 |
+
|
180 |
+
|
181 |
+
if __name__ == "__main__":
|
182 |
+
# Run examples
|
183 |
+
example_1_direct_download()
|
184 |
+
example_2_base64_processing()
|
185 |
+
example_3_programmatic_interface()
|
186 |
+
example_4_video_processing()
|