From 37c88791081b232f9f5267dc01d01a2eae54738d Mon Sep 17 00:00:00 2001 From: Sergio Date: Fri, 20 Feb 2026 09:43:48 +0100 Subject: [PATCH] backup per defecte covertir arregla extensions incorrectes --- README.md | 2 +- core/pipeline.py | 38 +++++++++++++++++++++++++++++++------- main.py | 9 +++++++++ 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 06c0c1e..4976041 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ python main.py --ruta ~/Comics --limpiar # Limpiar basura sin preguntar (modo automático) python main.py --ruta ~/Comics --limpiar --no-preguntar -# Limpiar y convertir todo a CBZ (pide confirmación para la limpieza) +# Limpiar, renumerar, uniformizar imágenes y convertir a CBZ (pide confirmación en cada paso) python main.py --ruta ~/Comics --estandarizar # Convertir todo a CBZ sin confirmación diff --git a/core/pipeline.py b/core/pipeline.py index c724cf8..641fff8 100644 --- a/core/pipeline.py +++ b/core/pipeline.py @@ -7,6 +7,7 @@ import shutil import rarfile from core.archive import detect_real_format, extract_archive, repack_as_cbz, ArchiveError, list_archive_names +from core.backup import move_to_backup from core.collision import CollisionPolicy, resolve_collision from core.result import ComicResult, StepResult from processors.validator import validate_archive @@ -69,12 +70,16 @@ class Pipeline: return {} - def _needs_extraction(self, step_results: list, real_format: str) -> bool: + def _needs_extraction(self, step_results: list, real_format: str, path: str) -> bool: for step in self.steps: if step in ("normalize_pages", "normalize_images", "convert_images"): return True - if step == "convert" and needs_conversion(real_format, self.desired_format): - return True + if step == "convert": + if needs_conversion(real_format, self.desired_format): + return True + ext = os.path.splitext(path)[1].lower().lstrip(".") + if ext != self.desired_format: + return True if step == "clean": trash = next((r for r in step_results if r.step == "check_trash"), None) if trash and trash.warnings: @@ -108,7 +113,7 @@ class Pipeline: ] # 4. Pre-flight: si ningún step necesita extracción, salir sin tocar el archivo - if not self._needs_extraction(step_results, real_format): + if not self._needs_extraction(step_results, real_format, path): return ComicResult(original_path=path, final_path=path, steps=step_results) # 5. Extraer una sola vez @@ -167,6 +172,18 @@ class Pipeline: preview = self._compute_preview("convert", temp_dir, step_results) if confirm_fn is None or confirm_fn("convert", preview): conv_result = conversion_step_result(real_format, self.desired_format) + # Extensión incorrecta aunque el formato real ya sea correcto + file_ext = os.path.splitext(path)[1].lower().lstrip(".") + if ( + not conv_result.errors + and not conv_result.changed + and file_ext != self.desired_format + ): + conv_result = StepResult( + step="convert", + changed=True, + details=[f"Extensión incorrecta corregida: .{file_ext} → .{self.desired_format}"], + ) step_results.append(conv_result) if conv_result.errors: return ComicResult( @@ -176,9 +193,13 @@ class Pipeline: any_changed = True # 7. Reempaquetar si hubo cambios o conversión de formato + ext = os.path.splitext(path)[1].lower().lstrip(".") needs_repack = any_changed or ( "convert" in self.steps - and needs_conversion(real_format, self.desired_format) + and ( + needs_conversion(real_format, self.desired_format) + or ext != self.desired_format + ) ) if not needs_repack: @@ -192,9 +213,12 @@ class Pipeline: if not self.dry_run: safe_target = resolve_collision(target_path, self.collision_policy) repack_as_cbz(temp_dir, safe_target) - # Eliminar original si el nombre cambió + # Eliminar o mover a backup el original si el nombre cambió if safe_target != path and os.path.exists(path): - os.remove(path) + if self.collision_policy == CollisionPolicy.BACKUP: + move_to_backup(path) + else: + os.remove(path) else: safe_target = target_path diff --git a/main.py b/main.py index 047fb2f..7ba4b01 100644 --- a/main.py +++ b/main.py @@ -4,6 +4,7 @@ import argparse from core.scanner import find_comic_files from core.pipeline import Pipeline from core.summary import SummaryCollector +from core.collision import CollisionPolicy _COL_W = 30 _SEP = "─" * 44 @@ -70,6 +71,12 @@ def parse_args(): parser.add_argument("--formato-imagen", choices=["jpg", "png", "webp"], default="png") parser.add_argument("--no-preguntar", action="store_true") + parser.add_argument( + "--al-modificar", + choices=["backup", "borrar"], + default="backup", + dest="al_modificar", + ) return parser.parse_args() @@ -110,10 +117,12 @@ def main(): if steps: confirm_fn = None if args.no_preguntar else make_confirm_fn(args) + collision = CollisionPolicy.BACKUP if args.al_modificar == "backup" else CollisionPolicy.ABORT pipeline = Pipeline( steps=steps, desired_format=args.formato, desired_image_format="." + args.formato_imagen, + collision_policy=collision, ) collector = SummaryCollector() for f in comic_files: