Docker stuff and folder watcher
This commit is contained in:
+60
-1
@@ -161,6 +161,60 @@ def _al_id_from_metadata(md: dict) -> "int | None":
|
||||
return None
|
||||
|
||||
|
||||
def _chapter_image_size(chapter_dir: Path) -> int:
|
||||
"""Returns the total file size of all images in a chapter folder."""
|
||||
return sum(
|
||||
f.stat().st_size
|
||||
for f in chapter_dir.iterdir()
|
||||
if f.is_file() and f.suffix.lower() in _IMAGE_EXTS
|
||||
)
|
||||
|
||||
|
||||
def _deduplicate_chapters(
|
||||
chapter_items: list[tuple[Path, dict, str]],
|
||||
) -> tuple[list[tuple[Path, dict, str]], list[Path]]:
|
||||
"""
|
||||
When multiple chapter folders share the exact same chapter number
|
||||
(e.g. two folders for chapter "2" — not "2" vs "2.2"), keeps only the
|
||||
one with the highest total image file size, which is a reliable proxy
|
||||
for image quality.
|
||||
|
||||
Chapter number comes from ComicInfo.xml <Number>; comparison is an exact
|
||||
string match so "2" and "2.2" are never considered duplicates.
|
||||
|
||||
Returns
|
||||
-------
|
||||
kept : deduplicated chapter_items list (original sort order preserved)
|
||||
rejected : Path list of lower-quality duplicate folders to be removed
|
||||
"""
|
||||
best: dict[str, tuple[Path, dict, str]] = {}
|
||||
best_size: dict[str, int] = {}
|
||||
rejected: list[Path] = []
|
||||
|
||||
for item in chapter_items:
|
||||
chapter_dir, fields, chapter_num = item
|
||||
size = _chapter_image_size(chapter_dir)
|
||||
|
||||
if chapter_num not in best:
|
||||
best[chapter_num] = item
|
||||
best_size[chapter_num] = size
|
||||
elif size > best_size[chapter_num]:
|
||||
prev_dir = best[chapter_num][0]
|
||||
print(f" [dup] ch.{chapter_num}: replacing {prev_dir.name!r} "
|
||||
f"({best_size[chapter_num]:,}B) with {chapter_dir.name!r} "
|
||||
f"({size:,}B) — higher quality")
|
||||
rejected.append(prev_dir)
|
||||
best[chapter_num] = item
|
||||
best_size[chapter_num] = size
|
||||
else:
|
||||
print(f" [dup] ch.{chapter_num}: skipping {chapter_dir.name!r} "
|
||||
f"({size:,}B), keeping {best[chapter_num][0].name!r} "
|
||||
f"({best_size[chapter_num]:,}B)")
|
||||
rejected.append(chapter_dir)
|
||||
|
||||
return list(best.values()), rejected
|
||||
|
||||
|
||||
def _extract_chapter_num(folder_name: str) -> "str | None":
|
||||
"""
|
||||
Fallback: extracts chapter number from the folder name.
|
||||
@@ -325,6 +379,11 @@ class SuwayomiMover:
|
||||
continue
|
||||
chapter_items.append((chapter_dir, fields, chapter_num))
|
||||
|
||||
chapter_items, rejected_dirs = _deduplicate_chapters(chapter_items)
|
||||
if self._delete_source:
|
||||
for d in rejected_dirs:
|
||||
shutil.rmtree(d, ignore_errors=True)
|
||||
|
||||
# <Series> from the first chapter's XML → strip source labels → clean title
|
||||
# for the MangaBaka search. Folder name is the last resort.
|
||||
raw_series = manga_title
|
||||
@@ -436,7 +495,7 @@ if __name__ == "__main__":
|
||||
)
|
||||
|
||||
# Process a single series
|
||||
result = mover.process_series("Yofukashi no Uta_ Rakuen-hen")
|
||||
result = mover.process_series("Yofukashi no Uta")
|
||||
ok = sum(1 for c in result["chapters"] if c["ok"])
|
||||
failed = sum(1 for c in result["chapters"] if not c["ok"])
|
||||
print(f"\nDone: {ok} ok, {failed} failed")
|
||||
|
||||
Reference in New Issue
Block a user