File size: 3,590 Bytes
c77c558
f5f8d0d
c77c558
130e1be
 
f5f8d0d
130e1be
 
c77c558
 
 
 
130e1be
 
c77c558
 
f5f8d0d
c77c558
130e1be
 
 
 
c77c558
f5f8d0d
b60e9d6
 
 
 
f5f8d0d
c77c558
130e1be
 
 
 
 
 
 
 
 
 
 
 
 
c77c558
f5f8d0d
130e1be
f5f8d0d
c77c558
 
130e1be
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5f8d0d
 
 
 
c77c558
 
f5f8d0d
130e1be
f5f8d0d
c77c558
 
f5f8d0d
130e1be
c77c558
 
 
f5f8d0d
 
 
130e1be
f5f8d0d
 
130e1be
 
 
c77c558
 
 
 
 
 
f5f8d0d
c77c558
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""
Static sample audio and scene image for the Examples tab.

Files are read from ``examples/`` when present locally. Missing assets are
downloaded from the FFASR Hub bucket (``examples/<filename>``) and cached there.

Do not commit ``.wav`` / ``.png`` to the Space git repo — Hugging Face rejects
binary pushes unless Git LFS is configured. Upload assets to the bucket instead.
"""

from __future__ import annotations

import os
import shutil
from pathlib import Path

EXAMPLES_DIR = Path(__file__).resolve().parent / "examples"

# Bucket prefix inside FFASR_BUCKET_ID (override with FFASR_EXAMPLES_BUCKET_PREFIX).
DEFAULT_BUCKET_PREFIX = "examples/"

SCENE_IMAGE = "scene.png"

SAMPLE_FILES: dict[str, list[str]] = {
    "dry": ["dry_speech_example_1.wav"],
    "high_snr": ["high_snr_example_1.wav"],
    "mid_snr": ["mid_snr_example_1.wav"],
    "low_snr": ["low_snr_example_1.wav"],
}

ALL_ASSET_NAMES: list[str] = [SCENE_IMAGE] + [
    name for names in SAMPLE_FILES.values() for name in names
]


def _bucket_prefix() -> str:
    raw = (os.environ.get("FFASR_EXAMPLES_BUCKET_PREFIX") or DEFAULT_BUCKET_PREFIX).strip()
    return raw if raw.endswith("/") else f"{raw}/"


def _local_path(name: str) -> Path:
    return EXAMPLES_DIR / name


def _resolve(name: str) -> Path | None:
    path = _local_path(name)
    return path if path.is_file() else None


def _download_from_bucket(name: str) -> bool:
    try:
        from storage import STORAGE_BACKEND, download_bucket_file

        if STORAGE_BACKEND != "hf_bucket":
            return False
        bucket_path = f"{_bucket_prefix()}{name}"
        downloaded = Path(download_bucket_file(bucket_path))
        EXAMPLES_DIR.mkdir(parents=True, exist_ok=True)
        dest = _local_path(name)
        shutil.copy2(downloaded, dest)
        return dest.is_file()
    except Exception:
        return False


def ensure_assets() -> None:
    """Ensure example files exist locally, fetching missing ones from the bucket."""
    EXAMPLES_DIR.mkdir(parents=True, exist_ok=True)
    for name in ALL_ASSET_NAMES:
        if _resolve(name):
            continue
        _download_from_bucket(name)


def sample_paths(condition: str) -> list[str | None]:
    """Return absolute paths for each clip in a condition (``None`` if missing)."""
    names = SAMPLE_FILES.get(condition, [])
    return [str(p) if (p := _resolve(n)) else None for n in names]


def scene_image_path() -> str | None:
    p = _resolve(SCENE_IMAGE)
    return str(p) if p else None


def missing_files() -> list[str]:
    return [n for n in ALL_ASSET_NAMES if not _resolve(n)]


def status() -> tuple[bool, str]:
    missing = missing_files()
    if not missing:
        return True, "All example clips and the scene screenshot are available."
    prefix = _bucket_prefix()
    return (
        False,
        "Missing example files (not in examples/ and not in the Hub bucket):\n• "
        + "\n• ".join(sorted(set(missing)))
        + f"\n\nUpload them to the bucket under `{prefix}` (see examples/README.md).",
    )


def status_html() -> str:
    ready, msg = status()
    color = "#38BFA1" if ready else "#E8A838"
    title = "Examples ready" if ready else "Some example files are missing"
    body = msg.replace("\n", "<br>")
    return (
        f"<div class='ffasr-examples-status' style='font-size:0.9em;"
        f"padding:0.75em 1em;border-radius:8px;"
        f"border:1px solid rgba(160,160,160,0.25);margin-bottom:1em'>"
        f"<strong style='color:{color}'>{title}</strong><br>"
        f"<span style='opacity:0.9'>{body}</span></div>"
    )