diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..d21a205 Binary files /dev/null and b/assets/logo.png differ diff --git a/qwen_tts/cli/demo.py b/qwen_tts/cli/demo.py index e267861..686c3f6 100644 --- a/qwen_tts/cli/demo.py +++ b/qwen_tts/cli/demo.py @@ -18,9 +18,11 @@ A gradio demo for Qwen3 TTS models. """ import argparse +import base64 import os import tempfile from dataclasses import asdict +from pathlib import Path from typing import Any, Dict, List, Optional, Tuple import gradio as gr @@ -59,6 +61,31 @@ def _maybe(v): return v if v is not None else gr.update() +def _find_icon() -> Optional[Path]: + candidates = [ + Path(os.environ.get("UMBRA_AUDIO_ICON", "")) if os.environ.get("UMBRA_AUDIO_ICON") else None, + Path("/Volumes/1TB_APL_Storage/UmbraAudio/UmbraAudio_icon.png"), + Path(__file__).resolve().parents[2] / "assets" / "logo.png", + Path("/Volumes/1TB_APL_Storage/UmbraAudio/UmbraAudio_icon.jpeg"), + ] + for c in candidates: + if c is not None and c.is_file(): + return c + return None + + +def _icon_data_uri() -> Optional[str]: + icon = _find_icon() + if icon is None: + return None + try: + data = base64.b64encode(icon.read_bytes()).decode("ascii") + mime = "image/png" if icon.suffix.lower() == ".png" else "image/jpeg" + return f"data:{mime};base64,{data}" + except OSError: + return None + + def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( prog="qwen-tts-demo", @@ -168,6 +195,18 @@ def build_parser() -> argparse.ArgumentParser: return parser +def _find_favicon() -> Optional[Path]: + for name in ("UmbraAudio_icon.png", "logo.png"): + p = Path(__file__).resolve().parents[3] / name + if p.is_file(): + return p + alt = Path("/Volumes/1TB_APL_Storage/UmbraAudio") / name + if alt.is_file(): + return alt + icon = _find_icon() + return icon if icon is not None and icon.suffix.lower() in (".png", ".gif", ".ico") else None + + def _resolve_checkpoint(args: argparse.Namespace) -> str: ckpt = args.checkpoint or args.checkpoint_pos if not ckpt: @@ -275,13 +314,30 @@ def build_demo(tts: Qwen3TTSModel, ckpt: str, gen_kwargs_default: Dict[str, Any] css = ".gradio-container {max-width: none !important;}" with gr.Blocks(theme=theme, css=css) as demo: - gr.Markdown( - f""" + icon_uri = _icon_data_uri() + if icon_uri: + with gr.Row(): + with gr.Column(scale=1, min_width=120): + gr.HTML( + f'UmbraAudio' + ) + with gr.Column(scale=4): + gr.Markdown( + f""" +# UmbraAudio Voice Studio +**Checkpoint:** `{ckpt}` +**Model Type:** `{model_kind}` +""" + ) + else: + gr.Markdown( + f""" # Qwen3 TTS Demo **Checkpoint:** `{ckpt}` **Model Type:** `{model_kind}` """ - ) + ) if model_kind == "custom_voice": with gr.Row(): @@ -621,6 +677,9 @@ def main(argv=None) -> int: share=args.share, ssl_verify=True if args.ssl_verify else False, ) + favicon = _find_favicon() + if favicon is not None: + launch_kwargs["favicon_path"] = str(favicon) if args.ssl_certfile is not None: launch_kwargs["ssl_certfile"] = args.ssl_certfile if args.ssl_keyfile is not None: