2.1.0: el asistente se fusiona con la lista de acciones
Había dos caminos para lo mismo. Ahora encima de la lista sale «Hacer las N de una vez», que ejecuta seguidas las acciones que no necesitan elegir nada, con el mismo motor que tenía el asistente. Convertir y respaldar se quedan fuera porque hay que elegir; lo dudoso nunca entra. - POST /api/acciones/hacer; acciones() trae y el progreso. - Se quita el punto de entrada «Asistente paso a paso» de la interfaz (el motor y la entrada por línea de comandos siguen). - Las acciones de ordenar no cuentan como pendientes sin un plan calculado: tras ordenarlo todo la lista seguía diciendo que faltaban dos cosas. Probado de punta a punta: 19 archivos antes y después, ninguno perdido, árbol completo ordenado en un clic. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Este commit está contenido en:
@@ -327,7 +327,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="inicioLleno">
|
||||
<div class="cab"><div><h2 class="v" id="cabTitulo">…</h2><div class="sub" id="cabSub"></div></div><div class="der"><button class="lnk" id="btnEscanear">Volver a escanear</button><button class="lnk" id="btnAsistente">Asistente paso a paso</button><button class="lnk" id="btnDesconectar" title="La carpeta deja de aparecer en la lista de la izquierda. No borra ni mueve nada del disco.">Quitar de la lista</button></div></div>
|
||||
<div class="cab"><div><h2 class="v" id="cabTitulo">…</h2><div class="sub" id="cabSub"></div></div><div class="der"><button class="lnk" id="btnEscanear">Volver a escanear</button><button class="lnk" id="btnDesconectar" title="La carpeta deja de aparecer en la lista de la izquierda. No borra ni mueve nada del disco.">Quitar de la lista</button></div></div>
|
||||
<div id="acciones" class="acc"></div>
|
||||
<div class="sig" id="siguiente" style="display:none"></div>
|
||||
<div class="row">
|
||||
@@ -588,7 +588,8 @@ function detectarCambios(){
|
||||
fin('esc:' + raiz, 'Escaneo terminado.');
|
||||
if (antes['ana:' + raiz] && !ahora['ana:' + raiz]) { if (BUSCANDO || vistaActual() === 'convertir') { BUSCANDO = false; irA('convertir'); cargarCandidatos().catch(e => aviso(e.message, 'bad')); } else aviso('Lectura de clips terminada.', 'ok', {texto: 'Ver clips', ir: () => { irA('convertir'); cargarCandidatos(); }}); }
|
||||
fin('cola', 'Conversión terminada.', {texto: 'Ver', ir: () => irA('convertir')});
|
||||
fin('asis', 'El asistente terminó.' + (ESTADO.asistente.resultado && ESTADO.asistente.resultado.errores && ESTADO.asistente.resultado.errores.length ? ` ${ESTADO.asistente.resultado.errores.length} avisos.` : ''), {texto: 'Ver historial', ir: () => irA('historial')});
|
||||
if (antes.asis && !ahora.asis && vistaActual() === 'carpeta') pintarAcciones().catch(() => {});
|
||||
fin('asis', 'Listo: se hizo todo lo seguro de esa carpeta.' + (ESTADO.asistente.resultado && ESTADO.asistente.resultado.errores && ESTADO.asistente.resultado.errores.length ? ` ${ESTADO.asistente.resultado.errores.length} avisos.` : ''), {texto: 'Ver historial', ir: () => irA('historial')});
|
||||
fin('resp', 'Copia de respaldo terminada.', {texto: 'Ver', ir: () => irA('respaldar')});
|
||||
if ((antes.cola !== ahora.cola || ahora.cola) && vistaActual() === 'convertir' && !document.querySelector('#convLista input:focus')) cargarCola();
|
||||
}
|
||||
@@ -736,9 +737,24 @@ async function pintarAcciones(){
|
||||
if (raizActual() !== raiz) return; // cambió de carpeta mientras llegaba
|
||||
ACCIONES = d.acciones || [];
|
||||
const quedan = ACCIONES.filter(a => a.estado === 'toca').length;
|
||||
$('#acciones').innerHTML = `<div class="cabsec">${quedan ? `Lo que se puede hacer ahora · ${quedan}` : 'Esta carpeta está al día'}</div>` +
|
||||
const auto = d.automatico || [], asis = d.asistente;
|
||||
const corriendo = asis && asis.activo;
|
||||
const cab = corriendo
|
||||
? `<div class="cabsec">Trabajando en esta carpeta<span class="sep"></span><span class="mut" style="text-transform:none;letter-spacing:0">${esc(NOMBRE_PASO[asis.paso] || asis.paso || '')} · ${asis.hechas || 0} de ${asis.total || 0}</span></div>`
|
||||
: `<div class="cabsec">${quedan ? `Lo que se puede hacer ahora · ${quedan}` : 'Esta carpeta está al día'}${auto.length > 1 ? '<span class="sep"></span><button class="p" id="btnHacerTodo" style="text-transform:none;letter-spacing:0">Hacer las ' + auto.length + ' de una vez</button>' : ''}</div>`;
|
||||
$('#acciones').innerHTML = cab +
|
||||
ACCIONES.map((a, i) => `<div class="ac ${a.estado}"><span class="ico">${a.estado === 'hecho' ? '✓' : a.estado === 'espera' ? '·' : i + 1}</span><div class="tx"><b>${esc(a.titulo)}</b><div class="q">${esc(a.que)}</div>${a.detalle ? `<div class="d">${esc(a.detalle)}</div>` : ''}</div><div class="der">${a.cifra ? `<span class="cifra">${esc(a.cifra)}</span>` : ''}${a.boton ? `<button class="${a.estado === 'espera' ? 'lnk' : 'p'}" data-a="${i}">${esc(a.boton)}</button>` : ''}</div></div>`).join('');
|
||||
$$('#acciones [data-a]').forEach(b => b.onclick = () => { const a = ACCIONES[+b.dataset.a]; (IR_ACCION[a.ir] || (() => {}))(a.paso); });
|
||||
const bt = $('#btnHacerTodo');
|
||||
if (bt) bt.onclick = () => modal('Hacer todo lo seguro de una vez',
|
||||
`<p>La app va a hacer seguidas las <b>${auto.length}</b> cosas de la lista que no necesitan que elijas nada, en orden y sin preguntar.</p>
|
||||
<p>Solo mueve lo que considera seguro: <b>lo dudoso se queda como está</b> para que lo mires tú. Todo se queda dentro de <span class="path">${esc(raiz)}</span>, nada se borra, y cada cambio se deshace desde el Historial.</p>
|
||||
<p class="mut" style="font-size:13px">No entran aquí convertir los clips ni copiar el respaldo: esos tienen su propio botón porque hay que elegir cosas.</p>`,
|
||||
() => accion(bt, async () => {
|
||||
await api('/api/acciones/hacer', {raiz});
|
||||
aviso('En marcha. El avance está en la barra de abajo y aquí arriba.', 'ok');
|
||||
await cargarEstado(); await pintarAcciones();
|
||||
}));
|
||||
}
|
||||
function queSeHizo(lote){
|
||||
if (!lote) return 'Cambio';
|
||||
@@ -785,13 +801,12 @@ function pasosDe(C, r){
|
||||
function pintarSiguiente(P){
|
||||
const C = CARPETA; const s = {t:'', p:'', acts:[]};
|
||||
const b = (txt, fn, p) => s.acts.push({txt, fn, p});
|
||||
const asis = () => asistente(raizActual(), false);
|
||||
if (P[0].e === 'curso') { s.t = 'Escaneando la carpeta'; s.p = 'Está leyendo nombres, tamaños y fechas. No toca nada. Cuando termine, aquí aparece el diagnóstico.'; }
|
||||
else if (P[0].e === 'pend') { s.t = 'Empieza por escanear'; s.p = 'La app lee los nombres, tamaños y fechas de todos los archivos. No mueve nada. En una carpeta grande del NAS tarda unos minutos.'; b('Escanear ahora', () => $('#btnEscanear').click(), true); }
|
||||
else if (P[2].e === 'curso') { s.t = 'El asistente está trabajando'; s.p = 'Puedes seguir el avance en la barra de abajo.'; }
|
||||
else if (P[1].e === 'listo' && !C.plan && P[2].e !== 'hecho') { const n = C.sin_nombre_n; s.t = `Antes de ordenar: nombra ${n} carpeta${n > 1 ? 's' : ''}`; s.p = `La app no sabe qué son ${C.sin_nombre.slice(0, 4).map(x => '«' + x.ejemplo + '»').join(', ')}${n > 4 ? ' y otras' : ''}. Si le dices qué es cada una, las ordena bien y se lo aprende para todos los discos. Si prefieres, sáltalo: quedarán como «dudoso» y no se moverán.`; b('Nombrar las carpetas', () => { irA('revisar'); subRev('nombrar'); }, true); b('Saltar y calcular', () => { irA('revisar'); subRev('plan'); $('#btnPlan').click(); }); b('Asistente paso a paso', asis); }
|
||||
else if (P[1].e === 'listo' && !C.plan && P[2].e !== 'hecho') { const n = C.sin_nombre_n; s.t = `Antes de ordenar: nombra ${n} carpeta${n > 1 ? 's' : ''}`; s.p = `La app no sabe qué son ${C.sin_nombre.slice(0, 4).map(x => '«' + x.ejemplo + '»').join(', ')}${n > 4 ? ' y otras' : ''}. Si le dices qué es cada una, las ordena bien y se lo aprende para todos los discos. Si prefieres, sáltalo: quedarán como «dudoso» y no se moverán.`; b('Nombrar las carpetas', () => { irA('revisar'); subRev('nombrar'); }, true); b('Saltar y calcular', () => { irA('revisar'); subRev('plan'); $('#btnPlan').click(); }); }
|
||||
else if (C.plan) { s.t = `Plan listo: ${C.plan.marcadas} de ${C.plan.filas} movimientos marcados`; s.p = `Todo se mueve dentro de la misma carpeta y nada se borra. ${C.plan.baja ? C.plan.baja + ' son dudosos y están sin marcar. ' : ''}Revisa la lista y aplica; se puede deshacer desde Historial.`; b('Revisar y aplicar', () => { irA('revisar'); subRev('plan'); }, true); }
|
||||
else if (P[2].e === 'pend') { s.t = 'Siguiente: ordenar la carpeta'; s.p = 'Calcula qué va a pasar (basura y proxies a _Para borrar, grabaciones por mes y cámara, piezas numeradas) y aplícalo con un clic. O deja que el asistente te pregunte paso a paso.'; b('Calcular qué va a pasar', () => { irA('revisar'); subRev('plan'); $('#btnPlan').click(); }, true); b('Asistente paso a paso', asis); }
|
||||
else if (P[2].e === 'pend') { s.t = 'Siguiente: ordenar la carpeta'; s.p = 'Calcula qué va a pasar (basura y proxies a _Para borrar, grabaciones por mes y cámara, piezas numeradas) y aplícalo con un clic. O deja que el asistente te pregunte paso a paso.'; b('Calcular qué va a pasar', () => { irA('revisar'); subRev('plan'); $('#btnPlan').click(); }, true); }
|
||||
else if (P[3].e === 'listo') { const d = P[3].d; s.t = 'Siguiente: ' + (d.includes('verificar') ? 'verificar los clips convertidos' : d.includes('reemplazar') ? 'reemplazar los originales' : 'retomar la conversión'); s.p = d.includes('verificar') ? 'Verificar comprueba códec, 10 bits y duración. Después puedes reemplazar los originales (quedan en _Para borrar/originales).' : d.includes('reemplazar') ? 'Los originales se apartan a _Para borrar/originales y el MP4 10 bits queda en su lugar. Nada se borra.' : 'Hay clips en cola con la conversión parada.'; b('Ir a Convertir', () => irA('convertir'), true); }
|
||||
else if (P[3].e === 'curso') { s.t = 'Convirtiendo clips'; s.p = 'Sigue en segundo plano. El avance está en la barra de abajo; cuando termine, toca reemplazar.'; b('Ver la cola', () => irA('convertir')); }
|
||||
else if (P[3].e === 'pend') { const cv = C.convertir; s.t = 'Siguiente: aligerar el material de cámara'; s.p = `Hay ${gb(cv.camara_bytes)} de clips de cámara. Los de proyectos ya entregados pueden pasar a MP4 10 bits, unas 10 veces más ligeros. ${cv.raw_bytes ? 'Los BRAW necesitan DaVinci Resolve Studio abierto. ' : ''}El original no se toca hasta que reemplaces.`; b('Buscar clips a convertir', () => { irA('convertir'); $('#btnBuscarClips').click(); }, true); b('Saltar a respaldar', () => irA('respaldar')); }
|
||||
@@ -1261,7 +1276,6 @@ const asistente = seguro(async (raiz, escanearAntes) => {
|
||||
$('#modalSi').textContent = 'Empezar';
|
||||
});
|
||||
const NOMBRE_PASO = {plan: 'pensando el plan', ordenar: 'ordenando carpetas y archivos', stock: 'apartando stock viejo', duplicados: 'juntando repetidos', analizar: 'leyendo los clips', convertir: 'convirtiendo a 10 bits', respaldo: 'copiando el respaldo', fin: 'listo'};
|
||||
$('#btnAsistente').onclick = () => asistente(raizActual(), false);
|
||||
|
||||
// ---------------- modal ----------------
|
||||
function modal(titulo, html, onSi){ $('#modalTitulo').textContent = titulo; $('#modalCuerpo').innerHTML = html; $('#modal').classList.add('on'); $('#modalSi').style.display = onSi ? '' : 'none'; $('#modalSi').textContent = 'Confirmar'; $('#modalNo').textContent = onSi ? 'Cancelar' : 'Cerrar'; $('#modalSi').onclick = () => { $('#modal').classList.remove('on'); onSi && onSi(); }; $('#modalNo').onclick = () => $('#modal').classList.remove('on'); setTimeout(() => { const f = $('#modalCuerpo input, #modalCuerpo select') || $('#modalSi'); if (f && f.offsetParent) f.focus(); }, 30); }
|
||||
|
||||
Referencia en una nueva incidencia
Block a user