Commit ·
31de45b
1
Parent(s): f276253
Add OpenVINO face models, video normalization, and Qwen multimodal model
Browse files- Marquee.html +220 -0
- README.md +94 -10
- _gitignore +220 -0
- app.py +179 -55
- commentary.py +166 -0
- download_model.py +53 -0
- events.py +45 -0
- faces.py +186 -0
- marquee.css +468 -0
- marquee.js +412 -0
- marquee_inlined_preview.html +1093 -0
- models/intel/face-detection-retail-0004/FP16/face-detection-retail-0004.bin +3 -0
- models/intel/face-detection-retail-0004/FP16/face-detection-retail-0004.xml +0 -0
- models/intel/face-reidentification-retail-0095/FP16/face-reidentification-retail-0095.bin +3 -0
- models/intel/face-reidentification-retail-0095/FP16/face-reidentification-retail-0095.xml +0 -0
- models/intel/landmarks-regression-retail-0009/FP16/landmarks-regression-retail-0009.bin +3 -0
- models/intel/landmarks-regression-retail-0009/FP16/landmarks-regression-retail-0009.xml +2607 -0
- ov_models.py +89 -0
- packages.txt +1 -0
- qwen_download.py +23 -0
- requirements.txt +20 -0
- video.py +76 -0
Marquee.html
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
+
<title>Marquee — Setup Console & Broadcast</title>
|
| 7 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 8 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 9 |
+
<link href="https://fonts.googleapis.com/css2?family=Unbounded:wght@400;500;600;700;800&family=Bebas+Neue&family=Nunito:wght@600;700;800;900&display=swap" rel="stylesheet">
|
| 10 |
+
<link rel="stylesheet" href="marquee.css">
|
| 11 |
+
</head>
|
| 12 |
+
<body>
|
| 13 |
+
|
| 14 |
+
<!-- =======================================================
|
| 15 |
+
ICON SPRITE — line icons, broadcast vernacular
|
| 16 |
+
======================================================= -->
|
| 17 |
+
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
|
| 18 |
+
<defs>
|
| 19 |
+
<symbol id="i-clap" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"><path d="M3 8h18v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8Z"/><path d="m3 8 1.6-3.4 3.8 1L6.7 9M9.4 5.6l3.8 1L11.5 10M15 7l3.8 1-1.5 3.4M21 8l-.8-2.2-3.4-.9"/></symbol>
|
| 20 |
+
<symbol id="i-play" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></symbol>
|
| 21 |
+
<symbol id="i-pause" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="5" width="4" height="14" rx="1"/><rect x="14" y="5" width="4" height="14" rx="1"/></symbol>
|
| 22 |
+
<symbol id="i-star" viewBox="0 0 24 24" fill="currentColor"><path d="m12 2 2.9 6.3 6.9.8-5.1 4.7 1.4 6.8L12 17.8 5.9 20.6l1.4-6.8L2.2 9.1l6.9-.8z"/></symbol>
|
| 23 |
+
<symbol id="i-spot" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><path d="M12 3v3M5 6l2 2M19 6l-2 2"/><path d="M9 9h6l3 11H6z" fill="currentColor" opacity=".15"/><path d="M9 9h6l3 11H6z"/></symbol>
|
| 24 |
+
<symbol id="i-crown" viewBox="0 0 24 24" fill="currentColor"><path d="M3 7l4 4 5-7 5 7 4-4-2 12H5z"/></symbol>
|
| 25 |
+
<symbol id="i-bird" viewBox="0 0 24 24" fill="currentColor"><path d="M14 4a3 3 0 0 0-3 3c0 .5-.4 1-1 1.5-1.8 1.3-3 3.4-3 5.8C7 17.9 9.6 20 13 20c.6 0 1-.6.8-1.2C13.3 17 13 16 14 15c2.2-.4 4-2.3 4-4.8V7a3 3 0 0 0-3-3zm.6 3.2a.9.9 0 1 1 0-.1z"/></symbol>
|
| 26 |
+
<symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V8a4 4 0 0 1 8 0v3"/></symbol>
|
| 27 |
+
<symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4v10m0 0 4-4m-4 4-4-4M5 20h14"/></symbol>
|
| 28 |
+
<symbol id="i-mic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M6 11a6 6 0 0 0 12 0M12 17v4"/></symbol>
|
| 29 |
+
<symbol id="i-list" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><path d="M8 6h12M8 12h12M8 18h12M4 6h.01M4 12h.01M4 18h.01"/></symbol>
|
| 30 |
+
<symbol id="i-whistle" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><path d="M3 11a5 5 0 0 0 5 5h4l5 3v-8a3 3 0 0 0-3-3H8a5 5 0 0 0-5 3Z"/><path d="M14 4v3"/></symbol>
|
| 31 |
+
<symbol id="i-glam" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><path d="M3 14c2-1 4-1 5 0M16 14c2-1 4-1 5 0M3 11h18M7 11l1 5M17 11l-1 5"/><circle cx="6.5" cy="8.5" r="2"/><circle cx="17.5" cy="8.5" r="2"/></symbol>
|
| 32 |
+
<symbol id="i-paw" viewBox="0 0 24 24" fill="currentColor"><circle cx="6" cy="9" r="2"/><circle cx="11" cy="6" r="2"/><circle cx="16" cy="7" r="2"/><circle cx="19" cy="11" r="2"/><path d="M12 12c-3 0-6 2.5-6 5 0 2 2 3 6 3s6-1 6-3c0-2.5-3-5-6-5Z"/></symbol>
|
| 33 |
+
<symbol id="i-glove" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"><path d="M7 10V6a2 2 0 0 1 4 0v3M11 9V5a2 2 0 0 1 4 0v5l2 1a3 3 0 0 1 1.4 3.4l-.8 3A2 2 0 0 1 15.7 21H10a3 3 0 0 1-2.8-2L5.5 14A2.5 2.5 0 0 1 7 10Z"/></symbol>
|
| 34 |
+
<symbol id="i-cam" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="6" width="13" height="12" rx="2"/><path d="m16 10 5-3v10l-5-3z"/></symbol>
|
| 35 |
+
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="m4 12 5 5L20 6"/></symbol>
|
| 36 |
+
<symbol id="i-back" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M15 5l-7 7 7 7"/></symbol>
|
| 37 |
+
<symbol id="i-portrait" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="8" r="4.4"/><path d="M3.5 22c0-4.7 3.8-8 8.5-8s8.5 3.3 8.5 8z"/></symbol>
|
| 38 |
+
</defs>
|
| 39 |
+
</svg>
|
| 40 |
+
|
| 41 |
+
<div class="shell">
|
| 42 |
+
|
| 43 |
+
<!-- ============== MASTHEAD ============== -->
|
| 44 |
+
<header class="masthead">
|
| 45 |
+
<div class="brand">
|
| 46 |
+
<div class="sign">
|
| 47 |
+
<div class="bulbs t"><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>
|
| 48 |
+
<h1 class="disp">MARQUEE</h1>
|
| 49 |
+
<div class="bulbs b"><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>
|
| 50 |
+
</div>
|
| 51 |
+
<div class="tag">Everyone's a headliner.<small>Turn any clip into a live broadcast.</small></div>
|
| 52 |
+
</div>
|
| 53 |
+
<div class="broadcast-meta">
|
| 54 |
+
<span class="clock" id="clock">--:--:--</span>
|
| 55 |
+
<span class="status" id="status"><span class="led"></span><span id="statusTxt">Standby</span></span>
|
| 56 |
+
</div>
|
| 57 |
+
</header>
|
| 58 |
+
|
| 59 |
+
<!-- ============================================================
|
| 60 |
+
VIEW 1 — SETUP CONSOLE
|
| 61 |
+
============================================================ -->
|
| 62 |
+
<section class="view active" id="setupView">
|
| 63 |
+
|
| 64 |
+
<!-- rundown / running order -->
|
| 65 |
+
<nav class="rundown" id="rundown" aria-label="Show rundown">
|
| 66 |
+
<div class="cue on" data-step="1"><div class="node">01</div><div class="ttl">Roll Tape</div><div class="sub">Drop a clip</div></div>
|
| 67 |
+
<div class="cue" data-step="2"><div class="node">02</div><div class="ttl">Casting</div><div class="sub">Find the faces</div></div>
|
| 68 |
+
<div class="cue" data-step="3"><div class="node">03</div><div class="ttl">Star Select</div><div class="sub">Name the stars</div></div>
|
| 69 |
+
<div class="cue" data-step="4"><div class="node">04</div><div class="ttl">Showtime</div><div class="sub">Go live</div></div>
|
| 70 |
+
</nav>
|
| 71 |
+
|
| 72 |
+
<div class="console">
|
| 73 |
+
|
| 74 |
+
<!-- ---------- LEFT: the control desk ---------- -->
|
| 75 |
+
<div class="desk">
|
| 76 |
+
|
| 77 |
+
<!-- Roll Tape -->
|
| 78 |
+
<div class="card" id="rollCard">
|
| 79 |
+
<div class="card-h"><svg class="i ic"><use href="#i-clap"/></svg><h2>Roll Tape</h2><span class="meta">SRC 01</span></div>
|
| 80 |
+
<div class="drop" id="drop" tabindex="0" role="button" aria-label="Drop a video clip to roll tape">
|
| 81 |
+
<div class="ask">
|
| 82 |
+
<span class="clap"><svg class="i"><use href="#i-clap"/></svg></span>
|
| 83 |
+
<div class="t">Drop a clip on the desk</div>
|
| 84 |
+
<div class="s">MP4 · MOV · up to 60s · portrait, landscape or square</div>
|
| 85 |
+
</div>
|
| 86 |
+
<div class="loaded">
|
| 87 |
+
<div class="strip"><span class="play"><svg class="i"><use href="#i-play"/></svg></span></div>
|
| 88 |
+
<div>
|
| 89 |
+
<div class="fn" id="fileName">rooftop_freekick.mp4</div>
|
| 90 |
+
<div class="fd"><span id="fileDur">00:42</span> · 1080×1920 · ready to cast</div>
|
| 91 |
+
</div>
|
| 92 |
+
</div>
|
| 93 |
+
</div>
|
| 94 |
+
<input type="file" id="fileInput" accept="video/*" hidden>
|
| 95 |
+
<button class="btn cyan mt" id="castBtn" disabled><svg class="i"><use href="#i-spot"/></svg> Find the stars</button>
|
| 96 |
+
<div class="sweep"></div>
|
| 97 |
+
</div>
|
| 98 |
+
|
| 99 |
+
<!-- Pick the show / vibes -->
|
| 100 |
+
<div class="card" id="vibeCard">
|
| 101 |
+
<div class="card-h"><svg class="i ic"><use href="#i-mic"/></svg><h2>Pick the show</h2><span class="meta">TONE</span></div>
|
| 102 |
+
<div class="vibes" id="vibes" role="group" aria-label="Narration vibe">
|
| 103 |
+
<button class="vibe" data-vibe="football" style="--vc:var(--star-lime)" aria-pressed="false">
|
| 104 |
+
<span class="ic"><svg class="i"><use href="#i-whistle"/></svg></span>
|
| 105 |
+
<span><span class="vt">Football</span><span class="vd">Roaring play-by-play</span></span>
|
| 106 |
+
</button>
|
| 107 |
+
<button class="vibe" data-vibe="diva" style="--vc:var(--star-hot)" aria-pressed="false">
|
| 108 |
+
<span class="ic"><svg class="i"><use href="#i-glam"/></svg></span>
|
| 109 |
+
<span><span class="vt">Diva Hour</span><span class="vd">Red-carpet shade</span></span>
|
| 110 |
+
</button>
|
| 111 |
+
<button class="vibe" data-vibe="wildlife" style="--vc:var(--star-cyan)" aria-pressed="false">
|
| 112 |
+
<span class="ic"><svg class="i"><use href="#i-paw"/></svg></span>
|
| 113 |
+
<span><span class="vt">Wildlife</span><span class="vd">Hushed nature doc</span></span>
|
| 114 |
+
</button>
|
| 115 |
+
<button class="vibe" data-vibe="boxing" style="--vc:var(--star-tang)" aria-pressed="false">
|
| 116 |
+
<span class="ic"><svg class="i"><use href="#i-glove"/></svg></span>
|
| 117 |
+
<span><span class="vt">Boxing</span><span class="vd">Ringside hype</span></span>
|
| 118 |
+
</button>
|
| 119 |
+
</div>
|
| 120 |
+
|
| 121 |
+
<!-- Coco the host — re-dresses per vibe -->
|
| 122 |
+
<div class="coco" id="coco">
|
| 123 |
+
<div class="av">
|
| 124 |
+
<svg class="i bird"><use href="#i-bird"/></svg>
|
| 125 |
+
<span class="acc" id="cocoAcc"><svg><use href="#i-star"/></svg></span>
|
| 126 |
+
</div>
|
| 127 |
+
<div class="say">
|
| 128 |
+
<span class="who">Coco · your host</span>
|
| 129 |
+
<span id="cocoLine">Pick a show and I'll set the mood. I dress for the occasion.</span>
|
| 130 |
+
</div>
|
| 131 |
+
</div>
|
| 132 |
+
</div>
|
| 133 |
+
|
| 134 |
+
<!-- Showtime -->
|
| 135 |
+
<button class="btn show" id="liveBtn" disabled><svg class="i"><use href="#i-star"/></svg> Showtime — go live</button>
|
| 136 |
+
|
| 137 |
+
</div>
|
| 138 |
+
|
| 139 |
+
<!-- ---------- RIGHT: the stage ---------- -->
|
| 140 |
+
<div class="stage-panel" id="stagePanel">
|
| 141 |
+
<div class="stage-h">
|
| 142 |
+
<svg class="i ic"><use href="#i-spot"/></svg>
|
| 143 |
+
<h2>Star Select</h2>
|
| 144 |
+
<span class="count" id="starCount">NO CAST YET</span>
|
| 145 |
+
</div>
|
| 146 |
+
|
| 147 |
+
<!-- empty -->
|
| 148 |
+
<div class="empty" id="stageEmpty">
|
| 149 |
+
<div class="spot"><svg class="i"><use href="#i-spot"/></svg></div>
|
| 150 |
+
<div class="t">The stage is dark</div>
|
| 151 |
+
<div class="s">Roll a clip and hit <b>Find the stars</b> — everyone we spot gets a card under the spotlight.</div>
|
| 152 |
+
</div>
|
| 153 |
+
|
| 154 |
+
<!-- roster (filled by JS) -->
|
| 155 |
+
<div class="roster" id="roster" hidden></div>
|
| 156 |
+
</div>
|
| 157 |
+
|
| 158 |
+
</div>
|
| 159 |
+
|
| 160 |
+
<p class="note">
|
| 161 |
+
<svg class="i lock"><use href="#i-lock"/></svg>
|
| 162 |
+
Your clip stays on <b>your machine</b>. Faces are detected locally and never uploaded — Marquee only writes the captions you see.
|
| 163 |
+
</p>
|
| 164 |
+
</section>
|
| 165 |
+
|
| 166 |
+
<!-- ============================================================
|
| 167 |
+
VIEW 2 — BROADCAST PLAYER
|
| 168 |
+
============================================================ -->
|
| 169 |
+
<section class="view" id="broadcastView">
|
| 170 |
+
<div class="booth-top">
|
| 171 |
+
<button class="back" id="backBtn"><svg class="i"><use href="#i-back"/></svg> Back to console</button>
|
| 172 |
+
<div class="orient" role="group" aria-label="Clip orientation">
|
| 173 |
+
<button data-orient="landscape" aria-pressed="true">16:9</button>
|
| 174 |
+
<button data-orient="square" aria-pressed="false">1:1</button>
|
| 175 |
+
<button data-orient="portrait" aria-pressed="false">9:16</button>
|
| 176 |
+
</div>
|
| 177 |
+
</div>
|
| 178 |
+
|
| 179 |
+
<div class="booth" id="booth">
|
| 180 |
+
<!-- the screen -->
|
| 181 |
+
<div>
|
| 182 |
+
<div class="screen-wrap">
|
| 183 |
+
<div class="screen" id="screen">
|
| 184 |
+
<video id="vid" class="screen-vid" playsinline preload="metadata"></video>
|
| 185 |
+
<div class="ph"><svg class="i"><use href="#i-cam"/></svg>YOUR CLIP PLAYS HERE</div>
|
| 186 |
+
<div class="bug"><span class="dot"></span> ON AIR</div>
|
| 187 |
+
<div class="tc" id="tc">00:00</div>
|
| 188 |
+
<!-- lower-third chyron -->
|
| 189 |
+
<div class="chyron" id="chyron">
|
| 190 |
+
<div class="bar"></div>
|
| 191 |
+
<div class="row"><span class="who" id="chyWho">COCO</span><span class="line" id="chyLine">Standing by…</span></div>
|
| 192 |
+
</div>
|
| 193 |
+
</div>
|
| 194 |
+
</div>
|
| 195 |
+
<div class="transport">
|
| 196 |
+
<button class="pp" id="pp" aria-label="Play / pause"><svg class="i"><use href="#i-play"/></svg></button>
|
| 197 |
+
<div class="scrub" id="scrub"><div class="fill" id="fill"></div><div class="knob" id="knob"></div></div>
|
| 198 |
+
<span class="clock" id="tcDur" style="font-size:16px">00:42</span>
|
| 199 |
+
</div>
|
| 200 |
+
</div>
|
| 201 |
+
|
| 202 |
+
<!-- play-by-play rail -->
|
| 203 |
+
<div class="pbp">
|
| 204 |
+
<div class="pbp-h"><svg class="i ic"><use href="#i-list"/></svg><h3>Play-by-play</h3><span class="ct" id="pbpCt">— lines</span></div>
|
| 205 |
+
<div class="lines" id="lines"></div>
|
| 206 |
+
<div class="export">
|
| 207 |
+
<button class="btn-srt" id="srtBtn"><svg class="i ic"><use href="#i-download"/></svg> Download captions (.srt)</button>
|
| 208 |
+
<div class="hint">Drops straight into <b>CapCut</b>, Premiere & DaVinci — timed to your clip.</div>
|
| 209 |
+
</div>
|
| 210 |
+
</div>
|
| 211 |
+
</div>
|
| 212 |
+
</section>
|
| 213 |
+
|
| 214 |
+
</div>
|
| 215 |
+
|
| 216 |
+
<div class="toast" id="toast"><svg class="i ic"><use href="#i-check"/></svg><span id="toastTxt">Done</span></div>
|
| 217 |
+
|
| 218 |
+
<script src="marquee.js"></script>
|
| 219 |
+
</body>
|
| 220 |
+
</html>
|
README.md
CHANGED
|
@@ -1,16 +1,100 @@
|
|
| 1 |
---
|
| 2 |
title: Marquee
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version:
|
| 8 |
app_file: app.py
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
-
|
| 13 |
-
short_description: hyping up your daily life!
|
| 14 |
---
|
| 15 |
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
title: Marquee
|
| 3 |
+
emoji: 🎙️
|
| 4 |
+
colorFrom: purple
|
| 5 |
+
colorTo: pink
|
| 6 |
sdk: gradio
|
| 7 |
+
sdk_version: 5.34.0
|
| 8 |
app_file: app.py
|
| 9 |
+
license: apache-2.0
|
| 10 |
+
short_description: Turn any clip into a live broadcast where your friends are the stars
|
| 11 |
+
tags:
|
| 12 |
+
- build-small-hackathon
|
|
|
|
| 13 |
---
|
| 14 |
|
| 15 |
+
# Marquee 🎙️ — *Everyone's a headliner.*
|
| 16 |
+
|
| 17 |
+
Drop a clip of friends doing anything. Marquee recognizes who's in it, you give
|
| 18 |
+
each person a stage name, pick a show style, and an AI commentator calls the
|
| 19 |
+
action play-by-play, by name, in sync — as a broadcast you can play and export.
|
| 20 |
+
|
| 21 |
+
## ⚙️ Running it on a Space (read this first)
|
| 22 |
+
|
| 23 |
+
- **Hardware must be ZeroGPU.** The commentary model runs inside `@spaces.GPU`,
|
| 24 |
+
which only has a GPU on **Nvidia ZeroGPU** hardware. On a plain CPU Space the
|
| 25 |
+
7B VLM has no GPU and is effectively unusable. Set it in **Settings →
|
| 26 |
+
Hardware → ZeroGPU**.
|
| 27 |
+
- **No secrets needed at runtime.** Everything downloads from public sources
|
| 28 |
+
(Qwen from the HF Hub, the face models from Intel's OMZ storage). Do **not**
|
| 29 |
+
commit any `HF_TOKEN`/`.env` — if you need one for `gradio deploy`, keep it
|
| 30 |
+
local and add it as a **Space secret**, never a file in the repo.
|
| 31 |
+
- **Nothing to pre-download.** `download_models()` fetches the three tiny
|
| 32 |
+
OpenVINO IR files at first boot (~5 MB total). First boot also warms up Qwen.
|
| 33 |
+
|
| 34 |
+
## How it's wired (the important part)
|
| 35 |
+
|
| 36 |
+
Gradio is the **runtime** (so `@spaces.GPU` / ZeroGPU works). The custom UI is a
|
| 37 |
+
**static HTML page** served at `/`, talking to plain FastAPI JSON endpoints via
|
| 38 |
+
`fetch()` — no Gradio JS client, no iframe.
|
| 39 |
+
|
| 40 |
+
```
|
| 41 |
+
Static UI (Marquee.html + marquee.css + marquee.js) ──► served at "/"
|
| 42 |
+
│ plain fetch(), same-origin
|
| 43 |
+
▼
|
| 44 |
+
FastAPI (app.py)
|
| 45 |
+
├─ POST /api/scan (file) -> normalize 720p -> detect+cluster faces
|
| 46 |
+
│ -> session + roster (face crops as data URLs)
|
| 47 |
+
├─ GET /video/{sid} -> streams the normalized mp4
|
| 48 |
+
└─ POST /api/generate (session, names, vibe)-> key events -> name-grounded frames
|
| 49 |
+
-> Qwen2.5-VL (ZeroGPU) -> {t,text} script
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
An empty `gr.Blocks` is mounted at `/gradio` only so the HF "gradio" SDK runtime
|
| 53 |
+
and ZeroGPU machinery initialise.
|
| 54 |
+
|
| 55 |
+
Heavy per-session state (identities, motion, normalized video path) lives in a
|
| 56 |
+
server-side `SESSIONS` dict keyed by the `session_id` the scan call returns, so
|
| 57 |
+
the two stateless client calls never round-trip embeddings.
|
| 58 |
+
|
| 59 |
+
Face recognition is used **only** to feed names to the VLM — boxes are never
|
| 60 |
+
shown. The broadcast player reads the real normalized `<video>` and syncs the
|
| 61 |
+
chyron + play-by-play rail to `video.currentTime`.
|
| 62 |
+
|
| 63 |
+
## Files
|
| 64 |
+
- `app.py` — FastAPI backend + the two API endpoints + UI assembly
|
| 65 |
+
- `Marquee.html` / `marquee.css` / `marquee.js` — the custom UI (design + flow)
|
| 66 |
+
- `video.py` — rotation-aware 720p normalize (one upright source of truth)
|
| 67 |
+
- `faces.py` — OpenVINO detect → align → embed → cluster (CPU)
|
| 68 |
+
- `events.py` — motion-based key events
|
| 69 |
+
- `commentary.py` — Qwen2.5-VL personas (incl. Diva Hour) + strict JSON parse
|
| 70 |
+
- `ov_models.py` — OpenVINO IR auto-download + thin runtime wrappers
|
| 71 |
+
|
| 72 |
+
## Models (all open)
|
| 73 |
+
- face-detection-retail-0004, landmarks-regression-retail-0009,
|
| 74 |
+
face-reidentification-retail-0095 (OpenVINO IR, CPU)
|
| 75 |
+
- Qwen2.5-VL-7B-Instruct (ZeroGPU)
|
| 76 |
+
|
| 77 |
+
## First debugging spots
|
| 78 |
+
- **Scan returns 500 / "xml not found".** The OMZ download at boot failed (check
|
| 79 |
+
boot logs for the `[ov]`/`[warn]` lines). `ov_models.download_models()` pulls
|
| 80 |
+
from `storage.openvinotoolkit.org`; re-run it or commit the `models/` folder.
|
| 81 |
+
- **Commentary errors / wrong device.** Confirm the Space is on **ZeroGPU**, not
|
| 82 |
+
CPU. The model loads on CPU and moves to GPU inside `@spaces.GPU` — never call
|
| 83 |
+
`.cuda()` at import.
|
| 84 |
+
- **VLM cold start** eats the first request if boot warmup didn't run. Keep the
|
| 85 |
+
warmup (it loads weights to RAM so the first call only pays the CPU→GPU move).
|
| 86 |
+
- **Clustering merges/splits people** -> tune `SAME_PERSON` in `faces.py`.
|
| 87 |
+
- **Root `/` 404s on the Space** (rare) -> the gradio SDK didn't serve the
|
| 88 |
+
mounted FastAPI app; switch to `gradio.Server` mode for fully custom frontends.
|
| 89 |
+
|
| 90 |
+
## North star (next iteration)
|
| 91 |
+
- TTS per line (hyped delivery) mixed under the clip's own audio.
|
| 92 |
+
- Rendered MP4 export with captions (and voice) burned in.
|
| 93 |
+
- Canvas hype FX on key moments (data's already there).
|
| 94 |
+
|
| 95 |
+
## Local dev
|
| 96 |
+
```bash
|
| 97 |
+
pip install -r requirements.txt
|
| 98 |
+
python -c "from ov_models import download_models; download_models()"
|
| 99 |
+
python app.py # http://localhost:7860 (Qwen lazy-loads on first generate)
|
| 100 |
+
```
|
_gitignore
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / optimized / DLL files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[codz]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# C extensions
|
| 7 |
+
*.so
|
| 8 |
+
|
| 9 |
+
# Distribution / packaging
|
| 10 |
+
.Python
|
| 11 |
+
build/
|
| 12 |
+
develop-eggs/
|
| 13 |
+
dist/
|
| 14 |
+
downloads/
|
| 15 |
+
eggs/
|
| 16 |
+
.eggs/
|
| 17 |
+
lib/
|
| 18 |
+
lib64/
|
| 19 |
+
parts/
|
| 20 |
+
sdist/
|
| 21 |
+
var/
|
| 22 |
+
wheels/
|
| 23 |
+
share/python-wheels/
|
| 24 |
+
*.egg-info/
|
| 25 |
+
.installed.cfg
|
| 26 |
+
*.egg
|
| 27 |
+
MANIFEST
|
| 28 |
+
|
| 29 |
+
# PyInstaller
|
| 30 |
+
# Usually these files are written by a python script from a template
|
| 31 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 32 |
+
*.manifest
|
| 33 |
+
*.spec
|
| 34 |
+
|
| 35 |
+
# Installer logs
|
| 36 |
+
pip-log.txt
|
| 37 |
+
pip-delete-this-directory.txt
|
| 38 |
+
|
| 39 |
+
# Unit test / coverage reports
|
| 40 |
+
htmlcov/
|
| 41 |
+
.tox/
|
| 42 |
+
.nox/
|
| 43 |
+
.coverage
|
| 44 |
+
.coverage.*
|
| 45 |
+
.cache
|
| 46 |
+
nosetests.xml
|
| 47 |
+
coverage.xml
|
| 48 |
+
*.cover
|
| 49 |
+
*.py.cover
|
| 50 |
+
.hypothesis/
|
| 51 |
+
.pytest_cache/
|
| 52 |
+
cover/
|
| 53 |
+
|
| 54 |
+
# Translations
|
| 55 |
+
*.mo
|
| 56 |
+
*.pot
|
| 57 |
+
|
| 58 |
+
# Django stuff:
|
| 59 |
+
*.log
|
| 60 |
+
local_settings.py
|
| 61 |
+
db.sqlite3
|
| 62 |
+
db.sqlite3-journal
|
| 63 |
+
|
| 64 |
+
# Flask stuff:
|
| 65 |
+
instance/
|
| 66 |
+
.webassets-cache
|
| 67 |
+
|
| 68 |
+
# Scrapy stuff:
|
| 69 |
+
.scrapy
|
| 70 |
+
|
| 71 |
+
# Sphinx documentation
|
| 72 |
+
docs/_build/
|
| 73 |
+
|
| 74 |
+
# PyBuilder
|
| 75 |
+
.pybuilder/
|
| 76 |
+
target/
|
| 77 |
+
|
| 78 |
+
# Jupyter Notebook
|
| 79 |
+
.ipynb_checkpoints
|
| 80 |
+
|
| 81 |
+
# IPython
|
| 82 |
+
profile_default/
|
| 83 |
+
ipython_config.py
|
| 84 |
+
|
| 85 |
+
# pyenv
|
| 86 |
+
# For a library or package, you might want to ignore these files since the code is
|
| 87 |
+
# intended to run in multiple environments; otherwise, check them in:
|
| 88 |
+
# .python-version
|
| 89 |
+
|
| 90 |
+
# pipenv
|
| 91 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
| 92 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
| 93 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
| 94 |
+
# install all needed dependencies.
|
| 95 |
+
# Pipfile.lock
|
| 96 |
+
|
| 97 |
+
# UV
|
| 98 |
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
| 99 |
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
| 100 |
+
# commonly ignored for libraries.
|
| 101 |
+
# uv.lock
|
| 102 |
+
|
| 103 |
+
# poetry
|
| 104 |
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
| 105 |
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
| 106 |
+
# commonly ignored for libraries.
|
| 107 |
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
| 108 |
+
# poetry.lock
|
| 109 |
+
# poetry.toml
|
| 110 |
+
|
| 111 |
+
# pdm
|
| 112 |
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
| 113 |
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
| 114 |
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
| 115 |
+
# pdm.lock
|
| 116 |
+
# pdm.toml
|
| 117 |
+
.pdm-python
|
| 118 |
+
.pdm-build/
|
| 119 |
+
|
| 120 |
+
# pixi
|
| 121 |
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
| 122 |
+
# pixi.lock
|
| 123 |
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
| 124 |
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
| 125 |
+
.pixi
|
| 126 |
+
|
| 127 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
| 128 |
+
__pypackages__/
|
| 129 |
+
|
| 130 |
+
# Celery stuff
|
| 131 |
+
celerybeat-schedule
|
| 132 |
+
celerybeat.pid
|
| 133 |
+
|
| 134 |
+
# Redis
|
| 135 |
+
*.rdb
|
| 136 |
+
*.aof
|
| 137 |
+
*.pid
|
| 138 |
+
|
| 139 |
+
# RabbitMQ
|
| 140 |
+
mnesia/
|
| 141 |
+
rabbitmq/
|
| 142 |
+
rabbitmq-data/
|
| 143 |
+
|
| 144 |
+
# ActiveMQ
|
| 145 |
+
activemq-data/
|
| 146 |
+
|
| 147 |
+
# SageMath parsed files
|
| 148 |
+
*.sage.py
|
| 149 |
+
|
| 150 |
+
# Environments
|
| 151 |
+
.env
|
| 152 |
+
.envrc
|
| 153 |
+
.venv
|
| 154 |
+
env/
|
| 155 |
+
venv/
|
| 156 |
+
ENV/
|
| 157 |
+
env.bak/
|
| 158 |
+
venv.bak/
|
| 159 |
+
|
| 160 |
+
# Spyder project settings
|
| 161 |
+
.spyderproject
|
| 162 |
+
.spyproject
|
| 163 |
+
|
| 164 |
+
# Rope project settings
|
| 165 |
+
.ropeproject
|
| 166 |
+
|
| 167 |
+
# mkdocs documentation
|
| 168 |
+
/site
|
| 169 |
+
|
| 170 |
+
# mypy
|
| 171 |
+
.mypy_cache/
|
| 172 |
+
.dmypy.json
|
| 173 |
+
dmypy.json
|
| 174 |
+
|
| 175 |
+
# Pyre type checker
|
| 176 |
+
.pyre/
|
| 177 |
+
|
| 178 |
+
# pytype static type analyzer
|
| 179 |
+
.pytype/
|
| 180 |
+
|
| 181 |
+
# Cython debug symbols
|
| 182 |
+
cython_debug/
|
| 183 |
+
|
| 184 |
+
# PyCharm
|
| 185 |
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
| 186 |
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
| 187 |
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
| 188 |
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
| 189 |
+
# .idea/
|
| 190 |
+
|
| 191 |
+
# Abstra
|
| 192 |
+
# Abstra is an AI-powered process automation framework.
|
| 193 |
+
# Ignore directories containing user credentials, local state, and settings.
|
| 194 |
+
# Learn more at https://abstra.io/docs
|
| 195 |
+
.abstra/
|
| 196 |
+
|
| 197 |
+
# Visual Studio Code
|
| 198 |
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
| 199 |
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
| 200 |
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
| 201 |
+
# you could uncomment the following to ignore the entire vscode folder
|
| 202 |
+
# .vscode/
|
| 203 |
+
# Temporary file for partial code execution
|
| 204 |
+
tempCodeRunnerFile.py
|
| 205 |
+
|
| 206 |
+
# Ruff stuff:
|
| 207 |
+
.ruff_cache/
|
| 208 |
+
|
| 209 |
+
# PyPI configuration file
|
| 210 |
+
.pypirc
|
| 211 |
+
|
| 212 |
+
# Marimo
|
| 213 |
+
marimo/_static/
|
| 214 |
+
marimo/_lsp/
|
| 215 |
+
__marimo__/
|
| 216 |
+
|
| 217 |
+
# Streamlit
|
| 218 |
+
.streamlit/secrets.toml
|
| 219 |
+
|
| 220 |
+
.vscode
|
app.py
CHANGED
|
@@ -1,69 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
-
from
|
|
|
|
|
|
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
system_message,
|
| 9 |
-
max_tokens,
|
| 10 |
-
temperature,
|
| 11 |
-
top_p,
|
| 12 |
-
hf_token: gr.OAuthToken,
|
| 13 |
-
):
|
| 14 |
-
"""
|
| 15 |
-
For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
|
| 16 |
-
"""
|
| 17 |
-
client = InferenceClient(token=hf_token.token, model="openai/gpt-oss-20b")
|
| 18 |
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
-
response = ""
|
| 26 |
|
| 27 |
-
|
| 28 |
-
messages,
|
| 29 |
-
max_tokens=max_tokens,
|
| 30 |
-
stream=True,
|
| 31 |
-
temperature=temperature,
|
| 32 |
-
top_p=top_p,
|
| 33 |
-
):
|
| 34 |
-
choices = message.choices
|
| 35 |
-
token = ""
|
| 36 |
-
if len(choices) and choices[0].delta.content:
|
| 37 |
-
token = choices[0].delta.content
|
| 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 |
if __name__ == "__main__":
|
| 69 |
-
|
|
|
|
|
|
| 1 |
+
"""app.py — Marquee backend (Gradio SDK Space + FastAPI).
|
| 2 |
+
|
| 3 |
+
Architecture:
|
| 4 |
+
- The UI is a static HTML file (Marquee.html + marquee.css + marquee.js)
|
| 5 |
+
served from "/". It talks to plain JSON endpoints at /api/scan and
|
| 6 |
+
/api/generate with plain fetch() — no Gradio JS client needed.
|
| 7 |
+
- An empty gr.Blocks is mounted at /gradio purely so the HF Spaces "gradio"
|
| 8 |
+
SDK runtime + ZeroGPU (@spaces.GPU) machinery initialises.
|
| 9 |
+
|
| 10 |
+
Heavy per-session state (identities, motion, normalized video path) lives in a
|
| 11 |
+
server-side SESSIONS dict keyed by a session_id the scan call returns.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
import base64
|
| 15 |
+
import io
|
| 16 |
+
import json
|
| 17 |
+
import os
|
| 18 |
+
import tempfile
|
| 19 |
+
import uuid
|
| 20 |
+
from pathlib import Path
|
| 21 |
+
|
| 22 |
import gradio as gr
|
| 23 |
+
from fastapi import FastAPI, Request
|
| 24 |
+
from fastapi.responses import HTMLResponse, JSONResponse
|
| 25 |
+
from fastapi.staticfiles import StaticFiles
|
| 26 |
|
| 27 |
+
from commentary import STYLES, _warmup_model, generate_commentary
|
| 28 |
+
from events import events_to_frames, pick_key_events
|
| 29 |
+
from faces import annotate_event_frame, cluster_faces, frame_at, scan_video
|
| 30 |
+
from ov_models import download_models
|
| 31 |
+
from video import get_duration, normalize_video
|
| 32 |
|
| 33 |
+
MAX_SECONDS = 60.0 # keep the demo snappy; matches the UI copy
|
| 34 |
+
HERE = Path(__file__).parent
|
| 35 |
+
SESSIONS = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
+
TIERS = ["HEADLINER", "RISING", "DIVA", "WILDCARD", "BREAKOUT", "CAMEO"]
|
| 38 |
+
VIBE_TO_STYLE = {
|
| 39 |
+
"football": "Football (Premier League hype)",
|
| 40 |
+
"diva": "Diva Hour",
|
| 41 |
+
"wildlife": "Nature documentary",
|
| 42 |
+
"boxing": "Boxing announcer",
|
| 43 |
+
}
|
| 44 |
+
SUPPORTED_VIDEO = {".mp4", ".mov", ".webm", ".mkv", ".avi", ".m4v"}
|
| 45 |
|
| 46 |
+
try:
|
| 47 |
+
download_models()
|
| 48 |
+
except Exception as e:
|
| 49 |
+
print(f"[warn] model download failed at boot: {e}")
|
| 50 |
|
| 51 |
+
_ON_SPACES = bool(os.environ.get("SPACE_ID"))
|
| 52 |
+
if _ON_SPACES:
|
| 53 |
+
print("[boot] HF Space — warming up Qwen (CPU load)...")
|
| 54 |
+
try:
|
| 55 |
+
_warmup_model()
|
| 56 |
+
print("[boot] Model weights resident.")
|
| 57 |
+
except Exception as e:
|
| 58 |
+
print(f"[warn] warmup failed: {e}")
|
| 59 |
+
else:
|
| 60 |
+
print("[boot] Local — Qwen lazy-loads on first /api/generate call.")
|
| 61 |
|
|
|
|
| 62 |
|
| 63 |
+
# ── helpers ──────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
|
| 65 |
+
def _crop_b64(pil, size=160):
|
| 66 |
+
img = pil.copy(); img.thumbnail((size, size))
|
| 67 |
+
buf = io.BytesIO(); img.save(buf, format="PNG")
|
| 68 |
+
return "data:image/png;base64," + base64.b64encode(buf.getvalue()).decode()
|
| 69 |
|
| 70 |
|
| 71 |
+
def _build_ui() -> str:
|
| 72 |
+
"""Inline CSS + JS into Marquee.html so the page is fully self-contained."""
|
| 73 |
+
doc = (HERE / "Marquee.html").read_text()
|
| 74 |
+
css = (HERE / "marquee.css").read_text()
|
| 75 |
+
js = (HERE / "marquee.js").read_text()
|
| 76 |
+
|
| 77 |
+
doc = doc.replace('<link rel="stylesheet" href="marquee.css">',
|
| 78 |
+
f"<style>{css}</style>")
|
| 79 |
+
doc = doc.replace('<script src="marquee.js"></script>',
|
| 80 |
+
f"<script type='module'>{js}</script>")
|
| 81 |
+
return doc
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
# ── FastAPI app ───────────────────────────────────────────────────────────────
|
| 85 |
+
|
| 86 |
+
app = FastAPI()
|
| 87 |
+
|
| 88 |
+
_UI_HTML = _build_ui()
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
@app.get("/", response_class=HTMLResponse)
|
| 92 |
+
async def root():
|
| 93 |
+
return HTMLResponse(_UI_HTML)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
@app.post("/api/scan")
|
| 97 |
+
async def api_scan(request: Request):
|
| 98 |
+
try:
|
| 99 |
+
form = await request.form()
|
| 100 |
+
upload = form.get("file")
|
| 101 |
+
if upload is None:
|
| 102 |
+
return JSONResponse({"ok": False, "error": "no file"}, status_code=400)
|
| 103 |
+
|
| 104 |
+
suffix = Path(upload.filename).suffix.lower()
|
| 105 |
+
if suffix not in SUPPORTED_VIDEO:
|
| 106 |
+
return JSONResponse({"ok": False,
|
| 107 |
+
"error": f"Unsupported format '{suffix}'. Supported: {', '.join(sorted(SUPPORTED_VIDEO))}"})
|
| 108 |
+
|
| 109 |
+
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=suffix)
|
| 110 |
+
tmp.write(await upload.read()); tmp.flush(); tmp.close()
|
| 111 |
+
|
| 112 |
+
dur = get_duration(tmp.name)
|
| 113 |
+
if dur > MAX_SECONDS + 1:
|
| 114 |
+
return JSONResponse({"ok": False, "error": "Clip longer than 60 seconds"})
|
| 115 |
+
|
| 116 |
+
norm_path, w, h = normalize_video(tmp.name)
|
| 117 |
+
embeds, crops, motion, meta = scan_video(norm_path, MAX_SECONDS)
|
| 118 |
+
identities = cluster_faces(embeds, crops)
|
| 119 |
+
|
| 120 |
+
sid = uuid.uuid4().hex
|
| 121 |
+
SESSIONS[sid] = {"norm": norm_path, "identities": identities,
|
| 122 |
+
"motion": motion, "meta": meta}
|
| 123 |
+
|
| 124 |
+
stars = [{"idx": i, "tier": TIERS[i % len(TIERS)],
|
| 125 |
+
"crop": _crop_b64(idn["crop"])}
|
| 126 |
+
for i, idn in enumerate(identities)]
|
| 127 |
+
|
| 128 |
+
return JSONResponse({"ok": True, "session_id": sid,
|
| 129 |
+
"video_url": f"/video/{sid}",
|
| 130 |
+
"duration": get_duration(norm_path),
|
| 131 |
+
"w": w, "h": h, "portrait": h > w, "stars": stars})
|
| 132 |
+
|
| 133 |
+
except Exception as e:
|
| 134 |
+
import traceback; traceback.print_exc()
|
| 135 |
+
return JSONResponse({"ok": False, "error": str(e)}, status_code=500)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
@app.get("/video/{sid}")
|
| 139 |
+
async def serve_video(sid: str):
|
| 140 |
+
"""Stream the normalized video file."""
|
| 141 |
+
from fastapi.responses import FileResponse
|
| 142 |
+
sess = SESSIONS.get(sid)
|
| 143 |
+
if not sess:
|
| 144 |
+
return JSONResponse({"error": "session not found"}, status_code=404)
|
| 145 |
+
return FileResponse(sess["norm"], media_type="video/mp4",
|
| 146 |
+
headers={"Accept-Ranges": "bytes"})
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
@app.post("/api/generate")
|
| 150 |
+
async def api_generate(request: Request):
|
| 151 |
+
try:
|
| 152 |
+
body = await request.json()
|
| 153 |
+
sid = body.get("session_id", "")
|
| 154 |
+
names = body.get("names", [])
|
| 155 |
+
vibe = body.get("vibe", "football")
|
| 156 |
+
|
| 157 |
+
sess = SESSIONS.get(sid)
|
| 158 |
+
if not sess:
|
| 159 |
+
return JSONResponse({"ok": False, "error": "Session expired — re-cast"})
|
| 160 |
+
|
| 161 |
+
name_map = {i: str(n).strip() for i, n in enumerate(names) if str(n).strip()}
|
| 162 |
+
style = VIBE_TO_STYLE.get(vibe, next(iter(STYLES)))
|
| 163 |
+
|
| 164 |
+
fps = sess["meta"]["fps"]
|
| 165 |
+
event_times = pick_key_events(sess["motion"], fps)
|
| 166 |
+
event_idxs = events_to_frames(event_times, fps)
|
| 167 |
+
|
| 168 |
+
frames = []
|
| 169 |
+
for ts, fidx in zip(event_times, event_idxs):
|
| 170 |
+
bgr = frame_at(sess["norm"], fidx)
|
| 171 |
+
if bgr is None: continue
|
| 172 |
+
frames.append((ts, annotate_event_frame(bgr, sess["identities"], name_map)))
|
| 173 |
+
|
| 174 |
+
script = generate_commentary(frames, list(name_map.values()), style)
|
| 175 |
+
return JSONResponse({"ok": True,
|
| 176 |
+
"script": [{"t": round(float(l["time"]), 2), "text": l["text"]}
|
| 177 |
+
for l in script]})
|
| 178 |
+
|
| 179 |
+
except Exception as e:
|
| 180 |
+
import traceback; traceback.print_exc()
|
| 181 |
+
return JSONResponse({"ok": False, "error": str(e)}, status_code=500)
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
# ── Gradio shim (required for the HF "gradio" SDK runtime + ZeroGPU) ──────────
|
| 185 |
+
|
| 186 |
+
with gr.Blocks(title="Marquee") as _gr:
|
| 187 |
+
gr.HTML("<p style='display:none'>Marquee backend</p>")
|
| 188 |
|
| 189 |
+
app = gr.mount_gradio_app(app, _gr, path="/gradio")
|
| 190 |
|
| 191 |
if __name__ == "__main__":
|
| 192 |
+
import uvicorn
|
| 193 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
commentary.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""commentary.py — the VLM brain. Read the [VLM 101]/[LLM 101] notes once;
|
| 2 |
+
that's most of the mental model for working with these models anywhere.
|
| 3 |
+
|
| 4 |
+
Key design choice for the north star: the VLM does NOT invent timestamps.
|
| 5 |
+
events.py already picked real key-event times; we hand the model one annotated
|
| 6 |
+
frame per event and ask for one line per event. So every line is pinned to a
|
| 7 |
+
real moment -> speech can be placed exactly there later. Deterministic sync.
|
| 8 |
+
|
| 9 |
+
[ZeroGPU 101] On HF ZeroGPU, CUDA is NOT initialised in the main process — you
|
| 10 |
+
must never call .cuda() at import / module scope. The pattern that actually
|
| 11 |
+
works: load the model to CPU once (cheap RAM, no GPU), then move it to GPU
|
| 12 |
+
*inside* a @spaces.GPU function, which is the only place a GPU exists. Using
|
| 13 |
+
device_map="auto" at import races with that mechanism and is the usual cause of
|
| 14 |
+
"model on wrong device" errors on ZeroGPU.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
import json
|
| 18 |
+
import re
|
| 19 |
+
|
| 20 |
+
import spaces
|
| 21 |
+
import torch
|
| 22 |
+
|
| 23 |
+
MODEL_ID = "Qwen/Qwen2.5-VL-7B-Instruct"
|
| 24 |
+
_model = _processor = None
|
| 25 |
+
|
| 26 |
+
STYLES = {
|
| 27 |
+
"Football (Premier League hype)":
|
| 28 |
+
"an over-the-top English football commentator, dramatic and poetic",
|
| 29 |
+
"MasterChef judge":
|
| 30 |
+
"a brutally dramatic cooking-show commentator treating everything like "
|
| 31 |
+
"a high-stakes elimination",
|
| 32 |
+
"Nature documentary":
|
| 33 |
+
"a hushed, awestruck nature narrator observing these humans like rare "
|
| 34 |
+
"wildlife",
|
| 35 |
+
"Boxing announcer":
|
| 36 |
+
"a booming boxing announcer treating every move as championship-defining",
|
| 37 |
+
"Diva Hour":
|
| 38 |
+
"a fabulous, shady red-carpet diva commentator narrating like everyone "
|
| 39 |
+
"is a celebrity arriving at a star-studded gala, full of glamour and sass",
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
# [LLM 101] System prompt = persona + a STRICT output contract. Show the exact
|
| 43 |
+
# JSON schema, say what not to do, keep rules last. We tell it the names so it
|
| 44 |
+
# uses them even if a label is hard to read in a given frame.
|
| 45 |
+
SYSTEM_PROMPT = """You are {persona}.
|
| 46 |
+
|
| 47 |
+
You'll receive ordered keyframes from a short video. Each is tagged with its
|
| 48 |
+
timestamp. Some people have their NAME drawn in a box above their head — call
|
| 49 |
+
those people by name, loudly and often. Unnamed people: describe them ("the
|
| 50 |
+
challenger in red").
|
| 51 |
+
|
| 52 |
+
People in this clip: {roster}.
|
| 53 |
+
|
| 54 |
+
Write ONE punchy line of hype commentary per frame.
|
| 55 |
+
|
| 56 |
+
Rules:
|
| 57 |
+
- Output ONLY a JSON array. No markdown, no extra text.
|
| 58 |
+
- Schema: [{{"time": <float seconds>, "text": "<one line>"}}]
|
| 59 |
+
- Use the EXACT timestamp given for each frame as "time".
|
| 60 |
+
- Each line under 16 words (it gets spoken before the next event).
|
| 61 |
+
- USE CAPS + exclamation for big moments; the TTS reads punctuation as emotion.
|
| 62 |
+
- Be funny. Treat mundane actions as legendary athletic feats."""
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def _load():
|
| 66 |
+
"""Load Qwen weights to CPU and cache in globals. No CUDA here — that's
|
| 67 |
+
illegal at module scope on ZeroGPU. Call once at boot (warmup) so the first
|
| 68 |
+
real request only pays the CPU->GPU move, not a cold download.
|
| 69 |
+
"""
|
| 70 |
+
global _model, _processor
|
| 71 |
+
if _model is not None:
|
| 72 |
+
return _model, _processor
|
| 73 |
+
|
| 74 |
+
from transformers import (AutoProcessor,
|
| 75 |
+
Qwen2_5_VLForConditionalGeneration)
|
| 76 |
+
print("[info] Loading Qwen2.5-VL-7B to CPU (first time: download + load)...")
|
| 77 |
+
_model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
|
| 78 |
+
MODEL_ID, torch_dtype=torch.bfloat16)
|
| 79 |
+
_processor = AutoProcessor.from_pretrained(MODEL_ID)
|
| 80 |
+
print("[info] Qwen weights resident in RAM.")
|
| 81 |
+
return _model, _processor
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def _warmup_model():
|
| 85 |
+
"""Boot-time hook: pull weights to disk + RAM so the first /api/generate
|
| 86 |
+
only pays the CPU->GPU move. No GPU is touched here, so it's safe to call
|
| 87 |
+
outside any @spaces.GPU window at app startup.
|
| 88 |
+
"""
|
| 89 |
+
return _load()
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
@spaces.GPU(duration=120)
|
| 93 |
+
def generate_commentary(event_frames, roster, style_key):
|
| 94 |
+
"""event_frames: [(timestamp_sec, PIL.Image), ...] (annotated, key events)
|
| 95 |
+
roster: list of names the user assigned
|
| 96 |
+
-> [{"time": float, "text": str}, ...] aligned to the event times.
|
| 97 |
+
"""
|
| 98 |
+
model, processor = _load()
|
| 99 |
+
if torch.cuda.is_available(): # ZeroGPU: GPU only exists in here
|
| 100 |
+
model = model.to("cuda")
|
| 101 |
+
from qwen_vl_utils import process_vision_info
|
| 102 |
+
|
| 103 |
+
# [VLM 101] Multimodal user turn: content is a list mixing text + images.
|
| 104 |
+
# We label each image with its real timestamp so the model echoes it back.
|
| 105 |
+
content = []
|
| 106 |
+
for ts, img in event_frames:
|
| 107 |
+
content.append({"type": "text", "text": f"Frame at t={ts:.2f}s:"})
|
| 108 |
+
content.append({"type": "image", "image": img})
|
| 109 |
+
content.append({"type": "text", "text": "Write the commentary JSON now."})
|
| 110 |
+
|
| 111 |
+
persona = STYLES.get(style_key, list(STYLES.values())[0])
|
| 112 |
+
roster_str = ", ".join(roster) if roster else "nobody named yet"
|
| 113 |
+
messages = [
|
| 114 |
+
{"role": "system",
|
| 115 |
+
"content": SYSTEM_PROMPT.format(persona=persona, roster=roster_str)},
|
| 116 |
+
{"role": "user", "content": content},
|
| 117 |
+
]
|
| 118 |
+
|
| 119 |
+
# [LLM 101] Chat template converts message dicts into the exact special-
|
| 120 |
+
# token format the instruct model was trained on. Skip it and quality dies.
|
| 121 |
+
text = processor.apply_chat_template(messages, tokenize=False,
|
| 122 |
+
add_generation_prompt=True)
|
| 123 |
+
image_inputs, _ = process_vision_info(messages)
|
| 124 |
+
inputs = processor(text=[text], images=image_inputs,
|
| 125 |
+
return_tensors="pt").to(model.device)
|
| 126 |
+
|
| 127 |
+
# [LLM 101] temperature 0.8 -> a bit of comedic randomness; max_new_tokens
|
| 128 |
+
# caps output length (your latency/cost lever).
|
| 129 |
+
out = model.generate(**inputs, max_new_tokens=500, do_sample=True,
|
| 130 |
+
temperature=0.8, top_p=0.95)
|
| 131 |
+
new = out[:, inputs.input_ids.shape[1]:]
|
| 132 |
+
raw = processor.batch_decode(new, skip_special_tokens=True)[0]
|
| 133 |
+
|
| 134 |
+
valid_times = [round(ts, 2) for ts, _ in event_frames]
|
| 135 |
+
return _parse(raw, valid_times)
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def _parse(raw: str, valid_times: list[float]) -> list[dict]:
|
| 139 |
+
"""[LLM 101] Never trust LLM output formatting. Strip fences, grab the JSON
|
| 140 |
+
array, snap each line's time to the nearest real event time, degrade
|
| 141 |
+
gracefully if it returns garbage.
|
| 142 |
+
"""
|
| 143 |
+
raw = re.sub(r"```(?:json)?", "", raw).strip()
|
| 144 |
+
m = re.search(r"\[.*\]", raw, re.DOTALL)
|
| 145 |
+
try:
|
| 146 |
+
items = json.loads(m.group(0) if m else raw)
|
| 147 |
+
except (json.JSONDecodeError, AttributeError):
|
| 148 |
+
items = []
|
| 149 |
+
|
| 150 |
+
script = []
|
| 151 |
+
for i, it in enumerate(items):
|
| 152 |
+
try:
|
| 153 |
+
txt = str(it["text"]).strip()
|
| 154 |
+
t = float(it.get("time", valid_times[min(i, len(valid_times) - 1)]))
|
| 155 |
+
except (KeyError, TypeError, ValueError):
|
| 156 |
+
continue
|
| 157 |
+
# snap to nearest real event timestamp
|
| 158 |
+
t = min(valid_times, key=lambda v: abs(v - t)) if valid_times else t
|
| 159 |
+
if txt:
|
| 160 |
+
script.append({"time": t, "text": txt})
|
| 161 |
+
|
| 162 |
+
if not script and valid_times:
|
| 163 |
+
script = [{"time": valid_times[0],
|
| 164 |
+
"text": "WHAT A MOMENT, LADIES AND GENTLEMEN!"}]
|
| 165 |
+
script.sort(key=lambda x: x["time"])
|
| 166 |
+
return script
|
download_model.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""download_model.py — run this ONCE before `python app.py`.
|
| 2 |
+
|
| 3 |
+
Downloads Qwen2.5-VL-7B to your HF cache (~/.cache/huggingface/hub).
|
| 4 |
+
After this, app.py finds it in cache and loads in ~30 sec (no re-download).
|
| 5 |
+
|
| 6 |
+
Usage:
|
| 7 |
+
python download_model.py
|
| 8 |
+
|
| 9 |
+
If you interrupt (Ctrl+C), run again — huggingface_hub resumes partial downloads.
|
| 10 |
+
Set HF_TOKEN env var for higher rate limits (optional, not required for this model):
|
| 11 |
+
HF_TOKEN=hf_xxx python download_model.py
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
import os
|
| 15 |
+
import sys
|
| 16 |
+
|
| 17 |
+
MODEL_ID = "Qwen/Qwen2.5-VL-7B-Instruct"
|
| 18 |
+
# MODEL_ID = "Qwen2.5-VL-3B-Instruct"
|
| 19 |
+
|
| 20 |
+
def main():
|
| 21 |
+
try:
|
| 22 |
+
from huggingface_hub import snapshot_download
|
| 23 |
+
except ImportError:
|
| 24 |
+
print("[error] huggingface_hub not installed. Run: pip install huggingface_hub")
|
| 25 |
+
sys.exit(1)
|
| 26 |
+
|
| 27 |
+
token = os.environ.get("HF_TOKEN")
|
| 28 |
+
if not token:
|
| 29 |
+
print("[info] No HF_TOKEN set. Download will work but rate-limited.")
|
| 30 |
+
print("[info] Optional: set HF_TOKEN env var for faster downloads.")
|
| 31 |
+
|
| 32 |
+
print(f"[info] Downloading {MODEL_ID} to HF cache...")
|
| 33 |
+
print("[info] This is ~16 GB. First time: ~5-30 min depending on connection.")
|
| 34 |
+
print("[info] Safe to Ctrl+C and resume — will pick up where it left off.\n")
|
| 35 |
+
|
| 36 |
+
try:
|
| 37 |
+
path = snapshot_download(
|
| 38 |
+
repo_id=MODEL_ID,
|
| 39 |
+
token=token,
|
| 40 |
+
resume_download=True,
|
| 41 |
+
)
|
| 42 |
+
print(f"\n[done] Model cached at: {path}")
|
| 43 |
+
print("[done] You can now run: python app.py")
|
| 44 |
+
except KeyboardInterrupt:
|
| 45 |
+
print("\n[interrupted] Partial download saved. Run again to resume.")
|
| 46 |
+
sys.exit(0)
|
| 47 |
+
except Exception as e:
|
| 48 |
+
print(f"\n[error] Download failed: {e}")
|
| 49 |
+
sys.exit(1)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
if __name__ == "__main__":
|
| 53 |
+
main()
|
events.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""events.py — pick "key events" from the motion signal. No model: a key event
|
| 2 |
+
is just a local peak in frame-to-frame motion (someone moved a lot = something
|
| 3 |
+
happened). Returns timestamps the VLM will comment on and the TTS will sync to.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import numpy as np
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def pick_key_events(motion: dict, fps: float, max_events: int = 10,
|
| 10 |
+
min_gap_sec: float = 1.5) -> list[float]:
|
| 11 |
+
"""motion: {frame_idx: score}. Returns sorted list of timestamps (sec).
|
| 12 |
+
|
| 13 |
+
Greedy non-max suppression: take the highest-motion frame, suppress its
|
| 14 |
+
neighbours within min_gap, repeat. This keeps events spread out instead of
|
| 15 |
+
clustering 5 lines onto one busy moment.
|
| 16 |
+
"""
|
| 17 |
+
if not motion:
|
| 18 |
+
return [0.5]
|
| 19 |
+
|
| 20 |
+
# Only above-average-motion frames count as "events" — otherwise a calm
|
| 21 |
+
# clip would spawn fake events on still frames just to hit max_events.
|
| 22 |
+
scores = np.array(list(motion.values()), dtype=float)
|
| 23 |
+
floor = scores.mean()
|
| 24 |
+
candidates = {f: s for f, s in motion.items() if s > floor}
|
| 25 |
+
if not candidates: # totally flat clip -> uniform
|
| 26 |
+
n = min(max_events, max(1, int(len(motion) / (min_gap_sec * fps))))
|
| 27 |
+
step = len(motion) / max(1, n)
|
| 28 |
+
return sorted((i + 0.5) * step / fps for i in range(n))
|
| 29 |
+
|
| 30 |
+
items = sorted(candidates.items(), key=lambda kv: kv[1], reverse=True)
|
| 31 |
+
min_gap_frames = int(min_gap_sec * fps)
|
| 32 |
+
|
| 33 |
+
chosen = []
|
| 34 |
+
for frame_idx, _score in items:
|
| 35 |
+
if all(abs(frame_idx - c) >= min_gap_frames for c in chosen):
|
| 36 |
+
chosen.append(frame_idx)
|
| 37 |
+
if len(chosen) >= max_events:
|
| 38 |
+
break
|
| 39 |
+
|
| 40 |
+
chosen.sort()
|
| 41 |
+
return [c / fps for c in chosen]
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def events_to_frames(timestamps: list[float], fps: float) -> list[int]:
|
| 45 |
+
return [int(round(ts * fps)) for ts in timestamps]
|
faces.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""faces.py — face recog used PURELY to feed names to the VLM. Nothing here is
|
| 2 |
+
ever shown in the player; boxes/labels exist only as VLM input.
|
| 3 |
+
|
| 4 |
+
Because we don't draw labels on output, we don't need dense detection or
|
| 5 |
+
tracking. We:
|
| 6 |
+
1. sample sparsely (~2 fps) to enumerate everyone + score motion
|
| 7 |
+
2. align (landmarks) + embed (re-id) each face
|
| 8 |
+
3. cluster embeddings -> unique identities -> crops for the naming gallery
|
| 9 |
+
Then, separately, for the handful of key-event frames, we detect + match +
|
| 10 |
+
burn names on a throwaway copy that goes to the VLM.
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
import cv2
|
| 14 |
+
import numpy as np
|
| 15 |
+
from PIL import Image
|
| 16 |
+
|
| 17 |
+
from ov_models import OVModel
|
| 18 |
+
|
| 19 |
+
DET_CONF = 0.6
|
| 20 |
+
SAME_PERSON = 0.55 # cosine sim above this = same identity (tune 0.4-0.6)
|
| 21 |
+
SAMPLE_FPS = 2.0 # detection sampling rate (identity discovery)
|
| 22 |
+
|
| 23 |
+
# ArcFace-style reference 5 points, scaled to a 128x128 aligned crop.
|
| 24 |
+
_REF_5PTS = np.array([
|
| 25 |
+
[38.2946, 51.6963], [73.5318, 51.5014], [56.0252, 71.7366],
|
| 26 |
+
[41.5493, 92.3655], [70.7299, 92.2041],
|
| 27 |
+
], dtype=np.float32) * (128.0 / 112.0)
|
| 28 |
+
|
| 29 |
+
_det = _lm = _reid = None
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _load():
|
| 33 |
+
global _det, _lm, _reid
|
| 34 |
+
if _det is None:
|
| 35 |
+
_det = OVModel("face-detection-retail-0004")
|
| 36 |
+
_lm = OVModel("landmarks-regression-retail-0009")
|
| 37 |
+
_reid = OVModel("face-reidentification-retail-0095")
|
| 38 |
+
return _det, _lm, _reid
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def _detect(frame_bgr) -> list[list[int]]:
|
| 42 |
+
det, _, _ = _load()
|
| 43 |
+
h, w = frame_bgr.shape[:2]
|
| 44 |
+
out = det.infer(frame_bgr).reshape(-1, 7) # [_,_,conf,x1,y1,x2,y2] norm
|
| 45 |
+
boxes = []
|
| 46 |
+
for _, _, conf, x1, y1, x2, y2 in out:
|
| 47 |
+
if conf < DET_CONF:
|
| 48 |
+
continue
|
| 49 |
+
boxes.append([max(0, int(x1 * w)), max(0, int(y1 * h)),
|
| 50 |
+
min(w, int(x2 * w)), min(h, int(y2 * h))])
|
| 51 |
+
return boxes
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def _aligned_embed(frame_bgr, box) -> np.ndarray | None:
|
| 55 |
+
"""Crop -> landmark-align to 128x128 -> re-id embedding (L2-normalized).
|
| 56 |
+
Alignment matters: re-id-0095 expects an aligned face; raw crops cluster
|
| 57 |
+
badly on tilted/profile shots, which would split or merge people.
|
| 58 |
+
"""
|
| 59 |
+
_, lm, reid = _load()
|
| 60 |
+
x1, y1, x2, y2 = box
|
| 61 |
+
crop = frame_bgr[y1:y2, x1:x2]
|
| 62 |
+
if crop.size == 0:
|
| 63 |
+
return None
|
| 64 |
+
|
| 65 |
+
# landmarks-regression-retail-0009 -> 10 values = 5 (x,y) in crop-relative
|
| 66 |
+
pts = lm.infer(crop).reshape(5, 2)
|
| 67 |
+
pts[:, 0] *= (x2 - x1)
|
| 68 |
+
pts[:, 1] *= (y2 - y1)
|
| 69 |
+
pts[:, 0] += x1
|
| 70 |
+
pts[:, 1] += y1
|
| 71 |
+
|
| 72 |
+
M, _ = cv2.estimateAffinePartial2D(pts.astype(np.float32), _REF_5PTS)
|
| 73 |
+
if M is None:
|
| 74 |
+
aligned = cv2.resize(crop, (128, 128)) # fallback: no alignment
|
| 75 |
+
else:
|
| 76 |
+
aligned = cv2.warpAffine(frame_bgr, M, (128, 128))
|
| 77 |
+
|
| 78 |
+
v = reid.infer(aligned).reshape(-1)
|
| 79 |
+
return v / (np.linalg.norm(v) + 1e-9)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def scan_video(video_path: str, max_seconds: float = 600.0):
|
| 83 |
+
"""Sparse pass. Returns:
|
| 84 |
+
embeds: list[np.ndarray] # one per detected face
|
| 85 |
+
crops: list[PIL.Image] # matching crops
|
| 86 |
+
motion: {frame_idx: float} # for key-event picking
|
| 87 |
+
meta: {"fps", "n_frames"}
|
| 88 |
+
"""
|
| 89 |
+
_load()
|
| 90 |
+
cap = cv2.VideoCapture(video_path)
|
| 91 |
+
fps = cap.get(cv2.CAP_PROP_FPS) or 30.0
|
| 92 |
+
stride = max(1, int(round(fps / SAMPLE_FPS)))
|
| 93 |
+
|
| 94 |
+
embeds, crops, motion = [], [], {}
|
| 95 |
+
prev_small = None
|
| 96 |
+
idx = 0
|
| 97 |
+
while True:
|
| 98 |
+
ok, frame = cap.read()
|
| 99 |
+
if not ok or idx / fps > max_seconds:
|
| 100 |
+
break
|
| 101 |
+
if idx % stride == 0:
|
| 102 |
+
# motion vs previous SAMPLED frame (coarse but fine for events)
|
| 103 |
+
small = cv2.cvtColor(cv2.resize(frame, (160, 90)), cv2.COLOR_BGR2GRAY)
|
| 104 |
+
if prev_small is not None:
|
| 105 |
+
motion[idx] = float(np.mean(cv2.absdiff(small, prev_small)))
|
| 106 |
+
prev_small = small
|
| 107 |
+
|
| 108 |
+
for box in _detect(frame):
|
| 109 |
+
emb = _aligned_embed(frame, box)
|
| 110 |
+
if emb is None:
|
| 111 |
+
continue
|
| 112 |
+
embeds.append(emb)
|
| 113 |
+
x1, y1, x2, y2 = box
|
| 114 |
+
crops.append(Image.fromarray(
|
| 115 |
+
cv2.cvtColor(frame[y1:y2, x1:x2], cv2.COLOR_BGR2RGB)))
|
| 116 |
+
idx += 1
|
| 117 |
+
n_frames = idx
|
| 118 |
+
cap.release()
|
| 119 |
+
return embeds, crops, motion, {"fps": fps, "n_frames": n_frames}
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def cluster_faces(embeds, crops):
|
| 123 |
+
"""Greedy-cluster all detected faces into unique identities. Returns:
|
| 124 |
+
identities: [{"centroid": emb, "crop": best PIL, "count": n}, ...]
|
| 125 |
+
Index in this list == identity id used everywhere else.
|
| 126 |
+
"""
|
| 127 |
+
identities = []
|
| 128 |
+
for emb, crop in zip(embeds, crops):
|
| 129 |
+
best, best_sim = None, SAME_PERSON
|
| 130 |
+
for ident in identities:
|
| 131 |
+
sim = float(np.dot(emb, ident["centroid"]))
|
| 132 |
+
if sim >= best_sim:
|
| 133 |
+
best, best_sim = ident, sim
|
| 134 |
+
if best is None:
|
| 135 |
+
identities.append({"centroid": emb, "crop": crop,
|
| 136 |
+
"crop_area": crop.size[0] * crop.size[1],
|
| 137 |
+
"count": 1})
|
| 138 |
+
else:
|
| 139 |
+
n = best["count"]
|
| 140 |
+
best["centroid"] = (best["centroid"] * n + emb) / (n + 1)
|
| 141 |
+
best["centroid"] /= (np.linalg.norm(best["centroid"]) + 1e-9)
|
| 142 |
+
best["count"] = n + 1
|
| 143 |
+
if crop.size[0] * crop.size[1] > best["crop_area"]:
|
| 144 |
+
best["crop"] = crop
|
| 145 |
+
best["crop_area"] = crop.size[0] * crop.size[1]
|
| 146 |
+
# drop singletons (likely false detections) unless that leaves nothing
|
| 147 |
+
strong = [i for i in identities if i["count"] >= 2]
|
| 148 |
+
return strong or identities
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def annotate_event_frame(frame_bgr, identities, names) -> Image.Image:
|
| 152 |
+
"""For ONE key-event frame: detect faces, match each to a named identity,
|
| 153 |
+
burn the name above the box. This image is VLM-input only — the user never
|
| 154 |
+
sees it. This is how the VLM grounds names to the right person.
|
| 155 |
+
"""
|
| 156 |
+
img = frame_bgr.copy()
|
| 157 |
+
centroids = [i["centroid"] for i in identities]
|
| 158 |
+
for box in _detect(frame_bgr):
|
| 159 |
+
emb = _aligned_embed(frame_bgr, box)
|
| 160 |
+
if emb is None or not centroids:
|
| 161 |
+
continue
|
| 162 |
+
sims = [float(np.dot(emb, c)) for c in centroids]
|
| 163 |
+
iid = int(np.argmax(sims))
|
| 164 |
+
if sims[iid] < SAME_PERSON:
|
| 165 |
+
continue
|
| 166 |
+
name = names.get(iid, "").strip()
|
| 167 |
+
if not name:
|
| 168 |
+
continue
|
| 169 |
+
x1, y1, x2, y2 = box
|
| 170 |
+
scale = max(0.5, (x2 - x1) / 200.0)
|
| 171 |
+
thick = max(1, int(scale * 2))
|
| 172 |
+
(tw, th), _ = cv2.getTextSize(name, cv2.FONT_HERSHEY_DUPLEX, scale, thick)
|
| 173 |
+
tx, ty = x1, max(th + 6, y1 - 8)
|
| 174 |
+
cv2.rectangle(img, (tx - 4, ty - th - 6), (tx + tw + 4, ty + 4),
|
| 175 |
+
(0, 0, 0), -1)
|
| 176 |
+
cv2.putText(img, name, (tx, ty), cv2.FONT_HERSHEY_DUPLEX,
|
| 177 |
+
scale, (0, 255, 255), thick, cv2.LINE_AA)
|
| 178 |
+
return Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def frame_at(video_path: str, frame_idx: int):
|
| 182 |
+
cap = cv2.VideoCapture(video_path)
|
| 183 |
+
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_idx)
|
| 184 |
+
ok, frame = cap.read()
|
| 185 |
+
cap.release()
|
| 186 |
+
return frame if ok else None
|
marquee.css
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* ============================================================
|
| 2 |
+
MARQUEE — design system
|
| 3 |
+
Tokens straight from the brief so they lift into Gradio css=.
|
| 4 |
+
Sections: tokens · base · masthead · rundown · desk · stage
|
| 5 |
+
· star card · vibes + Coco · broadcast player · utilities
|
| 6 |
+
============================================================ */
|
| 7 |
+
|
| 8 |
+
:root{
|
| 9 |
+
/* night & surfaces */
|
| 10 |
+
--night:#190B2E; /* base */
|
| 11 |
+
--night-2:#120821; /* deeper, for wells */
|
| 12 |
+
--stage:#241241; /* panels */
|
| 13 |
+
--stage-2:#2E1A52; /* raised */
|
| 14 |
+
--raise:#382060; /* inputs-on-dark */
|
| 15 |
+
--line:#3A2A5E; /* borders */
|
| 16 |
+
--line-soft:#2C1E4C;
|
| 17 |
+
|
| 18 |
+
/* leads + helper */
|
| 19 |
+
--hot:#FF3D8B; /* primary / on-air / energy */
|
| 20 |
+
--gold:#FFC94D; /* stardom: lights, crowns */
|
| 21 |
+
--cyan:#3DE7FF; /* confirm / scan */
|
| 22 |
+
--violet:#B98BFF; /* playful / mascot */
|
| 23 |
+
|
| 24 |
+
/* text */
|
| 25 |
+
--cream:#FFF3E9;
|
| 26 |
+
--muted:#A98FC9;
|
| 27 |
+
--muted-2:#7C66A6;
|
| 28 |
+
|
| 29 |
+
/* the casting rainbow — each star gets their own character color.
|
| 30 |
+
all high-chroma, all in-family: the "agent select" punch. */
|
| 31 |
+
--star-gold:#FFC94D;
|
| 32 |
+
--star-hot:#FF4D9A;
|
| 33 |
+
--star-cyan:#3DE7FF;
|
| 34 |
+
--star-violet:#C18BFF;
|
| 35 |
+
--star-lime:#9BE85B;
|
| 36 |
+
--star-tang:#FF8A4D;
|
| 37 |
+
|
| 38 |
+
--fx:cubic-bezier(.2,.9,.25,1);
|
| 39 |
+
--pop:cubic-bezier(.2,1.7,.4,1);
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
*{box-sizing:border-box;margin:0;padding:0}
|
| 43 |
+
html{-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
|
| 44 |
+
|
| 45 |
+
body{
|
| 46 |
+
font-family:Nunito,system-ui,sans-serif;
|
| 47 |
+
color:var(--cream);
|
| 48 |
+
background:var(--night);
|
| 49 |
+
min-height:100vh;
|
| 50 |
+
line-height:1.5;
|
| 51 |
+
/* one justified light: a stage spotlight from above + a faint floor wash.
|
| 52 |
+
no decorative blob gradients. */
|
| 53 |
+
background-image:
|
| 54 |
+
radial-gradient(120% 70% at 50% -25%, rgba(255,201,77,.10), transparent 55%),
|
| 55 |
+
radial-gradient(140% 90% at 50% 120%, rgba(61,231,255,.05), transparent 60%);
|
| 56 |
+
background-attachment:fixed;
|
| 57 |
+
}
|
| 58 |
+
/* film grain — the only decorative texture, very subtle */
|
| 59 |
+
body::after{
|
| 60 |
+
content:"";position:fixed;inset:0;pointer-events:none;z-index:80;opacity:.04;
|
| 61 |
+
mix-blend-mode:overlay;
|
| 62 |
+
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
.disp{font-family:Unbounded,sans-serif}
|
| 66 |
+
.marq{font-family:'Bebas Neue',sans-serif;letter-spacing:.06em}
|
| 67 |
+
svg.i{width:1em;height:1em;display:block;flex:none}
|
| 68 |
+
|
| 69 |
+
.shell{max-width:1180px;margin:0 auto;padding:clamp(16px,3vw,34px) clamp(16px,4vw,40px) 40px;position:relative;z-index:1}
|
| 70 |
+
|
| 71 |
+
/* ============================================================
|
| 72 |
+
MASTHEAD — the broadcast header bar. signature lives here.
|
| 73 |
+
============================================================ */
|
| 74 |
+
.masthead{
|
| 75 |
+
display:flex;align-items:center;justify-content:space-between;gap:20px;
|
| 76 |
+
padding-bottom:22px;margin-bottom:6px;flex-wrap:wrap;
|
| 77 |
+
}
|
| 78 |
+
.brand{display:flex;align-items:center;gap:18px}
|
| 79 |
+
|
| 80 |
+
/* the lit marquee sign — spend the boldness here */
|
| 81 |
+
.sign{
|
| 82 |
+
position:relative;padding:14px 22px 15px;border-radius:14px;
|
| 83 |
+
border:2px solid rgba(255,201,77,.55);
|
| 84 |
+
background:
|
| 85 |
+
radial-gradient(120% 140% at 50% 0%, rgba(255,201,77,.14), transparent 70%),
|
| 86 |
+
linear-gradient(180deg, rgba(255,201,77,.05), rgba(25,11,46,.2));
|
| 87 |
+
box-shadow:inset 0 0 22px rgba(255,201,77,.16), 0 10px 40px rgba(255,61,139,.10);
|
| 88 |
+
}
|
| 89 |
+
.sign h1{
|
| 90 |
+
font-family:Unbounded,sans-serif;font-weight:800;font-size:clamp(26px,3.4vw,38px);
|
| 91 |
+
letter-spacing:.04em;line-height:.9;
|
| 92 |
+
color:var(--cream);
|
| 93 |
+
text-shadow:0 0 18px rgba(255,201,77,.45), 0 0 2px rgba(255,201,77,.8);
|
| 94 |
+
}
|
| 95 |
+
/* perimeter bulbs, top + bottom rows */
|
| 96 |
+
.sign .bulbs{position:absolute;left:14px;right:14px;display:flex;justify-content:space-between}
|
| 97 |
+
.sign .bulbs.t{top:-5px}.sign .bulbs.b{bottom:-5px}
|
| 98 |
+
.sign .bulbs i{width:6px;height:6px;border-radius:50%;background:var(--gold);
|
| 99 |
+
box-shadow:0 0 8px var(--gold),0 0 2px #fff;animation:tw 2.6s infinite}
|
| 100 |
+
.sign .bulbs i:nth-child(2n){animation-delay:.7s}
|
| 101 |
+
.sign .bulbs i:nth-child(3n){animation-delay:1.5s;background:var(--hot);box-shadow:0 0 8px var(--hot),0 0 2px #fff}
|
| 102 |
+
@keyframes tw{0%,100%{opacity:1}48%{opacity:.28}}
|
| 103 |
+
|
| 104 |
+
.tag{font-family:Unbounded,sans-serif;font-weight:500;font-size:13px;color:var(--gold);letter-spacing:.01em}
|
| 105 |
+
.tag small{display:block;font-family:Nunito;font-weight:600;color:var(--muted);font-size:12px;margin-top:3px;letter-spacing:0}
|
| 106 |
+
|
| 107 |
+
/* right side: live clock + status */
|
| 108 |
+
.broadcast-meta{display:flex;align-items:center;gap:14px}
|
| 109 |
+
.clock{font-family:'Bebas Neue',sans-serif;letter-spacing:.12em;font-size:20px;color:var(--muted);
|
| 110 |
+
font-variant-numeric:tabular-nums}
|
| 111 |
+
.status{display:flex;align-items:center;gap:9px;white-space:nowrap;font-family:'Bebas Neue',sans-serif;letter-spacing:.14em;
|
| 112 |
+
font-size:16px;color:var(--muted);border:1px solid var(--line);padding:8px 15px;border-radius:999px;
|
| 113 |
+
background:rgba(46,26,82,.5)}
|
| 114 |
+
.status .led{width:9px;height:9px;border-radius:50%;background:var(--muted-2);transition:.3s}
|
| 115 |
+
.status.live{color:var(--hot);border-color:rgba(255,61,139,.5);background:rgba(255,61,139,.08)}
|
| 116 |
+
.status.live .led{background:var(--hot);box-shadow:0 0 12px var(--hot);animation:tw 1.1s infinite}
|
| 117 |
+
|
| 118 |
+
/* ============================================================
|
| 119 |
+
RUNDOWN — broadcast running order, not 4 identical cards.
|
| 120 |
+
nodes on a connecting track.
|
| 121 |
+
============================================================ */
|
| 122 |
+
.rundown{position:relative;display:grid;grid-template-columns:repeat(4,1fr);
|
| 123 |
+
margin:8px 0 30px;padding:0 6px}
|
| 124 |
+
.rundown::before{content:"";position:absolute;top:19px;left:11%;right:11%;height:2px;
|
| 125 |
+
background:var(--line-soft)}
|
| 126 |
+
.cue{position:relative;display:flex;flex-direction:column;align-items:center;gap:9px;text-align:center}
|
| 127 |
+
.cue .node{width:40px;height:40px;border-radius:50%;display:grid;place-items:center;
|
| 128 |
+
background:var(--stage);border:2px solid var(--line);
|
| 129 |
+
font-family:'Bebas Neue',sans-serif;font-size:20px;color:var(--muted-2);
|
| 130 |
+
position:relative;z-index:1;transition:.35s var(--fx)}
|
| 131 |
+
.cue .ttl{font-family:Unbounded,sans-serif;font-weight:600;font-size:13px;color:var(--muted)}
|
| 132 |
+
.cue .sub{font-size:11px;color:var(--muted-2);letter-spacing:.01em}
|
| 133 |
+
/* done */
|
| 134 |
+
.cue.done .node{border-color:var(--cyan);color:var(--cyan);background:rgba(61,231,255,.08)}
|
| 135 |
+
.cue.done .ttl{color:var(--cream)}
|
| 136 |
+
/* active */
|
| 137 |
+
.cue.on .node{border-color:var(--gold);color:var(--gold);background:rgba(255,201,77,.12);
|
| 138 |
+
box-shadow:0 0 0 5px rgba(255,201,77,.1),0 0 22px rgba(255,201,77,.3)}
|
| 139 |
+
.cue.on .ttl{color:var(--gold)}
|
| 140 |
+
@media(prefers-reduced-motion:no-preference){
|
| 141 |
+
.cue.on .node{animation:cuePulse 2s infinite}
|
| 142 |
+
}
|
| 143 |
+
@keyframes cuePulse{0%,100%{box-shadow:0 0 0 5px rgba(255,201,77,.1),0 0 22px rgba(255,201,77,.28)}
|
| 144 |
+
50%{box-shadow:0 0 0 8px rgba(255,201,77,.04),0 0 30px rgba(255,201,77,.45)}}
|
| 145 |
+
|
| 146 |
+
/* ============================================================
|
| 147 |
+
LAYOUT — control desk (left) vs the stage (right).
|
| 148 |
+
intentionally asymmetric; the two halves are treated differently.
|
| 149 |
+
============================================================ */
|
| 150 |
+
.console{display:grid;grid-template-columns:362px 1fr;gap:20px;align-items:start}
|
| 151 |
+
@media(max-width:900px){.console{grid-template-columns:1fr}}
|
| 152 |
+
|
| 153 |
+
/* the desk: flat, utilitarian — it's the control surface, not a showpiece */
|
| 154 |
+
.desk{display:flex;flex-direction:column;gap:16px}
|
| 155 |
+
.card{background:var(--stage);border:1px solid var(--line-soft);border-radius:16px;padding:18px}
|
| 156 |
+
.card-h{display:flex;align-items:center;gap:10px;margin-bottom:14px}
|
| 157 |
+
.card-h .ic{color:var(--gold);font-size:18px}
|
| 158 |
+
.card-h h2{font-family:Unbounded,sans-serif;font-weight:600;font-size:14px;letter-spacing:.01em}
|
| 159 |
+
.card-h .meta{margin-left:auto;font-family:'Bebas Neue',sans-serif;letter-spacing:.07em;font-size:12px;color:var(--muted-2)}
|
| 160 |
+
|
| 161 |
+
/* roll tape drop zone — clapperboard motif */
|
| 162 |
+
.drop{position:relative;border:1.5px dashed var(--line);border-radius:13px;padding:26px 16px;
|
| 163 |
+
text-align:center;cursor:pointer;transition:.22s var(--fx);overflow:hidden;
|
| 164 |
+
background:radial-gradient(200px 110px at 50% -20%,rgba(255,201,77,.07),transparent 70%)}
|
| 165 |
+
.drop:hover{border-color:var(--gold);background:radial-gradient(200px 130px at 50% -10%,rgba(255,201,77,.12),transparent 70%)}
|
| 166 |
+
.drop:focus-visible{border-color:var(--cyan)}
|
| 167 |
+
.drop .clap{font-size:30px;color:var(--gold);display:inline-flex;margin-bottom:8px}
|
| 168 |
+
.drop .t{font-family:Unbounded,sans-serif;font-weight:600;font-size:15px}
|
| 169 |
+
.drop .s{font-size:12px;color:var(--muted);margin-top:4px}
|
| 170 |
+
/* loaded: a little filmstrip readout */
|
| 171 |
+
.drop .loaded{display:none;align-items:center;gap:12px;text-align:left}
|
| 172 |
+
.drop.loaded{border-style:solid;border-color:rgba(61,231,255,.45);background:rgba(61,231,255,.05);cursor:default}
|
| 173 |
+
.drop.loaded .ask{display:none}
|
| 174 |
+
.drop.loaded .loaded{display:flex}
|
| 175 |
+
.strip{width:52px;height:52px;border-radius:9px;flex:none;position:relative;overflow:hidden;
|
| 176 |
+
background:linear-gradient(135deg,var(--raise),var(--stage));border:1px solid var(--line)}
|
| 177 |
+
.strip::before,.strip::after{content:"";position:absolute;top:4px;bottom:4px;width:5px;
|
| 178 |
+
background:repeating-linear-gradient(var(--night) 0 3px,transparent 3px 7px)}
|
| 179 |
+
.strip::before{left:3px}.strip::after{right:3px}
|
| 180 |
+
.strip .play{position:absolute;inset:0;display:grid;place-items:center;color:var(--cyan);font-size:16px}
|
| 181 |
+
.loaded .fn{font-family:Unbounded,sans-serif;font-weight:600;font-size:13px;color:var(--cream)}
|
| 182 |
+
.loaded .fd{font-family:'Bebas Neue',sans-serif;letter-spacing:.05em;font-size:13px;color:var(--cyan);margin-top:3px}
|
| 183 |
+
|
| 184 |
+
/* buttons */
|
| 185 |
+
.btn{width:100%;border:0;cursor:pointer;font-family:Unbounded,sans-serif;font-weight:600;font-size:14px;
|
| 186 |
+
color:var(--night);padding:14px;border-radius:12px;display:flex;align-items:center;justify-content:center;
|
| 187 |
+
gap:9px;transition:.18s var(--fx)}
|
| 188 |
+
.btn:disabled{opacity:.4;cursor:not-allowed;background:var(--raise);color:var(--muted-2)}
|
| 189 |
+
.btn.cyan{background:linear-gradient(135deg,var(--cyan),#16b6d8)}
|
| 190 |
+
.btn.cyan:not(:disabled):hover{transform:translateY(-2px);box-shadow:0 12px 30px rgba(61,231,255,.3)}
|
| 191 |
+
.btn.show{font-size:17px;padding:17px;background:linear-gradient(110deg,var(--gold),var(--hot));position:relative;overflow:hidden}
|
| 192 |
+
.btn.show:not(:disabled):hover{transform:translateY(-2px);box-shadow:0 16px 38px rgba(255,61,139,.4)}
|
| 193 |
+
.btn.show:not(:disabled)::after{content:"";position:absolute;top:0;bottom:0;width:40%;
|
| 194 |
+
background:linear-gradient(90deg,transparent,rgba(255,255,255,.35),transparent);
|
| 195 |
+
transform:translateX(-200%)}
|
| 196 |
+
@media(prefers-reduced-motion:no-preference){
|
| 197 |
+
.btn.show:not(:disabled)::after{animation:shine 3.4s ease-in-out 1.2s infinite}
|
| 198 |
+
}
|
| 199 |
+
@keyframes shine{0%,100%{transform:translateX(-220%)}40%,60%{transform:translateX(360%)}}
|
| 200 |
+
.mt{margin-top:14px}
|
| 201 |
+
|
| 202 |
+
/* casting sweep bar */
|
| 203 |
+
.sweep{height:6px;border-radius:4px;margin-top:13px;background:var(--night-2);position:relative;overflow:hidden;
|
| 204 |
+
opacity:0;height:0;margin-top:0;transition:.25s}
|
| 205 |
+
.casting .sweep{opacity:1;height:6px;margin-top:13px}
|
| 206 |
+
.sweep::after{content:"";position:absolute;inset:0;width:36%;
|
| 207 |
+
background:linear-gradient(90deg,transparent,var(--gold),transparent)}
|
| 208 |
+
@media(prefers-reduced-motion:no-preference){.sweep::after{animation:sw 1s linear infinite}}
|
| 209 |
+
@keyframes sw{from{transform:translateX(-120%)}to{transform:translateX(330%)}}
|
| 210 |
+
|
| 211 |
+
/* ============================================================
|
| 212 |
+
THE STAGE — Star Select. theatrical, spotlit well.
|
| 213 |
+
============================================================ */
|
| 214 |
+
.stage-panel{background:
|
| 215 |
+
radial-gradient(80% 60% at 50% -8%, rgba(255,201,77,.07), transparent 60%),
|
| 216 |
+
linear-gradient(180deg,var(--stage),var(--night-2));
|
| 217 |
+
border:1px solid var(--line);border-radius:20px;padding:20px;
|
| 218 |
+
box-shadow:inset 0 1px 0 rgba(255,243,233,.04), 0 24px 60px rgba(8,2,20,.5)}
|
| 219 |
+
.stage-h{display:flex;align-items:center;gap:11px;margin-bottom:16px}
|
| 220 |
+
.stage-h .ic{color:var(--gold);font-size:19px}
|
| 221 |
+
.stage-h h2{font-family:Unbounded,sans-serif;font-weight:700;font-size:16px}
|
| 222 |
+
.stage-h .count{margin-left:auto;white-space:nowrap;font-family:'Bebas Neue',sans-serif;letter-spacing:.08em;font-size:14px;
|
| 223 |
+
color:var(--muted);background:rgba(46,26,82,.6);padding:4px 11px;border-radius:8px}
|
| 224 |
+
|
| 225 |
+
/* empty state */
|
| 226 |
+
.empty{display:grid;place-items:center;min-height:330px;text-align:center;padding:20px}
|
| 227 |
+
.empty .spot{width:96px;height:96px;border-radius:50%;display:grid;place-items:center;margin-bottom:16px;
|
| 228 |
+
color:var(--muted-2);font-size:46px;
|
| 229 |
+
background:radial-gradient(circle at 50% 30%,rgba(255,201,77,.08),transparent 70%)}
|
| 230 |
+
.empty .t{font-family:Unbounded,sans-serif;font-weight:600;font-size:17px;color:var(--cream);margin-bottom:6px}
|
| 231 |
+
.empty .s{font-size:13px;color:var(--muted);max-width:300px}
|
| 232 |
+
|
| 233 |
+
/* star grid */
|
| 234 |
+
.roster{display:grid;grid-template-columns:repeat(auto-fill,minmax(164px,1fr));gap:16px}
|
| 235 |
+
|
| 236 |
+
/* ---- the star card (one reusable block) ----
|
| 237 |
+
per-card character color is set inline as --c. */
|
| 238 |
+
/* base state is fully visible — entrance is driven via WAAPI in JS so a
|
| 239 |
+
backgrounded tab never leaves cards stuck invisible. */
|
| 240 |
+
.star{--c:var(--gold);position:relative;border-radius:16px;overflow:hidden;
|
| 241 |
+
background:linear-gradient(180deg,var(--stage-2),var(--stage));
|
| 242 |
+
border:1px solid var(--line);transition:.25s var(--fx)}
|
| 243 |
+
.star:hover{transform:translateY(-3px);border-color:color-mix(in srgb,var(--c) 60%,transparent)}
|
| 244 |
+
|
| 245 |
+
/* portrait well with a spotlight beam tinted to the character color,
|
| 246 |
+
and an angular framed headshot placeholder (agent-select energy) */
|
| 247 |
+
.portrait{position:relative;height:138px;overflow:hidden;
|
| 248 |
+
background:radial-gradient(90% 80% at 50% 120%,color-mix(in srgb,var(--c) 22%,var(--night-2)),var(--night-2))}
|
| 249 |
+
.portrait .beam{position:absolute;top:-30px;left:50%;width:150px;height:170px;transform:translateX(-50%);
|
| 250 |
+
pointer-events:none;clip-path:polygon(40% 0,60% 0,100% 100%,0 100%);
|
| 251 |
+
background:linear-gradient(180deg,color-mix(in srgb,var(--c) 55%,transparent),transparent 70%);opacity:.7}
|
| 252 |
+
.portrait .head-ph{position:absolute;left:50%;bottom:-6px;transform:translateX(-50%);
|
| 253 |
+
color:color-mix(in srgb,var(--c) 50%,#ffffff);opacity:.4}
|
| 254 |
+
.portrait .frame{position:absolute;inset:8px;border:1.5px solid color-mix(in srgb,var(--c) 35%,transparent);
|
| 255 |
+
border-radius:11px;pointer-events:none}
|
| 256 |
+
.portrait .idx{position:absolute;top:10px;left:10px;font-family:'Bebas Neue',sans-serif;font-size:14px;
|
| 257 |
+
color:var(--cream);background:rgba(15,6,30,.7);padding:2px 7px;border-radius:7px;display:flex;align-items:center;gap:3px}
|
| 258 |
+
.portrait .idx .st{color:var(--c)}
|
| 259 |
+
.tier{position:absolute;top:10px;right:10px;font-family:'Bebas Neue',sans-serif;font-size:11px;letter-spacing:.02em;
|
| 260 |
+
white-space:nowrap;color:var(--night);background:var(--c);padding:3px 8px;border-radius:7px;
|
| 261 |
+
box-shadow:0 2px 10px color-mix(in srgb,var(--c) 50%,transparent)}
|
| 262 |
+
|
| 263 |
+
/* crown drops in on naming */
|
| 264 |
+
/* crown lifts in above the star's head on naming */
|
| 265 |
+
.crown{position:absolute;top:50px;left:50%;transform:translateX(-50%) translateY(12px) scale(.4) rotate(-12deg);
|
| 266 |
+
opacity:0;width:30px;height:30px;color:var(--gold);
|
| 267 |
+
filter:drop-shadow(0 2px 6px color-mix(in srgb,var(--gold) 70%,transparent));
|
| 268 |
+
transition:.4s var(--pop)}
|
| 269 |
+
.star.named .crown{opacity:1;transform:translateX(-50%) scale(1) rotate(0)}
|
| 270 |
+
|
| 271 |
+
.star-body{padding:13px}
|
| 272 |
+
.star-body label{font-family:'Bebas Neue',sans-serif;letter-spacing:.08em;font-size:12px;color:var(--muted);
|
| 273 |
+
display:block;margin-bottom:7px}
|
| 274 |
+
.star input{width:100%;background:var(--night-2);border:1px solid var(--line);color:var(--cream);
|
| 275 |
+
border-radius:10px;padding:10px 12px;font-family:Nunito;font-weight:800;font-size:14px;outline:none;transition:.16s}
|
| 276 |
+
.star input::placeholder{color:var(--muted-2);font-weight:600}
|
| 277 |
+
.star input:focus{border-color:var(--c);box-shadow:0 0 0 3px color-mix(in srgb,var(--c) 25%,transparent)}
|
| 278 |
+
|
| 279 |
+
/* crowned: card lights in its own character color */
|
| 280 |
+
.star.named{border-color:color-mix(in srgb,var(--c) 65%,transparent);
|
| 281 |
+
box-shadow:0 0 0 1px color-mix(in srgb,var(--c) 30%,transparent),0 10px 30px color-mix(in srgb,var(--c) 18%,transparent)}
|
| 282 |
+
.star.named .portrait{background:radial-gradient(90% 90% at 50% 120%,color-mix(in srgb,var(--c) 40%,var(--night-2)),var(--night-2))}
|
| 283 |
+
.star.named .portrait .beam{opacity:1}
|
| 284 |
+
.star.named .head-ph{opacity:.6}
|
| 285 |
+
|
| 286 |
+
/* ============================================================
|
| 287 |
+
VIBES (narration modes) + COCO host
|
| 288 |
+
============================================================ */
|
| 289 |
+
.vibes{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
|
| 290 |
+
.vibe{display:flex;align-items:center;gap:11px;padding:12px;border-radius:13px;cursor:pointer;color:var(--cream);
|
| 291 |
+
border:1px solid var(--line-soft);background:var(--stage-2);transition:.18s var(--fx);text-align:left}
|
| 292 |
+
.vibe:hover{border-color:var(--violet);transform:translateY(-1px)}
|
| 293 |
+
.vibe .ic{font-size:20px;color:var(--muted);display:inline-flex;transition:.18s}
|
| 294 |
+
.vibe>span:last-child{display:flex;flex-direction:column;gap:2px;min-width:0}
|
| 295 |
+
.vibe .vt{display:block;font-family:Unbounded,sans-serif;font-weight:600;font-size:13px;color:var(--cream)}
|
| 296 |
+
.vibe .vd{display:block;font-size:11px;color:var(--muted);line-height:1.25}
|
| 297 |
+
.vibe[aria-pressed="true"]{border-color:var(--vc,var(--hot));background:color-mix(in srgb,var(--vc,var(--hot)) 12%,var(--stage-2))}
|
| 298 |
+
.vibe[aria-pressed="true"] .ic{color:var(--vc,var(--hot))}
|
| 299 |
+
|
| 300 |
+
/* Coco — the host bug. re-dresses per vibe. */
|
| 301 |
+
.coco{display:flex;align-items:center;gap:13px;margin-top:14px;padding:13px 14px;border-radius:14px;
|
| 302 |
+
background:linear-gradient(120deg,color-mix(in srgb,var(--vc,var(--violet)) 12%,var(--stage-2)),var(--stage-2));
|
| 303 |
+
border:1px solid color-mix(in srgb,var(--vc,var(--violet)) 30%,var(--line-soft));transition:.35s var(--fx)}
|
| 304 |
+
.coco .av{position:relative;width:46px;height:46px;border-radius:13px;flex:none;display:grid;place-items:center;
|
| 305 |
+
background:radial-gradient(circle at 50% 30%,color-mix(in srgb,var(--vc,var(--violet)) 70%,#fff 5%),var(--vc,var(--violet)));
|
| 306 |
+
box-shadow:0 6px 18px color-mix(in srgb,var(--vc,var(--violet)) 40%,transparent);transition:.35s var(--fx)}
|
| 307 |
+
.coco .av .bird{width:30px;height:30px;color:var(--night);filter:drop-shadow(0 1px 1px rgba(255,255,255,.3))}
|
| 308 |
+
/* accessory badge sits on the avatar, swapped per vibe */
|
| 309 |
+
.coco .acc{position:absolute;right:-6px;bottom:-6px;width:22px;height:22px;border-radius:50%;
|
| 310 |
+
display:grid;place-items:center;background:var(--night);border:2px solid var(--vc,var(--violet));
|
| 311 |
+
color:var(--vc,var(--violet));font-size:11px}
|
| 312 |
+
.coco .acc svg{width:12px;height:12px}
|
| 313 |
+
.coco .say{font-size:13px;line-height:1.45}
|
| 314 |
+
.coco .say b{color:var(--vc,var(--gold))}
|
| 315 |
+
.coco .who{font-family:'Bebas Neue',sans-serif;letter-spacing:.1em;font-size:11px;color:var(--vc,var(--violet));
|
| 316 |
+
display:block;margin-bottom:2px}
|
| 317 |
+
|
| 318 |
+
/* privacy footnote — visible, not buried */
|
| 319 |
+
.note{margin-top:26px;text-align:center;font-size:12.5px;color:var(--muted);line-height:1.6}
|
| 320 |
+
.note b{color:var(--cream)}
|
| 321 |
+
.note .lock{color:var(--cyan);vertical-align:-2px;margin-right:5px}
|
| 322 |
+
|
| 323 |
+
/* ============================================================
|
| 324 |
+
VIEW SWITCH
|
| 325 |
+
============================================================ */
|
| 326 |
+
.view{display:none}
|
| 327 |
+
.view.active{display:block;animation:viewIn .4s var(--fx)}
|
| 328 |
+
@keyframes viewIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
| 329 |
+
|
| 330 |
+
/* ============================================================
|
| 331 |
+
BROADCAST PLAYER
|
| 332 |
+
============================================================ */
|
| 333 |
+
.booth-top{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:18px;flex-wrap:wrap}
|
| 334 |
+
.back{display:inline-flex;align-items:center;gap:8px;background:var(--stage);border:1px solid var(--line);
|
| 335 |
+
color:var(--cream);font-family:Nunito;font-weight:700;font-size:13px;padding:9px 14px;border-radius:10px;cursor:pointer;transition:.16s}
|
| 336 |
+
.back:hover{border-color:var(--violet)}
|
| 337 |
+
.orient{display:flex;gap:4px;background:var(--stage);border:1px solid var(--line-soft);padding:4px;border-radius:11px}
|
| 338 |
+
.orient button{background:transparent;border:0;color:var(--muted);font-family:'Bebas Neue',sans-serif;letter-spacing:.06em;
|
| 339 |
+
font-size:13px;padding:7px 14px;border-radius:8px;cursor:pointer;transition:.16s}
|
| 340 |
+
.orient button[aria-pressed="true"]{background:var(--raise);color:var(--gold)}
|
| 341 |
+
|
| 342 |
+
.booth{display:grid;grid-template-columns:1fr 320px;gap:18px;align-items:start}
|
| 343 |
+
/* portrait: the clip is a phone-sized vertical frame, never a giant column-filling block.
|
| 344 |
+
capped width keeps the 9:16 height sane and the play-by-play rail beside it. */
|
| 345 |
+
.booth.portrait{grid-template-columns:minmax(0,340px) minmax(0,1fr)}
|
| 346 |
+
.booth.portrait .screen-wrap{max-width:340px}
|
| 347 |
+
@media(max-width:840px){
|
| 348 |
+
.booth,.booth.square{grid-template-columns:1fr}
|
| 349 |
+
.booth.portrait{grid-template-columns:1fr}
|
| 350 |
+
.booth.portrait .screen-wrap{margin-inline:auto}
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
/* the screen */
|
| 354 |
+
.screen-wrap{background:#000;border-radius:18px;overflow:hidden;border:1px solid var(--line);
|
| 355 |
+
box-shadow:0 24px 70px rgba(0,0,0,.6);position:relative}
|
| 356 |
+
.screen{position:relative;width:100%;aspect-ratio:16/9;background:
|
| 357 |
+
repeating-linear-gradient(135deg,#1c0f33 0 18px,#170c2b 18px 36px);
|
| 358 |
+
display:grid;place-items:center}
|
| 359 |
+
.booth.portrait .screen{aspect-ratio:9/16}
|
| 360 |
+
.booth.square .screen{aspect-ratio:1/1}
|
| 361 |
+
.screen .ph{font-family:'Bebas Neue',sans-serif;letter-spacing:.12em;color:var(--muted-2);font-size:15px;
|
| 362 |
+
text-align:center;padding:0 20px}
|
| 363 |
+
.screen .ph svg{width:40px;height:40px;margin:0 auto 10px;color:var(--line);display:block}
|
| 364 |
+
|
| 365 |
+
/* on-air bug */
|
| 366 |
+
.bug{position:absolute;top:14px;left:14px;display:flex;align-items:center;gap:8px;white-space:nowrap;
|
| 367 |
+
background:rgba(15,6,30,.6);backdrop-filter:blur(4px);padding:7px 12px;border-radius:9px;
|
| 368 |
+
font-family:'Bebas Neue',sans-serif;letter-spacing:.12em;font-size:14px;color:var(--cream)}
|
| 369 |
+
.bug .dot{width:10px;height:10px;border-radius:50%;background:var(--hot);box-shadow:0 0 10px var(--hot)}
|
| 370 |
+
@media(prefers-reduced-motion:no-preference){.bug .dot{animation:tw 1.1s infinite}}
|
| 371 |
+
|
| 372 |
+
/* timecode */
|
| 373 |
+
.tc{position:absolute;top:14px;right:14px;background:rgba(15,6,30,.6);backdrop-filter:blur(4px);
|
| 374 |
+
padding:7px 12px;border-radius:9px;font-family:'Bebas Neue',sans-serif;letter-spacing:.1em;font-size:15px;
|
| 375 |
+
color:var(--cyan);font-variant-numeric:tabular-nums}
|
| 376 |
+
|
| 377 |
+
/* lower-third chyron */
|
| 378 |
+
.chyron{position:absolute;left:14px;right:14px;bottom:14px;border-radius:12px;overflow:hidden;
|
| 379 |
+
background:linear-gradient(100deg,rgba(15,6,30,.92),rgba(36,18,65,.82));
|
| 380 |
+
border:1px solid color-mix(in srgb,var(--cc,var(--hot)) 40%,var(--line));
|
| 381 |
+
box-shadow:0 10px 30px rgba(0,0,0,.4);
|
| 382 |
+
transform:translateY(130%);opacity:0;transition:.45s var(--fx)}
|
| 383 |
+
.chyron.in{transform:none;opacity:1}
|
| 384 |
+
.chyron .bar{height:4px;background:linear-gradient(90deg,var(--cc,var(--hot)),var(--gold))}
|
| 385 |
+
.chyron .row{display:flex;align-items:stretch}
|
| 386 |
+
.chyron .who{display:flex;align-items:center;font-family:'Bebas Neue',sans-serif;letter-spacing:.06em;
|
| 387 |
+
font-size:17px;color:var(--night);background:var(--cc,var(--hot));padding:10px 14px;white-space:nowrap}
|
| 388 |
+
.chyron .line{padding:10px 16px;font-family:Nunito;font-weight:700;font-size:15px;color:var(--cream);
|
| 389 |
+
display:flex;align-items:center;line-height:1.3}
|
| 390 |
+
@media(prefers-reduced-motion:no-preference){
|
| 391 |
+
.chyron.in .line{animation:sharpen .5s var(--fx)}
|
| 392 |
+
}
|
| 393 |
+
@keyframes sharpen{from{filter:blur(7px);opacity:.3}to{filter:blur(0);opacity:1}}
|
| 394 |
+
|
| 395 |
+
/* transport */
|
| 396 |
+
.transport{display:flex;align-items:center;gap:14px;margin-top:14px}
|
| 397 |
+
.transport .pp{width:48px;height:48px;border-radius:50%;border:0;cursor:pointer;flex:none;display:grid;place-items:center;
|
| 398 |
+
background:linear-gradient(135deg,var(--gold),var(--hot));color:var(--night);font-size:20px;transition:.16s}
|
| 399 |
+
.transport .pp:hover{transform:scale(1.05)}
|
| 400 |
+
.scrub{flex:1;height:6px;border-radius:4px;background:var(--raise);position:relative;cursor:pointer}
|
| 401 |
+
.scrub .fill{position:absolute;left:0;top:0;bottom:0;border-radius:4px;width:0;
|
| 402 |
+
background:linear-gradient(90deg,var(--hot),var(--gold))}
|
| 403 |
+
.scrub .knob{position:absolute;top:50%;width:14px;height:14px;border-radius:50%;background:var(--cream);
|
| 404 |
+
transform:translate(-50%,-50%);left:0;box-shadow:0 2px 6px rgba(0,0,0,.5)}
|
| 405 |
+
|
| 406 |
+
/* play-by-play rail */
|
| 407 |
+
.pbp{background:var(--stage);border:1px solid var(--line-soft);border-radius:16px;overflow:hidden;
|
| 408 |
+
display:flex;flex-direction:column;max-height:520px}
|
| 409 |
+
.pbp .pbp-h{display:flex;align-items:center;gap:9px;padding:14px 16px;border-bottom:1px solid var(--line-soft)}
|
| 410 |
+
.pbp .pbp-h .ic{color:var(--gold);font-size:16px}
|
| 411 |
+
.pbp .pbp-h h3{font-family:Unbounded,sans-serif;font-weight:600;font-size:13px}
|
| 412 |
+
.pbp .pbp-h .ct{margin-left:auto;font-family:'Bebas Neue',sans-serif;letter-spacing:.06em;font-size:12px;color:var(--muted-2)}
|
| 413 |
+
.pbp .lines{overflow-y:auto;padding:6px}
|
| 414 |
+
.pbp .pl{display:flex;gap:11px;padding:11px 12px;border-radius:10px;cursor:pointer;transition:.15s;align-items:baseline}
|
| 415 |
+
.pbp .pl:hover{background:var(--stage-2)}
|
| 416 |
+
.pbp .pl .t{font-family:'Bebas Neue',sans-serif;letter-spacing:.04em;font-size:14px;color:var(--muted-2);
|
| 417 |
+
flex:none;width:42px;font-variant-numeric:tabular-nums;padding-top:1px}
|
| 418 |
+
.pbp .pl .tx{font-size:13px;line-height:1.45;color:var(--muted)}
|
| 419 |
+
.pbp .pl .tx b{color:var(--cream);font-weight:800}
|
| 420 |
+
.pbp .pl.now{background:color-mix(in srgb,var(--cc,var(--hot)) 14%,var(--stage-2))}
|
| 421 |
+
.pbp .pl.now .t{color:var(--cc,var(--hot))}
|
| 422 |
+
.pbp .pl.now .tx{color:var(--cream)}
|
| 423 |
+
.pbp .pl.now .tx b{color:var(--cc,var(--hot))}
|
| 424 |
+
|
| 425 |
+
/* export bar */
|
| 426 |
+
.export{padding:13px 16px;border-top:1px solid var(--line-soft);background:var(--night-2)}
|
| 427 |
+
.export .btn-srt{width:100%;display:flex;align-items:center;justify-content:center;gap:9px;cursor:pointer;
|
| 428 |
+
background:var(--stage-2);border:1px solid var(--line);color:var(--cream);font-family:Nunito;font-weight:700;
|
| 429 |
+
font-size:13px;padding:12px;border-radius:11px;transition:.16s}
|
| 430 |
+
.export .btn-srt:hover{border-color:var(--cyan);color:var(--cyan)}
|
| 431 |
+
.export .btn-srt .ic{color:var(--cyan);font-size:16px}
|
| 432 |
+
.export .hint{text-align:center;font-size:11px;color:var(--muted-2);margin-top:8px}
|
| 433 |
+
.export .hint b{color:var(--muted)}
|
| 434 |
+
|
| 435 |
+
/* confetti */
|
| 436 |
+
.confetti{position:fixed;top:-20px;width:11px;height:14px;z-index:90;pointer-events:none;border-radius:2px}
|
| 437 |
+
@media(prefers-reduced-motion:no-preference){.confetti{animation:fall linear forwards}}
|
| 438 |
+
@keyframes fall{to{transform:translateY(105vh) rotate(540deg);opacity:.2}}
|
| 439 |
+
|
| 440 |
+
/* ============================================================
|
| 441 |
+
UTILITIES
|
| 442 |
+
============================================================ */
|
| 443 |
+
:focus-visible{outline:2px solid var(--cyan);outline-offset:3px;border-radius:8px}
|
| 444 |
+
.toast{position:fixed;left:50%;bottom:28px;transform:translateX(-50%) translateY(20px);opacity:0;
|
| 445 |
+
background:var(--stage-2);border:1px solid var(--cyan);color:var(--cream);padding:12px 20px;border-radius:12px;
|
| 446 |
+
font-weight:700;font-size:13px;z-index:95;transition:.3s var(--fx);display:flex;align-items:center;gap:9px;
|
| 447 |
+
box-shadow:0 14px 40px rgba(0,0,0,.5)}
|
| 448 |
+
.toast.show{transform:translateX(-50%);opacity:1}
|
| 449 |
+
.toast .ic{color:var(--cyan)}
|
| 450 |
+
|
| 451 |
+
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
|
| 452 |
+
|
| 453 |
+
@media(max-width:560px){
|
| 454 |
+
.masthead{gap:14px}
|
| 455 |
+
.rundown .sub{display:none}
|
| 456 |
+
.vibes{grid-template-columns:1fr}
|
| 457 |
+
.roster{grid-template-columns:repeat(auto-fill,minmax(140px,1fr))}
|
| 458 |
+
}
|
| 459 |
+
|
| 460 |
+
/* ============================================================
|
| 461 |
+
INTEGRATION ADDITIONS — real <video> in the screen, real face
|
| 462 |
+
crops in star cards (added for the Gradio wiring)
|
| 463 |
+
============================================================ */
|
| 464 |
+
.screen-vid{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:#000;display:none}
|
| 465 |
+
.screen.has-video .ph{display:none}
|
| 466 |
+
.screen.has-video .screen-vid{display:block}
|
| 467 |
+
.portrait .head-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
|
| 468 |
+
.star.named .head-img{filter:saturate(1.05)}
|
marquee.js
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* ============================================================
|
| 2 |
+
MARQUEE — flow engine, wired to the Gradio backend.
|
| 3 |
+
Runs as a module inside the app iframe.
|
| 4 |
+
Presentation code is unchanged from the design; the mock data
|
| 5 |
+
hooks (casting, commentary, playback) now call the real pipeline.
|
| 6 |
+
============================================================ */
|
| 7 |
+
const $ = (s, r = document) => r.querySelector(s);
|
| 8 |
+
const $$ = (s, r = document) => [...r.querySelectorAll(s)];
|
| 9 |
+
const reduce = matchMedia('(prefers-reduced-motion:reduce)').matches;
|
| 10 |
+
const pad = n => String(n).padStart(2, '0');
|
| 11 |
+
|
| 12 |
+
/* ---------- Plain fetch API helpers — talk to FastAPI endpoints ---------- */
|
| 13 |
+
const BASE = window.location.origin;
|
| 14 |
+
|
| 15 |
+
async function apiScan(file) {
|
| 16 |
+
const fd = new FormData();
|
| 17 |
+
fd.append('file', file, file.name);
|
| 18 |
+
const res = await fetch(`${BASE}/api/scan`, {method:'POST', body: fd});
|
| 19 |
+
const data = await res.json();
|
| 20 |
+
if (!data.ok) throw new Error(data.error || 'scan failed');
|
| 21 |
+
return data;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
async function apiGenerate(sessionId, names, vibe) {
|
| 25 |
+
const res = await fetch(`${BASE}/api/generate`, {
|
| 26 |
+
method: 'POST',
|
| 27 |
+
headers: {'Content-Type': 'application/json'},
|
| 28 |
+
body: JSON.stringify({session_id: sessionId, names, vibe})
|
| 29 |
+
});
|
| 30 |
+
const data = await res.json();
|
| 31 |
+
if (!data.ok) throw new Error(data.error || 'generate failed');
|
| 32 |
+
return data;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
/* ---------- session state from the backend ---------- */
|
| 36 |
+
let SESSION = null, VIDEO_URL = null, DUR = 0, REAL_SCRIPT = [], selectedFile = null;
|
| 37 |
+
|
| 38 |
+
/* ---------- auto-fit the iframe to content (same-origin srcdoc) ---------- */
|
| 39 |
+
function fitFrame() {
|
| 40 |
+
try { if (window.frameElement) window.frameElement.style.height = document.documentElement.scrollHeight + 'px'; }
|
| 41 |
+
catch (e) {}
|
| 42 |
+
}
|
| 43 |
+
new ResizeObserver(fitFrame).observe(document.body);
|
| 44 |
+
window.addEventListener('load', fitFrame);
|
| 45 |
+
|
| 46 |
+
/* ---------- live clock ---------- */
|
| 47 |
+
const clocks = $$('#clock');
|
| 48 |
+
function fmtClock(d){return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;}
|
| 49 |
+
setInterval(()=>{const t=fmtClock(new Date());clocks.forEach(c=>c.textContent=t);},1000);
|
| 50 |
+
clocks.forEach(c=>c.textContent=fmtClock(new Date()));
|
| 51 |
+
|
| 52 |
+
/* ---------- status pill ---------- */
|
| 53 |
+
const status=$('#status'), statusTxt=$('#statusTxt');
|
| 54 |
+
function setStatus(txt,live){statusTxt.textContent=txt;status.classList.toggle('live',!!live);}
|
| 55 |
+
|
| 56 |
+
/* ---------- rundown ---------- */
|
| 57 |
+
const cues=$$('#rundown .cue');
|
| 58 |
+
function setStep(n){
|
| 59 |
+
cues.forEach(c=>{
|
| 60 |
+
const s=+c.dataset.step;
|
| 61 |
+
c.classList.toggle('done',s<n);
|
| 62 |
+
c.classList.toggle('on',s===n);
|
| 63 |
+
if(s<n)c.querySelector('.node').innerHTML='<svg class="i" style="width:18px;height:18px"><use href="#i-check"/></svg>';
|
| 64 |
+
else c.querySelector('.node').textContent='0'+s;
|
| 65 |
+
});
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
/* ---------- cast colors + tiers (presentation) ---------- */
|
| 69 |
+
const COLORS=['--star-gold','--star-hot','--star-cyan','--star-violet','--star-lime','--star-tang'];
|
| 70 |
+
let stars=[]; // {name, color, tier, crop}
|
| 71 |
+
|
| 72 |
+
/* ---------- VIBES — Coco's wardrobe + the show's voice ----------
|
| 73 |
+
NOTE: the play-by-play LINES are no longer templated here — the real
|
| 74 |
+
commentary comes from the VLM. We keep Coco's persona + colors. */
|
| 75 |
+
const VIBES={
|
| 76 |
+
football:{ acc:'#i-whistle', vc:'--star-lime', who:'COCO · TOUCHLINE',
|
| 77 |
+
coco:"Whistle's on. I'm calling this one like a cup final — keep your eyes on the pitch!"},
|
| 78 |
+
diva:{ acc:'#i-glam', vc:'--star-hot', who:'COCO · RED CARPET',
|
| 79 |
+
coco:"Shades on, boa fluffed. Darling, we are SO ready for the carpet. Bring the drama."},
|
| 80 |
+
wildlife:{ acc:'#i-paw', vc:'--star-cyan', who:'COCO · FIELD UNIT',
|
| 81 |
+
coco:"*whispering* …we must be very quiet now. A rare specimen has entered the frame."},
|
| 82 |
+
boxing:{ acc:'#i-glove', vc:'--star-tang', who:'COCO · RINGSIDE',
|
| 83 |
+
coco:"Gloves laced, ringside seat warm. Ladies and gentlemen… in the RED corner!"}
|
| 84 |
+
};
|
| 85 |
+
let currentVibe=null;
|
| 86 |
+
|
| 87 |
+
/* ---------- ROLL TAPE (real file) ---------- */
|
| 88 |
+
const drop=$('#drop'), castBtn=$('#castBtn'), liveBtn=$('#liveBtn'), rollCard=$('#rollCard');
|
| 89 |
+
const fileInput=$('#fileInput'), fileName=$('#fileName'), fileDur=$('#fileDur');
|
| 90 |
+
|
| 91 |
+
function handleFile(file){
|
| 92 |
+
if(!file) return;
|
| 93 |
+
selectedFile=file;
|
| 94 |
+
drop.classList.add('loaded');
|
| 95 |
+
fileName.textContent=file.name;
|
| 96 |
+
fileDur.textContent='reading…';
|
| 97 |
+
const url=URL.createObjectURL(file);
|
| 98 |
+
const probe=document.createElement('video'); probe.preload='metadata';
|
| 99 |
+
probe.onloadedmetadata=()=>{ fileDur.textContent=fmt(probe.duration)+' · ready to cast'; URL.revokeObjectURL(url); };
|
| 100 |
+
probe.onerror=()=>{ fileDur.textContent='ready to cast'; };
|
| 101 |
+
probe.src=url;
|
| 102 |
+
castBtn.disabled=false;
|
| 103 |
+
setStatus('Tape loaded'); toast('Tape loaded — ready to cast');
|
| 104 |
+
}
|
| 105 |
+
drop.addEventListener('click',()=>fileInput.click());
|
| 106 |
+
drop.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();fileInput.click();}});
|
| 107 |
+
fileInput.addEventListener('change',e=>handleFile(e.target.files[0]));
|
| 108 |
+
['dragover','dragenter'].forEach(ev=>drop.addEventListener(ev,e=>{e.preventDefault();drop.style.borderColor='var(--cyan)';}));
|
| 109 |
+
['dragleave','drop'].forEach(ev=>drop.addEventListener(ev,e=>{
|
| 110 |
+
e.preventDefault();drop.style.borderColor='';
|
| 111 |
+
if(ev==='drop'&&e.dataTransfer.files[0])handleFile(e.dataTransfer.files[0]);
|
| 112 |
+
}));
|
| 113 |
+
|
| 114 |
+
/* ---------- CASTING (real scan) ---------- */
|
| 115 |
+
const stagePanel=$('#stagePanel'), stageEmpty=$('#stageEmpty'), roster=$('#roster'), starCount=$('#starCount');
|
| 116 |
+
castBtn.addEventListener('click',startCasting);
|
| 117 |
+
|
| 118 |
+
async function startCasting(){
|
| 119 |
+
if(castBtn.disabled||!selectedFile) return;
|
| 120 |
+
castBtn.disabled=true; rollCard.classList.add('casting');
|
| 121 |
+
setStep(2); setStatus('Casting…');
|
| 122 |
+
castBtn.innerHTML='<svg class="i"><use href="#i-spot"/></svg> Scanning the tape…';
|
| 123 |
+
try{
|
| 124 |
+
toast('Uploading tape…');
|
| 125 |
+
const data = await apiScan(selectedFile);
|
| 126 |
+
SESSION=data.session_id; VIDEO_URL=data.video_url; DUR=data.duration||0;
|
| 127 |
+
setBoothOrient(data.w,data.h);
|
| 128 |
+
const vid=$('#vid'); vid.src=VIDEO_URL; $('#screen').classList.add('has-video');
|
| 129 |
+
revealCast(data.stars||[]);
|
| 130 |
+
}catch(err){
|
| 131 |
+
setStatus('Casting failed');
|
| 132 |
+
toast('Casting failed: '+(err.message||err));
|
| 133 |
+
rollCard.classList.remove('casting');
|
| 134 |
+
castBtn.disabled=false;
|
| 135 |
+
castBtn.innerHTML='<svg class="i"><use href="#i-spot"/></svg> Find the stars';
|
| 136 |
+
}
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
function revealCast(starData){
|
| 140 |
+
rollCard.classList.remove('casting');
|
| 141 |
+
castBtn.innerHTML='<svg class="i"><use href="#i-spot"/></svg> Re-cast';
|
| 142 |
+
castBtn.disabled=false;
|
| 143 |
+
setStep(3); setStatus(starData.length+' stars found');
|
| 144 |
+
|
| 145 |
+
stars = starData.map((s,i)=>({name:'',tier:s.tier||'STAR',color:COLORS[i%COLORS.length],crop:s.crop||null}));
|
| 146 |
+
stageEmpty.style.display='none';
|
| 147 |
+
roster.hidden=false; roster.innerHTML='';
|
| 148 |
+
starCount.textContent=stars.length+' STARS FOUND';
|
| 149 |
+
stars.forEach((s,i)=>roster.appendChild(buildStar(s,i)));
|
| 150 |
+
|
| 151 |
+
if(!stars.length){
|
| 152 |
+
stageEmpty.style.display='';
|
| 153 |
+
stageEmpty.querySelector('.t').textContent='No stars spotted';
|
| 154 |
+
stageEmpty.querySelector('.s').innerHTML='No faces found — but the show still goes on. Pick a vibe and go live for nameless narration.';
|
| 155 |
+
roster.hidden=true;
|
| 156 |
+
}
|
| 157 |
+
toast(stars.length?stars.length+' stars found — give them stage names':'No faces found — you can still go live');
|
| 158 |
+
refreshLive();
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
/* ---------- THE STAR CARD (one reusable block) ---------- */
|
| 162 |
+
function buildStar(s,i){
|
| 163 |
+
const el=document.createElement('div');
|
| 164 |
+
el.className='star';
|
| 165 |
+
el.style.setProperty('--c',`var(${s.color})`);
|
| 166 |
+
if(!reduce){
|
| 167 |
+
el.animate(
|
| 168 |
+
[{opacity:0,transform:'translateY(14px) scale(.98)'},{opacity:1,transform:'none'}],
|
| 169 |
+
{duration:480,delay:i*90,easing:'cubic-bezier(.2,.9,.25,1)',fill:'backwards'}
|
| 170 |
+
);
|
| 171 |
+
}
|
| 172 |
+
const portrait = s.crop
|
| 173 |
+
? `<img class="head-img" src="${s.crop}" alt="Player ${i+1}">`
|
| 174 |
+
: `<svg class="i head-ph" style="width:64px;height:68px"><use href="#i-portrait"/></svg>`;
|
| 175 |
+
el.innerHTML=`
|
| 176 |
+
<div class="portrait">
|
| 177 |
+
<div class="beam"></div>
|
| 178 |
+
${portrait}
|
| 179 |
+
<div class="frame"></div>
|
| 180 |
+
<span class="idx"><span class="st">★</span>${i+1}</span>
|
| 181 |
+
<span class="tier">${s.tier}</span>
|
| 182 |
+
<svg class="i crown"><use href="#i-crown"/></svg>
|
| 183 |
+
</div>
|
| 184 |
+
<div class="star-body">
|
| 185 |
+
<label for="star${i}">Stage name</label>
|
| 186 |
+
<input id="star${i}" type="text" placeholder="e.g. The Maestro" maxlength="22" autocomplete="off">
|
| 187 |
+
</div>`;
|
| 188 |
+
const input=el.querySelector('input');
|
| 189 |
+
input.addEventListener('input',()=>{
|
| 190 |
+
const v=input.value.trim();
|
| 191 |
+
s.name=v;
|
| 192 |
+
const wasNamed=el.classList.contains('named');
|
| 193 |
+
el.classList.toggle('named',!!v);
|
| 194 |
+
if(v && !wasNamed && !reduce) popCrown(el);
|
| 195 |
+
refreshLive();
|
| 196 |
+
});
|
| 197 |
+
return el;
|
| 198 |
+
}
|
| 199 |
+
function popCrown(el){
|
| 200 |
+
const c=el.querySelector('.crown');
|
| 201 |
+
c.style.animation='none';void c.offsetWidth;
|
| 202 |
+
c.animate([{transform:'translateX(-50%) translateY(10px) scale(.4) rotate(-12deg)',opacity:0},
|
| 203 |
+
{transform:'translateX(-50%) scale(1.25) rotate(8deg)',opacity:1,offset:.6},
|
| 204 |
+
{transform:'translateX(-50%) scale(1) rotate(0)',opacity:1}],
|
| 205 |
+
{duration:480,easing:'cubic-bezier(.2,1.7,.4,1)'});
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
/* ---------- VIBES + COCO ---------- */
|
| 209 |
+
const vibesWrap=$('#vibes'), coco=$('#coco'), cocoLine=$('#cocoLine'),
|
| 210 |
+
cocoAcc=$('#cocoAcc'), cocoWho=coco.querySelector('.who');
|
| 211 |
+
vibesWrap.addEventListener('click',e=>{
|
| 212 |
+
const b=e.target.closest('.vibe'); if(!b)return;
|
| 213 |
+
$$('.vibe',vibesWrap).forEach(v=>v.setAttribute('aria-pressed','false'));
|
| 214 |
+
b.setAttribute('aria-pressed','true');
|
| 215 |
+
currentVibe=b.dataset.vibe;
|
| 216 |
+
dressCoco(currentVibe);
|
| 217 |
+
refreshLive();
|
| 218 |
+
});
|
| 219 |
+
function dressCoco(v){
|
| 220 |
+
const d=VIBES[v]; if(!d)return;
|
| 221 |
+
coco.style.setProperty('--vc',`var(${d.vc})`);
|
| 222 |
+
cocoAcc.innerHTML=`<svg><use href="${d.acc}"/></svg>`;
|
| 223 |
+
cocoWho.textContent=d.who;
|
| 224 |
+
cocoLine.textContent=d.coco;
|
| 225 |
+
if(!reduce){
|
| 226 |
+
coco.animate([{transform:'scale(.98)',opacity:.6},{transform:'scale(1)',opacity:1}],{duration:300,easing:'ease-out'});
|
| 227 |
+
const av=coco.querySelector('.av');
|
| 228 |
+
av.animate([{transform:'rotate(-8deg) scale(.9)'},{transform:'rotate(6deg) scale(1.08)'},{transform:'none'}],{duration:420,easing:'cubic-bezier(.2,1.7,.4,1)'});
|
| 229 |
+
}
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
/* ---------- LIVE-READY GATE ----------
|
| 233 |
+
ready when a vibe is picked (names optional — nameless narration is allowed) */
|
| 234 |
+
function namedCount(){return stars.filter(s=>s.name).length;}
|
| 235 |
+
function refreshLive(){
|
| 236 |
+
liveBtn.disabled=!(currentVibe && SESSION);
|
| 237 |
+
if(stars.length){
|
| 238 |
+
const n=namedCount();
|
| 239 |
+
starCount.textContent = n? `${n}/${stars.length} NAMED` : `${stars.length} STARS FOUND`;
|
| 240 |
+
}
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
/* ---------- SHOWTIME (real commentary) ---------- */
|
| 244 |
+
liveBtn.addEventListener('click',async ()=>{
|
| 245 |
+
if(liveBtn.disabled)return;
|
| 246 |
+
liveBtn.disabled=true;
|
| 247 |
+
liveBtn.innerHTML='<svg class="i"><use href="#i-star"/></svg> Rolling cameras…';
|
| 248 |
+
setStatus('Generating…');
|
| 249 |
+
try{
|
| 250 |
+
const names=stars.map(s=>s.name||'');
|
| 251 |
+
const data=await apiGenerate(SESSION, names, currentVibe);
|
| 252 |
+
REAL_SCRIPT=data.script||[];
|
| 253 |
+
setStep(4); setStatus('On air',true);
|
| 254 |
+
if(!reduce) confetti();
|
| 255 |
+
buildBroadcast();
|
| 256 |
+
setTimeout(()=>{switchView('broadcast');fitFrame();}, reduce?0:450);
|
| 257 |
+
}catch(err){
|
| 258 |
+
setStatus('Showtime failed');
|
| 259 |
+
toast('Showtime failed: '+(err.message||err));
|
| 260 |
+
}finally{
|
| 261 |
+
liveBtn.disabled=false;
|
| 262 |
+
liveBtn.innerHTML='<svg class="i"><use href="#i-star"/></svg> Showtime — go live';
|
| 263 |
+
}
|
| 264 |
+
});
|
| 265 |
+
|
| 266 |
+
/* ---------- VIEW SWITCH ---------- */
|
| 267 |
+
const setupView=$('#setupView'), broadcastView=$('#broadcastView');
|
| 268 |
+
function switchView(which){
|
| 269 |
+
const toBroadcast=which==='broadcast';
|
| 270 |
+
setupView.classList.toggle('active',!toBroadcast);
|
| 271 |
+
broadcastView.classList.toggle('active',toBroadcast);
|
| 272 |
+
window.scrollTo({top:0,behavior:reduce?'auto':'smooth'});
|
| 273 |
+
fitFrame();
|
| 274 |
+
}
|
| 275 |
+
$('#backBtn').addEventListener('click',()=>{switchView('setup');const v=$('#vid');if(v)v.pause();});
|
| 276 |
+
|
| 277 |
+
/* ============================================================
|
| 278 |
+
BROADCAST PLAYER — driven by a real <video>
|
| 279 |
+
============================================================ */
|
| 280 |
+
let script=[]; // {t, who, text, cc}
|
| 281 |
+
const vid=$('#vid'), screen=$('#screen'), chyron=$('#chyron'), chyWho=$('#chyWho'), chyLine=$('#chyLine'),
|
| 282 |
+
linesEl=$('#lines'), pbpCt=$('#pbpCt'), tcEl=$('#tc'), fill=$('#fill'), knob=$('#knob'),
|
| 283 |
+
pp=$('#pp'), scrub=$('#scrub'), tcDur=$('#tcDur');
|
| 284 |
+
const ICON_PLAY='<svg class="i"><use href="#i-play"/></svg>';
|
| 285 |
+
const ICON_PAUSE='<svg class="i"><use href="#i-pause"/></svg>';
|
| 286 |
+
|
| 287 |
+
function fmt(s){s=isFinite(s)?s:0;return pad(Math.floor(s/60))+':'+pad(Math.floor(s%60));}
|
| 288 |
+
|
| 289 |
+
function buildBroadcast(){
|
| 290 |
+
const d=VIBES[currentVibe]||{vc:'--hot',who:'COCO'};
|
| 291 |
+
const who=(d.who||'COCO').replace('COCO · ','');
|
| 292 |
+
script=(REAL_SCRIPT||[]).map(l=>({t:+l.t||0, text:l.text||'', who, cc:d.vc}));
|
| 293 |
+
linesEl.innerHTML='';
|
| 294 |
+
pbpCt.textContent=script.length+' LINES';
|
| 295 |
+
if(!script.length){
|
| 296 |
+
linesEl.innerHTML='<div class="pl"><span class="tx">No commentary generated — try another clip.</span></div>';
|
| 297 |
+
}
|
| 298 |
+
script.forEach((l,i)=>{
|
| 299 |
+
const row=document.createElement('div');
|
| 300 |
+
row.className='pl'; row.dataset.i=i;
|
| 301 |
+
row.style.setProperty('--cc',`var(${l.cc})`);
|
| 302 |
+
row.innerHTML=`<span class="t">${fmt(l.t)}</span><span class="tx"><b>${l.who}</b> · ${l.text}</span>`;
|
| 303 |
+
row.addEventListener('click',()=>seek(l.t));
|
| 304 |
+
linesEl.appendChild(row);
|
| 305 |
+
});
|
| 306 |
+
if(vid){ try{vid.currentTime=0;}catch(e){} }
|
| 307 |
+
activeLine=-1; renderPlayhead();
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
/* playback engine — reads the real video element */
|
| 311 |
+
let activeLine=-1, rafP=null;
|
| 312 |
+
function dur(){ return DUR || (vid&&vid.duration) || 0; }
|
| 313 |
+
function renderPlayhead(){
|
| 314 |
+
const D=dur(), cur=(vid&&vid.currentTime)||0;
|
| 315 |
+
const pct=D?(cur/D)*100:0;
|
| 316 |
+
fill.style.width=pct+'%'; knob.style.left=pct+'%'; tcEl.textContent=fmt(cur);
|
| 317 |
+
let idx=-1;
|
| 318 |
+
for(let i=0;i<script.length;i++){if(script[i].t<=cur)idx=i;else break;}
|
| 319 |
+
if(idx!==activeLine){
|
| 320 |
+
activeLine=idx;
|
| 321 |
+
$$('.pl',linesEl).forEach((r,i)=>r.classList.toggle('now',i===idx));
|
| 322 |
+
showChyron(idx);
|
| 323 |
+
if(idx>=0){const r=linesEl.children[idx];const top=r.offsetTop-linesEl.clientHeight/2+r.clientHeight/2;
|
| 324 |
+
linesEl.scrollTo({top,behavior:reduce?'auto':'smooth'});}
|
| 325 |
+
}
|
| 326 |
+
}
|
| 327 |
+
function showChyron(idx){
|
| 328 |
+
if(idx<0){chyron.classList.remove('in');return;}
|
| 329 |
+
const l=script[idx];
|
| 330 |
+
chyron.classList.remove('in');
|
| 331 |
+
requestAnimationFrame(()=>{
|
| 332 |
+
chyron.style.setProperty('--cc',`var(${l.cc})`);
|
| 333 |
+
chyWho.textContent=l.who; chyLine.textContent=l.text;
|
| 334 |
+
requestAnimationFrame(()=>chyron.classList.add('in'));
|
| 335 |
+
});
|
| 336 |
+
}
|
| 337 |
+
function loop(){ renderPlayhead(); if(vid && !vid.paused && !vid.ended) rafP=requestAnimationFrame(loop); }
|
| 338 |
+
function seek(t){ if(vid) vid.currentTime=Math.max(0,Math.min(dur(),t)); }
|
| 339 |
+
|
| 340 |
+
if(vid){
|
| 341 |
+
vid.addEventListener('loadedmetadata',()=>{ if(vid.duration&&isFinite(vid.duration))DUR=vid.duration; tcDur.textContent=fmt(dur()); renderPlayhead(); });
|
| 342 |
+
vid.addEventListener('play',()=>{ pp.innerHTML=ICON_PAUSE; loop(); });
|
| 343 |
+
vid.addEventListener('pause',()=>{ pp.innerHTML=ICON_PLAY; if(rafP)cancelAnimationFrame(rafP); });
|
| 344 |
+
vid.addEventListener('ended',()=>{ pp.innerHTML=ICON_PLAY; });
|
| 345 |
+
vid.addEventListener('timeupdate',renderPlayhead);
|
| 346 |
+
}
|
| 347 |
+
pp.addEventListener('click',()=>{ if(!vid)return; vid.paused?vid.play():vid.pause(); });
|
| 348 |
+
scrub.addEventListener('click',e=>{
|
| 349 |
+
const r=scrub.getBoundingClientRect();
|
| 350 |
+
seek(((e.clientX-r.left)/r.width)*dur());
|
| 351 |
+
});
|
| 352 |
+
|
| 353 |
+
/* orientation toggle (manual) + auto from real dims */
|
| 354 |
+
const booth=$('#booth');
|
| 355 |
+
function setBoothOrient(w,h){
|
| 356 |
+
booth.classList.remove('portrait','square');
|
| 357 |
+
let o='landscape';
|
| 358 |
+
if(w&&h){ if(Math.abs(w-h)/Math.max(w,h)<0.08) o='square'; else if(h>w) o='portrait'; }
|
| 359 |
+
if(o!=='landscape') booth.classList.add(o);
|
| 360 |
+
$$('.orient button').forEach(x=>x.setAttribute('aria-pressed', String(x.dataset.orient===o)));
|
| 361 |
+
}
|
| 362 |
+
$$('.orient button').forEach(b=>b.addEventListener('click',()=>{
|
| 363 |
+
$$('.orient button').forEach(x=>x.setAttribute('aria-pressed','false'));
|
| 364 |
+
b.setAttribute('aria-pressed','true');
|
| 365 |
+
booth.classList.remove('portrait','square');
|
| 366 |
+
if(b.dataset.orient!=='landscape')booth.classList.add(b.dataset.orient);
|
| 367 |
+
fitFrame();
|
| 368 |
+
}));
|
| 369 |
+
|
| 370 |
+
/* ---------- SRT EXPORT (CapCut-ready) ---------- */
|
| 371 |
+
$('#srtBtn').addEventListener('click',()=>{
|
| 372 |
+
if(!script.length){toast('Go live first');return;}
|
| 373 |
+
const D=dur();
|
| 374 |
+
const srt=script.map((l,i)=>{
|
| 375 |
+
const start=l.t, end=Math.min(D||l.t+4, (script[i+1]?script[i+1].t:l.t+4));
|
| 376 |
+
return `${i+1}\n${srtTime(start)} --> ${srtTime(end)}\n${l.who}: ${l.text}\n`;
|
| 377 |
+
}).join('\n');
|
| 378 |
+
const blob=new Blob([srt],{type:'text/plain'});
|
| 379 |
+
const a=document.createElement('a');
|
| 380 |
+
a.href=URL.createObjectURL(blob);
|
| 381 |
+
a.download=`marquee_${currentVibe||'show'}_captions.srt`;
|
| 382 |
+
a.click(); URL.revokeObjectURL(a.href);
|
| 383 |
+
toast('Captions exported — drop the .srt into CapCut');
|
| 384 |
+
});
|
| 385 |
+
function srtTime(s){
|
| 386 |
+
const h=Math.floor(s/3600), m=Math.floor((s%3600)/60), sec=Math.floor(s%60);
|
| 387 |
+
return `${pad(h)}:${pad(m)}:${pad(sec)},000`;
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
+
/* ---------- confetti ---------- */
|
| 391 |
+
function confetti(){
|
| 392 |
+
const cols=['#FFC94D','#FF4D9A','#3DE7FF','#C18BFF','#9BE85B','#FF8A4D'];
|
| 393 |
+
for(let i=0;i<70;i++){
|
| 394 |
+
const c=document.createElement('div');
|
| 395 |
+
c.className='confetti';
|
| 396 |
+
c.style.left=Math.random()*100+'vw';
|
| 397 |
+
c.style.background=cols[i%cols.length];
|
| 398 |
+
c.style.animationDuration=(1.6+Math.random()*1.6)+'s';
|
| 399 |
+
c.style.animationDelay=(Math.random()*.3)+'s';
|
| 400 |
+
c.style.transform=`rotate(${Math.random()*360}deg)`;
|
| 401 |
+
if(Math.random()>.5)c.style.borderRadius='50%';
|
| 402 |
+
document.body.appendChild(c);
|
| 403 |
+
setTimeout(()=>c.remove(),3600);
|
| 404 |
+
}
|
| 405 |
+
}
|
| 406 |
+
|
| 407 |
+
/* ---------- toast ---------- */
|
| 408 |
+
const toastEl=$('#toast'), toastTxt=$('#toastTxt'); let toastT;
|
| 409 |
+
function toast(msg){
|
| 410 |
+
toastTxt.textContent=msg; toastEl.classList.add('show');
|
| 411 |
+
clearTimeout(toastT); toastT=setTimeout(()=>toastEl.classList.remove('show'),2600);
|
| 412 |
+
}
|
marquee_inlined_preview.html
ADDED
|
@@ -0,0 +1,1093 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
+
<title>Marquee — Setup Console & Broadcast</title>
|
| 7 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 8 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 9 |
+
<link href="https://fonts.googleapis.com/css2?family=Unbounded:wght@400;500;600;700;800&family=Bebas+Neue&family=Nunito:wght@600;700;800;900&display=swap" rel="stylesheet">
|
| 10 |
+
<style>/* ============================================================
|
| 11 |
+
MARQUEE — design system
|
| 12 |
+
Tokens straight from the brief so they lift into Gradio css=.
|
| 13 |
+
Sections: tokens · base · masthead · rundown · desk · stage
|
| 14 |
+
· star card · vibes + Coco · broadcast player · utilities
|
| 15 |
+
============================================================ */
|
| 16 |
+
|
| 17 |
+
:root{
|
| 18 |
+
/* night & surfaces */
|
| 19 |
+
--night:#190B2E; /* base */
|
| 20 |
+
--night-2:#120821; /* deeper, for wells */
|
| 21 |
+
--stage:#241241; /* panels */
|
| 22 |
+
--stage-2:#2E1A52; /* raised */
|
| 23 |
+
--raise:#382060; /* inputs-on-dark */
|
| 24 |
+
--line:#3A2A5E; /* borders */
|
| 25 |
+
--line-soft:#2C1E4C;
|
| 26 |
+
|
| 27 |
+
/* leads + helper */
|
| 28 |
+
--hot:#FF3D8B; /* primary / on-air / energy */
|
| 29 |
+
--gold:#FFC94D; /* stardom: lights, crowns */
|
| 30 |
+
--cyan:#3DE7FF; /* confirm / scan */
|
| 31 |
+
--violet:#B98BFF; /* playful / mascot */
|
| 32 |
+
|
| 33 |
+
/* text */
|
| 34 |
+
--cream:#FFF3E9;
|
| 35 |
+
--muted:#A98FC9;
|
| 36 |
+
--muted-2:#7C66A6;
|
| 37 |
+
|
| 38 |
+
/* the casting rainbow — each star gets their own character color.
|
| 39 |
+
all high-chroma, all in-family: the "agent select" punch. */
|
| 40 |
+
--star-gold:#FFC94D;
|
| 41 |
+
--star-hot:#FF4D9A;
|
| 42 |
+
--star-cyan:#3DE7FF;
|
| 43 |
+
--star-violet:#C18BFF;
|
| 44 |
+
--star-lime:#9BE85B;
|
| 45 |
+
--star-tang:#FF8A4D;
|
| 46 |
+
|
| 47 |
+
--fx:cubic-bezier(.2,.9,.25,1);
|
| 48 |
+
--pop:cubic-bezier(.2,1.7,.4,1);
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
*{box-sizing:border-box;margin:0;padding:0}
|
| 52 |
+
html{-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
|
| 53 |
+
|
| 54 |
+
body{
|
| 55 |
+
font-family:Nunito,system-ui,sans-serif;
|
| 56 |
+
color:var(--cream);
|
| 57 |
+
background:var(--night);
|
| 58 |
+
min-height:100vh;
|
| 59 |
+
line-height:1.5;
|
| 60 |
+
/* one justified light: a stage spotlight from above + a faint floor wash.
|
| 61 |
+
no decorative blob gradients. */
|
| 62 |
+
background-image:
|
| 63 |
+
radial-gradient(120% 70% at 50% -25%, rgba(255,201,77,.10), transparent 55%),
|
| 64 |
+
radial-gradient(140% 90% at 50% 120%, rgba(61,231,255,.05), transparent 60%);
|
| 65 |
+
background-attachment:fixed;
|
| 66 |
+
}
|
| 67 |
+
/* film grain — the only decorative texture, very subtle */
|
| 68 |
+
body::after{
|
| 69 |
+
content:"";position:fixed;inset:0;pointer-events:none;z-index:80;opacity:.04;
|
| 70 |
+
mix-blend-mode:overlay;
|
| 71 |
+
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
.disp{font-family:Unbounded,sans-serif}
|
| 75 |
+
.marq{font-family:'Bebas Neue',sans-serif;letter-spacing:.06em}
|
| 76 |
+
svg.i{width:1em;height:1em;display:block;flex:none}
|
| 77 |
+
|
| 78 |
+
.shell{max-width:1180px;margin:0 auto;padding:clamp(16px,3vw,34px) clamp(16px,4vw,40px) 40px;position:relative;z-index:1}
|
| 79 |
+
|
| 80 |
+
/* ============================================================
|
| 81 |
+
MASTHEAD — the broadcast header bar. signature lives here.
|
| 82 |
+
============================================================ */
|
| 83 |
+
.masthead{
|
| 84 |
+
display:flex;align-items:center;justify-content:space-between;gap:20px;
|
| 85 |
+
padding-bottom:22px;margin-bottom:6px;flex-wrap:wrap;
|
| 86 |
+
}
|
| 87 |
+
.brand{display:flex;align-items:center;gap:18px}
|
| 88 |
+
|
| 89 |
+
/* the lit marquee sign — spend the boldness here */
|
| 90 |
+
.sign{
|
| 91 |
+
position:relative;padding:14px 22px 15px;border-radius:14px;
|
| 92 |
+
border:2px solid rgba(255,201,77,.55);
|
| 93 |
+
background:
|
| 94 |
+
radial-gradient(120% 140% at 50% 0%, rgba(255,201,77,.14), transparent 70%),
|
| 95 |
+
linear-gradient(180deg, rgba(255,201,77,.05), rgba(25,11,46,.2));
|
| 96 |
+
box-shadow:inset 0 0 22px rgba(255,201,77,.16), 0 10px 40px rgba(255,61,139,.10);
|
| 97 |
+
}
|
| 98 |
+
.sign h1{
|
| 99 |
+
font-family:Unbounded,sans-serif;font-weight:800;font-size:clamp(26px,3.4vw,38px);
|
| 100 |
+
letter-spacing:.04em;line-height:.9;
|
| 101 |
+
color:var(--cream);
|
| 102 |
+
text-shadow:0 0 18px rgba(255,201,77,.45), 0 0 2px rgba(255,201,77,.8);
|
| 103 |
+
}
|
| 104 |
+
/* perimeter bulbs, top + bottom rows */
|
| 105 |
+
.sign .bulbs{position:absolute;left:14px;right:14px;display:flex;justify-content:space-between}
|
| 106 |
+
.sign .bulbs.t{top:-5px}.sign .bulbs.b{bottom:-5px}
|
| 107 |
+
.sign .bulbs i{width:6px;height:6px;border-radius:50%;background:var(--gold);
|
| 108 |
+
box-shadow:0 0 8px var(--gold),0 0 2px #fff;animation:tw 2.6s infinite}
|
| 109 |
+
.sign .bulbs i:nth-child(2n){animation-delay:.7s}
|
| 110 |
+
.sign .bulbs i:nth-child(3n){animation-delay:1.5s;background:var(--hot);box-shadow:0 0 8px var(--hot),0 0 2px #fff}
|
| 111 |
+
@keyframes tw{0%,100%{opacity:1}48%{opacity:.28}}
|
| 112 |
+
|
| 113 |
+
.tag{font-family:Unbounded,sans-serif;font-weight:500;font-size:13px;color:var(--gold);letter-spacing:.01em}
|
| 114 |
+
.tag small{display:block;font-family:Nunito;font-weight:600;color:var(--muted);font-size:12px;margin-top:3px;letter-spacing:0}
|
| 115 |
+
|
| 116 |
+
/* right side: live clock + status */
|
| 117 |
+
.broadcast-meta{display:flex;align-items:center;gap:14px}
|
| 118 |
+
.clock{font-family:'Bebas Neue',sans-serif;letter-spacing:.12em;font-size:20px;color:var(--muted);
|
| 119 |
+
font-variant-numeric:tabular-nums}
|
| 120 |
+
.status{display:flex;align-items:center;gap:9px;white-space:nowrap;font-family:'Bebas Neue',sans-serif;letter-spacing:.14em;
|
| 121 |
+
font-size:16px;color:var(--muted);border:1px solid var(--line);padding:8px 15px;border-radius:999px;
|
| 122 |
+
background:rgba(46,26,82,.5)}
|
| 123 |
+
.status .led{width:9px;height:9px;border-radius:50%;background:var(--muted-2);transition:.3s}
|
| 124 |
+
.status.live{color:var(--hot);border-color:rgba(255,61,139,.5);background:rgba(255,61,139,.08)}
|
| 125 |
+
.status.live .led{background:var(--hot);box-shadow:0 0 12px var(--hot);animation:tw 1.1s infinite}
|
| 126 |
+
|
| 127 |
+
/* ============================================================
|
| 128 |
+
RUNDOWN — broadcast running order, not 4 identical cards.
|
| 129 |
+
nodes on a connecting track.
|
| 130 |
+
============================================================ */
|
| 131 |
+
.rundown{position:relative;display:grid;grid-template-columns:repeat(4,1fr);
|
| 132 |
+
margin:8px 0 30px;padding:0 6px}
|
| 133 |
+
.rundown::before{content:"";position:absolute;top:19px;left:11%;right:11%;height:2px;
|
| 134 |
+
background:var(--line-soft)}
|
| 135 |
+
.cue{position:relative;display:flex;flex-direction:column;align-items:center;gap:9px;text-align:center}
|
| 136 |
+
.cue .node{width:40px;height:40px;border-radius:50%;display:grid;place-items:center;
|
| 137 |
+
background:var(--stage);border:2px solid var(--line);
|
| 138 |
+
font-family:'Bebas Neue',sans-serif;font-size:20px;color:var(--muted-2);
|
| 139 |
+
position:relative;z-index:1;transition:.35s var(--fx)}
|
| 140 |
+
.cue .ttl{font-family:Unbounded,sans-serif;font-weight:600;font-size:13px;color:var(--muted)}
|
| 141 |
+
.cue .sub{font-size:11px;color:var(--muted-2);letter-spacing:.01em}
|
| 142 |
+
/* done */
|
| 143 |
+
.cue.done .node{border-color:var(--cyan);color:var(--cyan);background:rgba(61,231,255,.08)}
|
| 144 |
+
.cue.done .ttl{color:var(--cream)}
|
| 145 |
+
/* active */
|
| 146 |
+
.cue.on .node{border-color:var(--gold);color:var(--gold);background:rgba(255,201,77,.12);
|
| 147 |
+
box-shadow:0 0 0 5px rgba(255,201,77,.1),0 0 22px rgba(255,201,77,.3)}
|
| 148 |
+
.cue.on .ttl{color:var(--gold)}
|
| 149 |
+
@media(prefers-reduced-motion:no-preference){
|
| 150 |
+
.cue.on .node{animation:cuePulse 2s infinite}
|
| 151 |
+
}
|
| 152 |
+
@keyframes cuePulse{0%,100%{box-shadow:0 0 0 5px rgba(255,201,77,.1),0 0 22px rgba(255,201,77,.28)}
|
| 153 |
+
50%{box-shadow:0 0 0 8px rgba(255,201,77,.04),0 0 30px rgba(255,201,77,.45)}}
|
| 154 |
+
|
| 155 |
+
/* ============================================================
|
| 156 |
+
LAYOUT — control desk (left) vs the stage (right).
|
| 157 |
+
intentionally asymmetric; the two halves are treated differently.
|
| 158 |
+
============================================================ */
|
| 159 |
+
.console{display:grid;grid-template-columns:362px 1fr;gap:20px;align-items:start}
|
| 160 |
+
@media(max-width:900px){.console{grid-template-columns:1fr}}
|
| 161 |
+
|
| 162 |
+
/* the desk: flat, utilitarian — it's the control surface, not a showpiece */
|
| 163 |
+
.desk{display:flex;flex-direction:column;gap:16px}
|
| 164 |
+
.card{background:var(--stage);border:1px solid var(--line-soft);border-radius:16px;padding:18px}
|
| 165 |
+
.card-h{display:flex;align-items:center;gap:10px;margin-bottom:14px}
|
| 166 |
+
.card-h .ic{color:var(--gold);font-size:18px}
|
| 167 |
+
.card-h h2{font-family:Unbounded,sans-serif;font-weight:600;font-size:14px;letter-spacing:.01em}
|
| 168 |
+
.card-h .meta{margin-left:auto;font-family:'Bebas Neue',sans-serif;letter-spacing:.07em;font-size:12px;color:var(--muted-2)}
|
| 169 |
+
|
| 170 |
+
/* roll tape drop zone — clapperboard motif */
|
| 171 |
+
.drop{position:relative;border:1.5px dashed var(--line);border-radius:13px;padding:26px 16px;
|
| 172 |
+
text-align:center;cursor:pointer;transition:.22s var(--fx);overflow:hidden;
|
| 173 |
+
background:radial-gradient(200px 110px at 50% -20%,rgba(255,201,77,.07),transparent 70%)}
|
| 174 |
+
.drop:hover{border-color:var(--gold);background:radial-gradient(200px 130px at 50% -10%,rgba(255,201,77,.12),transparent 70%)}
|
| 175 |
+
.drop:focus-visible{border-color:var(--cyan)}
|
| 176 |
+
.drop .clap{font-size:30px;color:var(--gold);display:inline-flex;margin-bottom:8px}
|
| 177 |
+
.drop .t{font-family:Unbounded,sans-serif;font-weight:600;font-size:15px}
|
| 178 |
+
.drop .s{font-size:12px;color:var(--muted);margin-top:4px}
|
| 179 |
+
/* loaded: a little filmstrip readout */
|
| 180 |
+
.drop .loaded{display:none;align-items:center;gap:12px;text-align:left}
|
| 181 |
+
.drop.loaded{border-style:solid;border-color:rgba(61,231,255,.45);background:rgba(61,231,255,.05);cursor:default}
|
| 182 |
+
.drop.loaded .ask{display:none}
|
| 183 |
+
.drop.loaded .loaded{display:flex}
|
| 184 |
+
.strip{width:52px;height:52px;border-radius:9px;flex:none;position:relative;overflow:hidden;
|
| 185 |
+
background:linear-gradient(135deg,var(--raise),var(--stage));border:1px solid var(--line)}
|
| 186 |
+
.strip::before,.strip::after{content:"";position:absolute;top:4px;bottom:4px;width:5px;
|
| 187 |
+
background:repeating-linear-gradient(var(--night) 0 3px,transparent 3px 7px)}
|
| 188 |
+
.strip::before{left:3px}.strip::after{right:3px}
|
| 189 |
+
.strip .play{position:absolute;inset:0;display:grid;place-items:center;color:var(--cyan);font-size:16px}
|
| 190 |
+
.loaded .fn{font-family:Unbounded,sans-serif;font-weight:600;font-size:13px;color:var(--cream)}
|
| 191 |
+
.loaded .fd{font-family:'Bebas Neue',sans-serif;letter-spacing:.05em;font-size:13px;color:var(--cyan);margin-top:3px}
|
| 192 |
+
|
| 193 |
+
/* buttons */
|
| 194 |
+
.btn{width:100%;border:0;cursor:pointer;font-family:Unbounded,sans-serif;font-weight:600;font-size:14px;
|
| 195 |
+
color:var(--night);padding:14px;border-radius:12px;display:flex;align-items:center;justify-content:center;
|
| 196 |
+
gap:9px;transition:.18s var(--fx)}
|
| 197 |
+
.btn:disabled{opacity:.4;cursor:not-allowed;background:var(--raise);color:var(--muted-2)}
|
| 198 |
+
.btn.cyan{background:linear-gradient(135deg,var(--cyan),#16b6d8)}
|
| 199 |
+
.btn.cyan:not(:disabled):hover{transform:translateY(-2px);box-shadow:0 12px 30px rgba(61,231,255,.3)}
|
| 200 |
+
.btn.show{font-size:17px;padding:17px;background:linear-gradient(110deg,var(--gold),var(--hot));position:relative;overflow:hidden}
|
| 201 |
+
.btn.show:not(:disabled):hover{transform:translateY(-2px);box-shadow:0 16px 38px rgba(255,61,139,.4)}
|
| 202 |
+
.btn.show:not(:disabled)::after{content:"";position:absolute;top:0;bottom:0;width:40%;
|
| 203 |
+
background:linear-gradient(90deg,transparent,rgba(255,255,255,.35),transparent);
|
| 204 |
+
transform:translateX(-200%)}
|
| 205 |
+
@media(prefers-reduced-motion:no-preference){
|
| 206 |
+
.btn.show:not(:disabled)::after{animation:shine 3.4s ease-in-out 1.2s infinite}
|
| 207 |
+
}
|
| 208 |
+
@keyframes shine{0%,100%{transform:translateX(-220%)}40%,60%{transform:translateX(360%)}}
|
| 209 |
+
.mt{margin-top:14px}
|
| 210 |
+
|
| 211 |
+
/* casting sweep bar */
|
| 212 |
+
.sweep{height:6px;border-radius:4px;margin-top:13px;background:var(--night-2);position:relative;overflow:hidden;
|
| 213 |
+
opacity:0;height:0;margin-top:0;transition:.25s}
|
| 214 |
+
.casting .sweep{opacity:1;height:6px;margin-top:13px}
|
| 215 |
+
.sweep::after{content:"";position:absolute;inset:0;width:36%;
|
| 216 |
+
background:linear-gradient(90deg,transparent,var(--gold),transparent)}
|
| 217 |
+
@media(prefers-reduced-motion:no-preference){.sweep::after{animation:sw 1s linear infinite}}
|
| 218 |
+
@keyframes sw{from{transform:translateX(-120%)}to{transform:translateX(330%)}}
|
| 219 |
+
|
| 220 |
+
/* ============================================================
|
| 221 |
+
THE STAGE — Star Select. theatrical, spotlit well.
|
| 222 |
+
============================================================ */
|
| 223 |
+
.stage-panel{background:
|
| 224 |
+
radial-gradient(80% 60% at 50% -8%, rgba(255,201,77,.07), transparent 60%),
|
| 225 |
+
linear-gradient(180deg,var(--stage),var(--night-2));
|
| 226 |
+
border:1px solid var(--line);border-radius:20px;padding:20px;
|
| 227 |
+
box-shadow:inset 0 1px 0 rgba(255,243,233,.04), 0 24px 60px rgba(8,2,20,.5)}
|
| 228 |
+
.stage-h{display:flex;align-items:center;gap:11px;margin-bottom:16px}
|
| 229 |
+
.stage-h .ic{color:var(--gold);font-size:19px}
|
| 230 |
+
.stage-h h2{font-family:Unbounded,sans-serif;font-weight:700;font-size:16px}
|
| 231 |
+
.stage-h .count{margin-left:auto;white-space:nowrap;font-family:'Bebas Neue',sans-serif;letter-spacing:.08em;font-size:14px;
|
| 232 |
+
color:var(--muted);background:rgba(46,26,82,.6);padding:4px 11px;border-radius:8px}
|
| 233 |
+
|
| 234 |
+
/* empty state */
|
| 235 |
+
.empty{display:grid;place-items:center;min-height:330px;text-align:center;padding:20px}
|
| 236 |
+
.empty .spot{width:96px;height:96px;border-radius:50%;display:grid;place-items:center;margin-bottom:16px;
|
| 237 |
+
color:var(--muted-2);font-size:46px;
|
| 238 |
+
background:radial-gradient(circle at 50% 30%,rgba(255,201,77,.08),transparent 70%)}
|
| 239 |
+
.empty .t{font-family:Unbounded,sans-serif;font-weight:600;font-size:17px;color:var(--cream);margin-bottom:6px}
|
| 240 |
+
.empty .s{font-size:13px;color:var(--muted);max-width:300px}
|
| 241 |
+
|
| 242 |
+
/* star grid */
|
| 243 |
+
.roster{display:grid;grid-template-columns:repeat(auto-fill,minmax(164px,1fr));gap:16px}
|
| 244 |
+
|
| 245 |
+
/* ---- the star card (one reusable block) ----
|
| 246 |
+
per-card character color is set inline as --c. */
|
| 247 |
+
/* base state is fully visible — entrance is driven via WAAPI in JS so a
|
| 248 |
+
backgrounded tab never leaves cards stuck invisible. */
|
| 249 |
+
.star{--c:var(--gold);position:relative;border-radius:16px;overflow:hidden;
|
| 250 |
+
background:linear-gradient(180deg,var(--stage-2),var(--stage));
|
| 251 |
+
border:1px solid var(--line);transition:.25s var(--fx)}
|
| 252 |
+
.star:hover{transform:translateY(-3px);border-color:color-mix(in srgb,var(--c) 60%,transparent)}
|
| 253 |
+
|
| 254 |
+
/* portrait well with a spotlight beam tinted to the character color,
|
| 255 |
+
and an angular framed headshot placeholder (agent-select energy) */
|
| 256 |
+
.portrait{position:relative;height:138px;overflow:hidden;
|
| 257 |
+
background:radial-gradient(90% 80% at 50% 120%,color-mix(in srgb,var(--c) 22%,var(--night-2)),var(--night-2))}
|
| 258 |
+
.portrait .beam{position:absolute;top:-30px;left:50%;width:150px;height:170px;transform:translateX(-50%);
|
| 259 |
+
pointer-events:none;clip-path:polygon(40% 0,60% 0,100% 100%,0 100%);
|
| 260 |
+
background:linear-gradient(180deg,color-mix(in srgb,var(--c) 55%,transparent),transparent 70%);opacity:.7}
|
| 261 |
+
.portrait .head-ph{position:absolute;left:50%;bottom:-6px;transform:translateX(-50%);
|
| 262 |
+
color:color-mix(in srgb,var(--c) 50%,#ffffff);opacity:.4}
|
| 263 |
+
.portrait .frame{position:absolute;inset:8px;border:1.5px solid color-mix(in srgb,var(--c) 35%,transparent);
|
| 264 |
+
border-radius:11px;pointer-events:none}
|
| 265 |
+
.portrait .idx{position:absolute;top:10px;left:10px;font-family:'Bebas Neue',sans-serif;font-size:14px;
|
| 266 |
+
color:var(--cream);background:rgba(15,6,30,.7);padding:2px 7px;border-radius:7px;display:flex;align-items:center;gap:3px}
|
| 267 |
+
.portrait .idx .st{color:var(--c)}
|
| 268 |
+
.tier{position:absolute;top:10px;right:10px;font-family:'Bebas Neue',sans-serif;font-size:11px;letter-spacing:.02em;
|
| 269 |
+
white-space:nowrap;color:var(--night);background:var(--c);padding:3px 8px;border-radius:7px;
|
| 270 |
+
box-shadow:0 2px 10px color-mix(in srgb,var(--c) 50%,transparent)}
|
| 271 |
+
|
| 272 |
+
/* crown drops in on naming */
|
| 273 |
+
/* crown lifts in above the star's head on naming */
|
| 274 |
+
.crown{position:absolute;top:50px;left:50%;transform:translateX(-50%) translateY(12px) scale(.4) rotate(-12deg);
|
| 275 |
+
opacity:0;width:30px;height:30px;color:var(--gold);
|
| 276 |
+
filter:drop-shadow(0 2px 6px color-mix(in srgb,var(--gold) 70%,transparent));
|
| 277 |
+
transition:.4s var(--pop)}
|
| 278 |
+
.star.named .crown{opacity:1;transform:translateX(-50%) scale(1) rotate(0)}
|
| 279 |
+
|
| 280 |
+
.star-body{padding:13px}
|
| 281 |
+
.star-body label{font-family:'Bebas Neue',sans-serif;letter-spacing:.08em;font-size:12px;color:var(--muted);
|
| 282 |
+
display:block;margin-bottom:7px}
|
| 283 |
+
.star input{width:100%;background:var(--night-2);border:1px solid var(--line);color:var(--cream);
|
| 284 |
+
border-radius:10px;padding:10px 12px;font-family:Nunito;font-weight:800;font-size:14px;outline:none;transition:.16s}
|
| 285 |
+
.star input::placeholder{color:var(--muted-2);font-weight:600}
|
| 286 |
+
.star input:focus{border-color:var(--c);box-shadow:0 0 0 3px color-mix(in srgb,var(--c) 25%,transparent)}
|
| 287 |
+
|
| 288 |
+
/* crowned: card lights in its own character color */
|
| 289 |
+
.star.named{border-color:color-mix(in srgb,var(--c) 65%,transparent);
|
| 290 |
+
box-shadow:0 0 0 1px color-mix(in srgb,var(--c) 30%,transparent),0 10px 30px color-mix(in srgb,var(--c) 18%,transparent)}
|
| 291 |
+
.star.named .portrait{background:radial-gradient(90% 90% at 50% 120%,color-mix(in srgb,var(--c) 40%,var(--night-2)),var(--night-2))}
|
| 292 |
+
.star.named .portrait .beam{opacity:1}
|
| 293 |
+
.star.named .head-ph{opacity:.6}
|
| 294 |
+
|
| 295 |
+
/* ============================================================
|
| 296 |
+
VIBES (narration modes) + COCO host
|
| 297 |
+
============================================================ */
|
| 298 |
+
.vibes{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
|
| 299 |
+
.vibe{display:flex;align-items:center;gap:11px;padding:12px;border-radius:13px;cursor:pointer;color:var(--cream);
|
| 300 |
+
border:1px solid var(--line-soft);background:var(--stage-2);transition:.18s var(--fx);text-align:left}
|
| 301 |
+
.vibe:hover{border-color:var(--violet);transform:translateY(-1px)}
|
| 302 |
+
.vibe .ic{font-size:20px;color:var(--muted);display:inline-flex;transition:.18s}
|
| 303 |
+
.vibe>span:last-child{display:flex;flex-direction:column;gap:2px;min-width:0}
|
| 304 |
+
.vibe .vt{display:block;font-family:Unbounded,sans-serif;font-weight:600;font-size:13px;color:var(--cream)}
|
| 305 |
+
.vibe .vd{display:block;font-size:11px;color:var(--muted);line-height:1.25}
|
| 306 |
+
.vibe[aria-pressed="true"]{border-color:var(--vc,var(--hot));background:color-mix(in srgb,var(--vc,var(--hot)) 12%,var(--stage-2))}
|
| 307 |
+
.vibe[aria-pressed="true"] .ic{color:var(--vc,var(--hot))}
|
| 308 |
+
|
| 309 |
+
/* Coco — the host bug. re-dresses per vibe. */
|
| 310 |
+
.coco{display:flex;align-items:center;gap:13px;margin-top:14px;padding:13px 14px;border-radius:14px;
|
| 311 |
+
background:linear-gradient(120deg,color-mix(in srgb,var(--vc,var(--violet)) 12%,var(--stage-2)),var(--stage-2));
|
| 312 |
+
border:1px solid color-mix(in srgb,var(--vc,var(--violet)) 30%,var(--line-soft));transition:.35s var(--fx)}
|
| 313 |
+
.coco .av{position:relative;width:46px;height:46px;border-radius:13px;flex:none;display:grid;place-items:center;
|
| 314 |
+
background:radial-gradient(circle at 50% 30%,color-mix(in srgb,var(--vc,var(--violet)) 70%,#fff 5%),var(--vc,var(--violet)));
|
| 315 |
+
box-shadow:0 6px 18px color-mix(in srgb,var(--vc,var(--violet)) 40%,transparent);transition:.35s var(--fx)}
|
| 316 |
+
.coco .av .bird{width:30px;height:30px;color:var(--night);filter:drop-shadow(0 1px 1px rgba(255,255,255,.3))}
|
| 317 |
+
/* accessory badge sits on the avatar, swapped per vibe */
|
| 318 |
+
.coco .acc{position:absolute;right:-6px;bottom:-6px;width:22px;height:22px;border-radius:50%;
|
| 319 |
+
display:grid;place-items:center;background:var(--night);border:2px solid var(--vc,var(--violet));
|
| 320 |
+
color:var(--vc,var(--violet));font-size:11px}
|
| 321 |
+
.coco .acc svg{width:12px;height:12px}
|
| 322 |
+
.coco .say{font-size:13px;line-height:1.45}
|
| 323 |
+
.coco .say b{color:var(--vc,var(--gold))}
|
| 324 |
+
.coco .who{font-family:'Bebas Neue',sans-serif;letter-spacing:.1em;font-size:11px;color:var(--vc,var(--violet));
|
| 325 |
+
display:block;margin-bottom:2px}
|
| 326 |
+
|
| 327 |
+
/* privacy footnote — visible, not buried */
|
| 328 |
+
.note{margin-top:26px;text-align:center;font-size:12.5px;color:var(--muted);line-height:1.6}
|
| 329 |
+
.note b{color:var(--cream)}
|
| 330 |
+
.note .lock{color:var(--cyan);vertical-align:-2px;margin-right:5px}
|
| 331 |
+
|
| 332 |
+
/* ============================================================
|
| 333 |
+
VIEW SWITCH
|
| 334 |
+
============================================================ */
|
| 335 |
+
.view{display:none}
|
| 336 |
+
.view.active{display:block;animation:viewIn .4s var(--fx)}
|
| 337 |
+
@keyframes viewIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
| 338 |
+
|
| 339 |
+
/* ============================================================
|
| 340 |
+
BROADCAST PLAYER
|
| 341 |
+
============================================================ */
|
| 342 |
+
.booth-top{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:18px;flex-wrap:wrap}
|
| 343 |
+
.back{display:inline-flex;align-items:center;gap:8px;background:var(--stage);border:1px solid var(--line);
|
| 344 |
+
color:var(--cream);font-family:Nunito;font-weight:700;font-size:13px;padding:9px 14px;border-radius:10px;cursor:pointer;transition:.16s}
|
| 345 |
+
.back:hover{border-color:var(--violet)}
|
| 346 |
+
.orient{display:flex;gap:4px;background:var(--stage);border:1px solid var(--line-soft);padding:4px;border-radius:11px}
|
| 347 |
+
.orient button{background:transparent;border:0;color:var(--muted);font-family:'Bebas Neue',sans-serif;letter-spacing:.06em;
|
| 348 |
+
font-size:13px;padding:7px 14px;border-radius:8px;cursor:pointer;transition:.16s}
|
| 349 |
+
.orient button[aria-pressed="true"]{background:var(--raise);color:var(--gold)}
|
| 350 |
+
|
| 351 |
+
.booth{display:grid;grid-template-columns:1fr 320px;gap:18px;align-items:start}
|
| 352 |
+
/* portrait: the clip is a phone-sized vertical frame, never a giant column-filling block.
|
| 353 |
+
capped width keeps the 9:16 height sane and the play-by-play rail beside it. */
|
| 354 |
+
.booth.portrait{grid-template-columns:minmax(0,340px) minmax(0,1fr)}
|
| 355 |
+
.booth.portrait .screen-wrap{max-width:340px}
|
| 356 |
+
@media(max-width:840px){
|
| 357 |
+
.booth,.booth.square{grid-template-columns:1fr}
|
| 358 |
+
.booth.portrait{grid-template-columns:1fr}
|
| 359 |
+
.booth.portrait .screen-wrap{margin-inline:auto}
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
/* the screen */
|
| 363 |
+
.screen-wrap{background:#000;border-radius:18px;overflow:hidden;border:1px solid var(--line);
|
| 364 |
+
box-shadow:0 24px 70px rgba(0,0,0,.6);position:relative}
|
| 365 |
+
.screen{position:relative;width:100%;aspect-ratio:16/9;background:
|
| 366 |
+
repeating-linear-gradient(135deg,#1c0f33 0 18px,#170c2b 18px 36px);
|
| 367 |
+
display:grid;place-items:center}
|
| 368 |
+
.booth.portrait .screen{aspect-ratio:9/16}
|
| 369 |
+
.booth.square .screen{aspect-ratio:1/1}
|
| 370 |
+
.screen .ph{font-family:'Bebas Neue',sans-serif;letter-spacing:.12em;color:var(--muted-2);font-size:15px;
|
| 371 |
+
text-align:center;padding:0 20px}
|
| 372 |
+
.screen .ph svg{width:40px;height:40px;margin:0 auto 10px;color:var(--line);display:block}
|
| 373 |
+
|
| 374 |
+
/* on-air bug */
|
| 375 |
+
.bug{position:absolute;top:14px;left:14px;display:flex;align-items:center;gap:8px;white-space:nowrap;
|
| 376 |
+
background:rgba(15,6,30,.6);backdrop-filter:blur(4px);padding:7px 12px;border-radius:9px;
|
| 377 |
+
font-family:'Bebas Neue',sans-serif;letter-spacing:.12em;font-size:14px;color:var(--cream)}
|
| 378 |
+
.bug .dot{width:10px;height:10px;border-radius:50%;background:var(--hot);box-shadow:0 0 10px var(--hot)}
|
| 379 |
+
@media(prefers-reduced-motion:no-preference){.bug .dot{animation:tw 1.1s infinite}}
|
| 380 |
+
|
| 381 |
+
/* timecode */
|
| 382 |
+
.tc{position:absolute;top:14px;right:14px;background:rgba(15,6,30,.6);backdrop-filter:blur(4px);
|
| 383 |
+
padding:7px 12px;border-radius:9px;font-family:'Bebas Neue',sans-serif;letter-spacing:.1em;font-size:15px;
|
| 384 |
+
color:var(--cyan);font-variant-numeric:tabular-nums}
|
| 385 |
+
|
| 386 |
+
/* lower-third chyron */
|
| 387 |
+
.chyron{position:absolute;left:14px;right:14px;bottom:14px;border-radius:12px;overflow:hidden;
|
| 388 |
+
background:linear-gradient(100deg,rgba(15,6,30,.92),rgba(36,18,65,.82));
|
| 389 |
+
border:1px solid color-mix(in srgb,var(--cc,var(--hot)) 40%,var(--line));
|
| 390 |
+
box-shadow:0 10px 30px rgba(0,0,0,.4);
|
| 391 |
+
transform:translateY(130%);opacity:0;transition:.45s var(--fx)}
|
| 392 |
+
.chyron.in{transform:none;opacity:1}
|
| 393 |
+
.chyron .bar{height:4px;background:linear-gradient(90deg,var(--cc,var(--hot)),var(--gold))}
|
| 394 |
+
.chyron .row{display:flex;align-items:stretch}
|
| 395 |
+
.chyron .who{display:flex;align-items:center;font-family:'Bebas Neue',sans-serif;letter-spacing:.06em;
|
| 396 |
+
font-size:17px;color:var(--night);background:var(--cc,var(--hot));padding:10px 14px;white-space:nowrap}
|
| 397 |
+
.chyron .line{padding:10px 16px;font-family:Nunito;font-weight:700;font-size:15px;color:var(--cream);
|
| 398 |
+
display:flex;align-items:center;line-height:1.3}
|
| 399 |
+
@media(prefers-reduced-motion:no-preference){
|
| 400 |
+
.chyron.in .line{animation:sharpen .5s var(--fx)}
|
| 401 |
+
}
|
| 402 |
+
@keyframes sharpen{from{filter:blur(7px);opacity:.3}to{filter:blur(0);opacity:1}}
|
| 403 |
+
|
| 404 |
+
/* transport */
|
| 405 |
+
.transport{display:flex;align-items:center;gap:14px;margin-top:14px}
|
| 406 |
+
.transport .pp{width:48px;height:48px;border-radius:50%;border:0;cursor:pointer;flex:none;display:grid;place-items:center;
|
| 407 |
+
background:linear-gradient(135deg,var(--gold),var(--hot));color:var(--night);font-size:20px;transition:.16s}
|
| 408 |
+
.transport .pp:hover{transform:scale(1.05)}
|
| 409 |
+
.scrub{flex:1;height:6px;border-radius:4px;background:var(--raise);position:relative;cursor:pointer}
|
| 410 |
+
.scrub .fill{position:absolute;left:0;top:0;bottom:0;border-radius:4px;width:0;
|
| 411 |
+
background:linear-gradient(90deg,var(--hot),var(--gold))}
|
| 412 |
+
.scrub .knob{position:absolute;top:50%;width:14px;height:14px;border-radius:50%;background:var(--cream);
|
| 413 |
+
transform:translate(-50%,-50%);left:0;box-shadow:0 2px 6px rgba(0,0,0,.5)}
|
| 414 |
+
|
| 415 |
+
/* play-by-play rail */
|
| 416 |
+
.pbp{background:var(--stage);border:1px solid var(--line-soft);border-radius:16px;overflow:hidden;
|
| 417 |
+
display:flex;flex-direction:column;max-height:520px}
|
| 418 |
+
.pbp .pbp-h{display:flex;align-items:center;gap:9px;padding:14px 16px;border-bottom:1px solid var(--line-soft)}
|
| 419 |
+
.pbp .pbp-h .ic{color:var(--gold);font-size:16px}
|
| 420 |
+
.pbp .pbp-h h3{font-family:Unbounded,sans-serif;font-weight:600;font-size:13px}
|
| 421 |
+
.pbp .pbp-h .ct{margin-left:auto;font-family:'Bebas Neue',sans-serif;letter-spacing:.06em;font-size:12px;color:var(--muted-2)}
|
| 422 |
+
.pbp .lines{overflow-y:auto;padding:6px}
|
| 423 |
+
.pbp .pl{display:flex;gap:11px;padding:11px 12px;border-radius:10px;cursor:pointer;transition:.15s;align-items:baseline}
|
| 424 |
+
.pbp .pl:hover{background:var(--stage-2)}
|
| 425 |
+
.pbp .pl .t{font-family:'Bebas Neue',sans-serif;letter-spacing:.04em;font-size:14px;color:var(--muted-2);
|
| 426 |
+
flex:none;width:42px;font-variant-numeric:tabular-nums;padding-top:1px}
|
| 427 |
+
.pbp .pl .tx{font-size:13px;line-height:1.45;color:var(--muted)}
|
| 428 |
+
.pbp .pl .tx b{color:var(--cream);font-weight:800}
|
| 429 |
+
.pbp .pl.now{background:color-mix(in srgb,var(--cc,var(--hot)) 14%,var(--stage-2))}
|
| 430 |
+
.pbp .pl.now .t{color:var(--cc,var(--hot))}
|
| 431 |
+
.pbp .pl.now .tx{color:var(--cream)}
|
| 432 |
+
.pbp .pl.now .tx b{color:var(--cc,var(--hot))}
|
| 433 |
+
|
| 434 |
+
/* export bar */
|
| 435 |
+
.export{padding:13px 16px;border-top:1px solid var(--line-soft);background:var(--night-2)}
|
| 436 |
+
.export .btn-srt{width:100%;display:flex;align-items:center;justify-content:center;gap:9px;cursor:pointer;
|
| 437 |
+
background:var(--stage-2);border:1px solid var(--line);color:var(--cream);font-family:Nunito;font-weight:700;
|
| 438 |
+
font-size:13px;padding:12px;border-radius:11px;transition:.16s}
|
| 439 |
+
.export .btn-srt:hover{border-color:var(--cyan);color:var(--cyan)}
|
| 440 |
+
.export .btn-srt .ic{color:var(--cyan);font-size:16px}
|
| 441 |
+
.export .hint{text-align:center;font-size:11px;color:var(--muted-2);margin-top:8px}
|
| 442 |
+
.export .hint b{color:var(--muted)}
|
| 443 |
+
|
| 444 |
+
/* confetti */
|
| 445 |
+
.confetti{position:fixed;top:-20px;width:11px;height:14px;z-index:90;pointer-events:none;border-radius:2px}
|
| 446 |
+
@media(prefers-reduced-motion:no-preference){.confetti{animation:fall linear forwards}}
|
| 447 |
+
@keyframes fall{to{transform:translateY(105vh) rotate(540deg);opacity:.2}}
|
| 448 |
+
|
| 449 |
+
/* ============================================================
|
| 450 |
+
UTILITIES
|
| 451 |
+
============================================================ */
|
| 452 |
+
:focus-visible{outline:2px solid var(--cyan);outline-offset:3px;border-radius:8px}
|
| 453 |
+
.toast{position:fixed;left:50%;bottom:28px;transform:translateX(-50%) translateY(20px);opacity:0;
|
| 454 |
+
background:var(--stage-2);border:1px solid var(--cyan);color:var(--cream);padding:12px 20px;border-radius:12px;
|
| 455 |
+
font-weight:700;font-size:13px;z-index:95;transition:.3s var(--fx);display:flex;align-items:center;gap:9px;
|
| 456 |
+
box-shadow:0 14px 40px rgba(0,0,0,.5)}
|
| 457 |
+
.toast.show{transform:translateX(-50%);opacity:1}
|
| 458 |
+
.toast .ic{color:var(--cyan)}
|
| 459 |
+
|
| 460 |
+
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
|
| 461 |
+
|
| 462 |
+
@media(max-width:560px){
|
| 463 |
+
.masthead{gap:14px}
|
| 464 |
+
.rundown .sub{display:none}
|
| 465 |
+
.vibes{grid-template-columns:1fr}
|
| 466 |
+
.roster{grid-template-columns:repeat(auto-fill,minmax(140px,1fr))}
|
| 467 |
+
}
|
| 468 |
+
|
| 469 |
+
/* ============================================================
|
| 470 |
+
INTEGRATION ADDITIONS — real <video> in the screen, real face
|
| 471 |
+
crops in star cards (added for the Gradio wiring)
|
| 472 |
+
============================================================ */
|
| 473 |
+
.screen-vid{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:#000;display:none}
|
| 474 |
+
.screen.has-video .ph{display:none}
|
| 475 |
+
.screen.has-video .screen-vid{display:block}
|
| 476 |
+
.portrait .head-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
|
| 477 |
+
.star.named .head-img{filter:saturate(1.05)}
|
| 478 |
+
</style>
|
| 479 |
+
</head>
|
| 480 |
+
<body>
|
| 481 |
+
|
| 482 |
+
<!-- =======================================================
|
| 483 |
+
ICON SPRITE — line icons, broadcast vernacular
|
| 484 |
+
======================================================= -->
|
| 485 |
+
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
|
| 486 |
+
<defs>
|
| 487 |
+
<symbol id="i-clap" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"><path d="M3 8h18v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8Z"/><path d="m3 8 1.6-3.4 3.8 1L6.7 9M9.4 5.6l3.8 1L11.5 10M15 7l3.8 1-1.5 3.4M21 8l-.8-2.2-3.4-.9"/></symbol>
|
| 488 |
+
<symbol id="i-play" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></symbol>
|
| 489 |
+
<symbol id="i-pause" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="5" width="4" height="14" rx="1"/><rect x="14" y="5" width="4" height="14" rx="1"/></symbol>
|
| 490 |
+
<symbol id="i-star" viewBox="0 0 24 24" fill="currentColor"><path d="m12 2 2.9 6.3 6.9.8-5.1 4.7 1.4 6.8L12 17.8 5.9 20.6l1.4-6.8L2.2 9.1l6.9-.8z"/></symbol>
|
| 491 |
+
<symbol id="i-spot" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><path d="M12 3v3M5 6l2 2M19 6l-2 2"/><path d="M9 9h6l3 11H6z" fill="currentColor" opacity=".15"/><path d="M9 9h6l3 11H6z"/></symbol>
|
| 492 |
+
<symbol id="i-crown" viewBox="0 0 24 24" fill="currentColor"><path d="M3 7l4 4 5-7 5 7 4-4-2 12H5z"/></symbol>
|
| 493 |
+
<symbol id="i-bird" viewBox="0 0 24 24" fill="currentColor"><path d="M14 4a3 3 0 0 0-3 3c0 .5-.4 1-1 1.5-1.8 1.3-3 3.4-3 5.8C7 17.9 9.6 20 13 20c.6 0 1-.6.8-1.2C13.3 17 13 16 14 15c2.2-.4 4-2.3 4-4.8V7a3 3 0 0 0-3-3zm.6 3.2a.9.9 0 1 1 0-.1z"/></symbol>
|
| 494 |
+
<symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V8a4 4 0 0 1 8 0v3"/></symbol>
|
| 495 |
+
<symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4v10m0 0 4-4m-4 4-4-4M5 20h14"/></symbol>
|
| 496 |
+
<symbol id="i-mic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M6 11a6 6 0 0 0 12 0M12 17v4"/></symbol>
|
| 497 |
+
<symbol id="i-list" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><path d="M8 6h12M8 12h12M8 18h12M4 6h.01M4 12h.01M4 18h.01"/></symbol>
|
| 498 |
+
<symbol id="i-whistle" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><path d="M3 11a5 5 0 0 0 5 5h4l5 3v-8a3 3 0 0 0-3-3H8a5 5 0 0 0-5 3Z"/><path d="M14 4v3"/></symbol>
|
| 499 |
+
<symbol id="i-glam" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><path d="M3 14c2-1 4-1 5 0M16 14c2-1 4-1 5 0M3 11h18M7 11l1 5M17 11l-1 5"/><circle cx="6.5" cy="8.5" r="2"/><circle cx="17.5" cy="8.5" r="2"/></symbol>
|
| 500 |
+
<symbol id="i-paw" viewBox="0 0 24 24" fill="currentColor"><circle cx="6" cy="9" r="2"/><circle cx="11" cy="6" r="2"/><circle cx="16" cy="7" r="2"/><circle cx="19" cy="11" r="2"/><path d="M12 12c-3 0-6 2.5-6 5 0 2 2 3 6 3s6-1 6-3c0-2.5-3-5-6-5Z"/></symbol>
|
| 501 |
+
<symbol id="i-glove" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"><path d="M7 10V6a2 2 0 0 1 4 0v3M11 9V5a2 2 0 0 1 4 0v5l2 1a3 3 0 0 1 1.4 3.4l-.8 3A2 2 0 0 1 15.7 21H10a3 3 0 0 1-2.8-2L5.5 14A2.5 2.5 0 0 1 7 10Z"/></symbol>
|
| 502 |
+
<symbol id="i-cam" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="6" width="13" height="12" rx="2"/><path d="m16 10 5-3v10l-5-3z"/></symbol>
|
| 503 |
+
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="m4 12 5 5L20 6"/></symbol>
|
| 504 |
+
<symbol id="i-back" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M15 5l-7 7 7 7"/></symbol>
|
| 505 |
+
<symbol id="i-portrait" viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="8" r="4.4"/><path d="M3.5 22c0-4.7 3.8-8 8.5-8s8.5 3.3 8.5 8z"/></symbol>
|
| 506 |
+
</defs>
|
| 507 |
+
</svg>
|
| 508 |
+
|
| 509 |
+
<div class="shell">
|
| 510 |
+
|
| 511 |
+
<!-- ============== MASTHEAD ============== -->
|
| 512 |
+
<header class="masthead">
|
| 513 |
+
<div class="brand">
|
| 514 |
+
<div class="sign">
|
| 515 |
+
<div class="bulbs t"><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>
|
| 516 |
+
<h1 class="disp">MARQUEE</h1>
|
| 517 |
+
<div class="bulbs b"><i></i><i></i><i></i><i></i><i></i><i></i><i></i></div>
|
| 518 |
+
</div>
|
| 519 |
+
<div class="tag">Everyone's a headliner.<small>Turn any clip into a live broadcast.</small></div>
|
| 520 |
+
</div>
|
| 521 |
+
<div class="broadcast-meta">
|
| 522 |
+
<span class="clock" id="clock">--:--:--</span>
|
| 523 |
+
<span class="status" id="status"><span class="led"></span><span id="statusTxt">Standby</span></span>
|
| 524 |
+
</div>
|
| 525 |
+
</header>
|
| 526 |
+
|
| 527 |
+
<!-- ============================================================
|
| 528 |
+
VIEW 1 — SETUP CONSOLE
|
| 529 |
+
============================================================ -->
|
| 530 |
+
<section class="view active" id="setupView">
|
| 531 |
+
|
| 532 |
+
<!-- rundown / running order -->
|
| 533 |
+
<nav class="rundown" id="rundown" aria-label="Show rundown">
|
| 534 |
+
<div class="cue on" data-step="1"><div class="node">01</div><div class="ttl">Roll Tape</div><div class="sub">Drop a clip</div></div>
|
| 535 |
+
<div class="cue" data-step="2"><div class="node">02</div><div class="ttl">Casting</div><div class="sub">Find the faces</div></div>
|
| 536 |
+
<div class="cue" data-step="3"><div class="node">03</div><div class="ttl">Star Select</div><div class="sub">Name the stars</div></div>
|
| 537 |
+
<div class="cue" data-step="4"><div class="node">04</div><div class="ttl">Showtime</div><div class="sub">Go live</div></div>
|
| 538 |
+
</nav>
|
| 539 |
+
|
| 540 |
+
<div class="console">
|
| 541 |
+
|
| 542 |
+
<!-- ---------- LEFT: the control desk ---------- -->
|
| 543 |
+
<div class="desk">
|
| 544 |
+
|
| 545 |
+
<!-- Roll Tape -->
|
| 546 |
+
<div class="card" id="rollCard">
|
| 547 |
+
<div class="card-h"><svg class="i ic"><use href="#i-clap"/></svg><h2>Roll Tape</h2><span class="meta">SRC 01</span></div>
|
| 548 |
+
<div class="drop" id="drop" tabindex="0" role="button" aria-label="Drop a video clip to roll tape">
|
| 549 |
+
<div class="ask">
|
| 550 |
+
<span class="clap"><svg class="i"><use href="#i-clap"/></svg></span>
|
| 551 |
+
<div class="t">Drop a clip on the desk</div>
|
| 552 |
+
<div class="s">MP4 · MOV · up to 60s · portrait, landscape or square</div>
|
| 553 |
+
</div>
|
| 554 |
+
<div class="loaded">
|
| 555 |
+
<div class="strip"><span class="play"><svg class="i"><use href="#i-play"/></svg></span></div>
|
| 556 |
+
<div>
|
| 557 |
+
<div class="fn" id="fileName">rooftop_freekick.mp4</div>
|
| 558 |
+
<div class="fd"><span id="fileDur">00:42</span> · 1080×1920 · ready to cast</div>
|
| 559 |
+
</div>
|
| 560 |
+
</div>
|
| 561 |
+
</div>
|
| 562 |
+
<input type="file" id="fileInput" accept="video/*" hidden>
|
| 563 |
+
<button class="btn cyan mt" id="castBtn" disabled><svg class="i"><use href="#i-spot"/></svg> Find the stars</button>
|
| 564 |
+
<div class="sweep"></div>
|
| 565 |
+
</div>
|
| 566 |
+
|
| 567 |
+
<!-- Pick the show / vibes -->
|
| 568 |
+
<div class="card" id="vibeCard">
|
| 569 |
+
<div class="card-h"><svg class="i ic"><use href="#i-mic"/></svg><h2>Pick the show</h2><span class="meta">TONE</span></div>
|
| 570 |
+
<div class="vibes" id="vibes" role="group" aria-label="Narration vibe">
|
| 571 |
+
<button class="vibe" data-vibe="football" style="--vc:var(--star-lime)" aria-pressed="false">
|
| 572 |
+
<span class="ic"><svg class="i"><use href="#i-whistle"/></svg></span>
|
| 573 |
+
<span><span class="vt">Football</span><span class="vd">Roaring play-by-play</span></span>
|
| 574 |
+
</button>
|
| 575 |
+
<button class="vibe" data-vibe="diva" style="--vc:var(--star-hot)" aria-pressed="false">
|
| 576 |
+
<span class="ic"><svg class="i"><use href="#i-glam"/></svg></span>
|
| 577 |
+
<span><span class="vt">Diva Hour</span><span class="vd">Red-carpet shade</span></span>
|
| 578 |
+
</button>
|
| 579 |
+
<button class="vibe" data-vibe="wildlife" style="--vc:var(--star-cyan)" aria-pressed="false">
|
| 580 |
+
<span class="ic"><svg class="i"><use href="#i-paw"/></svg></span>
|
| 581 |
+
<span><span class="vt">Wildlife</span><span class="vd">Hushed nature doc</span></span>
|
| 582 |
+
</button>
|
| 583 |
+
<button class="vibe" data-vibe="boxing" style="--vc:var(--star-tang)" aria-pressed="false">
|
| 584 |
+
<span class="ic"><svg class="i"><use href="#i-glove"/></svg></span>
|
| 585 |
+
<span><span class="vt">Boxing</span><span class="vd">Ringside hype</span></span>
|
| 586 |
+
</button>
|
| 587 |
+
</div>
|
| 588 |
+
|
| 589 |
+
<!-- Coco the host — re-dresses per vibe -->
|
| 590 |
+
<div class="coco" id="coco">
|
| 591 |
+
<div class="av">
|
| 592 |
+
<svg class="i bird"><use href="#i-bird"/></svg>
|
| 593 |
+
<span class="acc" id="cocoAcc"><svg><use href="#i-star"/></svg></span>
|
| 594 |
+
</div>
|
| 595 |
+
<div class="say">
|
| 596 |
+
<span class="who">Coco · your host</span>
|
| 597 |
+
<span id="cocoLine">Pick a show and I'll set the mood. I dress for the occasion.</span>
|
| 598 |
+
</div>
|
| 599 |
+
</div>
|
| 600 |
+
</div>
|
| 601 |
+
|
| 602 |
+
<!-- Showtime -->
|
| 603 |
+
<button class="btn show" id="liveBtn" disabled><svg class="i"><use href="#i-star"/></svg> Showtime — go live</button>
|
| 604 |
+
|
| 605 |
+
</div>
|
| 606 |
+
|
| 607 |
+
<!-- ---------- RIGHT: the stage ---------- -->
|
| 608 |
+
<div class="stage-panel" id="stagePanel">
|
| 609 |
+
<div class="stage-h">
|
| 610 |
+
<svg class="i ic"><use href="#i-spot"/></svg>
|
| 611 |
+
<h2>Star Select</h2>
|
| 612 |
+
<span class="count" id="starCount">NO CAST YET</span>
|
| 613 |
+
</div>
|
| 614 |
+
|
| 615 |
+
<!-- empty -->
|
| 616 |
+
<div class="empty" id="stageEmpty">
|
| 617 |
+
<div class="spot"><svg class="i"><use href="#i-spot"/></svg></div>
|
| 618 |
+
<div class="t">The stage is dark</div>
|
| 619 |
+
<div class="s">Roll a clip and hit <b>Find the stars</b> — everyone we spot gets a card under the spotlight.</div>
|
| 620 |
+
</div>
|
| 621 |
+
|
| 622 |
+
<!-- roster (filled by JS) -->
|
| 623 |
+
<div class="roster" id="roster" hidden></div>
|
| 624 |
+
</div>
|
| 625 |
+
|
| 626 |
+
</div>
|
| 627 |
+
|
| 628 |
+
<p class="note">
|
| 629 |
+
<svg class="i lock"><use href="#i-lock"/></svg>
|
| 630 |
+
Your clip stays on <b>your machine</b>. Faces are detected locally and never uploaded — Marquee only writes the captions you see.
|
| 631 |
+
</p>
|
| 632 |
+
</section>
|
| 633 |
+
|
| 634 |
+
<!-- ============================================================
|
| 635 |
+
VIEW 2 — BROADCAST PLAYER
|
| 636 |
+
============================================================ -->
|
| 637 |
+
<section class="view" id="broadcastView">
|
| 638 |
+
<div class="booth-top">
|
| 639 |
+
<button class="back" id="backBtn"><svg class="i"><use href="#i-back"/></svg> Back to console</button>
|
| 640 |
+
<div class="orient" role="group" aria-label="Clip orientation">
|
| 641 |
+
<button data-orient="landscape" aria-pressed="true">16:9</button>
|
| 642 |
+
<button data-orient="square" aria-pressed="false">1:1</button>
|
| 643 |
+
<button data-orient="portrait" aria-pressed="false">9:16</button>
|
| 644 |
+
</div>
|
| 645 |
+
</div>
|
| 646 |
+
|
| 647 |
+
<div class="booth" id="booth">
|
| 648 |
+
<!-- the screen -->
|
| 649 |
+
<div>
|
| 650 |
+
<div class="screen-wrap">
|
| 651 |
+
<div class="screen" id="screen">
|
| 652 |
+
<video id="vid" class="screen-vid" playsinline preload="metadata"></video>
|
| 653 |
+
<div class="ph"><svg class="i"><use href="#i-cam"/></svg>YOUR CLIP PLAYS HERE</div>
|
| 654 |
+
<div class="bug"><span class="dot"></span> ON AIR</div>
|
| 655 |
+
<div class="tc" id="tc">00:00</div>
|
| 656 |
+
<!-- lower-third chyron -->
|
| 657 |
+
<div class="chyron" id="chyron">
|
| 658 |
+
<div class="bar"></div>
|
| 659 |
+
<div class="row"><span class="who" id="chyWho">COCO</span><span class="line" id="chyLine">Standing by…</span></div>
|
| 660 |
+
</div>
|
| 661 |
+
</div>
|
| 662 |
+
</div>
|
| 663 |
+
<div class="transport">
|
| 664 |
+
<button class="pp" id="pp" aria-label="Play / pause"><svg class="i"><use href="#i-play"/></svg></button>
|
| 665 |
+
<div class="scrub" id="scrub"><div class="fill" id="fill"></div><div class="knob" id="knob"></div></div>
|
| 666 |
+
<span class="clock" id="tcDur" style="font-size:16px">00:42</span>
|
| 667 |
+
</div>
|
| 668 |
+
</div>
|
| 669 |
+
|
| 670 |
+
<!-- play-by-play rail -->
|
| 671 |
+
<div class="pbp">
|
| 672 |
+
<div class="pbp-h"><svg class="i ic"><use href="#i-list"/></svg><h3>Play-by-play</h3><span class="ct" id="pbpCt">— lines</span></div>
|
| 673 |
+
<div class="lines" id="lines"></div>
|
| 674 |
+
<div class="export">
|
| 675 |
+
<button class="btn-srt" id="srtBtn"><svg class="i ic"><use href="#i-download"/></svg> Download captions (.srt)</button>
|
| 676 |
+
<div class="hint">Drops straight into <b>CapCut</b>, Premiere & DaVinci — timed to your clip.</div>
|
| 677 |
+
</div>
|
| 678 |
+
</div>
|
| 679 |
+
</div>
|
| 680 |
+
</section>
|
| 681 |
+
|
| 682 |
+
</div>
|
| 683 |
+
|
| 684 |
+
<div class="toast" id="toast"><svg class="i ic"><use href="#i-check"/></svg><span id="toastTxt">Done</span></div>
|
| 685 |
+
|
| 686 |
+
<script type="module">/* ============================================================
|
| 687 |
+
MARQUEE — flow engine, wired to the Gradio backend.
|
| 688 |
+
Runs as a module inside the app iframe.
|
| 689 |
+
Presentation code is unchanged from the design; the mock data
|
| 690 |
+
hooks (casting, commentary, playback) now call the real pipeline.
|
| 691 |
+
============================================================ */
|
| 692 |
+
import * as GradioClient from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
|
| 693 |
+
const Client = GradioClient.Client;
|
| 694 |
+
const handle_file = GradioClient.handle_file || ((f) => f);
|
| 695 |
+
|
| 696 |
+
const $ = (s, r = document) => r.querySelector(s);
|
| 697 |
+
const $$ = (s, r = document) => [...r.querySelectorAll(s)];
|
| 698 |
+
const reduce = matchMedia('(prefers-reduced-motion:reduce)').matches;
|
| 699 |
+
const pad = n => String(n).padStart(2, '0');
|
| 700 |
+
|
| 701 |
+
/* ---------- backend client (connects to this same Space) ---------- */
|
| 702 |
+
let _client = null;
|
| 703 |
+
async function api() {
|
| 704 |
+
if (!_client) _client = await Client.connect(window.location.origin);
|
| 705 |
+
return _client;
|
| 706 |
+
}
|
| 707 |
+
|
| 708 |
+
/* ---------- session state from the backend ---------- */
|
| 709 |
+
let SESSION = null, VIDEO_URL = null, DUR = 0, REAL_SCRIPT = [], selectedFile = null;
|
| 710 |
+
|
| 711 |
+
/* ---------- auto-fit the iframe to content (same-origin srcdoc) ---------- */
|
| 712 |
+
function fitFrame() {
|
| 713 |
+
try { if (window.frameElement) window.frameElement.style.height = document.documentElement.scrollHeight + 'px'; }
|
| 714 |
+
catch (e) {}
|
| 715 |
+
}
|
| 716 |
+
new ResizeObserver(fitFrame).observe(document.body);
|
| 717 |
+
window.addEventListener('load', fitFrame);
|
| 718 |
+
|
| 719 |
+
/* ---------- live clock ---------- */
|
| 720 |
+
const clocks = $$('#clock');
|
| 721 |
+
function fmtClock(d){return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;}
|
| 722 |
+
setInterval(()=>{const t=fmtClock(new Date());clocks.forEach(c=>c.textContent=t);},1000);
|
| 723 |
+
clocks.forEach(c=>c.textContent=fmtClock(new Date()));
|
| 724 |
+
|
| 725 |
+
/* ---------- status pill ---------- */
|
| 726 |
+
const status=$('#status'), statusTxt=$('#statusTxt');
|
| 727 |
+
function setStatus(txt,live){statusTxt.textContent=txt;status.classList.toggle('live',!!live);}
|
| 728 |
+
|
| 729 |
+
/* ---------- rundown ---------- */
|
| 730 |
+
const cues=$$('#rundown .cue');
|
| 731 |
+
function setStep(n){
|
| 732 |
+
cues.forEach(c=>{
|
| 733 |
+
const s=+c.dataset.step;
|
| 734 |
+
c.classList.toggle('done',s<n);
|
| 735 |
+
c.classList.toggle('on',s===n);
|
| 736 |
+
if(s<n)c.querySelector('.node').innerHTML='<svg class="i" style="width:18px;height:18px"><use href="#i-check"/></svg>';
|
| 737 |
+
else c.querySelector('.node').textContent='0'+s;
|
| 738 |
+
});
|
| 739 |
+
}
|
| 740 |
+
|
| 741 |
+
/* ---------- cast colors + tiers (presentation) ---------- */
|
| 742 |
+
const COLORS=['--star-gold','--star-hot','--star-cyan','--star-violet','--star-lime','--star-tang'];
|
| 743 |
+
let stars=[]; // {name, color, tier, crop}
|
| 744 |
+
|
| 745 |
+
/* ---------- VIBES — Coco's wardrobe + the show's voice ----------
|
| 746 |
+
NOTE: the play-by-play LINES are no longer templated here — the real
|
| 747 |
+
commentary comes from the VLM. We keep Coco's persona + colors. */
|
| 748 |
+
const VIBES={
|
| 749 |
+
football:{ acc:'#i-whistle', vc:'--star-lime', who:'COCO · TOUCHLINE',
|
| 750 |
+
coco:"Whistle's on. I'm calling this one like a cup final — keep your eyes on the pitch!"},
|
| 751 |
+
diva:{ acc:'#i-glam', vc:'--star-hot', who:'COCO · RED CARPET',
|
| 752 |
+
coco:"Shades on, boa fluffed. Darling, we are SO ready for the carpet. Bring the drama."},
|
| 753 |
+
wildlife:{ acc:'#i-paw', vc:'--star-cyan', who:'COCO · FIELD UNIT',
|
| 754 |
+
coco:"*whispering* …we must be very quiet now. A rare specimen has entered the frame."},
|
| 755 |
+
boxing:{ acc:'#i-glove', vc:'--star-tang', who:'COCO · RINGSIDE',
|
| 756 |
+
coco:"Gloves laced, ringside seat warm. Ladies and gentlemen… in the RED corner!"}
|
| 757 |
+
};
|
| 758 |
+
let currentVibe=null;
|
| 759 |
+
|
| 760 |
+
/* ---------- ROLL TAPE (real file) ---------- */
|
| 761 |
+
const drop=$('#drop'), castBtn=$('#castBtn'), liveBtn=$('#liveBtn'), rollCard=$('#rollCard');
|
| 762 |
+
const fileInput=$('#fileInput'), fileName=$('#fileName'), fileDur=$('#fileDur');
|
| 763 |
+
|
| 764 |
+
function handleFile(file){
|
| 765 |
+
if(!file) return;
|
| 766 |
+
selectedFile=file;
|
| 767 |
+
drop.classList.add('loaded');
|
| 768 |
+
fileName.textContent=file.name;
|
| 769 |
+
fileDur.textContent='reading…';
|
| 770 |
+
const url=URL.createObjectURL(file);
|
| 771 |
+
const probe=document.createElement('video'); probe.preload='metadata';
|
| 772 |
+
probe.onloadedmetadata=()=>{ fileDur.textContent=fmt(probe.duration)+' · ready to cast'; URL.revokeObjectURL(url); };
|
| 773 |
+
probe.onerror=()=>{ fileDur.textContent='ready to cast'; };
|
| 774 |
+
probe.src=url;
|
| 775 |
+
castBtn.disabled=false;
|
| 776 |
+
setStatus('Tape loaded'); toast('Tape loaded — ready to cast');
|
| 777 |
+
}
|
| 778 |
+
drop.addEventListener('click',()=>fileInput.click());
|
| 779 |
+
drop.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();fileInput.click();}});
|
| 780 |
+
fileInput.addEventListener('change',e=>handleFile(e.target.files[0]));
|
| 781 |
+
['dragover','dragenter'].forEach(ev=>drop.addEventListener(ev,e=>{e.preventDefault();drop.style.borderColor='var(--cyan)';}));
|
| 782 |
+
['dragleave','drop'].forEach(ev=>drop.addEventListener(ev,e=>{
|
| 783 |
+
e.preventDefault();drop.style.borderColor='';
|
| 784 |
+
if(ev==='drop'&&e.dataTransfer.files[0])handleFile(e.dataTransfer.files[0]);
|
| 785 |
+
}));
|
| 786 |
+
|
| 787 |
+
/* ---------- CASTING (real scan) ---------- */
|
| 788 |
+
const stagePanel=$('#stagePanel'), stageEmpty=$('#stageEmpty'), roster=$('#roster'), starCount=$('#starCount');
|
| 789 |
+
castBtn.addEventListener('click',startCasting);
|
| 790 |
+
|
| 791 |
+
async function startCasting(){
|
| 792 |
+
if(castBtn.disabled||!selectedFile) return;
|
| 793 |
+
castBtn.disabled=true; rollCard.classList.add('casting');
|
| 794 |
+
setStep(2); setStatus('Casting…');
|
| 795 |
+
castBtn.innerHTML='<svg class="i"><use href="#i-spot"/></svg> Scanning the tape…';
|
| 796 |
+
try{
|
| 797 |
+
const c=await api();
|
| 798 |
+
const res=await c.predict("/scan",[handle_file(selectedFile)]);
|
| 799 |
+
const data=res.data[0];
|
| 800 |
+
if(!data||!data.ok) throw new Error((data&&data.error)||'scan failed');
|
| 801 |
+
SESSION=data.session_id; VIDEO_URL=data.video_url; DUR=data.duration||0;
|
| 802 |
+
setBoothOrient(data.w,data.h);
|
| 803 |
+
const vid=$('#vid'); vid.src=VIDEO_URL; $('#screen').classList.add('has-video');
|
| 804 |
+
revealCast(data.stars||[]);
|
| 805 |
+
}catch(err){
|
| 806 |
+
setStatus('Casting failed');
|
| 807 |
+
toast('Casting failed: '+(err.message||err));
|
| 808 |
+
rollCard.classList.remove('casting');
|
| 809 |
+
castBtn.disabled=false;
|
| 810 |
+
castBtn.innerHTML='<svg class="i"><use href="#i-spot"/></svg> Find the stars';
|
| 811 |
+
}
|
| 812 |
+
}
|
| 813 |
+
|
| 814 |
+
function revealCast(starData){
|
| 815 |
+
rollCard.classList.remove('casting');
|
| 816 |
+
castBtn.innerHTML='<svg class="i"><use href="#i-spot"/></svg> Re-cast';
|
| 817 |
+
castBtn.disabled=false;
|
| 818 |
+
setStep(3); setStatus(starData.length+' stars found');
|
| 819 |
+
|
| 820 |
+
stars = starData.map((s,i)=>({name:'',tier:s.tier||'STAR',color:COLORS[i%COLORS.length],crop:s.crop||null}));
|
| 821 |
+
stageEmpty.style.display='none';
|
| 822 |
+
roster.hidden=false; roster.innerHTML='';
|
| 823 |
+
starCount.textContent=stars.length+' STARS FOUND';
|
| 824 |
+
stars.forEach((s,i)=>roster.appendChild(buildStar(s,i)));
|
| 825 |
+
|
| 826 |
+
if(!stars.length){
|
| 827 |
+
stageEmpty.style.display='';
|
| 828 |
+
stageEmpty.querySelector('.t').textContent='No stars spotted';
|
| 829 |
+
stageEmpty.querySelector('.s').innerHTML='No faces found — but the show still goes on. Pick a vibe and go live for nameless narration.';
|
| 830 |
+
roster.hidden=true;
|
| 831 |
+
}
|
| 832 |
+
toast(stars.length?stars.length+' stars found — give them stage names':'No faces found — you can still go live');
|
| 833 |
+
refreshLive();
|
| 834 |
+
}
|
| 835 |
+
|
| 836 |
+
/* ---------- THE STAR CARD (one reusable block) ---------- */
|
| 837 |
+
function buildStar(s,i){
|
| 838 |
+
const el=document.createElement('div');
|
| 839 |
+
el.className='star';
|
| 840 |
+
el.style.setProperty('--c',`var(${s.color})`);
|
| 841 |
+
if(!reduce){
|
| 842 |
+
el.animate(
|
| 843 |
+
[{opacity:0,transform:'translateY(14px) scale(.98)'},{opacity:1,transform:'none'}],
|
| 844 |
+
{duration:480,delay:i*90,easing:'cubic-bezier(.2,.9,.25,1)',fill:'backwards'}
|
| 845 |
+
);
|
| 846 |
+
}
|
| 847 |
+
const portrait = s.crop
|
| 848 |
+
? `<img class="head-img" src="${s.crop}" alt="Player ${i+1}">`
|
| 849 |
+
: `<svg class="i head-ph" style="width:64px;height:68px"><use href="#i-portrait"/></svg>`;
|
| 850 |
+
el.innerHTML=`
|
| 851 |
+
<div class="portrait">
|
| 852 |
+
<div class="beam"></div>
|
| 853 |
+
${portrait}
|
| 854 |
+
<div class="frame"></div>
|
| 855 |
+
<span class="idx"><span class="st">★</span>${i+1}</span>
|
| 856 |
+
<span class="tier">${s.tier}</span>
|
| 857 |
+
<svg class="i crown"><use href="#i-crown"/></svg>
|
| 858 |
+
</div>
|
| 859 |
+
<div class="star-body">
|
| 860 |
+
<label for="star${i}">Stage name</label>
|
| 861 |
+
<input id="star${i}" type="text" placeholder="e.g. The Maestro" maxlength="22" autocomplete="off">
|
| 862 |
+
</div>`;
|
| 863 |
+
const input=el.querySelector('input');
|
| 864 |
+
input.addEventListener('input',()=>{
|
| 865 |
+
const v=input.value.trim();
|
| 866 |
+
s.name=v;
|
| 867 |
+
const wasNamed=el.classList.contains('named');
|
| 868 |
+
el.classList.toggle('named',!!v);
|
| 869 |
+
if(v && !wasNamed && !reduce) popCrown(el);
|
| 870 |
+
refreshLive();
|
| 871 |
+
});
|
| 872 |
+
return el;
|
| 873 |
+
}
|
| 874 |
+
function popCrown(el){
|
| 875 |
+
const c=el.querySelector('.crown');
|
| 876 |
+
c.style.animation='none';void c.offsetWidth;
|
| 877 |
+
c.animate([{transform:'translateX(-50%) translateY(10px) scale(.4) rotate(-12deg)',opacity:0},
|
| 878 |
+
{transform:'translateX(-50%) scale(1.25) rotate(8deg)',opacity:1,offset:.6},
|
| 879 |
+
{transform:'translateX(-50%) scale(1) rotate(0)',opacity:1}],
|
| 880 |
+
{duration:480,easing:'cubic-bezier(.2,1.7,.4,1)'});
|
| 881 |
+
}
|
| 882 |
+
|
| 883 |
+
/* ---------- VIBES + COCO ---------- */
|
| 884 |
+
const vibesWrap=$('#vibes'), coco=$('#coco'), cocoLine=$('#cocoLine'),
|
| 885 |
+
cocoAcc=$('#cocoAcc'), cocoWho=coco.querySelector('.who');
|
| 886 |
+
vibesWrap.addEventListener('click',e=>{
|
| 887 |
+
const b=e.target.closest('.vibe'); if(!b)return;
|
| 888 |
+
$$('.vibe',vibesWrap).forEach(v=>v.setAttribute('aria-pressed','false'));
|
| 889 |
+
b.setAttribute('aria-pressed','true');
|
| 890 |
+
currentVibe=b.dataset.vibe;
|
| 891 |
+
dressCoco(currentVibe);
|
| 892 |
+
refreshLive();
|
| 893 |
+
});
|
| 894 |
+
function dressCoco(v){
|
| 895 |
+
const d=VIBES[v]; if(!d)return;
|
| 896 |
+
coco.style.setProperty('--vc',`var(${d.vc})`);
|
| 897 |
+
cocoAcc.innerHTML=`<svg><use href="${d.acc}"/></svg>`;
|
| 898 |
+
cocoWho.textContent=d.who;
|
| 899 |
+
cocoLine.textContent=d.coco;
|
| 900 |
+
if(!reduce){
|
| 901 |
+
coco.animate([{transform:'scale(.98)',opacity:.6},{transform:'scale(1)',opacity:1}],{duration:300,easing:'ease-out'});
|
| 902 |
+
const av=coco.querySelector('.av');
|
| 903 |
+
av.animate([{transform:'rotate(-8deg) scale(.9)'},{transform:'rotate(6deg) scale(1.08)'},{transform:'none'}],{duration:420,easing:'cubic-bezier(.2,1.7,.4,1)'});
|
| 904 |
+
}
|
| 905 |
+
}
|
| 906 |
+
|
| 907 |
+
/* ---------- LIVE-READY GATE ----------
|
| 908 |
+
ready when a vibe is picked (names optional — nameless narration is allowed) */
|
| 909 |
+
function namedCount(){return stars.filter(s=>s.name).length;}
|
| 910 |
+
function refreshLive(){
|
| 911 |
+
liveBtn.disabled=!(currentVibe && SESSION);
|
| 912 |
+
if(stars.length){
|
| 913 |
+
const n=namedCount();
|
| 914 |
+
starCount.textContent = n? `${n}/${stars.length} NAMED` : `${stars.length} STARS FOUND`;
|
| 915 |
+
}
|
| 916 |
+
}
|
| 917 |
+
|
| 918 |
+
/* ---------- SHOWTIME (real commentary) ---------- */
|
| 919 |
+
liveBtn.addEventListener('click',async ()=>{
|
| 920 |
+
if(liveBtn.disabled)return;
|
| 921 |
+
liveBtn.disabled=true;
|
| 922 |
+
liveBtn.innerHTML='<svg class="i"><use href="#i-star"/></svg> Rolling cameras…';
|
| 923 |
+
setStatus('Generating…');
|
| 924 |
+
try{
|
| 925 |
+
const names=stars.map(s=>s.name||'');
|
| 926 |
+
const c=await api();
|
| 927 |
+
const res=await c.predict("/generate",[SESSION, names, currentVibe]);
|
| 928 |
+
const data=res.data[0];
|
| 929 |
+
if(!data||!data.ok) throw new Error((data&&data.error)||'generate failed');
|
| 930 |
+
REAL_SCRIPT=data.script||[];
|
| 931 |
+
setStep(4); setStatus('On air',true);
|
| 932 |
+
if(!reduce) confetti();
|
| 933 |
+
buildBroadcast();
|
| 934 |
+
setTimeout(()=>{switchView('broadcast');fitFrame();}, reduce?0:450);
|
| 935 |
+
}catch(err){
|
| 936 |
+
setStatus('Showtime failed');
|
| 937 |
+
toast('Showtime failed: '+(err.message||err));
|
| 938 |
+
}finally{
|
| 939 |
+
liveBtn.disabled=false;
|
| 940 |
+
liveBtn.innerHTML='<svg class="i"><use href="#i-star"/></svg> Showtime — go live';
|
| 941 |
+
}
|
| 942 |
+
});
|
| 943 |
+
|
| 944 |
+
/* ---------- VIEW SWITCH ---------- */
|
| 945 |
+
const setupView=$('#setupView'), broadcastView=$('#broadcastView');
|
| 946 |
+
function switchView(which){
|
| 947 |
+
const toBroadcast=which==='broadcast';
|
| 948 |
+
setupView.classList.toggle('active',!toBroadcast);
|
| 949 |
+
broadcastView.classList.toggle('active',toBroadcast);
|
| 950 |
+
window.scrollTo({top:0,behavior:reduce?'auto':'smooth'});
|
| 951 |
+
fitFrame();
|
| 952 |
+
}
|
| 953 |
+
$('#backBtn').addEventListener('click',()=>{switchView('setup');const v=$('#vid');if(v)v.pause();});
|
| 954 |
+
|
| 955 |
+
/* ============================================================
|
| 956 |
+
BROADCAST PLAYER — driven by a real <video>
|
| 957 |
+
============================================================ */
|
| 958 |
+
let script=[]; // {t, who, text, cc}
|
| 959 |
+
const vid=$('#vid'), screen=$('#screen'), chyron=$('#chyron'), chyWho=$('#chyWho'), chyLine=$('#chyLine'),
|
| 960 |
+
linesEl=$('#lines'), pbpCt=$('#pbpCt'), tcEl=$('#tc'), fill=$('#fill'), knob=$('#knob'),
|
| 961 |
+
pp=$('#pp'), scrub=$('#scrub'), tcDur=$('#tcDur');
|
| 962 |
+
const ICON_PLAY='<svg class="i"><use href="#i-play"/></svg>';
|
| 963 |
+
const ICON_PAUSE='<svg class="i"><use href="#i-pause"/></svg>';
|
| 964 |
+
|
| 965 |
+
function fmt(s){s=isFinite(s)?s:0;return pad(Math.floor(s/60))+':'+pad(Math.floor(s%60));}
|
| 966 |
+
|
| 967 |
+
function buildBroadcast(){
|
| 968 |
+
const d=VIBES[currentVibe]||{vc:'--hot',who:'COCO'};
|
| 969 |
+
const who=(d.who||'COCO').replace('COCO · ','');
|
| 970 |
+
script=(REAL_SCRIPT||[]).map(l=>({t:+l.t||0, text:l.text||'', who, cc:d.vc}));
|
| 971 |
+
linesEl.innerHTML='';
|
| 972 |
+
pbpCt.textContent=script.length+' LINES';
|
| 973 |
+
if(!script.length){
|
| 974 |
+
linesEl.innerHTML='<div class="pl"><span class="tx">No commentary generated — try another clip.</span></div>';
|
| 975 |
+
}
|
| 976 |
+
script.forEach((l,i)=>{
|
| 977 |
+
const row=document.createElement('div');
|
| 978 |
+
row.className='pl'; row.dataset.i=i;
|
| 979 |
+
row.style.setProperty('--cc',`var(${l.cc})`);
|
| 980 |
+
row.innerHTML=`<span class="t">${fmt(l.t)}</span><span class="tx"><b>${l.who}</b> · ${l.text}</span>`;
|
| 981 |
+
row.addEventListener('click',()=>seek(l.t));
|
| 982 |
+
linesEl.appendChild(row);
|
| 983 |
+
});
|
| 984 |
+
if(vid){ try{vid.currentTime=0;}catch(e){} }
|
| 985 |
+
activeLine=-1; renderPlayhead();
|
| 986 |
+
}
|
| 987 |
+
|
| 988 |
+
/* playback engine — reads the real video element */
|
| 989 |
+
let activeLine=-1, rafP=null;
|
| 990 |
+
function dur(){ return DUR || (vid&&vid.duration) || 0; }
|
| 991 |
+
function renderPlayhead(){
|
| 992 |
+
const D=dur(), cur=(vid&&vid.currentTime)||0;
|
| 993 |
+
const pct=D?(cur/D)*100:0;
|
| 994 |
+
fill.style.width=pct+'%'; knob.style.left=pct+'%'; tcEl.textContent=fmt(cur);
|
| 995 |
+
let idx=-1;
|
| 996 |
+
for(let i=0;i<script.length;i++){if(script[i].t<=cur)idx=i;else break;}
|
| 997 |
+
if(idx!==activeLine){
|
| 998 |
+
activeLine=idx;
|
| 999 |
+
$$('.pl',linesEl).forEach((r,i)=>r.classList.toggle('now',i===idx));
|
| 1000 |
+
showChyron(idx);
|
| 1001 |
+
if(idx>=0){const r=linesEl.children[idx];const top=r.offsetTop-linesEl.clientHeight/2+r.clientHeight/2;
|
| 1002 |
+
linesEl.scrollTo({top,behavior:reduce?'auto':'smooth'});}
|
| 1003 |
+
}
|
| 1004 |
+
}
|
| 1005 |
+
function showChyron(idx){
|
| 1006 |
+
if(idx<0){chyron.classList.remove('in');return;}
|
| 1007 |
+
const l=script[idx];
|
| 1008 |
+
chyron.classList.remove('in');
|
| 1009 |
+
requestAnimationFrame(()=>{
|
| 1010 |
+
chyron.style.setProperty('--cc',`var(${l.cc})`);
|
| 1011 |
+
chyWho.textContent=l.who; chyLine.textContent=l.text;
|
| 1012 |
+
requestAnimationFrame(()=>chyron.classList.add('in'));
|
| 1013 |
+
});
|
| 1014 |
+
}
|
| 1015 |
+
function loop(){ renderPlayhead(); if(vid && !vid.paused && !vid.ended) rafP=requestAnimationFrame(loop); }
|
| 1016 |
+
function seek(t){ if(vid) vid.currentTime=Math.max(0,Math.min(dur(),t)); }
|
| 1017 |
+
|
| 1018 |
+
if(vid){
|
| 1019 |
+
vid.addEventListener('loadedmetadata',()=>{ if(vid.duration&&isFinite(vid.duration))DUR=vid.duration; tcDur.textContent=fmt(dur()); renderPlayhead(); });
|
| 1020 |
+
vid.addEventListener('play',()=>{ pp.innerHTML=ICON_PAUSE; loop(); });
|
| 1021 |
+
vid.addEventListener('pause',()=>{ pp.innerHTML=ICON_PLAY; if(rafP)cancelAnimationFrame(rafP); });
|
| 1022 |
+
vid.addEventListener('ended',()=>{ pp.innerHTML=ICON_PLAY; });
|
| 1023 |
+
vid.addEventListener('timeupdate',renderPlayhead);
|
| 1024 |
+
}
|
| 1025 |
+
pp.addEventListener('click',()=>{ if(!vid)return; vid.paused?vid.play():vid.pause(); });
|
| 1026 |
+
scrub.addEventListener('click',e=>{
|
| 1027 |
+
const r=scrub.getBoundingClientRect();
|
| 1028 |
+
seek(((e.clientX-r.left)/r.width)*dur());
|
| 1029 |
+
});
|
| 1030 |
+
|
| 1031 |
+
/* orientation toggle (manual) + auto from real dims */
|
| 1032 |
+
const booth=$('#booth');
|
| 1033 |
+
function setBoothOrient(w,h){
|
| 1034 |
+
booth.classList.remove('portrait','square');
|
| 1035 |
+
let o='landscape';
|
| 1036 |
+
if(w&&h){ if(Math.abs(w-h)/Math.max(w,h)<0.08) o='square'; else if(h>w) o='portrait'; }
|
| 1037 |
+
if(o!=='landscape') booth.classList.add(o);
|
| 1038 |
+
$$('.orient button').forEach(x=>x.setAttribute('aria-pressed', String(x.dataset.orient===o)));
|
| 1039 |
+
}
|
| 1040 |
+
$$('.orient button').forEach(b=>b.addEventListener('click',()=>{
|
| 1041 |
+
$$('.orient button').forEach(x=>x.setAttribute('aria-pressed','false'));
|
| 1042 |
+
b.setAttribute('aria-pressed','true');
|
| 1043 |
+
booth.classList.remove('portrait','square');
|
| 1044 |
+
if(b.dataset.orient!=='landscape')booth.classList.add(b.dataset.orient);
|
| 1045 |
+
fitFrame();
|
| 1046 |
+
}));
|
| 1047 |
+
|
| 1048 |
+
/* ---------- SRT EXPORT (CapCut-ready) ---------- */
|
| 1049 |
+
$('#srtBtn').addEventListener('click',()=>{
|
| 1050 |
+
if(!script.length){toast('Go live first');return;}
|
| 1051 |
+
const D=dur();
|
| 1052 |
+
const srt=script.map((l,i)=>{
|
| 1053 |
+
const start=l.t, end=Math.min(D||l.t+4, (script[i+1]?script[i+1].t:l.t+4));
|
| 1054 |
+
return `${i+1}\n${srtTime(start)} --> ${srtTime(end)}\n${l.who}: ${l.text}\n`;
|
| 1055 |
+
}).join('\n');
|
| 1056 |
+
const blob=new Blob([srt],{type:'text/plain'});
|
| 1057 |
+
const a=document.createElement('a');
|
| 1058 |
+
a.href=URL.createObjectURL(blob);
|
| 1059 |
+
a.download=`marquee_${currentVibe||'show'}_captions.srt`;
|
| 1060 |
+
a.click(); URL.revokeObjectURL(a.href);
|
| 1061 |
+
toast('Captions exported — drop the .srt into CapCut');
|
| 1062 |
+
});
|
| 1063 |
+
function srtTime(s){
|
| 1064 |
+
const h=Math.floor(s/3600), m=Math.floor((s%3600)/60), sec=Math.floor(s%60);
|
| 1065 |
+
return `${pad(h)}:${pad(m)}:${pad(sec)},000`;
|
| 1066 |
+
}
|
| 1067 |
+
|
| 1068 |
+
/* ---------- confetti ---------- */
|
| 1069 |
+
function confetti(){
|
| 1070 |
+
const cols=['#FFC94D','#FF4D9A','#3DE7FF','#C18BFF','#9BE85B','#FF8A4D'];
|
| 1071 |
+
for(let i=0;i<70;i++){
|
| 1072 |
+
const c=document.createElement('div');
|
| 1073 |
+
c.className='confetti';
|
| 1074 |
+
c.style.left=Math.random()*100+'vw';
|
| 1075 |
+
c.style.background=cols[i%cols.length];
|
| 1076 |
+
c.style.animationDuration=(1.6+Math.random()*1.6)+'s';
|
| 1077 |
+
c.style.animationDelay=(Math.random()*.3)+'s';
|
| 1078 |
+
c.style.transform=`rotate(${Math.random()*360}deg)`;
|
| 1079 |
+
if(Math.random()>.5)c.style.borderRadius='50%';
|
| 1080 |
+
document.body.appendChild(c);
|
| 1081 |
+
setTimeout(()=>c.remove(),3600);
|
| 1082 |
+
}
|
| 1083 |
+
}
|
| 1084 |
+
|
| 1085 |
+
/* ---------- toast ---------- */
|
| 1086 |
+
const toastEl=$('#toast'), toastTxt=$('#toastTxt'); let toastT;
|
| 1087 |
+
function toast(msg){
|
| 1088 |
+
toastTxt.textContent=msg; toastEl.classList.add('show');
|
| 1089 |
+
clearTimeout(toastT); toastT=setTimeout(()=>toastEl.classList.remove('show'),2600);
|
| 1090 |
+
}
|
| 1091 |
+
</script>
|
| 1092 |
+
</body>
|
| 1093 |
+
</html>
|
models/intel/face-detection-retail-0004/FP16/face-detection-retail-0004.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d0a8ca818c657b454ce6118715c49027b1b70d9d85a83a9a4a6606d773b02dfe
|
| 3 |
+
size 1176544
|
models/intel/face-detection-retail-0004/FP16/face-detection-retail-0004.xml
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
models/intel/face-reidentification-retail-0095/FP16/face-reidentification-retail-0095.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:241229ca3d206321868d46ce74a3c0b06c49cea58db7dc70b2e842ff287545d1
|
| 3 |
+
size 2213628
|
models/intel/face-reidentification-retail-0095/FP16/face-reidentification-retail-0095.xml
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
models/intel/landmarks-regression-retail-0009/FP16/landmarks-regression-retail-0009.bin
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a7285eec8cb20a50c0ed567c426db6c7b8753c51b47f798f8ef5d8e0fa12d3c5
|
| 3 |
+
size 381232
|
models/intel/landmarks-regression-retail-0009/FP16/landmarks-regression-retail-0009.xml
ADDED
|
@@ -0,0 +1,2607 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0"?>
|
| 2 |
+
<net name="torch-jit-export" version="11">
|
| 3 |
+
<layers>
|
| 4 |
+
<layer id="0" name="0" type="Parameter" version="opset1">
|
| 5 |
+
<data shape="1,3,48,48" element_type="f32" />
|
| 6 |
+
<rt_info>
|
| 7 |
+
<attribute name="old_api_map_element_type" version="0" value="f16" />
|
| 8 |
+
</rt_info>
|
| 9 |
+
<output>
|
| 10 |
+
<port id="0" precision="FP32" names="0">
|
| 11 |
+
<dim>1</dim>
|
| 12 |
+
<dim>3</dim>
|
| 13 |
+
<dim>48</dim>
|
| 14 |
+
<dim>48</dim>
|
| 15 |
+
<rt_info>
|
| 16 |
+
<attribute name="layout" version="0" layout="[N,C,H,W]" />
|
| 17 |
+
</rt_info>
|
| 18 |
+
</port>
|
| 19 |
+
</output>
|
| 20 |
+
</layer>
|
| 21 |
+
<layer id="1" name="Gather_2181_compressed" type="Const" version="opset1">
|
| 22 |
+
<data element_type="f16" shape="1, 3, 1, 1" offset="0" size="6" />
|
| 23 |
+
<rt_info>
|
| 24 |
+
<attribute name="preprocessing" version="0" />
|
| 25 |
+
</rt_info>
|
| 26 |
+
<output>
|
| 27 |
+
<port id="0" precision="FP16">
|
| 28 |
+
<dim>1</dim>
|
| 29 |
+
<dim>3</dim>
|
| 30 |
+
<dim>1</dim>
|
| 31 |
+
<dim>1</dim>
|
| 32 |
+
</port>
|
| 33 |
+
</output>
|
| 34 |
+
</layer>
|
| 35 |
+
<layer id="2" name="Gather_2181" type="Convert" version="opset1">
|
| 36 |
+
<data destination_type="f32" />
|
| 37 |
+
<rt_info>
|
| 38 |
+
<attribute name="decompression" version="0" />
|
| 39 |
+
<attribute name="preprocessing" version="0" />
|
| 40 |
+
</rt_info>
|
| 41 |
+
<input>
|
| 42 |
+
<port id="0" precision="FP16">
|
| 43 |
+
<dim>1</dim>
|
| 44 |
+
<dim>3</dim>
|
| 45 |
+
<dim>1</dim>
|
| 46 |
+
<dim>1</dim>
|
| 47 |
+
</port>
|
| 48 |
+
</input>
|
| 49 |
+
<output>
|
| 50 |
+
<port id="1" precision="FP32">
|
| 51 |
+
<dim>1</dim>
|
| 52 |
+
<dim>3</dim>
|
| 53 |
+
<dim>1</dim>
|
| 54 |
+
<dim>1</dim>
|
| 55 |
+
</port>
|
| 56 |
+
</output>
|
| 57 |
+
</layer>
|
| 58 |
+
<layer id="3" name="Multiply_2060" type="Multiply" version="opset1">
|
| 59 |
+
<data auto_broadcast="numpy" />
|
| 60 |
+
<rt_info>
|
| 61 |
+
<attribute name="preprocessing" version="0" />
|
| 62 |
+
</rt_info>
|
| 63 |
+
<input>
|
| 64 |
+
<port id="0" precision="FP32">
|
| 65 |
+
<dim>1</dim>
|
| 66 |
+
<dim>3</dim>
|
| 67 |
+
<dim>48</dim>
|
| 68 |
+
<dim>48</dim>
|
| 69 |
+
</port>
|
| 70 |
+
<port id="1" precision="FP32">
|
| 71 |
+
<dim>1</dim>
|
| 72 |
+
<dim>3</dim>
|
| 73 |
+
<dim>1</dim>
|
| 74 |
+
<dim>1</dim>
|
| 75 |
+
</port>
|
| 76 |
+
</input>
|
| 77 |
+
<output>
|
| 78 |
+
<port id="2" precision="FP32">
|
| 79 |
+
<dim>1</dim>
|
| 80 |
+
<dim>3</dim>
|
| 81 |
+
<dim>48</dim>
|
| 82 |
+
<dim>48</dim>
|
| 83 |
+
</port>
|
| 84 |
+
</output>
|
| 85 |
+
</layer>
|
| 86 |
+
<layer id="4" name="Gather_2184_compressed" type="Const" version="opset1">
|
| 87 |
+
<data element_type="f16" shape="1, 3, 1, 1" offset="6" size="6" />
|
| 88 |
+
<rt_info>
|
| 89 |
+
<attribute name="preprocessing" version="0" />
|
| 90 |
+
</rt_info>
|
| 91 |
+
<output>
|
| 92 |
+
<port id="0" precision="FP16">
|
| 93 |
+
<dim>1</dim>
|
| 94 |
+
<dim>3</dim>
|
| 95 |
+
<dim>1</dim>
|
| 96 |
+
<dim>1</dim>
|
| 97 |
+
</port>
|
| 98 |
+
</output>
|
| 99 |
+
</layer>
|
| 100 |
+
<layer id="5" name="Gather_2184" type="Convert" version="opset1">
|
| 101 |
+
<data destination_type="f32" />
|
| 102 |
+
<rt_info>
|
| 103 |
+
<attribute name="decompression" version="0" />
|
| 104 |
+
<attribute name="preprocessing" version="0" />
|
| 105 |
+
</rt_info>
|
| 106 |
+
<input>
|
| 107 |
+
<port id="0" precision="FP16">
|
| 108 |
+
<dim>1</dim>
|
| 109 |
+
<dim>3</dim>
|
| 110 |
+
<dim>1</dim>
|
| 111 |
+
<dim>1</dim>
|
| 112 |
+
</port>
|
| 113 |
+
</input>
|
| 114 |
+
<output>
|
| 115 |
+
<port id="1" precision="FP32">
|
| 116 |
+
<dim>1</dim>
|
| 117 |
+
<dim>3</dim>
|
| 118 |
+
<dim>1</dim>
|
| 119 |
+
<dim>1</dim>
|
| 120 |
+
</port>
|
| 121 |
+
</output>
|
| 122 |
+
</layer>
|
| 123 |
+
<layer id="6" name="67" type="Add" version="opset1">
|
| 124 |
+
<data auto_broadcast="numpy" />
|
| 125 |
+
<input>
|
| 126 |
+
<port id="0" precision="FP32">
|
| 127 |
+
<dim>1</dim>
|
| 128 |
+
<dim>3</dim>
|
| 129 |
+
<dim>48</dim>
|
| 130 |
+
<dim>48</dim>
|
| 131 |
+
</port>
|
| 132 |
+
<port id="1" precision="FP32">
|
| 133 |
+
<dim>1</dim>
|
| 134 |
+
<dim>3</dim>
|
| 135 |
+
<dim>1</dim>
|
| 136 |
+
<dim>1</dim>
|
| 137 |
+
</port>
|
| 138 |
+
</input>
|
| 139 |
+
<output>
|
| 140 |
+
<port id="2" precision="FP32" names="67">
|
| 141 |
+
<dim>1</dim>
|
| 142 |
+
<dim>3</dim>
|
| 143 |
+
<dim>48</dim>
|
| 144 |
+
<dim>48</dim>
|
| 145 |
+
</port>
|
| 146 |
+
</output>
|
| 147 |
+
</layer>
|
| 148 |
+
<layer id="7" name="Gather_2187_compressed" type="Const" version="opset1">
|
| 149 |
+
<data element_type="f16" shape="16, 3, 3, 3" offset="12" size="864" />
|
| 150 |
+
<rt_info>
|
| 151 |
+
<attribute name="preprocessing" version="0" />
|
| 152 |
+
</rt_info>
|
| 153 |
+
<output>
|
| 154 |
+
<port id="0" precision="FP16">
|
| 155 |
+
<dim>16</dim>
|
| 156 |
+
<dim>3</dim>
|
| 157 |
+
<dim>3</dim>
|
| 158 |
+
<dim>3</dim>
|
| 159 |
+
</port>
|
| 160 |
+
</output>
|
| 161 |
+
</layer>
|
| 162 |
+
<layer id="8" name="Gather_2187" type="Convert" version="opset1">
|
| 163 |
+
<data destination_type="f32" />
|
| 164 |
+
<rt_info>
|
| 165 |
+
<attribute name="decompression" version="0" />
|
| 166 |
+
<attribute name="preprocessing" version="0" />
|
| 167 |
+
</rt_info>
|
| 168 |
+
<input>
|
| 169 |
+
<port id="0" precision="FP16">
|
| 170 |
+
<dim>16</dim>
|
| 171 |
+
<dim>3</dim>
|
| 172 |
+
<dim>3</dim>
|
| 173 |
+
<dim>3</dim>
|
| 174 |
+
</port>
|
| 175 |
+
</input>
|
| 176 |
+
<output>
|
| 177 |
+
<port id="1" precision="FP32">
|
| 178 |
+
<dim>16</dim>
|
| 179 |
+
<dim>3</dim>
|
| 180 |
+
<dim>3</dim>
|
| 181 |
+
<dim>3</dim>
|
| 182 |
+
</port>
|
| 183 |
+
</output>
|
| 184 |
+
</layer>
|
| 185 |
+
<layer id="9" name="68/WithoutBiases" type="Convolution" version="opset1">
|
| 186 |
+
<data strides="1, 1" dilations="1, 1" pads_begin="1, 1" pads_end="1, 1" auto_pad="explicit" />
|
| 187 |
+
<input>
|
| 188 |
+
<port id="0" precision="FP32">
|
| 189 |
+
<dim>1</dim>
|
| 190 |
+
<dim>3</dim>
|
| 191 |
+
<dim>48</dim>
|
| 192 |
+
<dim>48</dim>
|
| 193 |
+
</port>
|
| 194 |
+
<port id="1" precision="FP32">
|
| 195 |
+
<dim>16</dim>
|
| 196 |
+
<dim>3</dim>
|
| 197 |
+
<dim>3</dim>
|
| 198 |
+
<dim>3</dim>
|
| 199 |
+
</port>
|
| 200 |
+
</input>
|
| 201 |
+
<output>
|
| 202 |
+
<port id="2" precision="FP32">
|
| 203 |
+
<dim>1</dim>
|
| 204 |
+
<dim>16</dim>
|
| 205 |
+
<dim>48</dim>
|
| 206 |
+
<dim>48</dim>
|
| 207 |
+
</port>
|
| 208 |
+
</output>
|
| 209 |
+
</layer>
|
| 210 |
+
<layer id="10" name="Reshape_84_compressed" type="Const" version="opset1">
|
| 211 |
+
<data element_type="f16" shape="1, 16, 1, 1" offset="876" size="32" />
|
| 212 |
+
<output>
|
| 213 |
+
<port id="0" precision="FP16">
|
| 214 |
+
<dim>1</dim>
|
| 215 |
+
<dim>16</dim>
|
| 216 |
+
<dim>1</dim>
|
| 217 |
+
<dim>1</dim>
|
| 218 |
+
</port>
|
| 219 |
+
</output>
|
| 220 |
+
</layer>
|
| 221 |
+
<layer id="11" name="Reshape_84" type="Convert" version="opset1">
|
| 222 |
+
<data destination_type="f32" />
|
| 223 |
+
<rt_info>
|
| 224 |
+
<attribute name="decompression" version="0" />
|
| 225 |
+
</rt_info>
|
| 226 |
+
<input>
|
| 227 |
+
<port id="0" precision="FP16">
|
| 228 |
+
<dim>1</dim>
|
| 229 |
+
<dim>16</dim>
|
| 230 |
+
<dim>1</dim>
|
| 231 |
+
<dim>1</dim>
|
| 232 |
+
</port>
|
| 233 |
+
</input>
|
| 234 |
+
<output>
|
| 235 |
+
<port id="1" precision="FP32">
|
| 236 |
+
<dim>1</dim>
|
| 237 |
+
<dim>16</dim>
|
| 238 |
+
<dim>1</dim>
|
| 239 |
+
<dim>1</dim>
|
| 240 |
+
</port>
|
| 241 |
+
</output>
|
| 242 |
+
</layer>
|
| 243 |
+
<layer id="12" name="68" type="Add" version="opset1">
|
| 244 |
+
<data auto_broadcast="numpy" />
|
| 245 |
+
<input>
|
| 246 |
+
<port id="0" precision="FP32">
|
| 247 |
+
<dim>1</dim>
|
| 248 |
+
<dim>16</dim>
|
| 249 |
+
<dim>48</dim>
|
| 250 |
+
<dim>48</dim>
|
| 251 |
+
</port>
|
| 252 |
+
<port id="1" precision="FP32">
|
| 253 |
+
<dim>1</dim>
|
| 254 |
+
<dim>16</dim>
|
| 255 |
+
<dim>1</dim>
|
| 256 |
+
<dim>1</dim>
|
| 257 |
+
</port>
|
| 258 |
+
</input>
|
| 259 |
+
<output>
|
| 260 |
+
<port id="2" precision="FP32" names="68">
|
| 261 |
+
<dim>1</dim>
|
| 262 |
+
<dim>16</dim>
|
| 263 |
+
<dim>48</dim>
|
| 264 |
+
<dim>48</dim>
|
| 265 |
+
</port>
|
| 266 |
+
</output>
|
| 267 |
+
</layer>
|
| 268 |
+
<layer id="13" name="8_compressed" type="Const" version="opset1">
|
| 269 |
+
<data element_type="f16" shape="1" offset="908" size="2" />
|
| 270 |
+
<output>
|
| 271 |
+
<port id="0" precision="FP16">
|
| 272 |
+
<dim>1</dim>
|
| 273 |
+
</port>
|
| 274 |
+
</output>
|
| 275 |
+
</layer>
|
| 276 |
+
<layer id="14" name="8" type="Convert" version="opset1">
|
| 277 |
+
<data destination_type="f32" />
|
| 278 |
+
<rt_info>
|
| 279 |
+
<attribute name="decompression" version="0" />
|
| 280 |
+
</rt_info>
|
| 281 |
+
<input>
|
| 282 |
+
<port id="0" precision="FP16">
|
| 283 |
+
<dim>1</dim>
|
| 284 |
+
</port>
|
| 285 |
+
</input>
|
| 286 |
+
<output>
|
| 287 |
+
<port id="1" precision="FP32" names="8">
|
| 288 |
+
<dim>1</dim>
|
| 289 |
+
</port>
|
| 290 |
+
</output>
|
| 291 |
+
</layer>
|
| 292 |
+
<layer id="15" name="69" type="PReLU" version="opset1">
|
| 293 |
+
<input>
|
| 294 |
+
<port id="0" precision="FP32">
|
| 295 |
+
<dim>1</dim>
|
| 296 |
+
<dim>16</dim>
|
| 297 |
+
<dim>48</dim>
|
| 298 |
+
<dim>48</dim>
|
| 299 |
+
</port>
|
| 300 |
+
<port id="1" precision="FP32">
|
| 301 |
+
<dim>1</dim>
|
| 302 |
+
</port>
|
| 303 |
+
</input>
|
| 304 |
+
<output>
|
| 305 |
+
<port id="2" precision="FP32" names="69">
|
| 306 |
+
<dim>1</dim>
|
| 307 |
+
<dim>16</dim>
|
| 308 |
+
<dim>48</dim>
|
| 309 |
+
<dim>48</dim>
|
| 310 |
+
</port>
|
| 311 |
+
</output>
|
| 312 |
+
</layer>
|
| 313 |
+
<layer id="16" name="70" type="MaxPool" version="opset1">
|
| 314 |
+
<data strides="2, 2" pads_begin="0, 0" pads_end="0, 0" kernel="2, 2" rounding_type="floor" auto_pad="explicit" />
|
| 315 |
+
<input>
|
| 316 |
+
<port id="0" precision="FP32">
|
| 317 |
+
<dim>1</dim>
|
| 318 |
+
<dim>16</dim>
|
| 319 |
+
<dim>48</dim>
|
| 320 |
+
<dim>48</dim>
|
| 321 |
+
</port>
|
| 322 |
+
</input>
|
| 323 |
+
<output>
|
| 324 |
+
<port id="1" precision="FP32" names="70">
|
| 325 |
+
<dim>1</dim>
|
| 326 |
+
<dim>16</dim>
|
| 327 |
+
<dim>24</dim>
|
| 328 |
+
<dim>24</dim>
|
| 329 |
+
</port>
|
| 330 |
+
</output>
|
| 331 |
+
</layer>
|
| 332 |
+
<layer id="17" name="Constant_1660_compressed" type="Const" version="opset1">
|
| 333 |
+
<data element_type="f16" shape="1, 16, 1, 1" offset="910" size="32" />
|
| 334 |
+
<output>
|
| 335 |
+
<port id="0" precision="FP16">
|
| 336 |
+
<dim>1</dim>
|
| 337 |
+
<dim>16</dim>
|
| 338 |
+
<dim>1</dim>
|
| 339 |
+
<dim>1</dim>
|
| 340 |
+
</port>
|
| 341 |
+
</output>
|
| 342 |
+
</layer>
|
| 343 |
+
<layer id="18" name="Constant_1660" type="Convert" version="opset1">
|
| 344 |
+
<data destination_type="f32" />
|
| 345 |
+
<rt_info>
|
| 346 |
+
<attribute name="decompression" version="0" />
|
| 347 |
+
</rt_info>
|
| 348 |
+
<input>
|
| 349 |
+
<port id="0" precision="FP16">
|
| 350 |
+
<dim>1</dim>
|
| 351 |
+
<dim>16</dim>
|
| 352 |
+
<dim>1</dim>
|
| 353 |
+
<dim>1</dim>
|
| 354 |
+
</port>
|
| 355 |
+
</input>
|
| 356 |
+
<output>
|
| 357 |
+
<port id="1" precision="FP32">
|
| 358 |
+
<dim>1</dim>
|
| 359 |
+
<dim>16</dim>
|
| 360 |
+
<dim>1</dim>
|
| 361 |
+
<dim>1</dim>
|
| 362 |
+
</port>
|
| 363 |
+
</output>
|
| 364 |
+
</layer>
|
| 365 |
+
<layer id="19" name="Multiply_2070" type="Multiply" version="opset1">
|
| 366 |
+
<data auto_broadcast="numpy" />
|
| 367 |
+
<input>
|
| 368 |
+
<port id="0" precision="FP32">
|
| 369 |
+
<dim>1</dim>
|
| 370 |
+
<dim>16</dim>
|
| 371 |
+
<dim>24</dim>
|
| 372 |
+
<dim>24</dim>
|
| 373 |
+
</port>
|
| 374 |
+
<port id="1" precision="FP32">
|
| 375 |
+
<dim>1</dim>
|
| 376 |
+
<dim>16</dim>
|
| 377 |
+
<dim>1</dim>
|
| 378 |
+
<dim>1</dim>
|
| 379 |
+
</port>
|
| 380 |
+
</input>
|
| 381 |
+
<output>
|
| 382 |
+
<port id="2" precision="FP32">
|
| 383 |
+
<dim>1</dim>
|
| 384 |
+
<dim>16</dim>
|
| 385 |
+
<dim>24</dim>
|
| 386 |
+
<dim>24</dim>
|
| 387 |
+
</port>
|
| 388 |
+
</output>
|
| 389 |
+
</layer>
|
| 390 |
+
<layer id="20" name="Constant_2075_compressed" type="Const" version="opset1">
|
| 391 |
+
<data element_type="f16" shape="1, 16, 1, 1" offset="942" size="32" />
|
| 392 |
+
<output>
|
| 393 |
+
<port id="0" precision="FP16">
|
| 394 |
+
<dim>1</dim>
|
| 395 |
+
<dim>16</dim>
|
| 396 |
+
<dim>1</dim>
|
| 397 |
+
<dim>1</dim>
|
| 398 |
+
</port>
|
| 399 |
+
</output>
|
| 400 |
+
</layer>
|
| 401 |
+
<layer id="21" name="Constant_2075" type="Convert" version="opset1">
|
| 402 |
+
<data destination_type="f32" />
|
| 403 |
+
<rt_info>
|
| 404 |
+
<attribute name="decompression" version="0" />
|
| 405 |
+
</rt_info>
|
| 406 |
+
<input>
|
| 407 |
+
<port id="0" precision="FP16">
|
| 408 |
+
<dim>1</dim>
|
| 409 |
+
<dim>16</dim>
|
| 410 |
+
<dim>1</dim>
|
| 411 |
+
<dim>1</dim>
|
| 412 |
+
</port>
|
| 413 |
+
</input>
|
| 414 |
+
<output>
|
| 415 |
+
<port id="1" precision="FP32">
|
| 416 |
+
<dim>1</dim>
|
| 417 |
+
<dim>16</dim>
|
| 418 |
+
<dim>1</dim>
|
| 419 |
+
<dim>1</dim>
|
| 420 |
+
</port>
|
| 421 |
+
</output>
|
| 422 |
+
</layer>
|
| 423 |
+
<layer id="22" name="71" type="Add" version="opset1">
|
| 424 |
+
<data auto_broadcast="numpy" />
|
| 425 |
+
<input>
|
| 426 |
+
<port id="0" precision="FP32">
|
| 427 |
+
<dim>1</dim>
|
| 428 |
+
<dim>16</dim>
|
| 429 |
+
<dim>24</dim>
|
| 430 |
+
<dim>24</dim>
|
| 431 |
+
</port>
|
| 432 |
+
<port id="1" precision="FP32">
|
| 433 |
+
<dim>1</dim>
|
| 434 |
+
<dim>16</dim>
|
| 435 |
+
<dim>1</dim>
|
| 436 |
+
<dim>1</dim>
|
| 437 |
+
</port>
|
| 438 |
+
</input>
|
| 439 |
+
<output>
|
| 440 |
+
<port id="2" precision="FP32" names="71">
|
| 441 |
+
<dim>1</dim>
|
| 442 |
+
<dim>16</dim>
|
| 443 |
+
<dim>24</dim>
|
| 444 |
+
<dim>24</dim>
|
| 445 |
+
</port>
|
| 446 |
+
</output>
|
| 447 |
+
</layer>
|
| 448 |
+
<layer id="23" name="14_compressed" type="Const" version="opset1">
|
| 449 |
+
<data element_type="f16" shape="32, 16, 3, 3" offset="974" size="9216" />
|
| 450 |
+
<output>
|
| 451 |
+
<port id="0" precision="FP16">
|
| 452 |
+
<dim>32</dim>
|
| 453 |
+
<dim>16</dim>
|
| 454 |
+
<dim>3</dim>
|
| 455 |
+
<dim>3</dim>
|
| 456 |
+
</port>
|
| 457 |
+
</output>
|
| 458 |
+
</layer>
|
| 459 |
+
<layer id="24" name="14" type="Convert" version="opset1">
|
| 460 |
+
<data destination_type="f32" />
|
| 461 |
+
<rt_info>
|
| 462 |
+
<attribute name="decompression" version="0" />
|
| 463 |
+
</rt_info>
|
| 464 |
+
<input>
|
| 465 |
+
<port id="0" precision="FP16">
|
| 466 |
+
<dim>32</dim>
|
| 467 |
+
<dim>16</dim>
|
| 468 |
+
<dim>3</dim>
|
| 469 |
+
<dim>3</dim>
|
| 470 |
+
</port>
|
| 471 |
+
</input>
|
| 472 |
+
<output>
|
| 473 |
+
<port id="1" precision="FP32" names="14">
|
| 474 |
+
<dim>32</dim>
|
| 475 |
+
<dim>16</dim>
|
| 476 |
+
<dim>3</dim>
|
| 477 |
+
<dim>3</dim>
|
| 478 |
+
</port>
|
| 479 |
+
</output>
|
| 480 |
+
</layer>
|
| 481 |
+
<layer id="25" name="72/WithoutBiases" type="Convolution" version="opset1">
|
| 482 |
+
<data strides="1, 1" dilations="1, 1" pads_begin="1, 1" pads_end="1, 1" auto_pad="explicit" />
|
| 483 |
+
<input>
|
| 484 |
+
<port id="0" precision="FP32">
|
| 485 |
+
<dim>1</dim>
|
| 486 |
+
<dim>16</dim>
|
| 487 |
+
<dim>24</dim>
|
| 488 |
+
<dim>24</dim>
|
| 489 |
+
</port>
|
| 490 |
+
<port id="1" precision="FP32">
|
| 491 |
+
<dim>32</dim>
|
| 492 |
+
<dim>16</dim>
|
| 493 |
+
<dim>3</dim>
|
| 494 |
+
<dim>3</dim>
|
| 495 |
+
</port>
|
| 496 |
+
</input>
|
| 497 |
+
<output>
|
| 498 |
+
<port id="2" precision="FP32">
|
| 499 |
+
<dim>1</dim>
|
| 500 |
+
<dim>32</dim>
|
| 501 |
+
<dim>24</dim>
|
| 502 |
+
<dim>24</dim>
|
| 503 |
+
</port>
|
| 504 |
+
</output>
|
| 505 |
+
</layer>
|
| 506 |
+
<layer id="26" name="Reshape_107_compressed" type="Const" version="opset1">
|
| 507 |
+
<data element_type="f16" shape="1, 32, 1, 1" offset="10190" size="64" />
|
| 508 |
+
<output>
|
| 509 |
+
<port id="0" precision="FP16">
|
| 510 |
+
<dim>1</dim>
|
| 511 |
+
<dim>32</dim>
|
| 512 |
+
<dim>1</dim>
|
| 513 |
+
<dim>1</dim>
|
| 514 |
+
</port>
|
| 515 |
+
</output>
|
| 516 |
+
</layer>
|
| 517 |
+
<layer id="27" name="Reshape_107" type="Convert" version="opset1">
|
| 518 |
+
<data destination_type="f32" />
|
| 519 |
+
<rt_info>
|
| 520 |
+
<attribute name="decompression" version="0" />
|
| 521 |
+
</rt_info>
|
| 522 |
+
<input>
|
| 523 |
+
<port id="0" precision="FP16">
|
| 524 |
+
<dim>1</dim>
|
| 525 |
+
<dim>32</dim>
|
| 526 |
+
<dim>1</dim>
|
| 527 |
+
<dim>1</dim>
|
| 528 |
+
</port>
|
| 529 |
+
</input>
|
| 530 |
+
<output>
|
| 531 |
+
<port id="1" precision="FP32">
|
| 532 |
+
<dim>1</dim>
|
| 533 |
+
<dim>32</dim>
|
| 534 |
+
<dim>1</dim>
|
| 535 |
+
<dim>1</dim>
|
| 536 |
+
</port>
|
| 537 |
+
</output>
|
| 538 |
+
</layer>
|
| 539 |
+
<layer id="28" name="72" type="Add" version="opset1">
|
| 540 |
+
<data auto_broadcast="numpy" />
|
| 541 |
+
<input>
|
| 542 |
+
<port id="0" precision="FP32">
|
| 543 |
+
<dim>1</dim>
|
| 544 |
+
<dim>32</dim>
|
| 545 |
+
<dim>24</dim>
|
| 546 |
+
<dim>24</dim>
|
| 547 |
+
</port>
|
| 548 |
+
<port id="1" precision="FP32">
|
| 549 |
+
<dim>1</dim>
|
| 550 |
+
<dim>32</dim>
|
| 551 |
+
<dim>1</dim>
|
| 552 |
+
<dim>1</dim>
|
| 553 |
+
</port>
|
| 554 |
+
</input>
|
| 555 |
+
<output>
|
| 556 |
+
<port id="2" precision="FP32" names="72">
|
| 557 |
+
<dim>1</dim>
|
| 558 |
+
<dim>32</dim>
|
| 559 |
+
<dim>24</dim>
|
| 560 |
+
<dim>24</dim>
|
| 561 |
+
</port>
|
| 562 |
+
</output>
|
| 563 |
+
</layer>
|
| 564 |
+
<layer id="29" name="16_compressed" type="Const" version="opset1">
|
| 565 |
+
<data element_type="f16" shape="1" offset="10254" size="2" />
|
| 566 |
+
<output>
|
| 567 |
+
<port id="0" precision="FP16">
|
| 568 |
+
<dim>1</dim>
|
| 569 |
+
</port>
|
| 570 |
+
</output>
|
| 571 |
+
</layer>
|
| 572 |
+
<layer id="30" name="16" type="Convert" version="opset1">
|
| 573 |
+
<data destination_type="f32" />
|
| 574 |
+
<rt_info>
|
| 575 |
+
<attribute name="decompression" version="0" />
|
| 576 |
+
</rt_info>
|
| 577 |
+
<input>
|
| 578 |
+
<port id="0" precision="FP16">
|
| 579 |
+
<dim>1</dim>
|
| 580 |
+
</port>
|
| 581 |
+
</input>
|
| 582 |
+
<output>
|
| 583 |
+
<port id="1" precision="FP32" names="16">
|
| 584 |
+
<dim>1</dim>
|
| 585 |
+
</port>
|
| 586 |
+
</output>
|
| 587 |
+
</layer>
|
| 588 |
+
<layer id="31" name="73" type="PReLU" version="opset1">
|
| 589 |
+
<input>
|
| 590 |
+
<port id="0" precision="FP32">
|
| 591 |
+
<dim>1</dim>
|
| 592 |
+
<dim>32</dim>
|
| 593 |
+
<dim>24</dim>
|
| 594 |
+
<dim>24</dim>
|
| 595 |
+
</port>
|
| 596 |
+
<port id="1" precision="FP32">
|
| 597 |
+
<dim>1</dim>
|
| 598 |
+
</port>
|
| 599 |
+
</input>
|
| 600 |
+
<output>
|
| 601 |
+
<port id="2" precision="FP32" names="73">
|
| 602 |
+
<dim>1</dim>
|
| 603 |
+
<dim>32</dim>
|
| 604 |
+
<dim>24</dim>
|
| 605 |
+
<dim>24</dim>
|
| 606 |
+
</port>
|
| 607 |
+
</output>
|
| 608 |
+
</layer>
|
| 609 |
+
<layer id="32" name="74" type="MaxPool" version="opset1">
|
| 610 |
+
<data strides="2, 2" pads_begin="0, 0" pads_end="0, 0" kernel="2, 2" rounding_type="floor" auto_pad="explicit" />
|
| 611 |
+
<input>
|
| 612 |
+
<port id="0" precision="FP32">
|
| 613 |
+
<dim>1</dim>
|
| 614 |
+
<dim>32</dim>
|
| 615 |
+
<dim>24</dim>
|
| 616 |
+
<dim>24</dim>
|
| 617 |
+
</port>
|
| 618 |
+
</input>
|
| 619 |
+
<output>
|
| 620 |
+
<port id="1" precision="FP32" names="74">
|
| 621 |
+
<dim>1</dim>
|
| 622 |
+
<dim>32</dim>
|
| 623 |
+
<dim>12</dim>
|
| 624 |
+
<dim>12</dim>
|
| 625 |
+
</port>
|
| 626 |
+
</output>
|
| 627 |
+
</layer>
|
| 628 |
+
<layer id="33" name="Constant_1726_compressed" type="Const" version="opset1">
|
| 629 |
+
<data element_type="f16" shape="1, 32, 1, 1" offset="10256" size="64" />
|
| 630 |
+
<output>
|
| 631 |
+
<port id="0" precision="FP16">
|
| 632 |
+
<dim>1</dim>
|
| 633 |
+
<dim>32</dim>
|
| 634 |
+
<dim>1</dim>
|
| 635 |
+
<dim>1</dim>
|
| 636 |
+
</port>
|
| 637 |
+
</output>
|
| 638 |
+
</layer>
|
| 639 |
+
<layer id="34" name="Constant_1726" type="Convert" version="opset1">
|
| 640 |
+
<data destination_type="f32" />
|
| 641 |
+
<rt_info>
|
| 642 |
+
<attribute name="decompression" version="0" />
|
| 643 |
+
</rt_info>
|
| 644 |
+
<input>
|
| 645 |
+
<port id="0" precision="FP16">
|
| 646 |
+
<dim>1</dim>
|
| 647 |
+
<dim>32</dim>
|
| 648 |
+
<dim>1</dim>
|
| 649 |
+
<dim>1</dim>
|
| 650 |
+
</port>
|
| 651 |
+
</input>
|
| 652 |
+
<output>
|
| 653 |
+
<port id="1" precision="FP32">
|
| 654 |
+
<dim>1</dim>
|
| 655 |
+
<dim>32</dim>
|
| 656 |
+
<dim>1</dim>
|
| 657 |
+
<dim>1</dim>
|
| 658 |
+
</port>
|
| 659 |
+
</output>
|
| 660 |
+
</layer>
|
| 661 |
+
<layer id="35" name="Multiply_2077" type="Multiply" version="opset1">
|
| 662 |
+
<data auto_broadcast="numpy" />
|
| 663 |
+
<input>
|
| 664 |
+
<port id="0" precision="FP32">
|
| 665 |
+
<dim>1</dim>
|
| 666 |
+
<dim>32</dim>
|
| 667 |
+
<dim>12</dim>
|
| 668 |
+
<dim>12</dim>
|
| 669 |
+
</port>
|
| 670 |
+
<port id="1" precision="FP32">
|
| 671 |
+
<dim>1</dim>
|
| 672 |
+
<dim>32</dim>
|
| 673 |
+
<dim>1</dim>
|
| 674 |
+
<dim>1</dim>
|
| 675 |
+
</port>
|
| 676 |
+
</input>
|
| 677 |
+
<output>
|
| 678 |
+
<port id="2" precision="FP32">
|
| 679 |
+
<dim>1</dim>
|
| 680 |
+
<dim>32</dim>
|
| 681 |
+
<dim>12</dim>
|
| 682 |
+
<dim>12</dim>
|
| 683 |
+
</port>
|
| 684 |
+
</output>
|
| 685 |
+
</layer>
|
| 686 |
+
<layer id="36" name="Constant_2082_compressed" type="Const" version="opset1">
|
| 687 |
+
<data element_type="f16" shape="1, 32, 1, 1" offset="10320" size="64" />
|
| 688 |
+
<output>
|
| 689 |
+
<port id="0" precision="FP16">
|
| 690 |
+
<dim>1</dim>
|
| 691 |
+
<dim>32</dim>
|
| 692 |
+
<dim>1</dim>
|
| 693 |
+
<dim>1</dim>
|
| 694 |
+
</port>
|
| 695 |
+
</output>
|
| 696 |
+
</layer>
|
| 697 |
+
<layer id="37" name="Constant_2082" type="Convert" version="opset1">
|
| 698 |
+
<data destination_type="f32" />
|
| 699 |
+
<rt_info>
|
| 700 |
+
<attribute name="decompression" version="0" />
|
| 701 |
+
</rt_info>
|
| 702 |
+
<input>
|
| 703 |
+
<port id="0" precision="FP16">
|
| 704 |
+
<dim>1</dim>
|
| 705 |
+
<dim>32</dim>
|
| 706 |
+
<dim>1</dim>
|
| 707 |
+
<dim>1</dim>
|
| 708 |
+
</port>
|
| 709 |
+
</input>
|
| 710 |
+
<output>
|
| 711 |
+
<port id="1" precision="FP32">
|
| 712 |
+
<dim>1</dim>
|
| 713 |
+
<dim>32</dim>
|
| 714 |
+
<dim>1</dim>
|
| 715 |
+
<dim>1</dim>
|
| 716 |
+
</port>
|
| 717 |
+
</output>
|
| 718 |
+
</layer>
|
| 719 |
+
<layer id="38" name="75" type="Add" version="opset1">
|
| 720 |
+
<data auto_broadcast="numpy" />
|
| 721 |
+
<input>
|
| 722 |
+
<port id="0" precision="FP32">
|
| 723 |
+
<dim>1</dim>
|
| 724 |
+
<dim>32</dim>
|
| 725 |
+
<dim>12</dim>
|
| 726 |
+
<dim>12</dim>
|
| 727 |
+
</port>
|
| 728 |
+
<port id="1" precision="FP32">
|
| 729 |
+
<dim>1</dim>
|
| 730 |
+
<dim>32</dim>
|
| 731 |
+
<dim>1</dim>
|
| 732 |
+
<dim>1</dim>
|
| 733 |
+
</port>
|
| 734 |
+
</input>
|
| 735 |
+
<output>
|
| 736 |
+
<port id="2" precision="FP32" names="75">
|
| 737 |
+
<dim>1</dim>
|
| 738 |
+
<dim>32</dim>
|
| 739 |
+
<dim>12</dim>
|
| 740 |
+
<dim>12</dim>
|
| 741 |
+
</port>
|
| 742 |
+
</output>
|
| 743 |
+
</layer>
|
| 744 |
+
<layer id="39" name="22_compressed" type="Const" version="opset1">
|
| 745 |
+
<data element_type="f16" shape="64, 32, 3, 3" offset="10384" size="36864" />
|
| 746 |
+
<output>
|
| 747 |
+
<port id="0" precision="FP16">
|
| 748 |
+
<dim>64</dim>
|
| 749 |
+
<dim>32</dim>
|
| 750 |
+
<dim>3</dim>
|
| 751 |
+
<dim>3</dim>
|
| 752 |
+
</port>
|
| 753 |
+
</output>
|
| 754 |
+
</layer>
|
| 755 |
+
<layer id="40" name="22" type="Convert" version="opset1">
|
| 756 |
+
<data destination_type="f32" />
|
| 757 |
+
<rt_info>
|
| 758 |
+
<attribute name="decompression" version="0" />
|
| 759 |
+
</rt_info>
|
| 760 |
+
<input>
|
| 761 |
+
<port id="0" precision="FP16">
|
| 762 |
+
<dim>64</dim>
|
| 763 |
+
<dim>32</dim>
|
| 764 |
+
<dim>3</dim>
|
| 765 |
+
<dim>3</dim>
|
| 766 |
+
</port>
|
| 767 |
+
</input>
|
| 768 |
+
<output>
|
| 769 |
+
<port id="1" precision="FP32" names="22">
|
| 770 |
+
<dim>64</dim>
|
| 771 |
+
<dim>32</dim>
|
| 772 |
+
<dim>3</dim>
|
| 773 |
+
<dim>3</dim>
|
| 774 |
+
</port>
|
| 775 |
+
</output>
|
| 776 |
+
</layer>
|
| 777 |
+
<layer id="41" name="76/WithoutBiases" type="Convolution" version="opset1">
|
| 778 |
+
<data strides="1, 1" dilations="1, 1" pads_begin="1, 1" pads_end="1, 1" auto_pad="explicit" />
|
| 779 |
+
<input>
|
| 780 |
+
<port id="0" precision="FP32">
|
| 781 |
+
<dim>1</dim>
|
| 782 |
+
<dim>32</dim>
|
| 783 |
+
<dim>12</dim>
|
| 784 |
+
<dim>12</dim>
|
| 785 |
+
</port>
|
| 786 |
+
<port id="1" precision="FP32">
|
| 787 |
+
<dim>64</dim>
|
| 788 |
+
<dim>32</dim>
|
| 789 |
+
<dim>3</dim>
|
| 790 |
+
<dim>3</dim>
|
| 791 |
+
</port>
|
| 792 |
+
</input>
|
| 793 |
+
<output>
|
| 794 |
+
<port id="2" precision="FP32">
|
| 795 |
+
<dim>1</dim>
|
| 796 |
+
<dim>64</dim>
|
| 797 |
+
<dim>12</dim>
|
| 798 |
+
<dim>12</dim>
|
| 799 |
+
</port>
|
| 800 |
+
</output>
|
| 801 |
+
</layer>
|
| 802 |
+
<layer id="42" name="Reshape_130_compressed" type="Const" version="opset1">
|
| 803 |
+
<data element_type="f16" shape="1, 64, 1, 1" offset="47248" size="128" />
|
| 804 |
+
<output>
|
| 805 |
+
<port id="0" precision="FP16">
|
| 806 |
+
<dim>1</dim>
|
| 807 |
+
<dim>64</dim>
|
| 808 |
+
<dim>1</dim>
|
| 809 |
+
<dim>1</dim>
|
| 810 |
+
</port>
|
| 811 |
+
</output>
|
| 812 |
+
</layer>
|
| 813 |
+
<layer id="43" name="Reshape_130" type="Convert" version="opset1">
|
| 814 |
+
<data destination_type="f32" />
|
| 815 |
+
<rt_info>
|
| 816 |
+
<attribute name="decompression" version="0" />
|
| 817 |
+
</rt_info>
|
| 818 |
+
<input>
|
| 819 |
+
<port id="0" precision="FP16">
|
| 820 |
+
<dim>1</dim>
|
| 821 |
+
<dim>64</dim>
|
| 822 |
+
<dim>1</dim>
|
| 823 |
+
<dim>1</dim>
|
| 824 |
+
</port>
|
| 825 |
+
</input>
|
| 826 |
+
<output>
|
| 827 |
+
<port id="1" precision="FP32">
|
| 828 |
+
<dim>1</dim>
|
| 829 |
+
<dim>64</dim>
|
| 830 |
+
<dim>1</dim>
|
| 831 |
+
<dim>1</dim>
|
| 832 |
+
</port>
|
| 833 |
+
</output>
|
| 834 |
+
</layer>
|
| 835 |
+
<layer id="44" name="76" type="Add" version="opset1">
|
| 836 |
+
<data auto_broadcast="numpy" />
|
| 837 |
+
<input>
|
| 838 |
+
<port id="0" precision="FP32">
|
| 839 |
+
<dim>1</dim>
|
| 840 |
+
<dim>64</dim>
|
| 841 |
+
<dim>12</dim>
|
| 842 |
+
<dim>12</dim>
|
| 843 |
+
</port>
|
| 844 |
+
<port id="1" precision="FP32">
|
| 845 |
+
<dim>1</dim>
|
| 846 |
+
<dim>64</dim>
|
| 847 |
+
<dim>1</dim>
|
| 848 |
+
<dim>1</dim>
|
| 849 |
+
</port>
|
| 850 |
+
</input>
|
| 851 |
+
<output>
|
| 852 |
+
<port id="2" precision="FP32" names="76">
|
| 853 |
+
<dim>1</dim>
|
| 854 |
+
<dim>64</dim>
|
| 855 |
+
<dim>12</dim>
|
| 856 |
+
<dim>12</dim>
|
| 857 |
+
</port>
|
| 858 |
+
</output>
|
| 859 |
+
</layer>
|
| 860 |
+
<layer id="45" name="24_compressed" type="Const" version="opset1">
|
| 861 |
+
<data element_type="f16" shape="1" offset="47376" size="2" />
|
| 862 |
+
<output>
|
| 863 |
+
<port id="0" precision="FP16">
|
| 864 |
+
<dim>1</dim>
|
| 865 |
+
</port>
|
| 866 |
+
</output>
|
| 867 |
+
</layer>
|
| 868 |
+
<layer id="46" name="24" type="Convert" version="opset1">
|
| 869 |
+
<data destination_type="f32" />
|
| 870 |
+
<rt_info>
|
| 871 |
+
<attribute name="decompression" version="0" />
|
| 872 |
+
</rt_info>
|
| 873 |
+
<input>
|
| 874 |
+
<port id="0" precision="FP16">
|
| 875 |
+
<dim>1</dim>
|
| 876 |
+
</port>
|
| 877 |
+
</input>
|
| 878 |
+
<output>
|
| 879 |
+
<port id="1" precision="FP32" names="24">
|
| 880 |
+
<dim>1</dim>
|
| 881 |
+
</port>
|
| 882 |
+
</output>
|
| 883 |
+
</layer>
|
| 884 |
+
<layer id="47" name="77" type="PReLU" version="opset1">
|
| 885 |
+
<input>
|
| 886 |
+
<port id="0" precision="FP32">
|
| 887 |
+
<dim>1</dim>
|
| 888 |
+
<dim>64</dim>
|
| 889 |
+
<dim>12</dim>
|
| 890 |
+
<dim>12</dim>
|
| 891 |
+
</port>
|
| 892 |
+
<port id="1" precision="FP32">
|
| 893 |
+
<dim>1</dim>
|
| 894 |
+
</port>
|
| 895 |
+
</input>
|
| 896 |
+
<output>
|
| 897 |
+
<port id="2" precision="FP32" names="77">
|
| 898 |
+
<dim>1</dim>
|
| 899 |
+
<dim>64</dim>
|
| 900 |
+
<dim>12</dim>
|
| 901 |
+
<dim>12</dim>
|
| 902 |
+
</port>
|
| 903 |
+
</output>
|
| 904 |
+
</layer>
|
| 905 |
+
<layer id="48" name="78" type="MaxPool" version="opset1">
|
| 906 |
+
<data strides="2, 2" pads_begin="0, 0" pads_end="0, 0" kernel="2, 2" rounding_type="floor" auto_pad="explicit" />
|
| 907 |
+
<input>
|
| 908 |
+
<port id="0" precision="FP32">
|
| 909 |
+
<dim>1</dim>
|
| 910 |
+
<dim>64</dim>
|
| 911 |
+
<dim>12</dim>
|
| 912 |
+
<dim>12</dim>
|
| 913 |
+
</port>
|
| 914 |
+
</input>
|
| 915 |
+
<output>
|
| 916 |
+
<port id="1" precision="FP32" names="78">
|
| 917 |
+
<dim>1</dim>
|
| 918 |
+
<dim>64</dim>
|
| 919 |
+
<dim>6</dim>
|
| 920 |
+
<dim>6</dim>
|
| 921 |
+
</port>
|
| 922 |
+
</output>
|
| 923 |
+
</layer>
|
| 924 |
+
<layer id="49" name="Constant_1792_compressed" type="Const" version="opset1">
|
| 925 |
+
<data element_type="f16" shape="1, 64, 1, 1" offset="47378" size="128" />
|
| 926 |
+
<output>
|
| 927 |
+
<port id="0" precision="FP16">
|
| 928 |
+
<dim>1</dim>
|
| 929 |
+
<dim>64</dim>
|
| 930 |
+
<dim>1</dim>
|
| 931 |
+
<dim>1</dim>
|
| 932 |
+
</port>
|
| 933 |
+
</output>
|
| 934 |
+
</layer>
|
| 935 |
+
<layer id="50" name="Constant_1792" type="Convert" version="opset1">
|
| 936 |
+
<data destination_type="f32" />
|
| 937 |
+
<rt_info>
|
| 938 |
+
<attribute name="decompression" version="0" />
|
| 939 |
+
</rt_info>
|
| 940 |
+
<input>
|
| 941 |
+
<port id="0" precision="FP16">
|
| 942 |
+
<dim>1</dim>
|
| 943 |
+
<dim>64</dim>
|
| 944 |
+
<dim>1</dim>
|
| 945 |
+
<dim>1</dim>
|
| 946 |
+
</port>
|
| 947 |
+
</input>
|
| 948 |
+
<output>
|
| 949 |
+
<port id="1" precision="FP32">
|
| 950 |
+
<dim>1</dim>
|
| 951 |
+
<dim>64</dim>
|
| 952 |
+
<dim>1</dim>
|
| 953 |
+
<dim>1</dim>
|
| 954 |
+
</port>
|
| 955 |
+
</output>
|
| 956 |
+
</layer>
|
| 957 |
+
<layer id="51" name="Multiply_2084" type="Multiply" version="opset1">
|
| 958 |
+
<data auto_broadcast="numpy" />
|
| 959 |
+
<input>
|
| 960 |
+
<port id="0" precision="FP32">
|
| 961 |
+
<dim>1</dim>
|
| 962 |
+
<dim>64</dim>
|
| 963 |
+
<dim>6</dim>
|
| 964 |
+
<dim>6</dim>
|
| 965 |
+
</port>
|
| 966 |
+
<port id="1" precision="FP32">
|
| 967 |
+
<dim>1</dim>
|
| 968 |
+
<dim>64</dim>
|
| 969 |
+
<dim>1</dim>
|
| 970 |
+
<dim>1</dim>
|
| 971 |
+
</port>
|
| 972 |
+
</input>
|
| 973 |
+
<output>
|
| 974 |
+
<port id="2" precision="FP32">
|
| 975 |
+
<dim>1</dim>
|
| 976 |
+
<dim>64</dim>
|
| 977 |
+
<dim>6</dim>
|
| 978 |
+
<dim>6</dim>
|
| 979 |
+
</port>
|
| 980 |
+
</output>
|
| 981 |
+
</layer>
|
| 982 |
+
<layer id="52" name="Constant_2089_compressed" type="Const" version="opset1">
|
| 983 |
+
<data element_type="f16" shape="1, 64, 1, 1" offset="47506" size="128" />
|
| 984 |
+
<output>
|
| 985 |
+
<port id="0" precision="FP16">
|
| 986 |
+
<dim>1</dim>
|
| 987 |
+
<dim>64</dim>
|
| 988 |
+
<dim>1</dim>
|
| 989 |
+
<dim>1</dim>
|
| 990 |
+
</port>
|
| 991 |
+
</output>
|
| 992 |
+
</layer>
|
| 993 |
+
<layer id="53" name="Constant_2089" type="Convert" version="opset1">
|
| 994 |
+
<data destination_type="f32" />
|
| 995 |
+
<rt_info>
|
| 996 |
+
<attribute name="decompression" version="0" />
|
| 997 |
+
</rt_info>
|
| 998 |
+
<input>
|
| 999 |
+
<port id="0" precision="FP16">
|
| 1000 |
+
<dim>1</dim>
|
| 1001 |
+
<dim>64</dim>
|
| 1002 |
+
<dim>1</dim>
|
| 1003 |
+
<dim>1</dim>
|
| 1004 |
+
</port>
|
| 1005 |
+
</input>
|
| 1006 |
+
<output>
|
| 1007 |
+
<port id="1" precision="FP32">
|
| 1008 |
+
<dim>1</dim>
|
| 1009 |
+
<dim>64</dim>
|
| 1010 |
+
<dim>1</dim>
|
| 1011 |
+
<dim>1</dim>
|
| 1012 |
+
</port>
|
| 1013 |
+
</output>
|
| 1014 |
+
</layer>
|
| 1015 |
+
<layer id="54" name="79" type="Add" version="opset1">
|
| 1016 |
+
<data auto_broadcast="numpy" />
|
| 1017 |
+
<input>
|
| 1018 |
+
<port id="0" precision="FP32">
|
| 1019 |
+
<dim>1</dim>
|
| 1020 |
+
<dim>64</dim>
|
| 1021 |
+
<dim>6</dim>
|
| 1022 |
+
<dim>6</dim>
|
| 1023 |
+
</port>
|
| 1024 |
+
<port id="1" precision="FP32">
|
| 1025 |
+
<dim>1</dim>
|
| 1026 |
+
<dim>64</dim>
|
| 1027 |
+
<dim>1</dim>
|
| 1028 |
+
<dim>1</dim>
|
| 1029 |
+
</port>
|
| 1030 |
+
</input>
|
| 1031 |
+
<output>
|
| 1032 |
+
<port id="2" precision="FP32" names="79">
|
| 1033 |
+
<dim>1</dim>
|
| 1034 |
+
<dim>64</dim>
|
| 1035 |
+
<dim>6</dim>
|
| 1036 |
+
<dim>6</dim>
|
| 1037 |
+
</port>
|
| 1038 |
+
</output>
|
| 1039 |
+
</layer>
|
| 1040 |
+
<layer id="55" name="30_compressed" type="Const" version="opset1">
|
| 1041 |
+
<data element_type="f16" shape="64, 64, 3, 3" offset="47634" size="73728" />
|
| 1042 |
+
<output>
|
| 1043 |
+
<port id="0" precision="FP16">
|
| 1044 |
+
<dim>64</dim>
|
| 1045 |
+
<dim>64</dim>
|
| 1046 |
+
<dim>3</dim>
|
| 1047 |
+
<dim>3</dim>
|
| 1048 |
+
</port>
|
| 1049 |
+
</output>
|
| 1050 |
+
</layer>
|
| 1051 |
+
<layer id="56" name="30" type="Convert" version="opset1">
|
| 1052 |
+
<data destination_type="f32" />
|
| 1053 |
+
<rt_info>
|
| 1054 |
+
<attribute name="decompression" version="0" />
|
| 1055 |
+
</rt_info>
|
| 1056 |
+
<input>
|
| 1057 |
+
<port id="0" precision="FP16">
|
| 1058 |
+
<dim>64</dim>
|
| 1059 |
+
<dim>64</dim>
|
| 1060 |
+
<dim>3</dim>
|
| 1061 |
+
<dim>3</dim>
|
| 1062 |
+
</port>
|
| 1063 |
+
</input>
|
| 1064 |
+
<output>
|
| 1065 |
+
<port id="1" precision="FP32" names="30">
|
| 1066 |
+
<dim>64</dim>
|
| 1067 |
+
<dim>64</dim>
|
| 1068 |
+
<dim>3</dim>
|
| 1069 |
+
<dim>3</dim>
|
| 1070 |
+
</port>
|
| 1071 |
+
</output>
|
| 1072 |
+
</layer>
|
| 1073 |
+
<layer id="57" name="80/WithoutBiases" type="Convolution" version="opset1">
|
| 1074 |
+
<data strides="1, 1" dilations="1, 1" pads_begin="1, 1" pads_end="1, 1" auto_pad="explicit" />
|
| 1075 |
+
<input>
|
| 1076 |
+
<port id="0" precision="FP32">
|
| 1077 |
+
<dim>1</dim>
|
| 1078 |
+
<dim>64</dim>
|
| 1079 |
+
<dim>6</dim>
|
| 1080 |
+
<dim>6</dim>
|
| 1081 |
+
</port>
|
| 1082 |
+
<port id="1" precision="FP32">
|
| 1083 |
+
<dim>64</dim>
|
| 1084 |
+
<dim>64</dim>
|
| 1085 |
+
<dim>3</dim>
|
| 1086 |
+
<dim>3</dim>
|
| 1087 |
+
</port>
|
| 1088 |
+
</input>
|
| 1089 |
+
<output>
|
| 1090 |
+
<port id="2" precision="FP32">
|
| 1091 |
+
<dim>1</dim>
|
| 1092 |
+
<dim>64</dim>
|
| 1093 |
+
<dim>6</dim>
|
| 1094 |
+
<dim>6</dim>
|
| 1095 |
+
</port>
|
| 1096 |
+
</output>
|
| 1097 |
+
</layer>
|
| 1098 |
+
<layer id="58" name="Reshape_153_compressed" type="Const" version="opset1">
|
| 1099 |
+
<data element_type="f16" shape="1, 64, 1, 1" offset="121362" size="128" />
|
| 1100 |
+
<output>
|
| 1101 |
+
<port id="0" precision="FP16">
|
| 1102 |
+
<dim>1</dim>
|
| 1103 |
+
<dim>64</dim>
|
| 1104 |
+
<dim>1</dim>
|
| 1105 |
+
<dim>1</dim>
|
| 1106 |
+
</port>
|
| 1107 |
+
</output>
|
| 1108 |
+
</layer>
|
| 1109 |
+
<layer id="59" name="Reshape_153" type="Convert" version="opset1">
|
| 1110 |
+
<data destination_type="f32" />
|
| 1111 |
+
<rt_info>
|
| 1112 |
+
<attribute name="decompression" version="0" />
|
| 1113 |
+
</rt_info>
|
| 1114 |
+
<input>
|
| 1115 |
+
<port id="0" precision="FP16">
|
| 1116 |
+
<dim>1</dim>
|
| 1117 |
+
<dim>64</dim>
|
| 1118 |
+
<dim>1</dim>
|
| 1119 |
+
<dim>1</dim>
|
| 1120 |
+
</port>
|
| 1121 |
+
</input>
|
| 1122 |
+
<output>
|
| 1123 |
+
<port id="1" precision="FP32">
|
| 1124 |
+
<dim>1</dim>
|
| 1125 |
+
<dim>64</dim>
|
| 1126 |
+
<dim>1</dim>
|
| 1127 |
+
<dim>1</dim>
|
| 1128 |
+
</port>
|
| 1129 |
+
</output>
|
| 1130 |
+
</layer>
|
| 1131 |
+
<layer id="60" name="80" type="Add" version="opset1">
|
| 1132 |
+
<data auto_broadcast="numpy" />
|
| 1133 |
+
<input>
|
| 1134 |
+
<port id="0" precision="FP32">
|
| 1135 |
+
<dim>1</dim>
|
| 1136 |
+
<dim>64</dim>
|
| 1137 |
+
<dim>6</dim>
|
| 1138 |
+
<dim>6</dim>
|
| 1139 |
+
</port>
|
| 1140 |
+
<port id="1" precision="FP32">
|
| 1141 |
+
<dim>1</dim>
|
| 1142 |
+
<dim>64</dim>
|
| 1143 |
+
<dim>1</dim>
|
| 1144 |
+
<dim>1</dim>
|
| 1145 |
+
</port>
|
| 1146 |
+
</input>
|
| 1147 |
+
<output>
|
| 1148 |
+
<port id="2" precision="FP32" names="80">
|
| 1149 |
+
<dim>1</dim>
|
| 1150 |
+
<dim>64</dim>
|
| 1151 |
+
<dim>6</dim>
|
| 1152 |
+
<dim>6</dim>
|
| 1153 |
+
</port>
|
| 1154 |
+
</output>
|
| 1155 |
+
</layer>
|
| 1156 |
+
<layer id="61" name="32_compressed" type="Const" version="opset1">
|
| 1157 |
+
<data element_type="f16" shape="1" offset="121490" size="2" />
|
| 1158 |
+
<output>
|
| 1159 |
+
<port id="0" precision="FP16">
|
| 1160 |
+
<dim>1</dim>
|
| 1161 |
+
</port>
|
| 1162 |
+
</output>
|
| 1163 |
+
</layer>
|
| 1164 |
+
<layer id="62" name="32" type="Convert" version="opset1">
|
| 1165 |
+
<data destination_type="f32" />
|
| 1166 |
+
<rt_info>
|
| 1167 |
+
<attribute name="decompression" version="0" />
|
| 1168 |
+
</rt_info>
|
| 1169 |
+
<input>
|
| 1170 |
+
<port id="0" precision="FP16">
|
| 1171 |
+
<dim>1</dim>
|
| 1172 |
+
</port>
|
| 1173 |
+
</input>
|
| 1174 |
+
<output>
|
| 1175 |
+
<port id="1" precision="FP32" names="32">
|
| 1176 |
+
<dim>1</dim>
|
| 1177 |
+
</port>
|
| 1178 |
+
</output>
|
| 1179 |
+
</layer>
|
| 1180 |
+
<layer id="63" name="81" type="PReLU" version="opset1">
|
| 1181 |
+
<input>
|
| 1182 |
+
<port id="0" precision="FP32">
|
| 1183 |
+
<dim>1</dim>
|
| 1184 |
+
<dim>64</dim>
|
| 1185 |
+
<dim>6</dim>
|
| 1186 |
+
<dim>6</dim>
|
| 1187 |
+
</port>
|
| 1188 |
+
<port id="1" precision="FP32">
|
| 1189 |
+
<dim>1</dim>
|
| 1190 |
+
</port>
|
| 1191 |
+
</input>
|
| 1192 |
+
<output>
|
| 1193 |
+
<port id="2" precision="FP32" names="81">
|
| 1194 |
+
<dim>1</dim>
|
| 1195 |
+
<dim>64</dim>
|
| 1196 |
+
<dim>6</dim>
|
| 1197 |
+
<dim>6</dim>
|
| 1198 |
+
</port>
|
| 1199 |
+
</output>
|
| 1200 |
+
</layer>
|
| 1201 |
+
<layer id="64" name="Constant_1858_compressed" type="Const" version="opset1">
|
| 1202 |
+
<data element_type="f16" shape="1, 64, 1, 1" offset="121492" size="128" />
|
| 1203 |
+
<output>
|
| 1204 |
+
<port id="0" precision="FP16">
|
| 1205 |
+
<dim>1</dim>
|
| 1206 |
+
<dim>64</dim>
|
| 1207 |
+
<dim>1</dim>
|
| 1208 |
+
<dim>1</dim>
|
| 1209 |
+
</port>
|
| 1210 |
+
</output>
|
| 1211 |
+
</layer>
|
| 1212 |
+
<layer id="65" name="Constant_1858" type="Convert" version="opset1">
|
| 1213 |
+
<data destination_type="f32" />
|
| 1214 |
+
<rt_info>
|
| 1215 |
+
<attribute name="decompression" version="0" />
|
| 1216 |
+
</rt_info>
|
| 1217 |
+
<input>
|
| 1218 |
+
<port id="0" precision="FP16">
|
| 1219 |
+
<dim>1</dim>
|
| 1220 |
+
<dim>64</dim>
|
| 1221 |
+
<dim>1</dim>
|
| 1222 |
+
<dim>1</dim>
|
| 1223 |
+
</port>
|
| 1224 |
+
</input>
|
| 1225 |
+
<output>
|
| 1226 |
+
<port id="1" precision="FP32">
|
| 1227 |
+
<dim>1</dim>
|
| 1228 |
+
<dim>64</dim>
|
| 1229 |
+
<dim>1</dim>
|
| 1230 |
+
<dim>1</dim>
|
| 1231 |
+
</port>
|
| 1232 |
+
</output>
|
| 1233 |
+
</layer>
|
| 1234 |
+
<layer id="66" name="Multiply_2091" type="Multiply" version="opset1">
|
| 1235 |
+
<data auto_broadcast="numpy" />
|
| 1236 |
+
<input>
|
| 1237 |
+
<port id="0" precision="FP32">
|
| 1238 |
+
<dim>1</dim>
|
| 1239 |
+
<dim>64</dim>
|
| 1240 |
+
<dim>6</dim>
|
| 1241 |
+
<dim>6</dim>
|
| 1242 |
+
</port>
|
| 1243 |
+
<port id="1" precision="FP32">
|
| 1244 |
+
<dim>1</dim>
|
| 1245 |
+
<dim>64</dim>
|
| 1246 |
+
<dim>1</dim>
|
| 1247 |
+
<dim>1</dim>
|
| 1248 |
+
</port>
|
| 1249 |
+
</input>
|
| 1250 |
+
<output>
|
| 1251 |
+
<port id="2" precision="FP32">
|
| 1252 |
+
<dim>1</dim>
|
| 1253 |
+
<dim>64</dim>
|
| 1254 |
+
<dim>6</dim>
|
| 1255 |
+
<dim>6</dim>
|
| 1256 |
+
</port>
|
| 1257 |
+
</output>
|
| 1258 |
+
</layer>
|
| 1259 |
+
<layer id="67" name="Constant_2096_compressed" type="Const" version="opset1">
|
| 1260 |
+
<data element_type="f16" shape="1, 64, 1, 1" offset="121620" size="128" />
|
| 1261 |
+
<output>
|
| 1262 |
+
<port id="0" precision="FP16">
|
| 1263 |
+
<dim>1</dim>
|
| 1264 |
+
<dim>64</dim>
|
| 1265 |
+
<dim>1</dim>
|
| 1266 |
+
<dim>1</dim>
|
| 1267 |
+
</port>
|
| 1268 |
+
</output>
|
| 1269 |
+
</layer>
|
| 1270 |
+
<layer id="68" name="Constant_2096" type="Convert" version="opset1">
|
| 1271 |
+
<data destination_type="f32" />
|
| 1272 |
+
<rt_info>
|
| 1273 |
+
<attribute name="decompression" version="0" />
|
| 1274 |
+
</rt_info>
|
| 1275 |
+
<input>
|
| 1276 |
+
<port id="0" precision="FP16">
|
| 1277 |
+
<dim>1</dim>
|
| 1278 |
+
<dim>64</dim>
|
| 1279 |
+
<dim>1</dim>
|
| 1280 |
+
<dim>1</dim>
|
| 1281 |
+
</port>
|
| 1282 |
+
</input>
|
| 1283 |
+
<output>
|
| 1284 |
+
<port id="1" precision="FP32">
|
| 1285 |
+
<dim>1</dim>
|
| 1286 |
+
<dim>64</dim>
|
| 1287 |
+
<dim>1</dim>
|
| 1288 |
+
<dim>1</dim>
|
| 1289 |
+
</port>
|
| 1290 |
+
</output>
|
| 1291 |
+
</layer>
|
| 1292 |
+
<layer id="69" name="82" type="Add" version="opset1">
|
| 1293 |
+
<data auto_broadcast="numpy" />
|
| 1294 |
+
<input>
|
| 1295 |
+
<port id="0" precision="FP32">
|
| 1296 |
+
<dim>1</dim>
|
| 1297 |
+
<dim>64</dim>
|
| 1298 |
+
<dim>6</dim>
|
| 1299 |
+
<dim>6</dim>
|
| 1300 |
+
</port>
|
| 1301 |
+
<port id="1" precision="FP32">
|
| 1302 |
+
<dim>1</dim>
|
| 1303 |
+
<dim>64</dim>
|
| 1304 |
+
<dim>1</dim>
|
| 1305 |
+
<dim>1</dim>
|
| 1306 |
+
</port>
|
| 1307 |
+
</input>
|
| 1308 |
+
<output>
|
| 1309 |
+
<port id="2" precision="FP32" names="82">
|
| 1310 |
+
<dim>1</dim>
|
| 1311 |
+
<dim>64</dim>
|
| 1312 |
+
<dim>6</dim>
|
| 1313 |
+
<dim>6</dim>
|
| 1314 |
+
</port>
|
| 1315 |
+
</output>
|
| 1316 |
+
</layer>
|
| 1317 |
+
<layer id="70" name="38_compressed" type="Const" version="opset1">
|
| 1318 |
+
<data element_type="f16" shape="128, 64, 3, 3" offset="121748" size="147456" />
|
| 1319 |
+
<output>
|
| 1320 |
+
<port id="0" precision="FP16">
|
| 1321 |
+
<dim>128</dim>
|
| 1322 |
+
<dim>64</dim>
|
| 1323 |
+
<dim>3</dim>
|
| 1324 |
+
<dim>3</dim>
|
| 1325 |
+
</port>
|
| 1326 |
+
</output>
|
| 1327 |
+
</layer>
|
| 1328 |
+
<layer id="71" name="38" type="Convert" version="opset1">
|
| 1329 |
+
<data destination_type="f32" />
|
| 1330 |
+
<rt_info>
|
| 1331 |
+
<attribute name="decompression" version="0" />
|
| 1332 |
+
</rt_info>
|
| 1333 |
+
<input>
|
| 1334 |
+
<port id="0" precision="FP16">
|
| 1335 |
+
<dim>128</dim>
|
| 1336 |
+
<dim>64</dim>
|
| 1337 |
+
<dim>3</dim>
|
| 1338 |
+
<dim>3</dim>
|
| 1339 |
+
</port>
|
| 1340 |
+
</input>
|
| 1341 |
+
<output>
|
| 1342 |
+
<port id="1" precision="FP32" names="38">
|
| 1343 |
+
<dim>128</dim>
|
| 1344 |
+
<dim>64</dim>
|
| 1345 |
+
<dim>3</dim>
|
| 1346 |
+
<dim>3</dim>
|
| 1347 |
+
</port>
|
| 1348 |
+
</output>
|
| 1349 |
+
</layer>
|
| 1350 |
+
<layer id="72" name="83/WithoutBiases" type="Convolution" version="opset1">
|
| 1351 |
+
<data strides="1, 1" dilations="1, 1" pads_begin="1, 1" pads_end="1, 1" auto_pad="explicit" />
|
| 1352 |
+
<input>
|
| 1353 |
+
<port id="0" precision="FP32">
|
| 1354 |
+
<dim>1</dim>
|
| 1355 |
+
<dim>64</dim>
|
| 1356 |
+
<dim>6</dim>
|
| 1357 |
+
<dim>6</dim>
|
| 1358 |
+
</port>
|
| 1359 |
+
<port id="1" precision="FP32">
|
| 1360 |
+
<dim>128</dim>
|
| 1361 |
+
<dim>64</dim>
|
| 1362 |
+
<dim>3</dim>
|
| 1363 |
+
<dim>3</dim>
|
| 1364 |
+
</port>
|
| 1365 |
+
</input>
|
| 1366 |
+
<output>
|
| 1367 |
+
<port id="2" precision="FP32">
|
| 1368 |
+
<dim>1</dim>
|
| 1369 |
+
<dim>128</dim>
|
| 1370 |
+
<dim>6</dim>
|
| 1371 |
+
<dim>6</dim>
|
| 1372 |
+
</port>
|
| 1373 |
+
</output>
|
| 1374 |
+
</layer>
|
| 1375 |
+
<layer id="73" name="Reshape_174_compressed" type="Const" version="opset1">
|
| 1376 |
+
<data element_type="f16" shape="1, 128, 1, 1" offset="269204" size="256" />
|
| 1377 |
+
<output>
|
| 1378 |
+
<port id="0" precision="FP16">
|
| 1379 |
+
<dim>1</dim>
|
| 1380 |
+
<dim>128</dim>
|
| 1381 |
+
<dim>1</dim>
|
| 1382 |
+
<dim>1</dim>
|
| 1383 |
+
</port>
|
| 1384 |
+
</output>
|
| 1385 |
+
</layer>
|
| 1386 |
+
<layer id="74" name="Reshape_174" type="Convert" version="opset1">
|
| 1387 |
+
<data destination_type="f32" />
|
| 1388 |
+
<rt_info>
|
| 1389 |
+
<attribute name="decompression" version="0" />
|
| 1390 |
+
</rt_info>
|
| 1391 |
+
<input>
|
| 1392 |
+
<port id="0" precision="FP16">
|
| 1393 |
+
<dim>1</dim>
|
| 1394 |
+
<dim>128</dim>
|
| 1395 |
+
<dim>1</dim>
|
| 1396 |
+
<dim>1</dim>
|
| 1397 |
+
</port>
|
| 1398 |
+
</input>
|
| 1399 |
+
<output>
|
| 1400 |
+
<port id="1" precision="FP32">
|
| 1401 |
+
<dim>1</dim>
|
| 1402 |
+
<dim>128</dim>
|
| 1403 |
+
<dim>1</dim>
|
| 1404 |
+
<dim>1</dim>
|
| 1405 |
+
</port>
|
| 1406 |
+
</output>
|
| 1407 |
+
</layer>
|
| 1408 |
+
<layer id="75" name="83" type="Add" version="opset1">
|
| 1409 |
+
<data auto_broadcast="numpy" />
|
| 1410 |
+
<input>
|
| 1411 |
+
<port id="0" precision="FP32">
|
| 1412 |
+
<dim>1</dim>
|
| 1413 |
+
<dim>128</dim>
|
| 1414 |
+
<dim>6</dim>
|
| 1415 |
+
<dim>6</dim>
|
| 1416 |
+
</port>
|
| 1417 |
+
<port id="1" precision="FP32">
|
| 1418 |
+
<dim>1</dim>
|
| 1419 |
+
<dim>128</dim>
|
| 1420 |
+
<dim>1</dim>
|
| 1421 |
+
<dim>1</dim>
|
| 1422 |
+
</port>
|
| 1423 |
+
</input>
|
| 1424 |
+
<output>
|
| 1425 |
+
<port id="2" precision="FP32" names="83">
|
| 1426 |
+
<dim>1</dim>
|
| 1427 |
+
<dim>128</dim>
|
| 1428 |
+
<dim>6</dim>
|
| 1429 |
+
<dim>6</dim>
|
| 1430 |
+
</port>
|
| 1431 |
+
</output>
|
| 1432 |
+
</layer>
|
| 1433 |
+
<layer id="76" name="40_compressed" type="Const" version="opset1">
|
| 1434 |
+
<data element_type="f16" shape="1" offset="269460" size="2" />
|
| 1435 |
+
<output>
|
| 1436 |
+
<port id="0" precision="FP16">
|
| 1437 |
+
<dim>1</dim>
|
| 1438 |
+
</port>
|
| 1439 |
+
</output>
|
| 1440 |
+
</layer>
|
| 1441 |
+
<layer id="77" name="40" type="Convert" version="opset1">
|
| 1442 |
+
<data destination_type="f32" />
|
| 1443 |
+
<rt_info>
|
| 1444 |
+
<attribute name="decompression" version="0" />
|
| 1445 |
+
</rt_info>
|
| 1446 |
+
<input>
|
| 1447 |
+
<port id="0" precision="FP16">
|
| 1448 |
+
<dim>1</dim>
|
| 1449 |
+
</port>
|
| 1450 |
+
</input>
|
| 1451 |
+
<output>
|
| 1452 |
+
<port id="1" precision="FP32" names="40">
|
| 1453 |
+
<dim>1</dim>
|
| 1454 |
+
</port>
|
| 1455 |
+
</output>
|
| 1456 |
+
</layer>
|
| 1457 |
+
<layer id="78" name="84" type="PReLU" version="opset1">
|
| 1458 |
+
<input>
|
| 1459 |
+
<port id="0" precision="FP32">
|
| 1460 |
+
<dim>1</dim>
|
| 1461 |
+
<dim>128</dim>
|
| 1462 |
+
<dim>6</dim>
|
| 1463 |
+
<dim>6</dim>
|
| 1464 |
+
</port>
|
| 1465 |
+
<port id="1" precision="FP32">
|
| 1466 |
+
<dim>1</dim>
|
| 1467 |
+
</port>
|
| 1468 |
+
</input>
|
| 1469 |
+
<output>
|
| 1470 |
+
<port id="2" precision="FP32" names="84">
|
| 1471 |
+
<dim>1</dim>
|
| 1472 |
+
<dim>128</dim>
|
| 1473 |
+
<dim>6</dim>
|
| 1474 |
+
<dim>6</dim>
|
| 1475 |
+
</port>
|
| 1476 |
+
</output>
|
| 1477 |
+
</layer>
|
| 1478 |
+
<layer id="79" name="Constant_1924_compressed" type="Const" version="opset1">
|
| 1479 |
+
<data element_type="f16" shape="1, 128, 1, 1" offset="269462" size="256" />
|
| 1480 |
+
<output>
|
| 1481 |
+
<port id="0" precision="FP16">
|
| 1482 |
+
<dim>1</dim>
|
| 1483 |
+
<dim>128</dim>
|
| 1484 |
+
<dim>1</dim>
|
| 1485 |
+
<dim>1</dim>
|
| 1486 |
+
</port>
|
| 1487 |
+
</output>
|
| 1488 |
+
</layer>
|
| 1489 |
+
<layer id="80" name="Constant_1924" type="Convert" version="opset1">
|
| 1490 |
+
<data destination_type="f32" />
|
| 1491 |
+
<rt_info>
|
| 1492 |
+
<attribute name="decompression" version="0" />
|
| 1493 |
+
</rt_info>
|
| 1494 |
+
<input>
|
| 1495 |
+
<port id="0" precision="FP16">
|
| 1496 |
+
<dim>1</dim>
|
| 1497 |
+
<dim>128</dim>
|
| 1498 |
+
<dim>1</dim>
|
| 1499 |
+
<dim>1</dim>
|
| 1500 |
+
</port>
|
| 1501 |
+
</input>
|
| 1502 |
+
<output>
|
| 1503 |
+
<port id="1" precision="FP32">
|
| 1504 |
+
<dim>1</dim>
|
| 1505 |
+
<dim>128</dim>
|
| 1506 |
+
<dim>1</dim>
|
| 1507 |
+
<dim>1</dim>
|
| 1508 |
+
</port>
|
| 1509 |
+
</output>
|
| 1510 |
+
</layer>
|
| 1511 |
+
<layer id="81" name="Multiply_2098" type="Multiply" version="opset1">
|
| 1512 |
+
<data auto_broadcast="numpy" />
|
| 1513 |
+
<input>
|
| 1514 |
+
<port id="0" precision="FP32">
|
| 1515 |
+
<dim>1</dim>
|
| 1516 |
+
<dim>128</dim>
|
| 1517 |
+
<dim>6</dim>
|
| 1518 |
+
<dim>6</dim>
|
| 1519 |
+
</port>
|
| 1520 |
+
<port id="1" precision="FP32">
|
| 1521 |
+
<dim>1</dim>
|
| 1522 |
+
<dim>128</dim>
|
| 1523 |
+
<dim>1</dim>
|
| 1524 |
+
<dim>1</dim>
|
| 1525 |
+
</port>
|
| 1526 |
+
</input>
|
| 1527 |
+
<output>
|
| 1528 |
+
<port id="2" precision="FP32">
|
| 1529 |
+
<dim>1</dim>
|
| 1530 |
+
<dim>128</dim>
|
| 1531 |
+
<dim>6</dim>
|
| 1532 |
+
<dim>6</dim>
|
| 1533 |
+
</port>
|
| 1534 |
+
</output>
|
| 1535 |
+
</layer>
|
| 1536 |
+
<layer id="82" name="Constant_2103_compressed" type="Const" version="opset1">
|
| 1537 |
+
<data element_type="f16" shape="1, 128, 1, 1" offset="269718" size="256" />
|
| 1538 |
+
<output>
|
| 1539 |
+
<port id="0" precision="FP16">
|
| 1540 |
+
<dim>1</dim>
|
| 1541 |
+
<dim>128</dim>
|
| 1542 |
+
<dim>1</dim>
|
| 1543 |
+
<dim>1</dim>
|
| 1544 |
+
</port>
|
| 1545 |
+
</output>
|
| 1546 |
+
</layer>
|
| 1547 |
+
<layer id="83" name="Constant_2103" type="Convert" version="opset1">
|
| 1548 |
+
<data destination_type="f32" />
|
| 1549 |
+
<rt_info>
|
| 1550 |
+
<attribute name="decompression" version="0" />
|
| 1551 |
+
</rt_info>
|
| 1552 |
+
<input>
|
| 1553 |
+
<port id="0" precision="FP16">
|
| 1554 |
+
<dim>1</dim>
|
| 1555 |
+
<dim>128</dim>
|
| 1556 |
+
<dim>1</dim>
|
| 1557 |
+
<dim>1</dim>
|
| 1558 |
+
</port>
|
| 1559 |
+
</input>
|
| 1560 |
+
<output>
|
| 1561 |
+
<port id="1" precision="FP32">
|
| 1562 |
+
<dim>1</dim>
|
| 1563 |
+
<dim>128</dim>
|
| 1564 |
+
<dim>1</dim>
|
| 1565 |
+
<dim>1</dim>
|
| 1566 |
+
</port>
|
| 1567 |
+
</output>
|
| 1568 |
+
</layer>
|
| 1569 |
+
<layer id="84" name="85" type="Add" version="opset1">
|
| 1570 |
+
<data auto_broadcast="numpy" />
|
| 1571 |
+
<input>
|
| 1572 |
+
<port id="0" precision="FP32">
|
| 1573 |
+
<dim>1</dim>
|
| 1574 |
+
<dim>128</dim>
|
| 1575 |
+
<dim>6</dim>
|
| 1576 |
+
<dim>6</dim>
|
| 1577 |
+
</port>
|
| 1578 |
+
<port id="1" precision="FP32">
|
| 1579 |
+
<dim>1</dim>
|
| 1580 |
+
<dim>128</dim>
|
| 1581 |
+
<dim>1</dim>
|
| 1582 |
+
<dim>1</dim>
|
| 1583 |
+
</port>
|
| 1584 |
+
</input>
|
| 1585 |
+
<output>
|
| 1586 |
+
<port id="2" precision="FP32" names="85">
|
| 1587 |
+
<dim>1</dim>
|
| 1588 |
+
<dim>128</dim>
|
| 1589 |
+
<dim>6</dim>
|
| 1590 |
+
<dim>6</dim>
|
| 1591 |
+
</port>
|
| 1592 |
+
</output>
|
| 1593 |
+
</layer>
|
| 1594 |
+
<layer id="85" name="Reshape_191_compressed" type="Const" version="opset1">
|
| 1595 |
+
<data element_type="f16" shape="128, 1, 1, 6, 6" offset="269974" size="9216" />
|
| 1596 |
+
<output>
|
| 1597 |
+
<port id="0" precision="FP16">
|
| 1598 |
+
<dim>128</dim>
|
| 1599 |
+
<dim>1</dim>
|
| 1600 |
+
<dim>1</dim>
|
| 1601 |
+
<dim>6</dim>
|
| 1602 |
+
<dim>6</dim>
|
| 1603 |
+
</port>
|
| 1604 |
+
</output>
|
| 1605 |
+
</layer>
|
| 1606 |
+
<layer id="86" name="Reshape_191" type="Convert" version="opset1">
|
| 1607 |
+
<data destination_type="f32" />
|
| 1608 |
+
<rt_info>
|
| 1609 |
+
<attribute name="decompression" version="0" />
|
| 1610 |
+
</rt_info>
|
| 1611 |
+
<input>
|
| 1612 |
+
<port id="0" precision="FP16">
|
| 1613 |
+
<dim>128</dim>
|
| 1614 |
+
<dim>1</dim>
|
| 1615 |
+
<dim>1</dim>
|
| 1616 |
+
<dim>6</dim>
|
| 1617 |
+
<dim>6</dim>
|
| 1618 |
+
</port>
|
| 1619 |
+
</input>
|
| 1620 |
+
<output>
|
| 1621 |
+
<port id="1" precision="FP32">
|
| 1622 |
+
<dim>128</dim>
|
| 1623 |
+
<dim>1</dim>
|
| 1624 |
+
<dim>1</dim>
|
| 1625 |
+
<dim>6</dim>
|
| 1626 |
+
<dim>6</dim>
|
| 1627 |
+
</port>
|
| 1628 |
+
</output>
|
| 1629 |
+
</layer>
|
| 1630 |
+
<layer id="87" name="86/WithoutBiases" type="GroupConvolution" version="opset1">
|
| 1631 |
+
<data strides="1, 1" pads_begin="0, 0" pads_end="0, 0" dilations="1, 1" auto_pad="explicit" />
|
| 1632 |
+
<input>
|
| 1633 |
+
<port id="0" precision="FP32">
|
| 1634 |
+
<dim>1</dim>
|
| 1635 |
+
<dim>128</dim>
|
| 1636 |
+
<dim>6</dim>
|
| 1637 |
+
<dim>6</dim>
|
| 1638 |
+
</port>
|
| 1639 |
+
<port id="1" precision="FP32">
|
| 1640 |
+
<dim>128</dim>
|
| 1641 |
+
<dim>1</dim>
|
| 1642 |
+
<dim>1</dim>
|
| 1643 |
+
<dim>6</dim>
|
| 1644 |
+
<dim>6</dim>
|
| 1645 |
+
</port>
|
| 1646 |
+
</input>
|
| 1647 |
+
<output>
|
| 1648 |
+
<port id="2" precision="FP32">
|
| 1649 |
+
<dim>1</dim>
|
| 1650 |
+
<dim>128</dim>
|
| 1651 |
+
<dim>1</dim>
|
| 1652 |
+
<dim>1</dim>
|
| 1653 |
+
</port>
|
| 1654 |
+
</output>
|
| 1655 |
+
</layer>
|
| 1656 |
+
<layer id="88" name="Reshape_243_compressed" type="Const" version="opset1">
|
| 1657 |
+
<data element_type="f16" shape="1, 128, 1, 1" offset="279190" size="256" />
|
| 1658 |
+
<output>
|
| 1659 |
+
<port id="0" precision="FP16">
|
| 1660 |
+
<dim>1</dim>
|
| 1661 |
+
<dim>128</dim>
|
| 1662 |
+
<dim>1</dim>
|
| 1663 |
+
<dim>1</dim>
|
| 1664 |
+
</port>
|
| 1665 |
+
</output>
|
| 1666 |
+
</layer>
|
| 1667 |
+
<layer id="89" name="Reshape_243" type="Convert" version="opset1">
|
| 1668 |
+
<data destination_type="f32" />
|
| 1669 |
+
<rt_info>
|
| 1670 |
+
<attribute name="decompression" version="0" />
|
| 1671 |
+
</rt_info>
|
| 1672 |
+
<input>
|
| 1673 |
+
<port id="0" precision="FP16">
|
| 1674 |
+
<dim>1</dim>
|
| 1675 |
+
<dim>128</dim>
|
| 1676 |
+
<dim>1</dim>
|
| 1677 |
+
<dim>1</dim>
|
| 1678 |
+
</port>
|
| 1679 |
+
</input>
|
| 1680 |
+
<output>
|
| 1681 |
+
<port id="1" precision="FP32">
|
| 1682 |
+
<dim>1</dim>
|
| 1683 |
+
<dim>128</dim>
|
| 1684 |
+
<dim>1</dim>
|
| 1685 |
+
<dim>1</dim>
|
| 1686 |
+
</port>
|
| 1687 |
+
</output>
|
| 1688 |
+
</layer>
|
| 1689 |
+
<layer id="90" name="86" type="Add" version="opset1">
|
| 1690 |
+
<data auto_broadcast="numpy" />
|
| 1691 |
+
<input>
|
| 1692 |
+
<port id="0" precision="FP32">
|
| 1693 |
+
<dim>1</dim>
|
| 1694 |
+
<dim>128</dim>
|
| 1695 |
+
<dim>1</dim>
|
| 1696 |
+
<dim>1</dim>
|
| 1697 |
+
</port>
|
| 1698 |
+
<port id="1" precision="FP32">
|
| 1699 |
+
<dim>1</dim>
|
| 1700 |
+
<dim>128</dim>
|
| 1701 |
+
<dim>1</dim>
|
| 1702 |
+
<dim>1</dim>
|
| 1703 |
+
</port>
|
| 1704 |
+
</input>
|
| 1705 |
+
<output>
|
| 1706 |
+
<port id="2" precision="FP32" names="86">
|
| 1707 |
+
<dim>1</dim>
|
| 1708 |
+
<dim>128</dim>
|
| 1709 |
+
<dim>1</dim>
|
| 1710 |
+
<dim>1</dim>
|
| 1711 |
+
</port>
|
| 1712 |
+
</output>
|
| 1713 |
+
</layer>
|
| 1714 |
+
<layer id="91" name="48_compressed" type="Const" version="opset1">
|
| 1715 |
+
<data element_type="f16" shape="1" offset="279446" size="2" />
|
| 1716 |
+
<output>
|
| 1717 |
+
<port id="0" precision="FP16">
|
| 1718 |
+
<dim>1</dim>
|
| 1719 |
+
</port>
|
| 1720 |
+
</output>
|
| 1721 |
+
</layer>
|
| 1722 |
+
<layer id="92" name="48" type="Convert" version="opset1">
|
| 1723 |
+
<data destination_type="f32" />
|
| 1724 |
+
<rt_info>
|
| 1725 |
+
<attribute name="decompression" version="0" />
|
| 1726 |
+
</rt_info>
|
| 1727 |
+
<input>
|
| 1728 |
+
<port id="0" precision="FP16">
|
| 1729 |
+
<dim>1</dim>
|
| 1730 |
+
</port>
|
| 1731 |
+
</input>
|
| 1732 |
+
<output>
|
| 1733 |
+
<port id="1" precision="FP32" names="48">
|
| 1734 |
+
<dim>1</dim>
|
| 1735 |
+
</port>
|
| 1736 |
+
</output>
|
| 1737 |
+
</layer>
|
| 1738 |
+
<layer id="93" name="87" type="PReLU" version="opset1">
|
| 1739 |
+
<input>
|
| 1740 |
+
<port id="0" precision="FP32">
|
| 1741 |
+
<dim>1</dim>
|
| 1742 |
+
<dim>128</dim>
|
| 1743 |
+
<dim>1</dim>
|
| 1744 |
+
<dim>1</dim>
|
| 1745 |
+
</port>
|
| 1746 |
+
<port id="1" precision="FP32">
|
| 1747 |
+
<dim>1</dim>
|
| 1748 |
+
</port>
|
| 1749 |
+
</input>
|
| 1750 |
+
<output>
|
| 1751 |
+
<port id="2" precision="FP32" names="87">
|
| 1752 |
+
<dim>1</dim>
|
| 1753 |
+
<dim>128</dim>
|
| 1754 |
+
<dim>1</dim>
|
| 1755 |
+
<dim>1</dim>
|
| 1756 |
+
</port>
|
| 1757 |
+
</output>
|
| 1758 |
+
</layer>
|
| 1759 |
+
<layer id="94" name="Constant_1990_compressed" type="Const" version="opset1">
|
| 1760 |
+
<data element_type="f16" shape="1, 128, 1, 1" offset="279448" size="256" />
|
| 1761 |
+
<output>
|
| 1762 |
+
<port id="0" precision="FP16">
|
| 1763 |
+
<dim>1</dim>
|
| 1764 |
+
<dim>128</dim>
|
| 1765 |
+
<dim>1</dim>
|
| 1766 |
+
<dim>1</dim>
|
| 1767 |
+
</port>
|
| 1768 |
+
</output>
|
| 1769 |
+
</layer>
|
| 1770 |
+
<layer id="95" name="Constant_1990" type="Convert" version="opset1">
|
| 1771 |
+
<data destination_type="f32" />
|
| 1772 |
+
<rt_info>
|
| 1773 |
+
<attribute name="decompression" version="0" />
|
| 1774 |
+
</rt_info>
|
| 1775 |
+
<input>
|
| 1776 |
+
<port id="0" precision="FP16">
|
| 1777 |
+
<dim>1</dim>
|
| 1778 |
+
<dim>128</dim>
|
| 1779 |
+
<dim>1</dim>
|
| 1780 |
+
<dim>1</dim>
|
| 1781 |
+
</port>
|
| 1782 |
+
</input>
|
| 1783 |
+
<output>
|
| 1784 |
+
<port id="1" precision="FP32">
|
| 1785 |
+
<dim>1</dim>
|
| 1786 |
+
<dim>128</dim>
|
| 1787 |
+
<dim>1</dim>
|
| 1788 |
+
<dim>1</dim>
|
| 1789 |
+
</port>
|
| 1790 |
+
</output>
|
| 1791 |
+
</layer>
|
| 1792 |
+
<layer id="96" name="Multiply_2105" type="Multiply" version="opset1">
|
| 1793 |
+
<data auto_broadcast="numpy" />
|
| 1794 |
+
<input>
|
| 1795 |
+
<port id="0" precision="FP32">
|
| 1796 |
+
<dim>1</dim>
|
| 1797 |
+
<dim>128</dim>
|
| 1798 |
+
<dim>1</dim>
|
| 1799 |
+
<dim>1</dim>
|
| 1800 |
+
</port>
|
| 1801 |
+
<port id="1" precision="FP32">
|
| 1802 |
+
<dim>1</dim>
|
| 1803 |
+
<dim>128</dim>
|
| 1804 |
+
<dim>1</dim>
|
| 1805 |
+
<dim>1</dim>
|
| 1806 |
+
</port>
|
| 1807 |
+
</input>
|
| 1808 |
+
<output>
|
| 1809 |
+
<port id="2" precision="FP32">
|
| 1810 |
+
<dim>1</dim>
|
| 1811 |
+
<dim>128</dim>
|
| 1812 |
+
<dim>1</dim>
|
| 1813 |
+
<dim>1</dim>
|
| 1814 |
+
</port>
|
| 1815 |
+
</output>
|
| 1816 |
+
</layer>
|
| 1817 |
+
<layer id="97" name="Constant_2110_compressed" type="Const" version="opset1">
|
| 1818 |
+
<data element_type="f16" shape="1, 128, 1, 1" offset="279704" size="256" />
|
| 1819 |
+
<output>
|
| 1820 |
+
<port id="0" precision="FP16">
|
| 1821 |
+
<dim>1</dim>
|
| 1822 |
+
<dim>128</dim>
|
| 1823 |
+
<dim>1</dim>
|
| 1824 |
+
<dim>1</dim>
|
| 1825 |
+
</port>
|
| 1826 |
+
</output>
|
| 1827 |
+
</layer>
|
| 1828 |
+
<layer id="98" name="Constant_2110" type="Convert" version="opset1">
|
| 1829 |
+
<data destination_type="f32" />
|
| 1830 |
+
<rt_info>
|
| 1831 |
+
<attribute name="decompression" version="0" />
|
| 1832 |
+
</rt_info>
|
| 1833 |
+
<input>
|
| 1834 |
+
<port id="0" precision="FP16">
|
| 1835 |
+
<dim>1</dim>
|
| 1836 |
+
<dim>128</dim>
|
| 1837 |
+
<dim>1</dim>
|
| 1838 |
+
<dim>1</dim>
|
| 1839 |
+
</port>
|
| 1840 |
+
</input>
|
| 1841 |
+
<output>
|
| 1842 |
+
<port id="1" precision="FP32">
|
| 1843 |
+
<dim>1</dim>
|
| 1844 |
+
<dim>128</dim>
|
| 1845 |
+
<dim>1</dim>
|
| 1846 |
+
<dim>1</dim>
|
| 1847 |
+
</port>
|
| 1848 |
+
</output>
|
| 1849 |
+
</layer>
|
| 1850 |
+
<layer id="99" name="88" type="Add" version="opset1">
|
| 1851 |
+
<data auto_broadcast="numpy" />
|
| 1852 |
+
<input>
|
| 1853 |
+
<port id="0" precision="FP32">
|
| 1854 |
+
<dim>1</dim>
|
| 1855 |
+
<dim>128</dim>
|
| 1856 |
+
<dim>1</dim>
|
| 1857 |
+
<dim>1</dim>
|
| 1858 |
+
</port>
|
| 1859 |
+
<port id="1" precision="FP32">
|
| 1860 |
+
<dim>1</dim>
|
| 1861 |
+
<dim>128</dim>
|
| 1862 |
+
<dim>1</dim>
|
| 1863 |
+
<dim>1</dim>
|
| 1864 |
+
</port>
|
| 1865 |
+
</input>
|
| 1866 |
+
<output>
|
| 1867 |
+
<port id="2" precision="FP32" names="88">
|
| 1868 |
+
<dim>1</dim>
|
| 1869 |
+
<dim>128</dim>
|
| 1870 |
+
<dim>1</dim>
|
| 1871 |
+
<dim>1</dim>
|
| 1872 |
+
</port>
|
| 1873 |
+
</output>
|
| 1874 |
+
</layer>
|
| 1875 |
+
<layer id="100" name="54_compressed" type="Const" version="opset1">
|
| 1876 |
+
<data element_type="f16" shape="256, 128, 1, 1" offset="279960" size="65536" />
|
| 1877 |
+
<output>
|
| 1878 |
+
<port id="0" precision="FP16">
|
| 1879 |
+
<dim>256</dim>
|
| 1880 |
+
<dim>128</dim>
|
| 1881 |
+
<dim>1</dim>
|
| 1882 |
+
<dim>1</dim>
|
| 1883 |
+
</port>
|
| 1884 |
+
</output>
|
| 1885 |
+
</layer>
|
| 1886 |
+
<layer id="101" name="54" type="Convert" version="opset1">
|
| 1887 |
+
<data destination_type="f32" />
|
| 1888 |
+
<rt_info>
|
| 1889 |
+
<attribute name="decompression" version="0" />
|
| 1890 |
+
</rt_info>
|
| 1891 |
+
<input>
|
| 1892 |
+
<port id="0" precision="FP16">
|
| 1893 |
+
<dim>256</dim>
|
| 1894 |
+
<dim>128</dim>
|
| 1895 |
+
<dim>1</dim>
|
| 1896 |
+
<dim>1</dim>
|
| 1897 |
+
</port>
|
| 1898 |
+
</input>
|
| 1899 |
+
<output>
|
| 1900 |
+
<port id="1" precision="FP32" names="54">
|
| 1901 |
+
<dim>256</dim>
|
| 1902 |
+
<dim>128</dim>
|
| 1903 |
+
<dim>1</dim>
|
| 1904 |
+
<dim>1</dim>
|
| 1905 |
+
</port>
|
| 1906 |
+
</output>
|
| 1907 |
+
</layer>
|
| 1908 |
+
<layer id="102" name="89/WithoutBiases" type="Convolution" version="opset1">
|
| 1909 |
+
<data strides="1, 1" dilations="1, 1" pads_begin="0, 0" pads_end="0, 0" auto_pad="explicit" />
|
| 1910 |
+
<input>
|
| 1911 |
+
<port id="0" precision="FP32">
|
| 1912 |
+
<dim>1</dim>
|
| 1913 |
+
<dim>128</dim>
|
| 1914 |
+
<dim>1</dim>
|
| 1915 |
+
<dim>1</dim>
|
| 1916 |
+
</port>
|
| 1917 |
+
<port id="1" precision="FP32">
|
| 1918 |
+
<dim>256</dim>
|
| 1919 |
+
<dim>128</dim>
|
| 1920 |
+
<dim>1</dim>
|
| 1921 |
+
<dim>1</dim>
|
| 1922 |
+
</port>
|
| 1923 |
+
</input>
|
| 1924 |
+
<output>
|
| 1925 |
+
<port id="2" precision="FP32">
|
| 1926 |
+
<dim>1</dim>
|
| 1927 |
+
<dim>256</dim>
|
| 1928 |
+
<dim>1</dim>
|
| 1929 |
+
<dim>1</dim>
|
| 1930 |
+
</port>
|
| 1931 |
+
</output>
|
| 1932 |
+
</layer>
|
| 1933 |
+
<layer id="103" name="Reshape_264_compressed" type="Const" version="opset1">
|
| 1934 |
+
<data element_type="f16" shape="1, 256, 1, 1" offset="345496" size="512" />
|
| 1935 |
+
<output>
|
| 1936 |
+
<port id="0" precision="FP16">
|
| 1937 |
+
<dim>1</dim>
|
| 1938 |
+
<dim>256</dim>
|
| 1939 |
+
<dim>1</dim>
|
| 1940 |
+
<dim>1</dim>
|
| 1941 |
+
</port>
|
| 1942 |
+
</output>
|
| 1943 |
+
</layer>
|
| 1944 |
+
<layer id="104" name="Reshape_264" type="Convert" version="opset1">
|
| 1945 |
+
<data destination_type="f32" />
|
| 1946 |
+
<rt_info>
|
| 1947 |
+
<attribute name="decompression" version="0" />
|
| 1948 |
+
</rt_info>
|
| 1949 |
+
<input>
|
| 1950 |
+
<port id="0" precision="FP16">
|
| 1951 |
+
<dim>1</dim>
|
| 1952 |
+
<dim>256</dim>
|
| 1953 |
+
<dim>1</dim>
|
| 1954 |
+
<dim>1</dim>
|
| 1955 |
+
</port>
|
| 1956 |
+
</input>
|
| 1957 |
+
<output>
|
| 1958 |
+
<port id="1" precision="FP32">
|
| 1959 |
+
<dim>1</dim>
|
| 1960 |
+
<dim>256</dim>
|
| 1961 |
+
<dim>1</dim>
|
| 1962 |
+
<dim>1</dim>
|
| 1963 |
+
</port>
|
| 1964 |
+
</output>
|
| 1965 |
+
</layer>
|
| 1966 |
+
<layer id="105" name="89" type="Add" version="opset1">
|
| 1967 |
+
<data auto_broadcast="numpy" />
|
| 1968 |
+
<input>
|
| 1969 |
+
<port id="0" precision="FP32">
|
| 1970 |
+
<dim>1</dim>
|
| 1971 |
+
<dim>256</dim>
|
| 1972 |
+
<dim>1</dim>
|
| 1973 |
+
<dim>1</dim>
|
| 1974 |
+
</port>
|
| 1975 |
+
<port id="1" precision="FP32">
|
| 1976 |
+
<dim>1</dim>
|
| 1977 |
+
<dim>256</dim>
|
| 1978 |
+
<dim>1</dim>
|
| 1979 |
+
<dim>1</dim>
|
| 1980 |
+
</port>
|
| 1981 |
+
</input>
|
| 1982 |
+
<output>
|
| 1983 |
+
<port id="2" precision="FP32" names="89">
|
| 1984 |
+
<dim>1</dim>
|
| 1985 |
+
<dim>256</dim>
|
| 1986 |
+
<dim>1</dim>
|
| 1987 |
+
<dim>1</dim>
|
| 1988 |
+
</port>
|
| 1989 |
+
</output>
|
| 1990 |
+
</layer>
|
| 1991 |
+
<layer id="106" name="56_compressed" type="Const" version="opset1">
|
| 1992 |
+
<data element_type="f16" shape="1" offset="346008" size="2" />
|
| 1993 |
+
<output>
|
| 1994 |
+
<port id="0" precision="FP16">
|
| 1995 |
+
<dim>1</dim>
|
| 1996 |
+
</port>
|
| 1997 |
+
</output>
|
| 1998 |
+
</layer>
|
| 1999 |
+
<layer id="107" name="56" type="Convert" version="opset1">
|
| 2000 |
+
<data destination_type="f32" />
|
| 2001 |
+
<rt_info>
|
| 2002 |
+
<attribute name="decompression" version="0" />
|
| 2003 |
+
</rt_info>
|
| 2004 |
+
<input>
|
| 2005 |
+
<port id="0" precision="FP16">
|
| 2006 |
+
<dim>1</dim>
|
| 2007 |
+
</port>
|
| 2008 |
+
</input>
|
| 2009 |
+
<output>
|
| 2010 |
+
<port id="1" precision="FP32" names="56">
|
| 2011 |
+
<dim>1</dim>
|
| 2012 |
+
</port>
|
| 2013 |
+
</output>
|
| 2014 |
+
</layer>
|
| 2015 |
+
<layer id="108" name="90" type="PReLU" version="opset1">
|
| 2016 |
+
<input>
|
| 2017 |
+
<port id="0" precision="FP32">
|
| 2018 |
+
<dim>1</dim>
|
| 2019 |
+
<dim>256</dim>
|
| 2020 |
+
<dim>1</dim>
|
| 2021 |
+
<dim>1</dim>
|
| 2022 |
+
</port>
|
| 2023 |
+
<port id="1" precision="FP32">
|
| 2024 |
+
<dim>1</dim>
|
| 2025 |
+
</port>
|
| 2026 |
+
</input>
|
| 2027 |
+
<output>
|
| 2028 |
+
<port id="2" precision="FP32" names="90">
|
| 2029 |
+
<dim>1</dim>
|
| 2030 |
+
<dim>256</dim>
|
| 2031 |
+
<dim>1</dim>
|
| 2032 |
+
<dim>1</dim>
|
| 2033 |
+
</port>
|
| 2034 |
+
</output>
|
| 2035 |
+
</layer>
|
| 2036 |
+
<layer id="109" name="Constant_2056_compressed" type="Const" version="opset1">
|
| 2037 |
+
<data element_type="f16" shape="1, 256, 1, 1" offset="346010" size="512" />
|
| 2038 |
+
<output>
|
| 2039 |
+
<port id="0" precision="FP16">
|
| 2040 |
+
<dim>1</dim>
|
| 2041 |
+
<dim>256</dim>
|
| 2042 |
+
<dim>1</dim>
|
| 2043 |
+
<dim>1</dim>
|
| 2044 |
+
</port>
|
| 2045 |
+
</output>
|
| 2046 |
+
</layer>
|
| 2047 |
+
<layer id="110" name="Constant_2056" type="Convert" version="opset1">
|
| 2048 |
+
<data destination_type="f32" />
|
| 2049 |
+
<rt_info>
|
| 2050 |
+
<attribute name="decompression" version="0" />
|
| 2051 |
+
</rt_info>
|
| 2052 |
+
<input>
|
| 2053 |
+
<port id="0" precision="FP16">
|
| 2054 |
+
<dim>1</dim>
|
| 2055 |
+
<dim>256</dim>
|
| 2056 |
+
<dim>1</dim>
|
| 2057 |
+
<dim>1</dim>
|
| 2058 |
+
</port>
|
| 2059 |
+
</input>
|
| 2060 |
+
<output>
|
| 2061 |
+
<port id="1" precision="FP32">
|
| 2062 |
+
<dim>1</dim>
|
| 2063 |
+
<dim>256</dim>
|
| 2064 |
+
<dim>1</dim>
|
| 2065 |
+
<dim>1</dim>
|
| 2066 |
+
</port>
|
| 2067 |
+
</output>
|
| 2068 |
+
</layer>
|
| 2069 |
+
<layer id="111" name="Multiply_2112" type="Multiply" version="opset1">
|
| 2070 |
+
<data auto_broadcast="numpy" />
|
| 2071 |
+
<input>
|
| 2072 |
+
<port id="0" precision="FP32">
|
| 2073 |
+
<dim>1</dim>
|
| 2074 |
+
<dim>256</dim>
|
| 2075 |
+
<dim>1</dim>
|
| 2076 |
+
<dim>1</dim>
|
| 2077 |
+
</port>
|
| 2078 |
+
<port id="1" precision="FP32">
|
| 2079 |
+
<dim>1</dim>
|
| 2080 |
+
<dim>256</dim>
|
| 2081 |
+
<dim>1</dim>
|
| 2082 |
+
<dim>1</dim>
|
| 2083 |
+
</port>
|
| 2084 |
+
</input>
|
| 2085 |
+
<output>
|
| 2086 |
+
<port id="2" precision="FP32">
|
| 2087 |
+
<dim>1</dim>
|
| 2088 |
+
<dim>256</dim>
|
| 2089 |
+
<dim>1</dim>
|
| 2090 |
+
<dim>1</dim>
|
| 2091 |
+
</port>
|
| 2092 |
+
</output>
|
| 2093 |
+
</layer>
|
| 2094 |
+
<layer id="112" name="Constant_2117_compressed" type="Const" version="opset1">
|
| 2095 |
+
<data element_type="f16" shape="1, 256, 1, 1" offset="346522" size="512" />
|
| 2096 |
+
<output>
|
| 2097 |
+
<port id="0" precision="FP16">
|
| 2098 |
+
<dim>1</dim>
|
| 2099 |
+
<dim>256</dim>
|
| 2100 |
+
<dim>1</dim>
|
| 2101 |
+
<dim>1</dim>
|
| 2102 |
+
</port>
|
| 2103 |
+
</output>
|
| 2104 |
+
</layer>
|
| 2105 |
+
<layer id="113" name="Constant_2117" type="Convert" version="opset1">
|
| 2106 |
+
<data destination_type="f32" />
|
| 2107 |
+
<rt_info>
|
| 2108 |
+
<attribute name="decompression" version="0" />
|
| 2109 |
+
</rt_info>
|
| 2110 |
+
<input>
|
| 2111 |
+
<port id="0" precision="FP16">
|
| 2112 |
+
<dim>1</dim>
|
| 2113 |
+
<dim>256</dim>
|
| 2114 |
+
<dim>1</dim>
|
| 2115 |
+
<dim>1</dim>
|
| 2116 |
+
</port>
|
| 2117 |
+
</input>
|
| 2118 |
+
<output>
|
| 2119 |
+
<port id="1" precision="FP32">
|
| 2120 |
+
<dim>1</dim>
|
| 2121 |
+
<dim>256</dim>
|
| 2122 |
+
<dim>1</dim>
|
| 2123 |
+
<dim>1</dim>
|
| 2124 |
+
</port>
|
| 2125 |
+
</output>
|
| 2126 |
+
</layer>
|
| 2127 |
+
<layer id="114" name="91" type="Add" version="opset1">
|
| 2128 |
+
<data auto_broadcast="numpy" />
|
| 2129 |
+
<input>
|
| 2130 |
+
<port id="0" precision="FP32">
|
| 2131 |
+
<dim>1</dim>
|
| 2132 |
+
<dim>256</dim>
|
| 2133 |
+
<dim>1</dim>
|
| 2134 |
+
<dim>1</dim>
|
| 2135 |
+
</port>
|
| 2136 |
+
<port id="1" precision="FP32">
|
| 2137 |
+
<dim>1</dim>
|
| 2138 |
+
<dim>256</dim>
|
| 2139 |
+
<dim>1</dim>
|
| 2140 |
+
<dim>1</dim>
|
| 2141 |
+
</port>
|
| 2142 |
+
</input>
|
| 2143 |
+
<output>
|
| 2144 |
+
<port id="2" precision="FP32" names="91">
|
| 2145 |
+
<dim>1</dim>
|
| 2146 |
+
<dim>256</dim>
|
| 2147 |
+
<dim>1</dim>
|
| 2148 |
+
<dim>1</dim>
|
| 2149 |
+
</port>
|
| 2150 |
+
</output>
|
| 2151 |
+
</layer>
|
| 2152 |
+
<layer id="115" name="62_compressed" type="Const" version="opset1">
|
| 2153 |
+
<data element_type="f16" shape="64, 256, 1, 1" offset="347034" size="32768" />
|
| 2154 |
+
<output>
|
| 2155 |
+
<port id="0" precision="FP16">
|
| 2156 |
+
<dim>64</dim>
|
| 2157 |
+
<dim>256</dim>
|
| 2158 |
+
<dim>1</dim>
|
| 2159 |
+
<dim>1</dim>
|
| 2160 |
+
</port>
|
| 2161 |
+
</output>
|
| 2162 |
+
</layer>
|
| 2163 |
+
<layer id="116" name="62" type="Convert" version="opset1">
|
| 2164 |
+
<data destination_type="f32" />
|
| 2165 |
+
<rt_info>
|
| 2166 |
+
<attribute name="decompression" version="0" />
|
| 2167 |
+
</rt_info>
|
| 2168 |
+
<input>
|
| 2169 |
+
<port id="0" precision="FP16">
|
| 2170 |
+
<dim>64</dim>
|
| 2171 |
+
<dim>256</dim>
|
| 2172 |
+
<dim>1</dim>
|
| 2173 |
+
<dim>1</dim>
|
| 2174 |
+
</port>
|
| 2175 |
+
</input>
|
| 2176 |
+
<output>
|
| 2177 |
+
<port id="1" precision="FP32" names="62">
|
| 2178 |
+
<dim>64</dim>
|
| 2179 |
+
<dim>256</dim>
|
| 2180 |
+
<dim>1</dim>
|
| 2181 |
+
<dim>1</dim>
|
| 2182 |
+
</port>
|
| 2183 |
+
</output>
|
| 2184 |
+
</layer>
|
| 2185 |
+
<layer id="117" name="92/WithoutBiases" type="Convolution" version="opset1">
|
| 2186 |
+
<data strides="1, 1" dilations="1, 1" pads_begin="0, 0" pads_end="0, 0" auto_pad="explicit" />
|
| 2187 |
+
<input>
|
| 2188 |
+
<port id="0" precision="FP32">
|
| 2189 |
+
<dim>1</dim>
|
| 2190 |
+
<dim>256</dim>
|
| 2191 |
+
<dim>1</dim>
|
| 2192 |
+
<dim>1</dim>
|
| 2193 |
+
</port>
|
| 2194 |
+
<port id="1" precision="FP32">
|
| 2195 |
+
<dim>64</dim>
|
| 2196 |
+
<dim>256</dim>
|
| 2197 |
+
<dim>1</dim>
|
| 2198 |
+
<dim>1</dim>
|
| 2199 |
+
</port>
|
| 2200 |
+
</input>
|
| 2201 |
+
<output>
|
| 2202 |
+
<port id="2" precision="FP32">
|
| 2203 |
+
<dim>1</dim>
|
| 2204 |
+
<dim>64</dim>
|
| 2205 |
+
<dim>1</dim>
|
| 2206 |
+
<dim>1</dim>
|
| 2207 |
+
</port>
|
| 2208 |
+
</output>
|
| 2209 |
+
</layer>
|
| 2210 |
+
<layer id="118" name="Reshape_285_compressed" type="Const" version="opset1">
|
| 2211 |
+
<data element_type="f16" shape="1, 64, 1, 1" offset="379802" size="128" />
|
| 2212 |
+
<output>
|
| 2213 |
+
<port id="0" precision="FP16">
|
| 2214 |
+
<dim>1</dim>
|
| 2215 |
+
<dim>64</dim>
|
| 2216 |
+
<dim>1</dim>
|
| 2217 |
+
<dim>1</dim>
|
| 2218 |
+
</port>
|
| 2219 |
+
</output>
|
| 2220 |
+
</layer>
|
| 2221 |
+
<layer id="119" name="Reshape_285" type="Convert" version="opset1">
|
| 2222 |
+
<data destination_type="f32" />
|
| 2223 |
+
<rt_info>
|
| 2224 |
+
<attribute name="decompression" version="0" />
|
| 2225 |
+
</rt_info>
|
| 2226 |
+
<input>
|
| 2227 |
+
<port id="0" precision="FP16">
|
| 2228 |
+
<dim>1</dim>
|
| 2229 |
+
<dim>64</dim>
|
| 2230 |
+
<dim>1</dim>
|
| 2231 |
+
<dim>1</dim>
|
| 2232 |
+
</port>
|
| 2233 |
+
</input>
|
| 2234 |
+
<output>
|
| 2235 |
+
<port id="1" precision="FP32">
|
| 2236 |
+
<dim>1</dim>
|
| 2237 |
+
<dim>64</dim>
|
| 2238 |
+
<dim>1</dim>
|
| 2239 |
+
<dim>1</dim>
|
| 2240 |
+
</port>
|
| 2241 |
+
</output>
|
| 2242 |
+
</layer>
|
| 2243 |
+
<layer id="120" name="92" type="Add" version="opset1">
|
| 2244 |
+
<data auto_broadcast="numpy" />
|
| 2245 |
+
<input>
|
| 2246 |
+
<port id="0" precision="FP32">
|
| 2247 |
+
<dim>1</dim>
|
| 2248 |
+
<dim>64</dim>
|
| 2249 |
+
<dim>1</dim>
|
| 2250 |
+
<dim>1</dim>
|
| 2251 |
+
</port>
|
| 2252 |
+
<port id="1" precision="FP32">
|
| 2253 |
+
<dim>1</dim>
|
| 2254 |
+
<dim>64</dim>
|
| 2255 |
+
<dim>1</dim>
|
| 2256 |
+
<dim>1</dim>
|
| 2257 |
+
</port>
|
| 2258 |
+
</input>
|
| 2259 |
+
<output>
|
| 2260 |
+
<port id="2" precision="FP32" names="92">
|
| 2261 |
+
<dim>1</dim>
|
| 2262 |
+
<dim>64</dim>
|
| 2263 |
+
<dim>1</dim>
|
| 2264 |
+
<dim>1</dim>
|
| 2265 |
+
</port>
|
| 2266 |
+
</output>
|
| 2267 |
+
</layer>
|
| 2268 |
+
<layer id="121" name="64_compressed" type="Const" version="opset1">
|
| 2269 |
+
<data element_type="f16" shape="1" offset="379930" size="2" />
|
| 2270 |
+
<output>
|
| 2271 |
+
<port id="0" precision="FP16">
|
| 2272 |
+
<dim>1</dim>
|
| 2273 |
+
</port>
|
| 2274 |
+
</output>
|
| 2275 |
+
</layer>
|
| 2276 |
+
<layer id="122" name="64" type="Convert" version="opset1">
|
| 2277 |
+
<data destination_type="f32" />
|
| 2278 |
+
<rt_info>
|
| 2279 |
+
<attribute name="decompression" version="0" />
|
| 2280 |
+
</rt_info>
|
| 2281 |
+
<input>
|
| 2282 |
+
<port id="0" precision="FP16">
|
| 2283 |
+
<dim>1</dim>
|
| 2284 |
+
</port>
|
| 2285 |
+
</input>
|
| 2286 |
+
<output>
|
| 2287 |
+
<port id="1" precision="FP32" names="64">
|
| 2288 |
+
<dim>1</dim>
|
| 2289 |
+
</port>
|
| 2290 |
+
</output>
|
| 2291 |
+
</layer>
|
| 2292 |
+
<layer id="123" name="93" type="PReLU" version="opset1">
|
| 2293 |
+
<input>
|
| 2294 |
+
<port id="0" precision="FP32">
|
| 2295 |
+
<dim>1</dim>
|
| 2296 |
+
<dim>64</dim>
|
| 2297 |
+
<dim>1</dim>
|
| 2298 |
+
<dim>1</dim>
|
| 2299 |
+
</port>
|
| 2300 |
+
<port id="1" precision="FP32">
|
| 2301 |
+
<dim>1</dim>
|
| 2302 |
+
</port>
|
| 2303 |
+
</input>
|
| 2304 |
+
<output>
|
| 2305 |
+
<port id="2" precision="FP32" names="93">
|
| 2306 |
+
<dim>1</dim>
|
| 2307 |
+
<dim>64</dim>
|
| 2308 |
+
<dim>1</dim>
|
| 2309 |
+
<dim>1</dim>
|
| 2310 |
+
</port>
|
| 2311 |
+
</output>
|
| 2312 |
+
</layer>
|
| 2313 |
+
<layer id="124" name="65_compressed" type="Const" version="opset1">
|
| 2314 |
+
<data element_type="f16" shape="10, 64, 1, 1" offset="379932" size="1280" />
|
| 2315 |
+
<output>
|
| 2316 |
+
<port id="0" precision="FP16">
|
| 2317 |
+
<dim>10</dim>
|
| 2318 |
+
<dim>64</dim>
|
| 2319 |
+
<dim>1</dim>
|
| 2320 |
+
<dim>1</dim>
|
| 2321 |
+
</port>
|
| 2322 |
+
</output>
|
| 2323 |
+
</layer>
|
| 2324 |
+
<layer id="125" name="65" type="Convert" version="opset1">
|
| 2325 |
+
<data destination_type="f32" />
|
| 2326 |
+
<rt_info>
|
| 2327 |
+
<attribute name="decompression" version="0" />
|
| 2328 |
+
</rt_info>
|
| 2329 |
+
<input>
|
| 2330 |
+
<port id="0" precision="FP16">
|
| 2331 |
+
<dim>10</dim>
|
| 2332 |
+
<dim>64</dim>
|
| 2333 |
+
<dim>1</dim>
|
| 2334 |
+
<dim>1</dim>
|
| 2335 |
+
</port>
|
| 2336 |
+
</input>
|
| 2337 |
+
<output>
|
| 2338 |
+
<port id="1" precision="FP32" names="65">
|
| 2339 |
+
<dim>10</dim>
|
| 2340 |
+
<dim>64</dim>
|
| 2341 |
+
<dim>1</dim>
|
| 2342 |
+
<dim>1</dim>
|
| 2343 |
+
</port>
|
| 2344 |
+
</output>
|
| 2345 |
+
</layer>
|
| 2346 |
+
<layer id="126" name="94/WithoutBiases" type="Convolution" version="opset1">
|
| 2347 |
+
<data strides="1, 1" dilations="1, 1" pads_begin="0, 0" pads_end="0, 0" auto_pad="explicit" />
|
| 2348 |
+
<input>
|
| 2349 |
+
<port id="0" precision="FP32">
|
| 2350 |
+
<dim>1</dim>
|
| 2351 |
+
<dim>64</dim>
|
| 2352 |
+
<dim>1</dim>
|
| 2353 |
+
<dim>1</dim>
|
| 2354 |
+
</port>
|
| 2355 |
+
<port id="1" precision="FP32">
|
| 2356 |
+
<dim>10</dim>
|
| 2357 |
+
<dim>64</dim>
|
| 2358 |
+
<dim>1</dim>
|
| 2359 |
+
<dim>1</dim>
|
| 2360 |
+
</port>
|
| 2361 |
+
</input>
|
| 2362 |
+
<output>
|
| 2363 |
+
<port id="2" precision="FP32">
|
| 2364 |
+
<dim>1</dim>
|
| 2365 |
+
<dim>10</dim>
|
| 2366 |
+
<dim>1</dim>
|
| 2367 |
+
<dim>1</dim>
|
| 2368 |
+
</port>
|
| 2369 |
+
</output>
|
| 2370 |
+
</layer>
|
| 2371 |
+
<layer id="127" name="Reshape_301_compressed" type="Const" version="opset1">
|
| 2372 |
+
<data element_type="f16" shape="1, 10, 1, 1" offset="381212" size="20" />
|
| 2373 |
+
<output>
|
| 2374 |
+
<port id="0" precision="FP16">
|
| 2375 |
+
<dim>1</dim>
|
| 2376 |
+
<dim>10</dim>
|
| 2377 |
+
<dim>1</dim>
|
| 2378 |
+
<dim>1</dim>
|
| 2379 |
+
</port>
|
| 2380 |
+
</output>
|
| 2381 |
+
</layer>
|
| 2382 |
+
<layer id="128" name="Reshape_301" type="Convert" version="opset1">
|
| 2383 |
+
<data destination_type="f32" />
|
| 2384 |
+
<rt_info>
|
| 2385 |
+
<attribute name="decompression" version="0" />
|
| 2386 |
+
</rt_info>
|
| 2387 |
+
<input>
|
| 2388 |
+
<port id="0" precision="FP16">
|
| 2389 |
+
<dim>1</dim>
|
| 2390 |
+
<dim>10</dim>
|
| 2391 |
+
<dim>1</dim>
|
| 2392 |
+
<dim>1</dim>
|
| 2393 |
+
</port>
|
| 2394 |
+
</input>
|
| 2395 |
+
<output>
|
| 2396 |
+
<port id="1" precision="FP32">
|
| 2397 |
+
<dim>1</dim>
|
| 2398 |
+
<dim>10</dim>
|
| 2399 |
+
<dim>1</dim>
|
| 2400 |
+
<dim>1</dim>
|
| 2401 |
+
</port>
|
| 2402 |
+
</output>
|
| 2403 |
+
</layer>
|
| 2404 |
+
<layer id="129" name="94" type="Add" version="opset1">
|
| 2405 |
+
<data auto_broadcast="numpy" />
|
| 2406 |
+
<input>
|
| 2407 |
+
<port id="0" precision="FP32">
|
| 2408 |
+
<dim>1</dim>
|
| 2409 |
+
<dim>10</dim>
|
| 2410 |
+
<dim>1</dim>
|
| 2411 |
+
<dim>1</dim>
|
| 2412 |
+
</port>
|
| 2413 |
+
<port id="1" precision="FP32">
|
| 2414 |
+
<dim>1</dim>
|
| 2415 |
+
<dim>10</dim>
|
| 2416 |
+
<dim>1</dim>
|
| 2417 |
+
<dim>1</dim>
|
| 2418 |
+
</port>
|
| 2419 |
+
</input>
|
| 2420 |
+
<output>
|
| 2421 |
+
<port id="2" precision="FP32" names="94">
|
| 2422 |
+
<dim>1</dim>
|
| 2423 |
+
<dim>10</dim>
|
| 2424 |
+
<dim>1</dim>
|
| 2425 |
+
<dim>1</dim>
|
| 2426 |
+
</port>
|
| 2427 |
+
</output>
|
| 2428 |
+
</layer>
|
| 2429 |
+
<layer id="130" name="95" type="Sigmoid" version="opset1">
|
| 2430 |
+
<input>
|
| 2431 |
+
<port id="0" precision="FP32">
|
| 2432 |
+
<dim>1</dim>
|
| 2433 |
+
<dim>10</dim>
|
| 2434 |
+
<dim>1</dim>
|
| 2435 |
+
<dim>1</dim>
|
| 2436 |
+
</port>
|
| 2437 |
+
</input>
|
| 2438 |
+
<output>
|
| 2439 |
+
<port id="1" precision="FP32" names="95">
|
| 2440 |
+
<dim>1</dim>
|
| 2441 |
+
<dim>10</dim>
|
| 2442 |
+
<dim>1</dim>
|
| 2443 |
+
<dim>1</dim>
|
| 2444 |
+
</port>
|
| 2445 |
+
</output>
|
| 2446 |
+
</layer>
|
| 2447 |
+
<layer id="131" name="95/sink_port_0" type="Result" version="opset1">
|
| 2448 |
+
<input>
|
| 2449 |
+
<port id="0" precision="FP32">
|
| 2450 |
+
<dim>1</dim>
|
| 2451 |
+
<dim>10</dim>
|
| 2452 |
+
<dim>1</dim>
|
| 2453 |
+
<dim>1</dim>
|
| 2454 |
+
</port>
|
| 2455 |
+
</input>
|
| 2456 |
+
</layer>
|
| 2457 |
+
</layers>
|
| 2458 |
+
<edges>
|
| 2459 |
+
<edge from-layer="0" from-port="0" to-layer="3" to-port="0" />
|
| 2460 |
+
<edge from-layer="1" from-port="0" to-layer="2" to-port="0" />
|
| 2461 |
+
<edge from-layer="2" from-port="1" to-layer="3" to-port="1" />
|
| 2462 |
+
<edge from-layer="3" from-port="2" to-layer="6" to-port="0" />
|
| 2463 |
+
<edge from-layer="4" from-port="0" to-layer="5" to-port="0" />
|
| 2464 |
+
<edge from-layer="5" from-port="1" to-layer="6" to-port="1" />
|
| 2465 |
+
<edge from-layer="6" from-port="2" to-layer="9" to-port="0" />
|
| 2466 |
+
<edge from-layer="7" from-port="0" to-layer="8" to-port="0" />
|
| 2467 |
+
<edge from-layer="8" from-port="1" to-layer="9" to-port="1" />
|
| 2468 |
+
<edge from-layer="9" from-port="2" to-layer="12" to-port="0" />
|
| 2469 |
+
<edge from-layer="10" from-port="0" to-layer="11" to-port="0" />
|
| 2470 |
+
<edge from-layer="11" from-port="1" to-layer="12" to-port="1" />
|
| 2471 |
+
<edge from-layer="12" from-port="2" to-layer="15" to-port="0" />
|
| 2472 |
+
<edge from-layer="13" from-port="0" to-layer="14" to-port="0" />
|
| 2473 |
+
<edge from-layer="14" from-port="1" to-layer="15" to-port="1" />
|
| 2474 |
+
<edge from-layer="15" from-port="2" to-layer="16" to-port="0" />
|
| 2475 |
+
<edge from-layer="16" from-port="1" to-layer="19" to-port="0" />
|
| 2476 |
+
<edge from-layer="17" from-port="0" to-layer="18" to-port="0" />
|
| 2477 |
+
<edge from-layer="18" from-port="1" to-layer="19" to-port="1" />
|
| 2478 |
+
<edge from-layer="19" from-port="2" to-layer="22" to-port="0" />
|
| 2479 |
+
<edge from-layer="20" from-port="0" to-layer="21" to-port="0" />
|
| 2480 |
+
<edge from-layer="21" from-port="1" to-layer="22" to-port="1" />
|
| 2481 |
+
<edge from-layer="22" from-port="2" to-layer="25" to-port="0" />
|
| 2482 |
+
<edge from-layer="23" from-port="0" to-layer="24" to-port="0" />
|
| 2483 |
+
<edge from-layer="24" from-port="1" to-layer="25" to-port="1" />
|
| 2484 |
+
<edge from-layer="25" from-port="2" to-layer="28" to-port="0" />
|
| 2485 |
+
<edge from-layer="26" from-port="0" to-layer="27" to-port="0" />
|
| 2486 |
+
<edge from-layer="27" from-port="1" to-layer="28" to-port="1" />
|
| 2487 |
+
<edge from-layer="28" from-port="2" to-layer="31" to-port="0" />
|
| 2488 |
+
<edge from-layer="29" from-port="0" to-layer="30" to-port="0" />
|
| 2489 |
+
<edge from-layer="30" from-port="1" to-layer="31" to-port="1" />
|
| 2490 |
+
<edge from-layer="31" from-port="2" to-layer="32" to-port="0" />
|
| 2491 |
+
<edge from-layer="32" from-port="1" to-layer="35" to-port="0" />
|
| 2492 |
+
<edge from-layer="33" from-port="0" to-layer="34" to-port="0" />
|
| 2493 |
+
<edge from-layer="34" from-port="1" to-layer="35" to-port="1" />
|
| 2494 |
+
<edge from-layer="35" from-port="2" to-layer="38" to-port="0" />
|
| 2495 |
+
<edge from-layer="36" from-port="0" to-layer="37" to-port="0" />
|
| 2496 |
+
<edge from-layer="37" from-port="1" to-layer="38" to-port="1" />
|
| 2497 |
+
<edge from-layer="38" from-port="2" to-layer="41" to-port="0" />
|
| 2498 |
+
<edge from-layer="39" from-port="0" to-layer="40" to-port="0" />
|
| 2499 |
+
<edge from-layer="40" from-port="1" to-layer="41" to-port="1" />
|
| 2500 |
+
<edge from-layer="41" from-port="2" to-layer="44" to-port="0" />
|
| 2501 |
+
<edge from-layer="42" from-port="0" to-layer="43" to-port="0" />
|
| 2502 |
+
<edge from-layer="43" from-port="1" to-layer="44" to-port="1" />
|
| 2503 |
+
<edge from-layer="44" from-port="2" to-layer="47" to-port="0" />
|
| 2504 |
+
<edge from-layer="45" from-port="0" to-layer="46" to-port="0" />
|
| 2505 |
+
<edge from-layer="46" from-port="1" to-layer="47" to-port="1" />
|
| 2506 |
+
<edge from-layer="47" from-port="2" to-layer="48" to-port="0" />
|
| 2507 |
+
<edge from-layer="48" from-port="1" to-layer="51" to-port="0" />
|
| 2508 |
+
<edge from-layer="49" from-port="0" to-layer="50" to-port="0" />
|
| 2509 |
+
<edge from-layer="50" from-port="1" to-layer="51" to-port="1" />
|
| 2510 |
+
<edge from-layer="51" from-port="2" to-layer="54" to-port="0" />
|
| 2511 |
+
<edge from-layer="52" from-port="0" to-layer="53" to-port="0" />
|
| 2512 |
+
<edge from-layer="53" from-port="1" to-layer="54" to-port="1" />
|
| 2513 |
+
<edge from-layer="54" from-port="2" to-layer="57" to-port="0" />
|
| 2514 |
+
<edge from-layer="55" from-port="0" to-layer="56" to-port="0" />
|
| 2515 |
+
<edge from-layer="56" from-port="1" to-layer="57" to-port="1" />
|
| 2516 |
+
<edge from-layer="57" from-port="2" to-layer="60" to-port="0" />
|
| 2517 |
+
<edge from-layer="58" from-port="0" to-layer="59" to-port="0" />
|
| 2518 |
+
<edge from-layer="59" from-port="1" to-layer="60" to-port="1" />
|
| 2519 |
+
<edge from-layer="60" from-port="2" to-layer="63" to-port="0" />
|
| 2520 |
+
<edge from-layer="61" from-port="0" to-layer="62" to-port="0" />
|
| 2521 |
+
<edge from-layer="62" from-port="1" to-layer="63" to-port="1" />
|
| 2522 |
+
<edge from-layer="63" from-port="2" to-layer="66" to-port="0" />
|
| 2523 |
+
<edge from-layer="64" from-port="0" to-layer="65" to-port="0" />
|
| 2524 |
+
<edge from-layer="65" from-port="1" to-layer="66" to-port="1" />
|
| 2525 |
+
<edge from-layer="66" from-port="2" to-layer="69" to-port="0" />
|
| 2526 |
+
<edge from-layer="67" from-port="0" to-layer="68" to-port="0" />
|
| 2527 |
+
<edge from-layer="68" from-port="1" to-layer="69" to-port="1" />
|
| 2528 |
+
<edge from-layer="69" from-port="2" to-layer="72" to-port="0" />
|
| 2529 |
+
<edge from-layer="70" from-port="0" to-layer="71" to-port="0" />
|
| 2530 |
+
<edge from-layer="71" from-port="1" to-layer="72" to-port="1" />
|
| 2531 |
+
<edge from-layer="72" from-port="2" to-layer="75" to-port="0" />
|
| 2532 |
+
<edge from-layer="73" from-port="0" to-layer="74" to-port="0" />
|
| 2533 |
+
<edge from-layer="74" from-port="1" to-layer="75" to-port="1" />
|
| 2534 |
+
<edge from-layer="75" from-port="2" to-layer="78" to-port="0" />
|
| 2535 |
+
<edge from-layer="76" from-port="0" to-layer="77" to-port="0" />
|
| 2536 |
+
<edge from-layer="77" from-port="1" to-layer="78" to-port="1" />
|
| 2537 |
+
<edge from-layer="78" from-port="2" to-layer="81" to-port="0" />
|
| 2538 |
+
<edge from-layer="79" from-port="0" to-layer="80" to-port="0" />
|
| 2539 |
+
<edge from-layer="80" from-port="1" to-layer="81" to-port="1" />
|
| 2540 |
+
<edge from-layer="81" from-port="2" to-layer="84" to-port="0" />
|
| 2541 |
+
<edge from-layer="82" from-port="0" to-layer="83" to-port="0" />
|
| 2542 |
+
<edge from-layer="83" from-port="1" to-layer="84" to-port="1" />
|
| 2543 |
+
<edge from-layer="84" from-port="2" to-layer="87" to-port="0" />
|
| 2544 |
+
<edge from-layer="85" from-port="0" to-layer="86" to-port="0" />
|
| 2545 |
+
<edge from-layer="86" from-port="1" to-layer="87" to-port="1" />
|
| 2546 |
+
<edge from-layer="87" from-port="2" to-layer="90" to-port="0" />
|
| 2547 |
+
<edge from-layer="88" from-port="0" to-layer="89" to-port="0" />
|
| 2548 |
+
<edge from-layer="89" from-port="1" to-layer="90" to-port="1" />
|
| 2549 |
+
<edge from-layer="90" from-port="2" to-layer="93" to-port="0" />
|
| 2550 |
+
<edge from-layer="91" from-port="0" to-layer="92" to-port="0" />
|
| 2551 |
+
<edge from-layer="92" from-port="1" to-layer="93" to-port="1" />
|
| 2552 |
+
<edge from-layer="93" from-port="2" to-layer="96" to-port="0" />
|
| 2553 |
+
<edge from-layer="94" from-port="0" to-layer="95" to-port="0" />
|
| 2554 |
+
<edge from-layer="95" from-port="1" to-layer="96" to-port="1" />
|
| 2555 |
+
<edge from-layer="96" from-port="2" to-layer="99" to-port="0" />
|
| 2556 |
+
<edge from-layer="97" from-port="0" to-layer="98" to-port="0" />
|
| 2557 |
+
<edge from-layer="98" from-port="1" to-layer="99" to-port="1" />
|
| 2558 |
+
<edge from-layer="99" from-port="2" to-layer="102" to-port="0" />
|
| 2559 |
+
<edge from-layer="100" from-port="0" to-layer="101" to-port="0" />
|
| 2560 |
+
<edge from-layer="101" from-port="1" to-layer="102" to-port="1" />
|
| 2561 |
+
<edge from-layer="102" from-port="2" to-layer="105" to-port="0" />
|
| 2562 |
+
<edge from-layer="103" from-port="0" to-layer="104" to-port="0" />
|
| 2563 |
+
<edge from-layer="104" from-port="1" to-layer="105" to-port="1" />
|
| 2564 |
+
<edge from-layer="105" from-port="2" to-layer="108" to-port="0" />
|
| 2565 |
+
<edge from-layer="106" from-port="0" to-layer="107" to-port="0" />
|
| 2566 |
+
<edge from-layer="107" from-port="1" to-layer="108" to-port="1" />
|
| 2567 |
+
<edge from-layer="108" from-port="2" to-layer="111" to-port="0" />
|
| 2568 |
+
<edge from-layer="109" from-port="0" to-layer="110" to-port="0" />
|
| 2569 |
+
<edge from-layer="110" from-port="1" to-layer="111" to-port="1" />
|
| 2570 |
+
<edge from-layer="111" from-port="2" to-layer="114" to-port="0" />
|
| 2571 |
+
<edge from-layer="112" from-port="0" to-layer="113" to-port="0" />
|
| 2572 |
+
<edge from-layer="113" from-port="1" to-layer="114" to-port="1" />
|
| 2573 |
+
<edge from-layer="114" from-port="2" to-layer="117" to-port="0" />
|
| 2574 |
+
<edge from-layer="115" from-port="0" to-layer="116" to-port="0" />
|
| 2575 |
+
<edge from-layer="116" from-port="1" to-layer="117" to-port="1" />
|
| 2576 |
+
<edge from-layer="117" from-port="2" to-layer="120" to-port="0" />
|
| 2577 |
+
<edge from-layer="118" from-port="0" to-layer="119" to-port="0" />
|
| 2578 |
+
<edge from-layer="119" from-port="1" to-layer="120" to-port="1" />
|
| 2579 |
+
<edge from-layer="120" from-port="2" to-layer="123" to-port="0" />
|
| 2580 |
+
<edge from-layer="121" from-port="0" to-layer="122" to-port="0" />
|
| 2581 |
+
<edge from-layer="122" from-port="1" to-layer="123" to-port="1" />
|
| 2582 |
+
<edge from-layer="123" from-port="2" to-layer="126" to-port="0" />
|
| 2583 |
+
<edge from-layer="124" from-port="0" to-layer="125" to-port="0" />
|
| 2584 |
+
<edge from-layer="125" from-port="1" to-layer="126" to-port="1" />
|
| 2585 |
+
<edge from-layer="126" from-port="2" to-layer="129" to-port="0" />
|
| 2586 |
+
<edge from-layer="127" from-port="0" to-layer="128" to-port="0" />
|
| 2587 |
+
<edge from-layer="128" from-port="1" to-layer="129" to-port="1" />
|
| 2588 |
+
<edge from-layer="129" from-port="2" to-layer="130" to-port="0" />
|
| 2589 |
+
<edge from-layer="130" from-port="1" to-layer="131" to-port="0" />
|
| 2590 |
+
</edges>
|
| 2591 |
+
<rt_info>
|
| 2592 |
+
<MO_version value="custom_HEAD_f6ee6e92f846a8c665e4a7089c51481f9689a3b5" />
|
| 2593 |
+
<Runtime_version value="2023.0.0-10521-f6ee6e92f84-HEAD" />
|
| 2594 |
+
<conversion_parameters>
|
| 2595 |
+
<framework value="onnx" />
|
| 2596 |
+
<input value="0" />
|
| 2597 |
+
<input_model value="DIR/Landnet_wing_blur2_21000.onnx" />
|
| 2598 |
+
<input_shape value="[1,3,48,48]" />
|
| 2599 |
+
<layout value="0(nchw)" />
|
| 2600 |
+
<model_name value="landmarks-regression-retail-0009" />
|
| 2601 |
+
<output_dir value="DIR" />
|
| 2602 |
+
<reverse_input_channels value="True" />
|
| 2603 |
+
<scale_values value="0[255.0]" />
|
| 2604 |
+
</conversion_parameters>
|
| 2605 |
+
<legacy_frontend value="False" />
|
| 2606 |
+
</rt_info>
|
| 2607 |
+
</net>
|
ov_models.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""ov_models.py — thin OpenVINO runtime wrappers for the Intel face models.
|
| 2 |
+
|
| 3 |
+
Three models, all CPU, all from Open Model Zoo:
|
| 4 |
+
- face-detection-retail-0004 : SSD face detector
|
| 5 |
+
- landmarks-regression-retail-0009 : 5 keypoints (for alignment)
|
| 6 |
+
- face-reidentification-retail-0095 : 256-d embedding, cosine-comparable
|
| 7 |
+
|
| 8 |
+
[OpenVINO 101] An OV model ships as two files: <name>.xml (graph) + <name>.bin
|
| 9 |
+
(weights), the "IR" format. You read it, compile it for a target device once
|
| 10 |
+
(slow), then call the compiled model many times (fast). compile_model("CPU")
|
| 11 |
+
picks CPU; nothing here ever touches a GPU, so it won't fight the VLM for
|
| 12 |
+
Zero GPU quota.
|
| 13 |
+
|
| 14 |
+
The IR files are pulled straight from Intel's public OMZ storage at boot — no
|
| 15 |
+
`omz_downloader` / `openvino-dev` needed (that package is deprecated and fights
|
| 16 |
+
modern `openvino`). Nothing to commit; first boot fetches ~5 MB total.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
import urllib.request
|
| 20 |
+
from pathlib import Path
|
| 21 |
+
|
| 22 |
+
import cv2
|
| 23 |
+
import numpy as np
|
| 24 |
+
import openvino as ov
|
| 25 |
+
|
| 26 |
+
MODELS_DIR = Path(__file__).parent / "models"
|
| 27 |
+
PRECISION = "FP16" # FP16 IR runs fine on CPU; smaller download than FP32
|
| 28 |
+
|
| 29 |
+
MODEL_NAMES = [
|
| 30 |
+
"face-detection-retail-0004", # detector
|
| 31 |
+
"landmarks-regression-retail-0009", # 5 keypoints -> alignment
|
| 32 |
+
"face-reidentification-retail-0095", # 256-d embedding
|
| 33 |
+
]
|
| 34 |
+
|
| 35 |
+
# All three live under the same 2023.0 / models_bin/1 path on OMZ storage.
|
| 36 |
+
_OMZ_BASE = ("https://storage.openvinotoolkit.org/repositories/open_model_zoo/"
|
| 37 |
+
"2023.0/models_bin/1")
|
| 38 |
+
|
| 39 |
+
_core = ov.Core()
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def download_models():
|
| 43 |
+
"""Fetch the FP16 IR (.xml + .bin) for each model into models/<name>/.
|
| 44 |
+
|
| 45 |
+
Idempotent: skips anything already on disk, so it's safe to call at every
|
| 46 |
+
boot. Downloads to a .part file first, then renames, so an interrupted
|
| 47 |
+
download never leaves a half-written file that looks complete.
|
| 48 |
+
"""
|
| 49 |
+
for name in MODEL_NAMES:
|
| 50 |
+
dst = MODELS_DIR / name
|
| 51 |
+
xml, binf = dst / f"{name}.xml", dst / f"{name}.bin"
|
| 52 |
+
if xml.exists() and binf.exists():
|
| 53 |
+
continue
|
| 54 |
+
dst.mkdir(parents=True, exist_ok=True)
|
| 55 |
+
for target in (xml, binf):
|
| 56 |
+
url = f"{_OMZ_BASE}/{name}/{PRECISION}/{target.name}"
|
| 57 |
+
print(f"[ov] downloading {target.name} ...")
|
| 58 |
+
part = target.with_suffix(target.suffix + ".part")
|
| 59 |
+
urllib.request.urlretrieve(url, part)
|
| 60 |
+
part.replace(target)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def _find_xml(name: str) -> str:
|
| 64 |
+
hits = list(MODELS_DIR.rglob(f"{name}.xml"))
|
| 65 |
+
if not hits:
|
| 66 |
+
raise FileNotFoundError(
|
| 67 |
+
f"{name}.xml not found under {MODELS_DIR}. Run download_models().")
|
| 68 |
+
return str(hits[0])
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class OVModel:
|
| 72 |
+
"""Compile once, infer many. Handles NCHW resize + BGR for you."""
|
| 73 |
+
|
| 74 |
+
def __init__(self, name: str):
|
| 75 |
+
model = _core.read_model(_find_xml(name))
|
| 76 |
+
self.compiled = _core.compile_model(model, "CPU")
|
| 77 |
+
self.input = self.compiled.input(0)
|
| 78 |
+
# IR input layout is N,C,H,W — pull the model's expected H,W.
|
| 79 |
+
_, _, self.h, self.w = self.input.shape
|
| 80 |
+
|
| 81 |
+
def _prep(self, bgr: np.ndarray) -> np.ndarray:
|
| 82 |
+
img = cv2.resize(bgr, (self.w, self.h))
|
| 83 |
+
img = img.transpose(2, 0, 1)[None].astype(np.float32) # HWC->NCHW
|
| 84 |
+
return img
|
| 85 |
+
|
| 86 |
+
def infer(self, bgr: np.ndarray):
|
| 87 |
+
"""Returns the first output blob as a numpy array."""
|
| 88 |
+
out = self.compiled({self.input: self._prep(bgr)})
|
| 89 |
+
return out[self.compiled.output(0)]
|
packages.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
ffmpeg
|
qwen_download.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from transformers import AutoProcessor, AutoModelForMultimodalLM
|
| 2 |
+
|
| 3 |
+
processor = AutoProcessor.from_pretrained("Qwen/Qwen2.5-VL-3B-Instruct")
|
| 4 |
+
model = AutoModelForMultimodalLM.from_pretrained("Qwen/Qwen2.5-VL-3B-Instruct")
|
| 5 |
+
messages = [
|
| 6 |
+
{
|
| 7 |
+
"role": "user",
|
| 8 |
+
"content": [
|
| 9 |
+
{"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"},
|
| 10 |
+
{"type": "text", "text": "What animal is on the candy?"}
|
| 11 |
+
]
|
| 12 |
+
},
|
| 13 |
+
]
|
| 14 |
+
inputs = processor.apply_chat_template(
|
| 15 |
+
messages,
|
| 16 |
+
add_generation_prompt=True,
|
| 17 |
+
tokenize=True,
|
| 18 |
+
return_dict=True,
|
| 19 |
+
return_tensors="pt",
|
| 20 |
+
).to(model.device)
|
| 21 |
+
|
| 22 |
+
outputs = model.generate(**inputs, max_new_tokens=40)
|
| 23 |
+
print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:]))
|
requirements.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# --- Gradio runtime ---
|
| 2 |
+
gradio>=5.0
|
| 3 |
+
spaces
|
| 4 |
+
|
| 5 |
+
# --- VLM (Qwen2.5-VL-7B, auto-downloads from HF Hub at first run) ---
|
| 6 |
+
torch
|
| 7 |
+
transformers>=4.49
|
| 8 |
+
accelerate
|
| 9 |
+
qwen-vl-utils
|
| 10 |
+
|
| 11 |
+
# --- OpenVINO face models (runtime only; IR files are committed in models/) ---
|
| 12 |
+
openvino>=2024.4
|
| 13 |
+
|
| 14 |
+
# --- media / utils ---
|
| 15 |
+
opencv-python-headless
|
| 16 |
+
pillow
|
| 17 |
+
numpy
|
| 18 |
+
uvicorn
|
| 19 |
+
fastapi
|
| 20 |
+
python-multipart
|
video.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""video.py — preprocessing: probe (rotation-aware), compute 720p target,
|
| 2 |
+
normalize to a single upright browser-friendly mp4 that BOTH cv2 and the
|
| 3 |
+
player use. One source of truth kills the cv2-ignores-rotation bug.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import json
|
| 7 |
+
import subprocess
|
| 8 |
+
import tempfile
|
| 9 |
+
|
| 10 |
+
SHORT_CAP = 720 # shorter side never exceeds this
|
| 11 |
+
LONG_CAP = 1280 # longer side never exceeds this
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def _probe(video_path: str) -> dict:
|
| 15 |
+
out = subprocess.run(
|
| 16 |
+
["ffprobe", "-v", "quiet", "-print_format", "json",
|
| 17 |
+
"-show_format", "-show_streams", video_path],
|
| 18 |
+
capture_output=True, text=True, check=True,
|
| 19 |
+
)
|
| 20 |
+
return json.loads(out.stdout)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def get_duration(video_path: str) -> float:
|
| 24 |
+
return float(_probe(video_path)["format"]["duration"])
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def get_display_dims(video_path: str) -> tuple[int, int]:
|
| 28 |
+
"""Return (width, height) as DISPLAYED — i.e. after rotation metadata is
|
| 29 |
+
applied. Phone portrait clips are often stored landscape + a rotate flag;
|
| 30 |
+
if we ignored it the target size (and detection) would be sideways.
|
| 31 |
+
"""
|
| 32 |
+
info = _probe(video_path)
|
| 33 |
+
v = next(s for s in info["streams"] if s["codec_type"] == "video")
|
| 34 |
+
w, h = int(v["width"]), int(v["height"])
|
| 35 |
+
|
| 36 |
+
rot = 0
|
| 37 |
+
if "tags" in v and "rotate" in v["tags"]:
|
| 38 |
+
rot = int(v["tags"]["rotate"])
|
| 39 |
+
for sd in v.get("side_data_list", []):
|
| 40 |
+
if "rotation" in sd:
|
| 41 |
+
rot = int(sd["rotation"])
|
| 42 |
+
if abs(rot) % 180 == 90: # 90 or 270 -> swap
|
| 43 |
+
w, h = h, w
|
| 44 |
+
return w, h
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def compute_target_size(w: int, h: int) -> tuple[int, int]:
|
| 48 |
+
"""Scale = min(720/short, 1280/long, 1.0); dims forced even; no upscale."""
|
| 49 |
+
short, long = min(w, h), max(w, h)
|
| 50 |
+
s = min(SHORT_CAP / short, LONG_CAP / long, 1.0)
|
| 51 |
+
out_w = max(2, int(round(w * s / 2)) * 2)
|
| 52 |
+
out_h = max(2, int(round(h * s / 2)) * 2)
|
| 53 |
+
return out_w, out_h
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def normalize_video(video_path: str) -> tuple[str, int, int]:
|
| 57 |
+
"""ffmpeg: autorotate + scale to target + re-encode H.264 (+ keep audio).
|
| 58 |
+
Returns (normalized_path, out_w, out_h). Everything downstream uses this.
|
| 59 |
+
"""
|
| 60 |
+
w, h = get_display_dims(video_path)
|
| 61 |
+
out_w, out_h = compute_target_size(w, h)
|
| 62 |
+
|
| 63 |
+
# NamedTemporaryFile gives us a safe unique path (mktemp is deprecated /
|
| 64 |
+
# racy); close it and let ffmpeg overwrite via -y.
|
| 65 |
+
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".mp4")
|
| 66 |
+
out_path = tmp.name
|
| 67 |
+
tmp.close()
|
| 68 |
+
|
| 69 |
+
subprocess.run(
|
| 70 |
+
["ffmpeg", "-y", "-i", video_path,
|
| 71 |
+
"-vf", f"scale={out_w}:{out_h}",
|
| 72 |
+
"-c:v", "libx264", "-preset", "veryfast", "-pix_fmt", "yuv420p",
|
| 73 |
+
"-c:a", "aac", "-movflags", "+faststart", out_path],
|
| 74 |
+
check=True, capture_output=True,
|
| 75 |
+
)
|
| 76 |
+
return out_path, out_w, out_h
|