Update app.py
Browse files
app.py
CHANGED
|
@@ -410,72 +410,6 @@ async def upload(request: Request, file: UploadFile = File(...), redirect: Optio
|
|
| 410 |
def progress(vid_stem: str):
|
| 411 |
return progress_data.get(vid_stem, {'percent': 0, 'logs': [], 'done': False})
|
| 412 |
|
| 413 |
-
# >>> B1_BEGIN warmup_routes
|
| 414 |
-
@app.post("/warmup/start", tags=["warmup"])
|
| 415 |
-
async def warmup_start(request: Request, payload: Optional[Dict[str, Any]] = Body(None)):
|
| 416 |
-
"""
|
| 417 |
-
Démarre un warm-up séquentiel.
|
| 418 |
-
Sources de la liste des modèles (dans cet ordre) :
|
| 419 |
-
1) payload JSON: {"models": ["repo/a", "repo/b", ...]}
|
| 420 |
-
2) query string: ?models=repo/a,repo/b
|
| 421 |
-
3) env WARMUP_MODELS (JSON array)
|
| 422 |
-
"""
|
| 423 |
-
with warmup_lock:
|
| 424 |
-
if warmup_state.get("running"):
|
| 425 |
-
return {
|
| 426 |
-
"ok": False,
|
| 427 |
-
"running": True,
|
| 428 |
-
"msg": "Warm-up déjà en cours",
|
| 429 |
-
"percent": warmup_state.get("percent", 0),
|
| 430 |
-
"current": warmup_state.get("current"),
|
| 431 |
-
"idx": warmup_state.get("idx", 0),
|
| 432 |
-
"total": warmup_state.get("total", 0),
|
| 433 |
-
}
|
| 434 |
-
|
| 435 |
-
# 1) payload JSON
|
| 436 |
-
models_list: List[str] = []
|
| 437 |
-
if isinstance(payload, dict):
|
| 438 |
-
ml = payload.get("models")
|
| 439 |
-
if isinstance(ml, list):
|
| 440 |
-
models_list = [str(x).strip() for x in ml if str(x).strip()]
|
| 441 |
-
|
| 442 |
-
# 2) query string
|
| 443 |
-
if not models_list:
|
| 444 |
-
qs_models = request.query_params.get("models") or ""
|
| 445 |
-
if qs_models:
|
| 446 |
-
models_list = [s.strip() for s in qs_models.split(",") if s.strip()]
|
| 447 |
-
|
| 448 |
-
# 3) env
|
| 449 |
-
if not models_list:
|
| 450 |
-
models_list = _default_model_list()
|
| 451 |
-
|
| 452 |
-
if not models_list:
|
| 453 |
-
raise HTTPException(400, "Aucun modèle fourni (body.models ou ?models=) et WARMUP_MODELS vide.")
|
| 454 |
-
|
| 455 |
-
t = threading.Thread(target=_warmup_thread, args=(models_list,), daemon=True)
|
| 456 |
-
t.start()
|
| 457 |
-
return {"ok": True, "started": True, "total": len(models_list)}
|
| 458 |
-
|
| 459 |
-
@app.get("/warmup/status", tags=["warmup"])
|
| 460 |
-
def warmup_status():
|
| 461 |
-
"""
|
| 462 |
-
État courant du warm-up + 200 dernières lignes de logs.
|
| 463 |
-
"""
|
| 464 |
-
with warmup_lock:
|
| 465 |
-
state = dict(warmup_state)
|
| 466 |
-
state["logs"] = warmup_state.get("logs", [])[-200:]
|
| 467 |
-
return state
|
| 468 |
-
|
| 469 |
-
@app.post("/warmup/stop", tags=["warmup"])
|
| 470 |
-
def warmup_stop_route():
|
| 471 |
-
"""
|
| 472 |
-
Demande d'arrêt gracieux. Le modèle en cours se termine puis la boucle stoppe.
|
| 473 |
-
"""
|
| 474 |
-
if not warmup_state.get("running"):
|
| 475 |
-
return {"ok": True, "was_running": False}
|
| 476 |
-
warmup_stop.set()
|
| 477 |
-
return {"ok": True, "was_running": True}
|
| 478 |
-
# >>> B1_END warmup_routes
|
| 479 |
|
| 480 |
|
| 481 |
@app.delete("/delete/{vid}", tags=["io"])
|
|
@@ -743,7 +677,24 @@ HTML_TEMPLATE = r"""
|
|
| 743 |
<button class="btn" type="submit">Uploader</button>
|
| 744 |
</form>
|
| 745 |
<span class="muted" id="msg">__MSG__</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 746 |
<span class="muted">Liens : <a href="/docs" target="_blank">/docs</a> • <a href="/files" target="_blank">/files</a></span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 747 |
</div>
|
| 748 |
<div class="layout">
|
| 749 |
<div>
|
|
@@ -816,6 +767,22 @@ HTML_TEMPLATE = r"""
|
|
| 816 |
<div id="popup-progress-bar"><div id="popup-progress-fill"></div></div>
|
| 817 |
<div id="popup-logs"></div>
|
| 818 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 819 |
<div id="toast"></div>
|
| 820 |
<script>
|
| 821 |
const serverVid = "__VID__";
|
|
@@ -855,6 +822,98 @@ const maskedCount = document.getElementById('maskedCount');
|
|
| 855 |
const hud = document.getElementById('hud');
|
| 856 |
const toastWrap = document.getElementById('toast');
|
| 857 |
const gotoInput = document.getElementById('gotoInput');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 858 |
const gotoBtn = document.getElementById('gotoBtn');
|
| 859 |
|
| 860 |
// State
|
|
|
|
| 410 |
def progress(vid_stem: str):
|
| 411 |
return progress_data.get(vid_stem, {'percent': 0, 'logs': [], 'done': False})
|
| 412 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 413 |
|
| 414 |
|
| 415 |
@app.delete("/delete/{vid}", tags=["io"])
|
|
|
|
| 677 |
<button class="btn" type="submit">Uploader</button>
|
| 678 |
</form>
|
| 679 |
<span class="muted" id="msg">__MSG__</span>
|
| 680 |
+
|
| 681 |
+
|
| 682 |
+
|
| 683 |
+
|
| 684 |
+
|
| 685 |
+
|
| 686 |
<span class="muted">Liens : <a href="/docs" target="_blank">/docs</a> • <a href="/files" target="_blank">/files</a></span>
|
| 687 |
+
|
| 688 |
+
<!-- >>> A2B2_BEGIN warmup_topbar -->
|
| 689 |
+
<div style="display:flex;gap:8px;align-items:center">
|
| 690 |
+
<button id="warmupStartBtn" class="btn" title="Télécharger les modèles (séquentiel)">🔥 Préparer modèles</button>
|
| 691 |
+
<button id="warmupStopBtn" class="btn" title="Arrêter le warm-up en cours" style="display:none">⏹️ Stop warm-up</button>
|
| 692 |
+
<button id="warmupLogsBtn" class="btn" title="Voir les logs du warm-up">📜 Logs warm-up</button>
|
| 693 |
+
<span id="warmupStatus" class="muted"></span>
|
| 694 |
+
</div>
|
| 695 |
+
<!-- >>> A2B2_END warmup_topbar -->
|
| 696 |
+
|
| 697 |
+
|
| 698 |
</div>
|
| 699 |
<div class="layout">
|
| 700 |
<div>
|
|
|
|
| 767 |
<div id="popup-progress-bar"><div id="popup-progress-fill"></div></div>
|
| 768 |
<div id="popup-logs"></div>
|
| 769 |
</div>
|
| 770 |
+
|
| 771 |
+
<!-- >>> A2B2_BEGIN warmup_popup -->
|
| 772 |
+
<div id="warmupPopup" style="display:none; position:fixed; top:22%; left:50%; transform:translate(-50%,-50%); background:#fff; padding:20px; border-radius:8px; box-shadow:0 0 10px rgba(0,0,0,0.2); z-index:1001; min-width:360px">
|
| 773 |
+
<h3>Préparation des modèles (warm-up)</h3>
|
| 774 |
+
<div class="muted" id="warmupPopupStatus">—</div>
|
| 775 |
+
<div style="background:#f3f4f6;border-radius:4px;height:8px;margin:10px 0">
|
| 776 |
+
<div id="warmup-progress-fill" style="background:#2563eb;height:100%;width:0;border-radius:4px"></div>
|
| 777 |
+
</div>
|
| 778 |
+
<div id="warmup-logs" style="max-height:220px; overflow:auto; font-size:12px; color:#6b7280; white-space:pre-wrap"></div>
|
| 779 |
+
<div style="margin-top:10px; display:flex; gap:8px; justify-content:flex-end">
|
| 780 |
+
<button id="warmupCloseBtn" class="btn">Fermer</button>
|
| 781 |
+
</div>
|
| 782 |
+
</div>
|
| 783 |
+
<!-- >>> A2B2_END warmup_popup -->
|
| 784 |
+
|
| 785 |
+
|
| 786 |
<div id="toast"></div>
|
| 787 |
<script>
|
| 788 |
const serverVid = "__VID__";
|
|
|
|
| 822 |
const hud = document.getElementById('hud');
|
| 823 |
const toastWrap = document.getElementById('toast');
|
| 824 |
const gotoInput = document.getElementById('gotoInput');
|
| 825 |
+
|
| 826 |
+
|
| 827 |
+
|
| 828 |
+
// >>> A2B2_BEGIN warmup_dom
|
| 829 |
+
// Références DOM (topbar + popup)
|
| 830 |
+
const warmupStartBtn = document.getElementById('warmupStartBtn');
|
| 831 |
+
const warmupStopBtn = document.getElementById('warmupStopBtn');
|
| 832 |
+
const warmupLogsBtn = document.getElementById('warmupLogsBtn');
|
| 833 |
+
const warmupStatusEl = document.getElementById('warmupStatus');
|
| 834 |
+
|
| 835 |
+
const warmupPopup = document.getElementById('warmupPopup');
|
| 836 |
+
const warmupPopupStatus = document.getElementById('warmupPopupStatus');
|
| 837 |
+
const warmupProgressFill = document.getElementById('warmup-progress-fill');
|
| 838 |
+
const warmupLogs = document.getElementById('warmup-logs');
|
| 839 |
+
const warmupCloseBtn = document.getElementById('warmupCloseBtn');
|
| 840 |
+
|
| 841 |
+
// Helpers popup
|
| 842 |
+
function openWarmupPopup(){ if(warmupPopup) warmupPopup.style.display = 'block'; }
|
| 843 |
+
function closeWarmupPopup(){ if(warmupPopup) warmupPopup.style.display = 'none'; }
|
| 844 |
+
if (warmupCloseBtn) warmupCloseBtn.addEventListener('click', closeWarmupPopup);
|
| 845 |
+
|
| 846 |
+
// Rafraîchissement d’état
|
| 847 |
+
let warmupTimer = null;
|
| 848 |
+
async function refreshWarmupUI(){
|
| 849 |
+
try{
|
| 850 |
+
const r = await fetch('/warmup/status');
|
| 851 |
+
if(!r.ok) return;
|
| 852 |
+
const s = await r.json();
|
| 853 |
+
const pct = Math.max(0, Math.min(100, parseInt(s.percent||0,10)));
|
| 854 |
+
const running = !!s.running;
|
| 855 |
+
|
| 856 |
+
if (warmupStatusEl) {
|
| 857 |
+
if (running) {
|
| 858 |
+
const idx = (s.idx ?? 0) + 1;
|
| 859 |
+
const tot = (s.total ?? 0);
|
| 860 |
+
warmupStatusEl.textContent = `⏳ ${pct}% — ${s.current||''} (${idx}/${tot})`;
|
| 861 |
+
} else {
|
| 862 |
+
warmupStatusEl.textContent = `✅ Terminé (${s.total||0} modèles)`;
|
| 863 |
+
}
|
| 864 |
+
}
|
| 865 |
+
if (warmupProgressFill) warmupProgressFill.style.width = pct + '%';
|
| 866 |
+
if (warmupPopupStatus) warmupPopupStatus.textContent = running ? 'Téléchargement en cours…' : 'Terminé';
|
| 867 |
+
if (warmupLogs && Array.isArray(s.logs)) warmupLogs.textContent = s.logs.join('\n');
|
| 868 |
+
if (warmupStopBtn) warmupStopBtn.style.display = running ? 'inline-block' : 'none';
|
| 869 |
+
|
| 870 |
+
if (running) {
|
| 871 |
+
openWarmupPopup();
|
| 872 |
+
if (!warmupTimer) warmupTimer = setInterval(refreshWarmupUI, 1000);
|
| 873 |
+
} else {
|
| 874 |
+
if (warmupTimer) { clearInterval(warmupTimer); warmupTimer = null; }
|
| 875 |
+
}
|
| 876 |
+
}catch(e){ /* no-op */ }
|
| 877 |
+
}
|
| 878 |
+
|
| 879 |
+
// Boutons
|
| 880 |
+
if (warmupStartBtn){
|
| 881 |
+
warmupStartBtn.addEventListener('click', async ()=>{
|
| 882 |
+
// Optionnel : l’utilisateur peut saisir une liste (CSV). Vide => configuration serveur (WARMUP_MODELS)
|
| 883 |
+
const txt = prompt('Modèles (optionnel, séparés par des virgules). Laisse vide pour utiliser la config du serveur :','');
|
| 884 |
+
let body = {};
|
| 885 |
+
if (txt && txt.trim()){
|
| 886 |
+
body = { models: txt.split(',').map(s=>s.trim()).filter(Boolean) };
|
| 887 |
+
}
|
| 888 |
+
try{
|
| 889 |
+
const r = await fetch('/warmup/start',{ method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(body) });
|
| 890 |
+
if(!r.ok){ const t = await r.text(); alert('Échec démarrage: ' + r.status + ' ' + t); return; }
|
| 891 |
+
openWarmupPopup();
|
| 892 |
+
await refreshWarmupUI();
|
| 893 |
+
if (!warmupTimer) warmupTimer = setInterval(refreshWarmupUI, 1000);
|
| 894 |
+
}catch(e){ alert('Erreur réseau'); }
|
| 895 |
+
});
|
| 896 |
+
}
|
| 897 |
+
if (warmupLogsBtn){
|
| 898 |
+
warmupLogsBtn.addEventListener('click', async ()=>{
|
| 899 |
+
openWarmupPopup();
|
| 900 |
+
await refreshWarmupUI();
|
| 901 |
+
if (!warmupTimer) warmupTimer = setInterval(refreshWarmupUI, 1000);
|
| 902 |
+
});
|
| 903 |
+
}
|
| 904 |
+
if (warmupStopBtn){
|
| 905 |
+
warmupStopBtn.addEventListener('click', async ()=>{
|
| 906 |
+
try{ await fetch('/warmup/stop',{method:'POST'}); await refreshWarmupUI(); }catch(e){}
|
| 907 |
+
});
|
| 908 |
+
}
|
| 909 |
+
|
| 910 |
+
// 1er affichage (badge de statut en topbar)
|
| 911 |
+
refreshWarmupUI();
|
| 912 |
+
// >>> A2B2_END warmup_dom
|
| 913 |
+
|
| 914 |
+
|
| 915 |
+
|
| 916 |
+
|
| 917 |
const gotoBtn = document.getElementById('gotoBtn');
|
| 918 |
|
| 919 |
// State
|