afegits fixes especifics amb confirmació

This commit is contained in:
2026-02-19 09:46:32 +01:00
parent 56954717e3
commit 0f8f904b97
4 changed files with 188 additions and 28 deletions
+61 -27
View File
@@ -16,8 +16,8 @@ from processors.checks import (
check_image_extensions,
check_comicinfo,
)
from processors.page_normalizer import normalize_pages
from processors.image_normalizer import normalize_images
from processors.page_normalizer import normalize_pages, preview_normalize_pages
from processors.image_normalizer import normalize_images, preview_normalize_images
class Pipeline:
@@ -35,7 +35,30 @@ class Pipeline:
self.collision_policy = collision_policy
self.dry_run = dry_run
def run(self, path: str) -> ComicResult:
def _compute_preview(self, step: str, temp_dir: str, step_results: list) -> dict:
if step == "clean":
trash_result = next((r for r in step_results if r.step == "check_trash"), None)
if trash_result:
prefix = "Basura detectada: "
items = [w.removeprefix(prefix) for w in trash_result.warnings if w.startswith(prefix)]
else:
items = []
return {"items": items}
elif step == "normalize_pages":
renames = preview_normalize_pages(temp_dir)
return {"renames": renames}
elif step == "normalize_images":
conversions = preview_normalize_images(temp_dir, self.desired_image_format)
return {"conversions": conversions, "target_ext": self.desired_image_format}
elif step == "convert":
return {"target_format": self.desired_format.upper()}
return {}
def run(self, path: str, confirm_fn=None) -> ComicResult:
step_results = []
# 1. Validar siempre, antes de extraer
@@ -63,36 +86,47 @@ class Pipeline:
any_changed = False
if "clean" in self.steps:
clean_result = clean_directory(temp_dir)
step_results.append(clean_result)
if clean_result.changed:
any_changed = True
preview = self._compute_preview("clean", temp_dir, step_results)
if preview.get("items"):
if confirm_fn is None or confirm_fn("clean", preview):
clean_result = clean_directory(temp_dir)
step_results.append(clean_result)
if clean_result.changed:
any_changed = True
if "normalize_pages" in self.steps:
norm_result = normalize_pages(temp_dir)
step_results.append(norm_result)
if norm_result.changed:
any_changed = True
preview = self._compute_preview("normalize_pages", temp_dir, step_results)
if preview.get("renames"):
if confirm_fn is None or confirm_fn("normalize_pages", preview):
norm_result = normalize_pages(temp_dir)
step_results.append(norm_result)
if norm_result.changed:
any_changed = True
if "normalize_images" in self.steps:
img_result = normalize_images(temp_dir, self.desired_image_format)
step_results.append(img_result)
if img_result.errors:
return ComicResult(
original_path=path, final_path=None, steps=step_results
)
if img_result.changed:
any_changed = True
preview = self._compute_preview("normalize_images", temp_dir, step_results)
if preview.get("conversions"):
if confirm_fn is None or confirm_fn("normalize_images", preview):
img_result = normalize_images(temp_dir, self.desired_image_format)
step_results.append(img_result)
if img_result.errors:
return ComicResult(
original_path=path, final_path=None, steps=step_results
)
if img_result.changed:
any_changed = True
if "convert" in self.steps:
conv_result = conversion_step_result(real_format, self.desired_format)
step_results.append(conv_result)
if conv_result.errors:
return ComicResult(
original_path=path, final_path=None, steps=step_results
)
if conv_result.changed:
any_changed = True
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)
step_results.append(conv_result)
if conv_result.errors:
return ComicResult(
original_path=path, final_path=None, steps=step_results
)
if conv_result.changed:
any_changed = True
# 5. Reempaquetar si hubo cambios o conversión de formato
needs_repack = any_changed or (
+80 -1
View File
@@ -4,6 +4,55 @@ import argparse
from core.scanner import find_comic_files
from core.pipeline import Pipeline
_COL_W = 30
_SEP = "" * 44
def _print_preview(step: str, preview: dict, formato: str) -> None:
fmt = formato.upper()
if step == "clean":
print("Ficheros basura a eliminar:")
for item in preview["items"]:
print(f" - {item}")
print(f"Formato final del archivo: {fmt}")
elif step == "normalize_pages":
renames = preview["renames"]
n = len(renames)
print(f"Renombrado de páginas ({n} páginas):")
print(f" {'Nombre actual':<{_COL_W}} → Nombre final")
print(f" {_SEP}")
display = renames[:10] if n > 20 else renames
for orig, final in display:
print(f" {orig:<{_COL_W}} {final}")
if n > 20:
print(f" ... y {n - 10} más")
print(f"Formato final del archivo: {fmt}")
elif step == "normalize_images":
conversions = preview["conversions"]
target_ext = preview["target_ext"].lstrip(".")
lossless = " (sin pérdida)" if target_ext.lower() == "png" else ""
print(f"Conversión de imágenes a {target_ext.upper()}{lossless}:")
print(f" {'Imagen actual':<{_COL_W}} → Imagen final")
print(f" {_SEP}")
for orig, final in conversions:
print(f" {orig:<{_COL_W}} {final}")
print(f"Formato final del archivo: {fmt}")
elif step == "convert":
print(f"Conversión de formato de archivo a {preview['target_format']}.")
def make_confirm_fn(args):
def confirm_fn(step: str, preview: dict) -> bool:
print()
_print_preview(step, preview, args.formato)
answer = input("¿Aplicar? [s/N] ").strip().lower()
return answer in ("s", "si", "", "y", "yes")
return confirm_fn
def parse_args():
parser = argparse.ArgumentParser(description="Gestor de cómics CBR/CBZ")
@@ -17,7 +66,14 @@ def parse_args():
parser.add_argument("--formato", choices=["cbz", "cbr"], default="cbz")
parser.add_argument("--renumerar", action="store_true")
parser.add_argument("--uniformizar-imagenes", action="store_true")
parser.add_argument("--formato-imagen", choices=["jpg", "png", "webp"], default="jpg")
parser.add_argument("--formato-imagen", choices=["jpg", "png", "webp"], default="png")
# Fix flags (interactivos)
parser.add_argument("--fix_remove_trash", action="store_true")
parser.add_argument("--fix_page_numbering", action="store_true")
parser.add_argument("--fix_image_extensions", action="store_true")
parser.add_argument("--fix_all", action="store_true")
parser.add_argument("--no-preguntar", action="store_true")
return parser.parse_args()
@@ -40,6 +96,29 @@ def main():
print()
return
# --- Fix flags (interactivos) ---
fix_steps = []
if args.fix_remove_trash or args.fix_all:
fix_steps.append("clean")
if args.fix_page_numbering or args.fix_all:
fix_steps.append("normalize_pages")
if args.fix_image_extensions or args.fix_all:
fix_steps.append("normalize_images")
if fix_steps:
confirm_fn = None if args.no_preguntar else make_confirm_fn(args)
pipeline = Pipeline(
steps=fix_steps,
desired_format=args.formato,
desired_image_format="." + args.formato_imagen,
)
for f in comic_files:
print(f"\n=== {f} ===")
result = pipeline.run(f, confirm_fn=confirm_fn)
print(result.summary())
return
# --- Flags clásicos (sin confirmación) ---
steps = []
if args.limpiar or args.estandarizar:
steps.append("clean")
+21
View File
@@ -6,6 +6,27 @@ from core.constants import IMAGE_EXTENSIONS
from core.result import StepResult
def preview_normalize_images(work_dir: str, target_ext: str) -> list[tuple[str, str]]:
"""Devuelve lista de (nombre_actual, nombre_final) sin convertir nada."""
target_ext = target_ext.lower()
if not target_ext.startswith("."):
target_ext = "." + target_ext
result = []
for root, _, files in os.walk(work_dir):
for f in files:
ext = os.path.splitext(f)[1].lower()
normalized_ext = ".jpg" if ext == ".jpeg" else ext
if ext not in IMAGE_EXTENSIONS:
continue
if normalized_ext == target_ext:
continue
stem = os.path.splitext(f)[0]
result.append((f, stem + target_ext))
return result
def normalize_images(work_dir: str, target_ext: str = ".jpg") -> StepResult:
"""
Convierte todas las imágenes al formato indicado por target_ext.
+26
View File
@@ -12,6 +12,32 @@ def _natural_sort_key(name: str):
return [int(p) if p.isdigit() else p.lower() for p in parts]
def preview_normalize_pages(work_dir: str) -> list[tuple[str, str]]:
"""Devuelve lista de (nombre_actual, nombre_final) sin modificar nada."""
images = []
for root, _, files in os.walk(work_dir):
for f in files:
ext = os.path.splitext(f)[1].lower()
if ext in IMAGE_EXTENSIONS:
images.append(os.path.join(root, f))
images.sort(key=lambda p: _natural_sort_key(os.path.splitext(os.path.basename(p))[0]))
total = len(images)
if total == 0:
return []
width = len(str(total))
result = []
for i, src in enumerate(images):
ext = os.path.splitext(src)[1].lower()
original_name = os.path.basename(src)
final_name = f"{str(i + 1).zfill(width)}{ext}"
if original_name != final_name:
result.append((original_name, final_name))
return result
def normalize_pages(work_dir: str) -> StepResult:
"""
Renombra las imágenes del cómic a una numeración secuencial con zero-padding.