Inventario fiel al disco: mayúsculas de las carpetas, mover durante el escaneo, y no convertir dos veces

Cotejado archivo por archivo cada disco con el inventario: el escaneo estaba completo, pero el inventario
apuntaba la carpeta con las mayúsculas del plan (1.039 fotos en Vía Sumapaz, tres carpetas donde hay una), un
proxy apartado en pleno escaneo quedó dos veces, y los 11 verificados de Línea Gene se habían vuelto a encolar
con la 3.7. Ahora la carpeta se escribe como en el disco y se recuenta donde quedó al final, el escaneo vuelve a
recorrer si la app movió algo mientras, y al reenganchar se quita la conversión que sobra.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Este commit está contenido en:
Labstream
2026-10-14 14:48:55 -05:00
padre eafd1984f3
commit 8ef3af6ad7
Se han modificado 3 ficheros con 249 adiciones y 20 borrados

Ver fichero

@@ -501,6 +501,60 @@ class Motor(unittest.TestCase):
self.assertEqual(R.firma_archivo("C0001_arch.mp4"), "SONY")
self.assertTrue(R.es_conversion("C0001_10bit.mp4")) # y los de siempre siguen valiendo
def test_el_inventario_escribe_la_carpeta_como_el_disco(self):
"""Vía Sumapaz, 14-oct-2026: el plan movió fotos a «Altas Print» y «Altas print», que en el disco son la misma
«ALTAS PRINT»; el inventario apuntó tres carpetas distintas con 1.039 fotos mal escritas."""
raiz = cliente("Mayusculas")
nr = os.path.normpath(raiz)
a = mk(raiz, "Sueltas/foto1.jpg", 2024, 5, 3)
b = mk(raiz, "Sueltas/foto2.jpg", 2024, 5, 3)
mk(raiz, "2024/05- Mayo/_Fotos/ALTAS PRINT/foto0.jpg", 2024, 5, 3)
O.escanear(raiz)
movidos = []
O.mover(a, os.path.join(nr, "2024", "05- Mayo", "_Fotos", "Altas Print", "foto1.jpg"), "prueba", movidos)
O.mover(b, os.path.join(nr, "2024", "05- mayo", "_fotos", "altas print", "foto2.jpg"), "prueba", movidos)
bien = os.path.join(nr, "2024", "05- Mayo", "_Fotos", "ALTAS PRINT")
self.assertEqual([d for _, d in movidos], [os.path.join(bien, "foto1.jpg"), os.path.join(bien, "foto2.jpg")])
O.actualizar_inventario(movidos)
con = O.db()
rutas = {r[0] for r in con.execute("SELECT ruta FROM archivos WHERE raiz = ?", (nr,))}
carpetas = [r[0] for r in con.execute("SELECT ruta FROM carpetas WHERE raiz = ? AND lower(ruta) = lower(?)", (nr, bien))]
con.close()
self.assertTrue({os.path.join(bien, n) for n in ("foto0.jpg", "foto1.jpg", "foto2.jpg")} <= rutas, rutas)
self.assertEqual(carpetas, [bien])
# una carpeta que no existía se crea como la escribe el plan, y las siguientes la siguen igual
c = mk(raiz, "Sueltas/foto3.jpg", 2024, 5, 3)
d = mk(raiz, "Sueltas/foto4.jpg", 2024, 5, 3)
movidos = []
O.mover(c, os.path.join(nr, "Nueva Carpeta", "foto3.jpg"), "prueba", movidos)
O.mover(d, os.path.join(nr, "NUEVA CARPETA", "foto4.jpg"), "prueba", movidos)
self.assertEqual({os.path.dirname(x) for _, x in movidos}, {os.path.join(nr, "Nueva Carpeta")})
def test_escaneo_con_movimientos_a_la_vez(self):
"""Vía Sumapaz, 14-oct-2026: un proxy apartado a _Para borrar en pleno escaneo quedó dos veces en el
inventario (visto en su sitio y luego en la papelera). Si la app mueve algo mientras se recorre, se recorre otra vez."""
raiz = cliente("EnMarcha")
nr = os.path.normpath(raiz)
p = mk(raiz, "2024/09- Septiembre/03- Botellitas/Proxy/Botellas Vr2.mov", 2024, 9, 3)
mk(raiz, "2024/09- Septiembre/03- Botellitas/Botellas Vr2.mov", 2024, 9, 3, tam=4096)
real = O._fechas_de_rodaje
llamadas = []
def a_la_vez(filas):
llamadas.append(len(filas))
if len(llamadas) == 1: # después de recorrer, antes de guardar: la app aparta el proxy
O.mover(p, os.path.join(nr, R.PAPELERA, "proxies", os.path.relpath(p, nr)), "aparte", [])
return real(filas)
O._fechas_de_rodaje = a_la_vez
self.addCleanup(setattr, O, "_fechas_de_rodaje", real)
O.escanear(raiz)
self.assertEqual(len(llamadas), 2)
con = O.db()
proxies = [r[0] for r in con.execute("SELECT ruta FROM archivos WHERE raiz = ? AND nombre = 'Botellas Vr2.mov' AND bytes = 2048", (nr,))]
con.close()
self.assertEqual(proxies, [os.path.join(nr, R.PAPELERA, "proxies", os.path.relpath(p, nr))])
self.assertEqual(O.PROGRESO[nr]["estado"], "listo")
def test_inventario_de_otra_version(self):
"""Lo que apunta el escaneo cambió: un inventario viejo se relee antes de planear nada."""
raiz = cliente("Viejo")
@@ -1214,6 +1268,53 @@ class Convertidor(unittest.TestCase):
self.assertTrue(os.path.isfile(os.path.join(nr, R.PAPELERA, "originales", "Otra carpeta", os.path.basename(c2))))
self.assertEqual(self.trabajo(ic)["salida"], junto)
def test_lo_ya_convertido_no_se_convierte_dos_veces(self):
"""Línea Gene, 14-oct-2026: con la 3.7 los 11 verificados se volvieron a encolar en su sitio nuevo, y uno ya
se había convertido otra vez. Al reengancharlos: la conversión que aún espera sobra; la ya hecha deja de más
la vieja."""
raiz = cliente("DosVeces")
nr = os.path.normpath(raiz)
a = mk(raiz, "Material Sony/20250722_Labstream3805.MP4", 2025, 7, 22, tam=300000)
b = mk(raiz, "Material Sony/20250722_Labstream3766.MP4", 2025, 7, 22, tam=300001)
O.escanear(raiz)
self.leidos(raiz, {os.path.basename(x): {"codec": "h264", "perfil": "High", "kbps": 120000} for x in (a, b)})
ia, ib = O.encolar([a, b], "equilibrado", "420", "rec709", nr)
for i in (ia, ib):
with open(self.trabajo(i)["salida"], "wb") as fh:
fh.write(b"x" * 90000)
O.set_trabajo(i, estado="verificado", bytes_out=90000)
# la 3.7: los clips se movieron, la cola no se enteró, y se volvieron a encolar en el sitio nuevo
nueva = os.path.join(nr, "2025", "07- Julio", "00_Grabacion", "22- Martes", "SONY")
os.makedirs(nueva)
a2, b2 = os.path.join(nueva, os.path.basename(a)), os.path.join(nueva, os.path.basename(b))
os.rename(a, a2)
os.rename(b, b2)
with open(os.path.splitext(a2)[0] + "_10bit.mp4", "wb") as fh: # el de a ya se convirtió otra vez allí
fh.write(b"y" * 90000)
con = O.db()
with con:
ids = []
for ruta, estado in ((a2, "verificado"), (b2, "pendiente")):
ids.append(con.execute("INSERT INTO trabajos (ruta, salida, motor, perfil, chroma, color, kbps, estado, bytes_in, bytes_out, creado, raiz, formato) "
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", (ruta, os.path.splitext(ruta)[0] + "_10bit.mp4", "ffmpeg", "equilibrado", "420",
"rec709", 21150, estado, os.path.getsize(ruta), 90000, time.time(), nr,
"h265_10bit")).lastrowid)
con.close()
O.escanear(raiz)
ta, tb = self.trabajo(ia), self.trabajo(ib)
# el de a ya se había vuelto a convertir: sobra el viejo, que queda «no compensa» con su porqué
self.assertEqual(ta["estado"], "descartado")
self.assertIn("se volvió a convertir en su sitio nuevo", ta["log"])
self.assertEqual(self.trabajo(ids[0])["estado"], "verificado")
# el de b aún esperaba: se quita de la cola, y el viejo se reengancha (se reemplaza junto a su original)
con = O.db()
self.assertIsNone(con.execute("SELECT id FROM trabajos WHERE id = ?", (ids[1],)).fetchone())
con.close()
self.assertEqual((tb["estado"], tb["ruta"]), ("verificado", b2))
r = O.reemplazar([ib])[0]
self.assertTrue(r["ok"], r)
self.assertTrue(os.path.isfile(os.path.join(nueva, os.path.basename(tb["salida"]))))
def test_el_codificador_que_toca(self):
fm = O.FORMATOS["h265_10bit"]
t = {"chroma": "420", "perfil": "equilibrado", "kbps": 30000}