raiseValueError("se está aplicando el plan de esa carpeta; espera a que termine")
raiseValueError("se está aplicando el plan de esa carpeta; espera a que termine")
recuperar_huerfanas()
con=db()
con=db()
activos=con.execute("SELECT COUNT(*) n FROM trabajos WHERE lower(raiz) = ? AND estado IN ('pendiente','corriendo')",(k.lower(),)).fetchone()["n"]
activos=[dict(x)forxincon.execute("SELECT id, ruta, estado, pct FROM trabajos WHERE lower(raiz) = ? AND estado IN ('pendiente','corriendo') ORDER BY id",(k.lower(),)).fetchall()]
<divid="zonaSoltar"role="button"tabindex="0"title="Suelta aquí una carpeta de cliente para añadirla, o haz clic para pegar la ruta">Suelta aquí la carpeta<br>de un cliente para añadirla</div>
<divid="zonaSoltar"role="button"tabindex="0"title="Suelta aquí una carpeta de cliente para añadirla, o haz clic para pegar la ruta">Suelta aquí la carpeta<br>de un cliente para añadirla</div>
<buttonclass="lnk"id="btnElegir"style="width:100%;margin-top:4px"title="Abre el selector de carpetas de Windows">Elegir carpeta…</button>
<divclass="lado-pie"><imgsrc="/static/labstream.png"alt="Labstream Studio"><spanid="reloj"></span><buttonid="btnBuscarAct"title="Consulta Labstream OS y avisa si hay una versión nueva">Buscar actualizaciones</button><spanid="actUltima"class="mut"style="font-size:11px"></span></div>
<divclass="lado-pie"><imgsrc="/static/labstream.png"alt="Labstream Studio"><spanid="reloj"></span><buttonid="btnBuscarAct"title="Consulta Labstream OS y avisa si hay una versión nueva">Buscar actualizaciones</button><spanid="actUltima"class="mut"style="font-size:11px"></span></div>
<divclass="box"id="convCaja"><divid="convLista"><spanclass="mut">Pulsa «Buscar clips» para ver qué se puede convertir en esta carpeta.</span></div></div>
<divclass="box"id="convCaja"><divid="convLista"><spanclass="mut">Pulsa «Buscar clips» para ver qué se puede convertir en esta carpeta.</span></div></div>
@@ -452,11 +454,18 @@ async function api(u, body){
if(ocupada){aviso('Esa carpeta tiene una tarea en curso. Espera a que termine.','bad');return;}
if(ocupada){aviso('Esa carpeta tiene una tarea en curso. Espera a que termine.','bad');return;}
modal('Desconectar «'+r.cliente+'»',`<p>La carpeta <span class="path">${esc(r.ruta)}</span> desaparece de la lista de la izquierda. <b>No se borra ni se mueve nada del disco</b>; puedes volver a soltarla cuando quieras.</p><label style="display:flex;gap:8px;align-items:flex-start;margin-top:10px;font-size:13px"><input type="checkbox" id="mDatos" style="margin-top:3px"><span>Olvidar también el inventario escaneado${r.escaneo?` (${num(r.escaneo.n)} archivos · ${gb(r.escaneo.bytes)})`:''}. Si la vuelves a conectar habrá que escanearla de nuevo.</span></label>`,async()=>{
modal('Desconectar «'+r.cliente+'»',`<p>La carpeta <span class="path">${esc(r.ruta)}</span> desaparece de la lista de la izquierda. <b>No se borra ni se mueve nada del disco</b>; puedes volver a soltarla cuando quieras.</p><label style="display:flex;gap:8px;align-items:flex-start;margin-top:10px;font-size:13px"><input type="checkbox" id="mDatos" style="margin-top:3px"><span>Olvidar también el inventario escaneado${r.escaneo?` (${num(r.escaneo.n)} archivos · ${gb(r.escaneo.bytes)})`:''}. Si la vuelves a conectar habrá que escanearla de nuevo.</span></label>`,async()=>{
// Conversiones sin terminar: se enseñan (cuáles, en qué estado) y, si ninguna está convirtiéndose ahora,
}catch(e){aviso(e.message,'bad');}
// se ofrece descartarlas y desconectar de una vez. Antes solo salía un globo sin decir cuáles.
apuntarAviso(e.message,c.archivos.join('\n'));
modal('No se pudo desconectar «'+r.cliente+'»',`<p>${esc(e.message)}.</p><p><b>${c.n}</b> conversión${c.n>1?'es':''} sin terminar${c.por_estado.corriendo?` (${c.por_estado.corriendo} convirtiéndose ahora)`:''}:</p><pre>${esc(c.archivos.join('\n'))}${c.n>c.archivos.length?`\n… y ${c.n-c.archivos.length} más`:''}</pre><p class="mut" style="font-size:12.5px">${c.en_curso?'Detén la cola en <b>Convertir a 10 bits</b> y vuelve a intentarlo.':'Descartarlas las quita de la cola sin tocar ningún archivo: los originales siguen donde están, y si vuelves a conectar la carpeta puedes encolarlas otra vez.'}</p>`,
P.push(!esc?{e:'bloq',t:'Convertir',d:'Después de escanear'}:!cv.camara_bytes?{e:'hecho',t:'Convertir',d:'No hay material de cámara',corto:'nada'}
constconError=n('error')+n('cancelado');
// Las conversiones sin terminar o con error se enseñan SIEMPRE, aunque la carpeta ya no tenga material de
// cámara (disco desconectado, material movido): antes el paso decía «nada» mientras la cola bloqueaba Desconectar.
P.push(!esc?{e:'bloq',t:'Convertir',d:'Después de escanear'}
:anaRun?{e:'curso',t:'Convertir',d:`Leyendo clips ${cv.analisis.hechos} de ${cv.analisis.total}`,corto:'leyendo'}
:anaRun?{e:'curso',t:'Convertir',d:`Leyendo clips ${cv.analisis.hechos} de ${cv.analisis.total}`,corto:'leyendo'}
:cv.cola_activa&&activos?{e:'curso',t:'Convertir',d:`${activos} clip${activos>1?'s':''} en cola`,corto:`${activos} en cola`}
:cv.cola_activa&&activos?{e:'curso',t:'Convertir',d:`${activos} clip${activos>1?'s':''} en cola`,corto:`${activos} en cola`}
:activos?{e:'listo',t:'Convertir',d:`${activos} conversión${activos>1?'es':''} sin terminar, cola parada`,corto:`${activos} parada${activos>1?'s':''}`}
:conError?{e:'listo',t:'Convertir',d:`${conError} conversión${conError>1?'es':''} con error`,corto:`${conError} con error`}
:!cv.camara_bytes?{e:'hecho',t:'Convertir',d:'No hay material de cámara',corto:'nada'}
:listos?{e:'listo',t:'Convertir',d:`${listos} convertido${listos>1?'s':''} por verificar`,corto:`${listos} sin verificar`}
:listos?{e:'listo',t:'Convertir',d:`${listos} convertido${listos>1?'s':''} por verificar`,corto:`${listos} sin verificar`}
:verif?{e:'listo',t:'Convertir',d:`${verif} verificado${verif>1?'s':''} por reemplazar`,corto:`${verif} por reemplazar`}
:verif?{e:'listo',t:'Convertir',d:`${verif} verificado${verif>1?'s':''} por reemplazar`,corto:`${verif} por reemplazar`}
:reemp&&!activos ?{e:'hecho',t:'Convertir',d:`${reemp} clip${reemp>1?'s':''} ya en 10 bits`,corto:`${reemp} hechos`}
:reemp?{e:'hecho',t:'Convertir',d:`${reemp} clip${reemp>1?'s':''} ya en 10 bits`,corto:`${reemp} hechos`}
:activos?{e:'listo',t:'Convertir',d:`${activos} en cola, parada`,corto:'parada'}
:{e:'pend',t:'Convertir',d:`${gb(cv.camara_bytes)} de cámara${cv.raw_bytes?' · '+gb(cv.raw_bytes)+' BRAW':''}`,corto:gb(cv.camara_bytes)});
:{e:'pend',t:'Convertir',d:`${gb(cv.camara_bytes)} de cámara${cv.raw_bytes?' · '+gb(cv.raw_bytes)+' BRAW':''}`,corto:gb(cv.camara_bytes)});
constrp=C.respaldo||{};
constrp=C.respaldo||{};
P.push(!esc?{e:'bloq',t:'Respaldar',d:'Después de escanear'}:rp.estado==='corriendo'?{e:'curso',t:'Respaldar',d:'Copiando…',corto:'copiando'}
P.push(!esc?{e:'bloq',t:'Respaldar',d:'Después de escanear'}:rp.estado==='corriendo'?{e:'curso',t:'Respaldar',d:'Copiando…',corto:'copiando'}
@@ -879,6 +902,7 @@ async function cargarCola(){
constest=$('#colaEstado');est.className='est '+(d.cola.activa?'corriendo':err?'error':verif?'verificado':'pendiente');est.textContent=d.cola.activa?`convirtiendo${d.cola.mensaje?' · '+d.cola.mensaje:''}`:pend?`${pend} en espera, parada`:ts.length?`${verif} verificados · ${n('reemplazado')} reemplazados${err?` · ${err} con error`:''}`:'sin actividad';
constest=$('#colaEstado');est.className='est '+(d.cola.activa?'corriendo':err?'error':verif?'verificado':'pendiente');est.textContent=d.cola.activa?`convirtiendo${d.cola.mensaje?' · '+d.cola.mensaje:''}`:pend?`${pend} en espera, parada`:ts.length?`${verif} verificados · ${n('reemplazado')} reemplazados${err?` · ${err} con error`:''}`:'sin actividad';
$('#btnReemplazar').onclick=()=>accion($('#btnReemplazar'),async()=>{constids=awaitidsPorEstado('verificado');modal('Reemplazar originales',`<p><b>${ids.length}</b> originales se apartan a <span class="path">_Para borrar/originales</span> dentro de su carpeta (no se borran). El MP4 10 bits queda en su lugar.</p><p>Cuando estés seguro, una persona vacía _Para borrar a mano.</p>`,()=>accion($('#btnReemplazar'),async()=>{constr=awaitapi('/api/optimizar/reemplazar',{ids});constok=r.resultados.filter(x=>x.ok).length;aviso(`${ok} de ${r.resultados.length} reemplazados.`,ok===r.resultados.length?'ok':'bad',{texto:'Ver historial',ir:()=>irA('historial')});if(ok<r.resultados.length)modal('Reemplazo',`<pre>${esc(r.resultados.map(x=>`#${x.id}${x.ok?'OK':'NO'} · ${x.motivo}`).join('\n'))}</pre>`,null);cargarCola();}));$('#modalSi').textContent='Reemplazar';});
$('#btnReemplazar').onclick=()=>accion($('#btnReemplazar'),async()=>{constids=awaitidsPorEstado('verificado');modal('Reemplazar originales',`<p><b>${ids.length}</b> originales se apartan a <span class="path">_Para borrar/originales</span> dentro de su carpeta (no se borran). El MP4 10 bits queda en su lugar.</p><p>Cuando estés seguro, una persona vacía _Para borrar a mano.</p>`,()=>accion($('#btnReemplazar'),async()=>{constr=awaitapi('/api/optimizar/reemplazar',{ids});constok=r.resultados.filter(x=>x.ok).length;aviso(`${ok} de ${r.resultados.length} reemplazados.`,ok===r.resultados.length?'ok':'bad',{texto:'Ver historial',ir:()=>irA('historial')});if(ok<r.resultados.length)modal('Reemplazo',`<pre>${esc(r.resultados.map(x=>`#${x.id}${x.ok?'OK':'NO'} · ${x.motivo}`).join('\n'))}</pre>`,null);cargarCola();}));$('#modalSi').textContent='Reemplazar';});
// Con la cola parada, las que esperan también se pueden quitar (antes no había forma desde la interfaz, y
// bloqueaban Desconectar). No toca archivos: solo las saca de la cola.
$('#btnQuitarEspera').onclick=()=>accion($('#btnQuitarEspera'),async()=>{constids=awaitidsPorEstado('pendiente');if(!ids.length)thrownewApiError('No hay conversiones en espera de esta carpeta.');modal('Quitar las conversiones en espera',`<p><b>${ids.length}</b> conversión${ids.length>1?'es':''} en espera se quita${ids.length>1?'n':''} de la cola. No se toca ningún archivo: los originales siguen donde están y puedes volver a encolarlas desde «Buscar clips».</p>`,()=>accion($('#btnQuitarEspera'),async()=>{awaitapi('/api/optimizar/control',{accion:'quitar',ids});awaitcargarEstado();cargarCola();}));$('#modalSi').textContent='Quitar';});
modal('¿Cuál es?',`<p>Hay varias carpetas con ese nombre:</p>${d.coincidencias.map((c,i)=>`<label style="display:block;margin:4px 0"><input type="radio" name="cand" value="${esc(c)}" ${i===0?'checked':''}> <span class="path">${esc(c)}</span></label>`).join('')}`,()=>proponerRaiz(document.querySelector('input[name=cand]:checked').value));
modal('¿Cuál es?',`<p>Hay varias carpetas con ese nombre:</p>${d.coincidencias.map((c,i)=>`<label style="display:block;margin:4px 0"><input type="radio" name="cand" value="${esc(c)}" ${i===0?'checked':''}> <span class="path">${esc(c)}</span></label>`).join('')}`,()=>proponerRaiz(document.querySelector('input[name=cand]:checked').value));
}));
}));
zona.onclick=()=>modal('Añadir carpeta de cliente','<p>Pega la ruta completa de la carpeta (en el Explorador: Mayús + clic derecho → «Copiar como ruta de acceso»).</p><input type="text" id="rutaPegada" style="width:100%" placeholder="Z:\\Dermatologas\\Nombre del cliente">',()=>proponerRaiz($('#rutaPegada').value.trim().replace(/^"|"$/g,'')));
asyncfunctionelegirCarpeta(btn){
awaitaccion(btn,async()=>{
constd=awaitapi('/api/elegir-carpeta',{});
if(d.ruta)awaitproponerRaiz(d.ruta);
});
}
zona.onclick=()=>{modal('Añadir carpeta de cliente','<p><button class="p" type="button" id="btnElegir3">Elegir carpeta…</button> <span class="mut">abre el selector de Windows</span></p><p style="margin-top:12px">O pega la ruta completa (en el Explorador: Mayús + clic derecho → «Copiar como ruta de acceso»):</p><input type="text" id="rutaPegada" style="width:100%" placeholder="Z:\\Dermatologas\\Nombre del cliente">',()=>{constv=$('#rutaPegada').value.trim().replace(/^"|"$/g,'');if(v)proponerRaiz(v);});$('#btnElegir3').onclick=()=>{$('#modal').classList.remove('on');elegirCarpeta($('#btnElegir'));};};
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.