Spaces:
Sleeping
Sleeping
| # app.py - Odyssey Backend (Gradio) | |
| import os | |
| import uuid | |
| import json | |
| from pathlib import Path | |
| from typing import Tuple | |
| import gradio as gr | |
| from huggingface_hub import HfApi | |
| # ===== Config ===== | |
| REPO_ID = "Fraser/odyssey-videos" # dataset to write to | |
| TOKEN = os.environ.get("HF_TOKEN") # set in Space Secrets | |
| if not TOKEN: | |
| raise RuntimeError("Missing HF_TOKEN secret") | |
| STAGING = Path("/tmp/staging") | |
| STAGING.mkdir(parents=True, exist_ok=True) | |
| api = HfApi(token=TOKEN) | |
| def _public_url(uuid_str: str) -> str: | |
| """Build the public resolve URL for dataset files""" | |
| return f"https://huggingface.co/datasets/{REPO_ID}/resolve/main/{uuid_str}.mp4" | |
| def upload_video(video_file) -> Tuple[str, str]: | |
| """ | |
| Upload a video file to the Fraser/odyssey-videos dataset | |
| Args: | |
| video_file: Video file (mp4) | |
| Returns: | |
| JSON string with: {"uuid": "...", "url": "...", "share": "..."} | |
| """ | |
| if video_file is None: | |
| return json.dumps({"error": "No video file provided"}), "" | |
| try: | |
| # Generate UUID | |
| uid = str(uuid.uuid4()) | |
| # Get the uploaded file path | |
| video_path = Path(video_file) | |
| # Upload to HuggingFace dataset | |
| api.upload_file( | |
| path_or_fileobj=str(video_path), | |
| path_in_repo=f"{uid}.mp4", | |
| repo_id=REPO_ID, | |
| repo_type="dataset", | |
| commit_message=f"Add video {uid}.mp4", | |
| ) | |
| # Build response | |
| url = _public_url(uid) | |
| result = { | |
| "uuid": uid, | |
| "url": url, | |
| "share": f"https://fraser-odyssey.static.hf.space/?id={uid}" | |
| } | |
| return json.dumps(result), url | |
| except Exception as e: | |
| return json.dumps({"error": str(e)}), "" | |
| def get_video_url(uuid_str: str) -> str: | |
| """Get the public URL for a video by UUID""" | |
| if not uuid_str: | |
| return "" | |
| return _public_url(uuid_str) | |
| # ===== Gradio Interface ===== | |
| with gr.Blocks(title="Odyssey Video Backend") as demo: | |
| gr.Markdown(""" | |
| # 🎬 Odyssey Video Backend | |
| Backend service for uploading Odyssey adventure videos to HuggingFace datasets. | |
| This Space is primarily accessed programmatically by the [Odyssey frontend](https://huggingface.co/spaces/Fraser/odyssey-frontend). | |
| """) | |
| with gr.Tab("Upload Video"): | |
| gr.Markdown("### Upload a video to the odyssey-videos dataset") | |
| video_input = gr.Video(label="Video File (MP4)") | |
| upload_btn = gr.Button("Upload to Dataset", variant="primary") | |
| result_json = gr.Textbox(label="Result (JSON)", lines=5) | |
| result_url = gr.Textbox(label="Video URL", interactive=False) | |
| upload_btn.click( | |
| fn=upload_video, | |
| inputs=[video_input], | |
| outputs=[result_json, result_url] | |
| ) | |
| with gr.Tab("Get Video URL"): | |
| gr.Markdown("### Get public URL for a video by UUID") | |
| uuid_input = gr.Textbox(label="Video UUID", placeholder="Enter UUID...") | |
| get_url_btn = gr.Button("Get URL") | |
| url_output = gr.Textbox(label="Video URL", interactive=False) | |
| get_url_btn.click( | |
| fn=get_video_url, | |
| inputs=[uuid_input], | |
| outputs=[url_output] | |
| ) | |
| gr.Markdown(""" | |
| --- | |
| ## API Usage | |
| ### JavaScript/TypeScript | |
| ```javascript | |
| import { Client } from "@gradio/client"; | |
| const client = await Client.connect("Fraser/odyssey-backend"); | |
| // Upload video | |
| const result = await client.predict("/upload_video", { | |
| video_file: blob // or file handle | |
| }); | |
| ``` | |
| ### Python | |
| ```python | |
| from gradio_client import Client | |
| client = Client("Fraser/odyssey-backend") | |
| result = client.predict("/upload_video", video_file="path/to/video.mp4") | |
| ``` | |
| """) | |
| if __name__ == "__main__": | |
| demo.launch() | |