# 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()