import os import tempfile from typing import Any import gradio as gr import httpx # NOTE: 0.0.0.0 is a bind address, not a connect address. # Use 127.0.0.1 by default and allow overrides for deployments. BASE_URL = os.getenv("API_BASE_URL", "http://127.0.0.1:7860") def _public_base_url(request: gr.Request | None) -> str: if request is None: return os.getenv("PUBLIC_BASE_URL", "").rstrip("/") headers = {k.lower(): v for k, v in dict(request.headers).items()} origin = headers.get("origin") if origin: return origin.rstrip("/") proto = headers.get("x-forwarded-proto") host = headers.get("x-forwarded-host") or headers.get("host") if proto and host: return f"{proto}://{host}".rstrip("/") if host: return f"https://{host}".rstrip("/") return os.getenv("PUBLIC_BASE_URL", "").rstrip("/") def create_gradio_app(): """Create and return the Gradio Blocks app for Music Memories.""" def _maybe_int(value: Any) -> int | None: if value is None: return None try: return int(value) except Exception: return None def _json_request( method: str, path: str, *, params: dict[str, Any] | None = None, data: dict[str, Any] | None = None, files: dict[str, Any] | None = None, follow_redirects: bool = True, timeout: float = 15.0, ) -> dict[str, Any]: url = f"{BASE_URL}{path}" with httpx.Client(follow_redirects=follow_redirects, timeout=timeout) as client: resp = client.request(method, url, params=params, data=data, files=files) resp.raise_for_status() return resp.json() def _redirect_location(path: str, *, params: dict[str, Any] | None = None) -> str: url = f"{BASE_URL}{path}" with httpx.Client(follow_redirects=False, timeout=15.0) as client: resp = client.get(url, params=params) if resp.status_code in (301, 302, 303, 307, 308): return resp.headers.get("location") or "" resp.raise_for_status() return "" def add_song_fn(title, artist, album, duration, bpm, energy_level, lyrics, audio_path): try: # /songs expects multipart/form-data (Form fields + optional file). data: dict[str, Any] = {"title": title, "artist": artist} if album: data["album"] = album duration_i = _maybe_int(duration) if duration_i is not None: data["duration"] = str(duration_i) bpm_i = _maybe_int(bpm) if bpm_i is not None: data["bpm"] = str(bpm_i) energy_i = _maybe_int(energy_level) if energy_i is not None: data["energy_level"] = str(energy_i) if lyrics: data["lyrics"] = lyrics if audio_path: filename = os.path.basename(str(audio_path)) with open(str(audio_path), "rb") as f: files = {"audio_file": (filename, f, "audio/mpeg")} out = _json_request("POST", "/songs", data=data, files=files) else: out = _json_request("POST", "/songs", data=data) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def add_user_fn(name): try: out = _json_request("POST", "/users", params={"name": name}, timeout=10.0) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def add_memory_fn(user_id, description, date, song_id): try: params: dict[str, Any] = {"user_id": _maybe_int(user_id), "description": description} if params["user_id"] is None: raise ValueError("user_id is required") if date: params["date"] = date song_id_i = _maybe_int(song_id) if song_id_i is not None: params["song_id"] = song_id_i out = _json_request("POST", "/memories", params=params) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def add_playlist_fn(name, vibe_code, mood_description): try: params: dict[str, Any] = {"name": name} if vibe_code: params["vibe_code"] = vibe_code if mood_description: params["mood_description"] = mood_description out = _json_request("POST", "/playlists", params=params) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def add_context_fn(weather, time_of_day, location_type): try: params: dict[str, Any] = {} if weather: params["weather"] = weather if time_of_day: params["time_of_day"] = time_of_day if location_type: params["location_type"] = location_type out = _json_request("POST", "/contexts", params=params, timeout=10.0) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def get_song_fn(song_id): try: song_id_i = _maybe_int(song_id) if song_id_i is None: raise ValueError("song_id is required") out = _json_request("GET", f"/songs/{song_id_i}") return "Success!", out except Exception as e: return "Error", {"error": str(e)} def delete_song_fn(song_id): try: song_id_i = _maybe_int(song_id) if song_id_i is None: raise ValueError("song_id is required") out = _json_request("DELETE", f"/songs/{song_id_i}") return "Success!", out except Exception as e: return "Error", {"error": str(e)} def upload_song_audio_fn(song_id, audio_path): try: song_id_i = _maybe_int(song_id) if song_id_i is None: raise ValueError("song_id is required") if not audio_path: raise ValueError("audio file is required") filename = os.path.basename(str(audio_path)) with open(str(audio_path), "rb") as f: files = {"audio_file": (filename, f, "audio/mpeg")} out = _json_request("POST", f"/songs/{song_id_i}/upload-audio", files=files) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def song_stream_url_fn(song_id, request: gr.Request | None = None): try: song_id_i = _maybe_int(song_id) if song_id_i is None: raise ValueError("song_id is required") public_base = _public_base_url(request) if public_base: return "Success!", f"{public_base}/songs/{song_id_i}/stream" return "Success!", f"/songs/{song_id_i}/stream" except Exception as e: return "Error", str(e) def song_download_fn(song_id): try: song_id_i = _maybe_int(song_id) if song_id_i is None: raise ValueError("song_id is required") url = f"{BASE_URL}/songs/{song_id_i}/download" with httpx.Client(timeout=30.0) as client: resp = client.get(url) resp.raise_for_status() tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") tmp.write(resp.content) tmp.flush() tmp.close() return "Success!", tmp.name except Exception as e: return "Error", None def get_user_fn(user_id): try: user_id_i = _maybe_int(user_id) if user_id_i is None: raise ValueError("user_id is required") out = _json_request("GET", f"/users/{user_id_i}") return "Success!", out except Exception as e: return "Error", {"error": str(e)} def list_user_memories_fn(user_id): try: user_id_i = _maybe_int(user_id) if user_id_i is None: raise ValueError("user_id is required") data = _json_request("GET", f"/users/{user_id_i}/memories", timeout=10.0) memories = data.get("memories", []) table = [[m.get("id"), m.get("user_id"), (m.get("description") or "")[:50], m.get("date", "")] for m in memories] return f"Found {len(memories)} memories", table except Exception as e: return "Error", [] def user_activity_fn(user_id, limit, action_type): try: user_id_i = _maybe_int(user_id) if user_id_i is None: raise ValueError("user_id is required") params: dict[str, Any] = {} limit_i = _maybe_int(limit) if limit_i is not None: params["limit"] = limit_i if action_type: params["action_type"] = action_type out = _json_request("GET", f"/users/{user_id_i}/activity", params=params) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def delete_playlist_fn(playlist_id): try: playlist_id_i = _maybe_int(playlist_id) if playlist_id_i is None: raise ValueError("playlist_id is required") out = _json_request("DELETE", f"/playlists/{playlist_id_i}") return "Success!", out except Exception as e: return "Error", {"error": str(e)} def list_playlists_fn(): try: data = _json_request("GET", "/playlists", timeout=10.0) playlists = data.get("playlists", []) return [[p.get("id"), p.get("name"), p.get("vibe_code", "")] for p in playlists] except Exception as e: return [["Error", str(e), ""]] def delete_memory_fn(memory_id): try: memory_id_i = _maybe_int(memory_id) if memory_id_i is None: raise ValueError("memory_id is required") out = _json_request("DELETE", f"/memories/{memory_id_i}") return "Success!", out except Exception as e: return "Error", {"error": str(e)} def list_contexts_fn(): try: data = _json_request("GET", "/contexts", timeout=10.0) contexts = data.get("contexts", []) return [[c.get("id"), c.get("weather", ""), c.get("time_of_day", ""), c.get("location_type", "")] for c in contexts] except Exception as e: return [["Error", str(e), "", ""]] def delete_context_fn(context_id): try: context_id_i = _maybe_int(context_id) if context_id_i is None: raise ValueError("context_id is required") out = _json_request("DELETE", f"/contexts/{context_id_i}") return "Success!", out except Exception as e: return "Error", {"error": str(e)} def search_songs_fn(query, n_results): try: data = _json_request("GET", "/search/songs", params={"q": query, "n": n_results}, timeout=15.0) results = data.get("results", []) table = [[r.get("id"), r.get("title"), r.get("artist"), f"{float(r.get('distance', 0.0)):.4f}"] for r in results] if results else [] return f"Found {len(results)} songs", table except Exception as e: return "Error", [] def search_memories_fn(query, n_results): try: data = _json_request("GET", "/search/memories", params={"q": query, "n": n_results}, timeout=15.0) results = data.get("results", []) table = [[r.get("id"), r.get("user_id"), (r.get("document") or "")[:50], f"{float(r.get('distance', 0.0)):.4f}"] for r in results] if results else [] return f"Found {len(results)} memories", table except Exception as e: return "Error", [] def search_contexts_fn(query, n_results): try: data = _json_request("GET", "/search/contexts", params={"q": query, "n": n_results}, timeout=15.0) results = data.get("results", []) table = [[r.get("id"), r.get("weather"), r.get("time_of_day"), r.get("location_type"), f"{float(r.get('distance', 0.0)):.4f}"] for r in results] if results else [] return f"Found {len(results)} contexts", table except Exception: return "Error", [] def search_playlists_fn(query, n_results): try: data = _json_request("GET", "/search/playlists", params={"q": query, "n": n_results}, timeout=15.0) results = data.get("results", []) table = [[r.get("id"), r.get("name"), f"{float(r.get('distance', 0.0)):.4f}"] for r in results] if results else [] return f"Found {len(results)} playlists", table except Exception as e: return "Error", [] def list_songs_fn(): try: data = _json_request("GET", "/songs", timeout=10.0) songs = data.get("songs", []) return [[s.get("id"), s.get("title"), s.get("artist"), s.get("album", ""), s.get("bpm", ""), s.get("has_audio"), s.get("play_count")] for s in songs] except Exception as e: return [["Error", str(e), "", "", "", "", ""]] def list_users_fn(): try: data = _json_request("GET", "/users", timeout=10.0) users = data.get("users", []) return [[u.get("id"), u.get("name"), u.get("created_at", "")] for u in users] except Exception as e: return [["Error", str(e), ""]] def list_memories_fn(): try: data = _json_request("GET", "/memories", timeout=10.0) memories = data.get("memories", []) return [[m.get("id"), m.get("user_id"), (m.get("description") or "")[:50], m.get("date", ""), m.get("song_id", "")] for m in memories] except Exception as e: return [["Error", str(e), "", "", ""]] def list_history_fn(user_id, limit): try: params: dict[str, Any] = {} user_id_i = _maybe_int(user_id) if user_id_i is not None: params["user_id"] = user_id_i limit_i = _maybe_int(limit) if limit_i is not None: params["limit"] = limit_i out = _json_request("GET", "/history", params=params, timeout=20.0) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def add_history_fn(user_id, song_id, context_id, duration_seconds): try: params: dict[str, Any] = {} user_id_i = _maybe_int(user_id) song_id_i = _maybe_int(song_id) if user_id_i is None or song_id_i is None: raise ValueError("user_id and song_id are required") params["user_id"] = user_id_i params["song_id"] = song_id_i context_id_i = _maybe_int(context_id) if context_id_i is not None: params["context_id"] = context_id_i dur_i = _maybe_int(duration_seconds) if dur_i is not None: params["duration_seconds"] = dur_i out = _json_request("POST", "/history", params=params, timeout=20.0) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def analytics_summary_fn(hours): try: hours_i = _maybe_int(hours) params = {"hours": hours_i} if hours_i is not None else {} out = _json_request("GET", "/analytics/summary", params=params, timeout=20.0) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def analytics_top_songs_fn(limit): try: limit_i = _maybe_int(limit) params = {"limit": limit_i} if limit_i is not None else {} out = _json_request("GET", "/analytics/top-songs", params=params, timeout=20.0) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def analytics_popular_searches_fn(hours, limit): try: params: dict[str, Any] = {} hours_i = _maybe_int(hours) limit_i = _maybe_int(limit) if hours_i is not None: params["hours"] = hours_i if limit_i is not None: params["limit"] = limit_i out = _json_request("GET", "/analytics/popular-searches", params=params, timeout=20.0) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def storage_files_fn(): try: out = _json_request("GET", "/storage/files", timeout=20.0) files = out.get("files", []) table = [[f.get("object_name"), f.get("size"), f.get("last_modified")] for f in files] return f"Found {len(files)} files", table except Exception as e: return "Error", [] def health_fn(): try: out = _json_request("GET", "/health", timeout=10.0) return "Success!", out except Exception as e: return "Error", {"error": str(e)} def root_fn(): try: out = _json_request("GET", "/", timeout=10.0) return "Success!", out except Exception as e: return "Error", {"error": str(e)} with gr.Blocks(title="Music Memories UI") as demo: gr.Markdown("# đŸŽĩ Music Memories") gr.Markdown("Manage your music library, memories, and playlists with semantic search.") with gr.Tab("đŸŽļ Songs"): with gr.Group(): gr.Markdown("### Add Song") song_title = gr.Textbox(label="Title", placeholder="Song title") song_artist = gr.Textbox(label="Artist", placeholder="Artist name") song_album = gr.Textbox(label="Album", placeholder="Album (optional)") song_duration = gr.Number(label="Duration (seconds)", precision=0) song_bpm = gr.Number(label="BPM", precision=0) song_energy = gr.Slider(1, 10, value=5, label="Energy Level") song_lyrics = gr.Textbox(label="Lyrics", placeholder="Lyrics for semantic search", lines=3) song_audio = gr.File(label="Audio File (MP3, optional)", file_types=[".mp3"], type="filepath") song_add_btn = gr.Button("Add Song") song_add_status = gr.Textbox(label="Status") song_add_output = gr.JSON(label="Response") song_add_btn.click( fn=add_song_fn, inputs=[song_title, song_artist, song_album, song_duration, song_bpm, song_energy, song_lyrics, song_audio], outputs=[song_add_status, song_add_output], ) with gr.Group(): gr.Markdown("### Get / Delete Song") song_id = gr.Number(label="Song ID", precision=0) song_get_btn = gr.Button("Get Song") song_del_btn = gr.Button("Delete Song") song_action_status = gr.Textbox(label="Status") song_action_output = gr.JSON(label="Response") song_get_btn.click(fn=get_song_fn, inputs=[song_id], outputs=[song_action_status, song_action_output]) song_del_btn.click(fn=delete_song_fn, inputs=[song_id], outputs=[song_action_status, song_action_output]) with gr.Group(): gr.Markdown("### Audio: Upload / Stream / Download") audio_song_id = gr.Number(label="Song ID", precision=0) audio_upload = gr.File(label="MP3 File", file_types=[".mp3"], type="filepath") audio_upload_btn = gr.Button("Upload/Update Audio") audio_upload_status = gr.Textbox(label="Status") audio_upload_output = gr.JSON(label="Response") audio_upload_btn.click( fn=upload_song_audio_fn, inputs=[audio_song_id, audio_upload], outputs=[audio_upload_status, audio_upload_output], ) stream_btn = gr.Button("Get Stream URL") stream_status = gr.Textbox(label="Status") stream_url = gr.Textbox(label="Presigned Stream URL") stream_btn.click(fn=song_stream_url_fn, inputs=[audio_song_id], outputs=[stream_status, stream_url]) download_btn = gr.Button("Download MP3 via API") download_status = gr.Textbox(label="Status") download_file = gr.File(label="Downloaded File") download_btn.click(fn=song_download_fn, inputs=[audio_song_id], outputs=[download_status, download_file]) with gr.Group(): gr.Markdown("### All Songs") songs_table = gr.Dataframe(headers=["ID", "Title", "Artist", "Album", "BPM", "Has Audio", "Play Count"], label="Songs") songs_load_btn = gr.Button("Load Songs") songs_load_btn.click(fn=list_songs_fn, outputs=songs_table) with gr.Tab("👤 Users"): with gr.Group(): gr.Markdown("### Add User") user_name = gr.Textbox(label="Name", placeholder="User name") user_add_btn = gr.Button("Add User") user_add_status = gr.Textbox(label="Status") user_add_output = gr.JSON(label="Response") user_add_btn.click(fn=add_user_fn, inputs=user_name, outputs=[user_add_status, user_add_output]) with gr.Group(): gr.Markdown("### Get User") get_user_id = gr.Number(label="User ID", precision=0) get_user_btn = gr.Button("Get User") get_user_status = gr.Textbox(label="Status") get_user_output = gr.JSON(label="Response") get_user_btn.click(fn=get_user_fn, inputs=[get_user_id], outputs=[get_user_status, get_user_output]) with gr.Group(): gr.Markdown("### User Memories") um_user_id = gr.Number(label="User ID", precision=0) um_btn = gr.Button("Load User Memories") um_summary = gr.Textbox(label="Results") um_table = gr.Dataframe(headers=["ID", "User ID", "Description", "Date"], label="Memories") um_btn.click(fn=list_user_memories_fn, inputs=[um_user_id], outputs=[um_summary, um_table]) with gr.Group(): gr.Markdown("### User Activity") ua_user_id = gr.Number(label="User ID", precision=0) ua_limit = gr.Number(label="Limit", value=50, precision=0) ua_action_type = gr.Textbox(label="Action Type (optional)") ua_btn = gr.Button("Load Activity") ua_status = gr.Textbox(label="Status") ua_output = gr.JSON(label="Response") ua_btn.click(fn=user_activity_fn, inputs=[ua_user_id, ua_limit, ua_action_type], outputs=[ua_status, ua_output]) with gr.Group(): gr.Markdown("### All Users") users_table = gr.Dataframe(headers=["ID", "Name", "Created At"], label="Users") users_load_btn = gr.Button("Load Users") users_load_btn.click(fn=list_users_fn, outputs=users_table) with gr.Tab("💭 Memories"): with gr.Group(): gr.Markdown("### Add Memory") mem_user_id = gr.Number(label="User ID", precision=0) mem_desc = gr.Textbox(label="Description", placeholder="Describe the memory...", lines=2) mem_date = gr.Textbox(label="Date (optional)", placeholder="YYYY-MM-DD") mem_song_id = gr.Number(label="Song ID (optional)", precision=0) mem_add_btn = gr.Button("Add Memory") mem_add_status = gr.Textbox(label="Status") mem_add_output = gr.JSON(label="Response") mem_add_btn.click(fn=add_memory_fn, inputs=[mem_user_id, mem_desc, mem_date, mem_song_id], outputs=[mem_add_status, mem_add_output]) with gr.Group(): gr.Markdown("### Delete Memory") del_mem_id = gr.Number(label="Memory ID", precision=0) del_mem_btn = gr.Button("Delete") del_mem_status = gr.Textbox(label="Status") del_mem_output = gr.JSON(label="Response") del_mem_btn.click(fn=delete_memory_fn, inputs=[del_mem_id], outputs=[del_mem_status, del_mem_output]) with gr.Group(): gr.Markdown("### All Memories") memories_table = gr.Dataframe(headers=["ID", "User ID", "Description", "Date", "Song ID"], label="Memories") memories_load_btn = gr.Button("Load Memories") memories_load_btn.click(fn=list_memories_fn, outputs=memories_table) with gr.Tab("đŸŽĩ Playlists"): with gr.Group(): gr.Markdown("### Add Playlist") pl_name = gr.Textbox(label="Name", placeholder="Playlist name") pl_vibe = gr.Textbox(label="Vibe Code", placeholder="e.g., chill, energetic") pl_mood = gr.Textbox(label="Mood Description", placeholder="Describe the mood journey...", lines=2) pl_add_btn = gr.Button("Add Playlist") pl_add_status = gr.Textbox(label="Status") pl_add_output = gr.JSON(label="Response") pl_add_btn.click(fn=add_playlist_fn, inputs=[pl_name, pl_vibe, pl_mood], outputs=[pl_add_status, pl_add_output]) with gr.Group(): gr.Markdown("### All Playlists") playlists_table = gr.Dataframe(headers=["ID", "Name", "Vibe Code"], label="Playlists") playlists_load_btn = gr.Button("Load Playlists") playlists_load_btn.click(fn=list_playlists_fn, outputs=playlists_table) with gr.Group(): gr.Markdown("### Delete Playlist") del_pl_id = gr.Number(label="Playlist ID", precision=0) del_pl_btn = gr.Button("Delete") del_pl_status = gr.Textbox(label="Status") del_pl_output = gr.JSON(label="Response") del_pl_btn.click(fn=delete_playlist_fn, inputs=[del_pl_id], outputs=[del_pl_status, del_pl_output]) with gr.Tab("đŸŒ¤ī¸ Contexts"): with gr.Group(): gr.Markdown("### Add Context") ctx_weather = gr.Textbox(label="Weather", placeholder="e.g., rainy, sunny") ctx_time = gr.Textbox(label="Time of Day", placeholder="e.g., morning, night") ctx_location = gr.Textbox(label="Location Type", placeholder="e.g., home, gym, car") ctx_add_btn = gr.Button("Add Context") ctx_add_status = gr.Textbox(label="Status") ctx_add_output = gr.JSON(label="Response") ctx_add_btn.click(fn=add_context_fn, inputs=[ctx_weather, ctx_time, ctx_location], outputs=[ctx_add_status, ctx_add_output]) with gr.Group(): gr.Markdown("### All Contexts") ctx_table = gr.Dataframe(headers=["ID", "Weather", "Time of Day", "Location"], label="Contexts") ctx_load_btn = gr.Button("Load Contexts") ctx_load_btn.click(fn=list_contexts_fn, outputs=ctx_table) with gr.Group(): gr.Markdown("### Delete Context") del_ctx_id = gr.Number(label="Context ID", precision=0) del_ctx_btn = gr.Button("Delete") del_ctx_status = gr.Textbox(label="Status") del_ctx_output = gr.JSON(label="Response") del_ctx_btn.click(fn=delete_context_fn, inputs=[del_ctx_id], outputs=[del_ctx_status, del_ctx_output]) with gr.Tab("🔍 Semantic Search"): with gr.Group(): gr.Markdown("### Search Songs by Vibe") search_songs_query = gr.Textbox(label="Query", placeholder="e.g., 'sad breakup song', 'workout energy'") search_songs_n = gr.Slider(1, 20, value=5, step=1, label="Results") search_songs_btn = gr.Button("Search") search_songs_summary = gr.Textbox(label="Results") search_songs_table = gr.Dataframe(headers=["ID", "Title", "Artist", "Distance"], label="Songs") search_songs_btn.click(fn=search_songs_fn, inputs=[search_songs_query, search_songs_n], outputs=[search_songs_summary, search_songs_table]) with gr.Group(): gr.Markdown("### Search Memories") search_mem_query = gr.Textbox(label="Query", placeholder="e.g., 'summer road trip', 'first dance'") search_mem_n = gr.Slider(1, 20, value=5, step=1, label="Results") search_mem_btn = gr.Button("Search") search_mem_summary = gr.Textbox(label="Results") search_mem_table = gr.Dataframe(headers=["ID", "User ID", "Description", "Distance"], label="Memories") search_mem_btn.click(fn=search_memories_fn, inputs=[search_mem_query, search_mem_n], outputs=[search_mem_summary, search_mem_table]) with gr.Group(): gr.Markdown("### Search Contexts") search_ctx_query = gr.Textbox(label="Query", placeholder="e.g., 'rainy night drive', 'sunny beach'") search_ctx_n = gr.Slider(1, 20, value=5, step=1, label="Results") search_ctx_btn = gr.Button("Search") search_ctx_summary = gr.Textbox(label="Results") search_ctx_table = gr.Dataframe(headers=["ID", "Weather", "Time", "Location", "Distance"], label="Contexts") search_ctx_btn.click(fn=search_contexts_fn, inputs=[search_ctx_query, search_ctx_n], outputs=[search_ctx_summary, search_ctx_table]) with gr.Group(): gr.Markdown("### Search Playlists by Mood") search_pl_query = gr.Textbox(label="Query", placeholder="e.g., 'chill evening vibes', 'party energy'") search_pl_n = gr.Slider(1, 20, value=5, step=1, label="Results") search_pl_btn = gr.Button("Search") search_pl_summary = gr.Textbox(label="Results") search_pl_table = gr.Dataframe(headers=["ID", "Name", "Distance"], label="Playlists") search_pl_btn.click(fn=search_playlists_fn, inputs=[search_pl_query, search_pl_n], outputs=[search_pl_summary, search_pl_table]) with gr.Tab("â„šī¸ Health"): with gr.Group(): gr.Markdown("### Root") root_btn = gr.Button("GET /") root_status = gr.Textbox(label="Status") root_output = gr.JSON(label="Response") root_btn.click(fn=root_fn, outputs=[root_status, root_output]) with gr.Group(): gr.Markdown("### Health") health_btn = gr.Button("GET /health") health_status = gr.Textbox(label="Status") health_output = gr.JSON(label="Response") health_btn.click(fn=health_fn, outputs=[health_status, health_output]) with gr.Tab("â–ļī¸ History"): with gr.Group(): gr.Markdown("### List History") hist_user_id = gr.Number(label="User ID (optional)", precision=0) hist_limit = gr.Number(label="Limit", value=50, precision=0) hist_list_btn = gr.Button("Load History") hist_status = gr.Textbox(label="Status") hist_output = gr.JSON(label="Response") hist_list_btn.click(fn=list_history_fn, inputs=[hist_user_id, hist_limit], outputs=[hist_status, hist_output]) with gr.Group(): gr.Markdown("### Add Play Event") hist_add_user = gr.Number(label="User ID", precision=0) hist_add_song = gr.Number(label="Song ID", precision=0) hist_add_ctx = gr.Number(label="Context ID (optional)", precision=0) hist_add_dur = gr.Number(label="Duration Seconds (optional)", precision=0) hist_add_btn = gr.Button("Add History") hist_add_status = gr.Textbox(label="Status") hist_add_output = gr.JSON(label="Response") hist_add_btn.click( fn=add_history_fn, inputs=[hist_add_user, hist_add_song, hist_add_ctx, hist_add_dur], outputs=[hist_add_status, hist_add_output], ) with gr.Tab("📊 Analytics"): with gr.Group(): gr.Markdown("### Summary") an_hours = gr.Number(label="Hours", value=24, precision=0) an_sum_btn = gr.Button("Get Summary") an_sum_status = gr.Textbox(label="Status") an_sum_output = gr.JSON(label="Response") an_sum_btn.click(fn=analytics_summary_fn, inputs=[an_hours], outputs=[an_sum_status, an_sum_output]) with gr.Group(): gr.Markdown("### Top Songs") an_limit = gr.Number(label="Limit", value=10, precision=0) an_top_btn = gr.Button("Get Top Songs") an_top_status = gr.Textbox(label="Status") an_top_output = gr.JSON(label="Response") an_top_btn.click(fn=analytics_top_songs_fn, inputs=[an_limit], outputs=[an_top_status, an_top_output]) with gr.Group(): gr.Markdown("### Popular Searches") ps_hours = gr.Number(label="Hours", value=24, precision=0) ps_limit = gr.Number(label="Limit", value=10, precision=0) ps_btn = gr.Button("Get Popular Searches") ps_status = gr.Textbox(label="Status") ps_output = gr.JSON(label="Response") ps_btn.click(fn=analytics_popular_searches_fn, inputs=[ps_hours, ps_limit], outputs=[ps_status, ps_output]) with gr.Group(): gr.Markdown("### Storage Files") sf_btn = gr.Button("List MP3 Files") sf_summary = gr.Textbox(label="Results") sf_table = gr.Dataframe(headers=["Object", "Size", "Last Modified"], label="Files") sf_btn.click(fn=storage_files_fn, outputs=[sf_summary, sf_table]) return demo # Create the Gradio app instance gradio_app = create_gradio_app() if __name__ == "__main__": gradio_app.launch(share=False, server_name="0.0.0.0", server_port=7860)