afegits fixes especifics amb confirmació
This commit is contained in:
+61
-27
@@ -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 (
|
||||
|
||||
@@ -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", "sí", "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")
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user