- Published on
Media Ingest — rendimiento: no hashear todo, no pintar de más
- Authors

- Name
- Mateo Londoño Toro
Después de Temporal → Media el cuello de botella no era copiar: era escanear miles de fotos y repintar Qt en cada señal del worker. El working tree de Media Ingest cierra eso en tres frentes.
Este corte es bitácora del proyecto Media Ingest (Windows USB + macOS Fase 1: carpeta local, Organizar, Historial, Limpieza).
1) Hash solo cuando el tamaño choca
scan_media_folder (domain/folder_cleanup.py) recorre con os.walk, agrupa por tamaño y solo entonces llama a hash_file.
walk (stat) → reglas de nombre (IMG_ vs IMG_E, $I*, caché)
→ _hash_needed: SHA-256 si size count ≥ 2
→ clasificar ($R*, staging, hash dup)
Un HEIC único de 2.4 MiB no se lee entero. Dos archivos del mismo byte-count sí: ahí el hash decide si es Recuva $R*, staging ya archivado, o duplicado canónico.
Quién se conserva (_keep_score): archivo sobre staging, ruta con YYYY/MM/DD, no Recuva, original sobre IMG_E, no IMG_1234_2, path más corto. Nunca ambos original y editado: política por defecto conservar IMG_, cuarentenar IMG_E.
Progreso en dos fases (walk cada 200 ficheros, hash con bytes_hashed) para no fingir un % sobre trabajo que no se va a hacer.
Reglas baratas primero (sin I/O de contenido): Recuva $I*, node_modules / .pnpm-store / .cache, IMG_E#### si existe IMG_#### misma carpeta + extensión. Live Photo .MOV (tope 20 MiB) y sidecars .AAE se listan sin selección por defecto: calidad / sidecar, no duplicado.
4K → 1080p está fuera de alcance: eso es calidad, no duplicado.
2) El hilo GUI no es un sink de cada archivo
UiCoalesce / UiBatchQueue (presentation/widgets/ui_coalesce.py): el worker no aplica cada señal al instante. Se queda el último estado (barra, caption) o una cola corta de tiles, y un QTimer drena. Qt puede interpolar paint e input. Patrón de la propia doc de Qt: no pintar cada tick del worker.
Animación decorativa apagada por flag:
# presentation/widgets/__init__.py
ANIMATIONS_ENABLED = False
El canvas de transferencia, los fades de preview y el barrido del scan siguen en el código; no arrancan timers ni QGraphicsOpacityEffect hasta que el flag vuelva a True. En un Mac con discos Mounty (~/.mounty/Disco C_C) eso se nota más que el Fluent en sí.
Limpieza no usa TransferStage. Spec: la tabla permanece; progreso inline + DiskScanIndicator. Inspeccionar ≠ transferir. Peer-busy solo durante scan / cuarentena / purge / restore; no dejar la vista “ocupada” entre escanear y Limpiar.
Tope de tabla: 400 filas en carpeta, 80 en disco. El plan completo vive en el resultado; la UI no materializa decenas de miles de QTableWidgetItem.
3) No trabajar placeholders ni cuota inventada
- iCloud:
icloud_skip_reasonomite stubs.iclouden Sync y Organizar (warning en log, no copia silenciosa). Prefiere origen/destino ya descargados. - Papelera macOS: no hay API de cuota tipo BitBucket; el preflight marca
quota_checked=Falsey trata el vaciado como riesgo. Vaciar sigue siendo la operación que el incidente 2026-07-31 enseñó a no hacer a la ligera. - Volúmenes Darwin:
list_volumesincluye/,/Volumesy mounts Mounty, condisk_usage— sin enumerar el árbol de media para pintar el home.

Captura regenerada del módulo Limpieza (docs/assets/ui-cleanup.png). Modo carpeta: Temporal vs archivo, no auditoría de todo el disco.
Qué es producto (además del perf)
Dos modos en la misma vista:
| Modo | Alcance |
|---|---|
| Disco | Auditoría de volumen (hints de caché / basura) |
| Carpeta | Duplicados de dump iPhone vs archivo (folder_cleanup) |
Invalidar el audit al cambiar disco o modo. send2trash en el stack para no inventar un recycle propio en Mac.
Empaquetado macOS (Homebrew Python 3.12 + Nuitka, ruta ASCII ~/MediaIngestBuild): existe en el working tree; Gatekeeper sin firmar = clic derecho → Abrir. Windows sigue siendo el ZIP en packaged-releases.
Esto no estaba en HEAD publicado al escribir el post (working tree vs e0111ab). Describe el corte que está en disco.
Serie · proyecto
- Windows / USB
- macOS Fase 1
- Captura de imagen
- Evidencia Temporal → Media
- Rendimiento y limpieza (este post)
- Ficha: Proyectos