Téléchargements de modèles : pause et reprise

Repris d'AJEAN 9c7423bf (v0.18.1) : POST /api/models/download/pause
{"filename", "pause"} met en pause ou reprend un téléchargement en cours.
La pause coupe les connexions (rien ne reste ouvert vers le CDN), garde le
fichier partiel, et chaque morceau attend la reprise puis repart de l'octet
où il s'était arrêté par une requête de plage ; une pause ne consomme pas
d'essai. /status expose « paused », la vitesse publiée tombe à zéro.

Façade Rust (downloads.rs) : même route, même porte (watch), mêmes
réponses ; la route est ajoutée à celles que le Go remet à la façade quand
LOKI_MODELS=rust. Tests portés de part et d'autre (pause puis reprise de
bout en bout, fichier identique à la source).

UI : bouton « Mettre en pause » / « Reprendre » à côté de « Annuler le
téléchargement », « en pause » dans la ligne de progression.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 5.5 committed 2026-10-16 15:16:39 +02:00
1 parent 615e58dd2f
commit 0ef2ed72f5
11 files changed
+580 -139

No files matched your search

+26 -3
View File
@@ -3500,6 +3500,7 @@ html[data-files="1"] #files-btn{color:var(--accent)}
</div>
<div id="m-hf-bar" class="pe-bar" style="display:none"><i></i></div>
<div id="m-hf-progress" class="pe-note" style="display:none"></div>
<button id="m-hf-pause" class="pe-link" onclick="togglePauseDownload()" style="display:none">Mettre en pause</button>
<button id="m-hf-cancel" class="pe-link" onclick="cancelDownload()" style="display:none">Annuler le téléchargement</button>
</div>
<div class="pe-row">
@@ -6754,6 +6755,7 @@ function dlEls(){
prog: document.getElementById('m-hf-progress'),
bar: document.getElementById('m-hf-bar'),
cancel: document.getElementById('m-hf-cancel'),
pause: document.getElementById('m-hf-pause'),
};
}
// Arrête le poll local sans toucher au téléchargement serveur.
@@ -6763,7 +6765,7 @@ function resetDlUI(){
const e = dlEls();
stopDlPoll(); dlName='';
e.prog.style.display='none'; e.bar.style.display='none';
e.cancel.style.display='none'; e.btn.disabled=false;
e.cancel.style.display='none'; e.pause.style.display='none'; e.btn.disabled=false;
}
// Remplit le sélecteur de destination avec les dossiers de modèles connus.
// L'espace libre est dans le libellé de l'option : c'est l'info utile au moment
@@ -6880,6 +6882,22 @@ async function cancelDownload(){
await jpost('/api/models/download/cancel', {filename: dlName});
// L'état « annulé » est confirmé par le prochain tick de poll.
}
// Met en pause ou reprend : le fichier partiel est gardé, la reprise repart
// de là où le transfert s'était arrêté (repris d'AJEAN v0.18.1).
let dlPaused = false;
async function togglePauseDownload(){
if(!dlName) return;
const e = dlEls();
e.pause.disabled = true;
try{
const r = await jpost('/api/models/download/pause', {filename: dlName, pause: !dlPaused});
if(r.ok) setDlPaused(r.paused);
}finally{ e.pause.disabled = false; }
}
function setDlPaused(p){
dlPaused = !!p;
dlEls().pause.textContent = dlPaused ? 'Reprendre' : 'Mettre en pause';
}
// Suit un téléchargement (nouveau ou déjà en cours) et pilote barre + texte.
function watchDownload(fname){
const e = dlEls();
@@ -6890,7 +6908,10 @@ function watchDownload(fname){
e.bar.style.display = 'block';
e.cancel.style.display = 'inline';
e.cancel.disabled = false;
const stop = ()=>{ stopDlPoll(); dlName=''; e.btn.disabled=false; e.cancel.style.display='none'; };
e.pause.style.display = 'inline';
e.pause.disabled = false;
setDlPaused(false);
const stop = ()=>{ stopDlPoll(); dlName=''; e.btn.disabled=false; e.cancel.style.display='none'; e.pause.style.display='none'; };
const tick = async ()=>{
const list = await jget('/api/models/download/status');
const st = (list||[]).find(d=>d.filename===fname);
@@ -6914,6 +6935,7 @@ function watchDownload(fname){
if(dlQueue.length) startDownloadURL(dlQueue.shift());
return;
}
if(!!st.paused !== dlPaused) setDlPaused(st.paused);
const pct = st.total>0 ? st.done*100/st.total : 0;
if(st.total>0){
e.bar.className = 'pe-bar';
@@ -6923,7 +6945,8 @@ function watchDownload(fname){
}
const tot = st.total>0 ? ' / '+fmtSize(st.total)+' ('+Math.round(pct)+'%)' : '';
let extra = '';
if(st.speed>0){
if(st.paused) extra += ' — en pause';
else if(st.speed>0){
extra += ' — '+fmtSize(st.speed)+'/s';
if(st.total>0){
const eta = Math.max(0, Math.round((st.total-st.done)/st.speed));
+1
View File
@@ -1215,6 +1215,7 @@ document.documentElement.setAttribute('data-side',localStorage.getItem('loki-sid
</div>
<div id="m-hf-bar" class="pe-bar" style="display:none"><i></i></div>
<div id="m-hf-progress" class="pe-note" style="display:none"></div>
<button id="m-hf-pause" class="pe-link" onclick="togglePauseDownload()" style="display:none">Mettre en pause</button>
<button id="m-hf-cancel" class="pe-link" onclick="cancelDownload()" style="display:none">Annuler le téléchargement</button>
</div>
<div class="pe-row">
+25 -3
View File
@@ -1501,6 +1501,7 @@ function dlEls(){
prog: document.getElementById('m-hf-progress'),
bar: document.getElementById('m-hf-bar'),
cancel: document.getElementById('m-hf-cancel'),
pause: document.getElementById('m-hf-pause'),
};
}
// Arrête le poll local sans toucher au téléchargement serveur.
@@ -1510,7 +1511,7 @@ function resetDlUI(){
const e = dlEls();
stopDlPoll(); dlName='';
e.prog.style.display='none'; e.bar.style.display='none';
e.cancel.style.display='none'; e.btn.disabled=false;
e.cancel.style.display='none'; e.pause.style.display='none'; e.btn.disabled=false;
}
// Remplit le sélecteur de destination avec les dossiers de modèles connus.
// L'espace libre est dans le libellé de l'option : c'est l'info utile au moment
@@ -1627,6 +1628,22 @@ async function cancelDownload(){
await jpost('/api/models/download/cancel', {filename: dlName});
// L'état « annulé » est confirmé par le prochain tick de poll.
}
// Met en pause ou reprend : le fichier partiel est gardé, la reprise repart
// de là où le transfert s'était arrêté (repris d'AJEAN v0.18.1).
let dlPaused = false;
async function togglePauseDownload(){
if(!dlName) return;
const e = dlEls();
e.pause.disabled = true;
try{
const r = await jpost('/api/models/download/pause', {filename: dlName, pause: !dlPaused});
if(r.ok) setDlPaused(r.paused);
}finally{ e.pause.disabled = false; }
}
function setDlPaused(p){
dlPaused = !!p;
dlEls().pause.textContent = dlPaused ? 'Reprendre' : 'Mettre en pause';
}
// Suit un téléchargement (nouveau ou déjà en cours) et pilote barre + texte.
function watchDownload(fname){
const e = dlEls();
@@ -1637,7 +1654,10 @@ function watchDownload(fname){
e.bar.style.display = 'block';
e.cancel.style.display = 'inline';
e.cancel.disabled = false;
const stop = ()=>{ stopDlPoll(); dlName=''; e.btn.disabled=false; e.cancel.style.display='none'; };
e.pause.style.display = 'inline';
e.pause.disabled = false;
setDlPaused(false);
const stop = ()=>{ stopDlPoll(); dlName=''; e.btn.disabled=false; e.cancel.style.display='none'; e.pause.style.display='none'; };
const tick = async ()=>{
const list = await jget('/api/models/download/status');
const st = (list||[]).find(d=>d.filename===fname);
@@ -1661,6 +1681,7 @@ function watchDownload(fname){
if(dlQueue.length) startDownloadURL(dlQueue.shift());
return;
}
if(!!st.paused !== dlPaused) setDlPaused(st.paused);
const pct = st.total>0 ? st.done*100/st.total : 0;
if(st.total>0){
e.bar.className = 'pe-bar';
@@ -1670,7 +1691,8 @@ function watchDownload(fname){
}
const tot = st.total>0 ? ' / '+fmtSize(st.total)+' ('+Math.round(pct)+'%)' : '';
let extra = '';
if(st.speed>0){
if(st.paused) extra += ' — en pause';
else if(st.speed>0){
extra += ' — '+fmtSize(st.speed)+'/s';
if(st.total>0){
const eta = Math.max(0, Math.round((st.total-st.done)/st.speed));