Init CBZ Volume Combiner v0.2.0
This commit is contained in:
129
bin/cbz-volume-combiner
Executable file
129
bin/cbz-volume-combiner
Executable file
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python
|
||||
# File: /home/code/projects/manga-organizer-1/cbz-volume-combiner/bin/cbz-volume-combiner
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
from cbz_volume_combiner.file_utils import find_cbz_files
|
||||
from cbz_volume_combiner.core import organize_by_volume
|
||||
from cbz_volume_combiner.volume import create_volume_cbz
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Combine individual CBZ chapters into volume CBZ files')
|
||||
parser.add_argument('folder', help='Folder containing CBZ chapter files')
|
||||
parser.add_argument('-r', '--recursive', action='store_true', help='Search for CBZ files recursively')
|
||||
parser.add_argument('-o', '--output', help='Output folder for volume CBZ files (defaults to same location as chapters)')
|
||||
parser.add_argument('-f', '--force', action='store_true', help='Force creation even if volume CBZ already exists')
|
||||
parser.add_argument('-v', '--verbose', action='store_true', help='Show detailed progress')
|
||||
parser.add_argument('-vv', '--extra-verbose', action='store_true', help='Show extremely detailed debugging information')
|
||||
parser.add_argument('-m', '--min-chapters', type=int, default=2,
|
||||
help='Minimum number of chapters required to create a volume (default: 2)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# If extra-verbose is enabled, automatically enable verbose too
|
||||
if args.extra_verbose:
|
||||
args.verbose = True
|
||||
|
||||
if not os.path.isdir(args.folder):
|
||||
print(f"Error: '{args.folder}' is not a valid directory")
|
||||
return 1
|
||||
|
||||
cbz_files = find_cbz_files(args.folder, args.recursive, args.extra_verbose)
|
||||
|
||||
if not cbz_files:
|
||||
print(f"No CBZ files found in '{args.folder}'")
|
||||
return 0
|
||||
|
||||
print(f"Found {len(cbz_files)} CBZ file(s)")
|
||||
|
||||
# Organize files by manga and volume
|
||||
volumes = organize_by_volume(cbz_files, args.extra_verbose)
|
||||
|
||||
total_manga = len(volumes)
|
||||
total_volumes = sum(len(volumes[manga]) for manga in volumes)
|
||||
|
||||
print(f"Found {total_manga} manga series with {total_volumes} volume(s) to process")
|
||||
|
||||
# Print detailed manga and volume information in extra verbose mode
|
||||
if args.extra_verbose:
|
||||
print("\nDetailed manga and volume breakdown:")
|
||||
for manga_name in volumes:
|
||||
try:
|
||||
first_volume = min(volumes[manga_name].keys())
|
||||
manga_display_name = volumes[manga_name][first_volume][0]['manga_name']
|
||||
print(f"\n{manga_display_name}:")
|
||||
for volume_num in sorted(volumes[manga_name].keys()):
|
||||
chapters = volumes[manga_name][volume_num]
|
||||
print(f" Volume {volume_num}: {len(chapters)} chapters")
|
||||
if args.extra_verbose:
|
||||
for chapter in chapters:
|
||||
print(f" - Chapter {chapter['chapter_str']}: {os.path.basename(chapter['filename'])}")
|
||||
print(f" Exists: {os.path.exists(chapter['filename'])}")
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
success_count = 0
|
||||
skip_count = 0
|
||||
fail_count = 0
|
||||
ignored_count = 0
|
||||
|
||||
# Process each manga and volume
|
||||
for manga_name in volumes:
|
||||
# Get a proper display name from the first volume's first chapter
|
||||
try:
|
||||
first_volume = min(volumes[manga_name].keys())
|
||||
manga_display_name = volumes[manga_name][first_volume][0]['manga_name']
|
||||
except (ValueError, IndexError, KeyError):
|
||||
# Fallback if we can't get proper name
|
||||
manga_display_name = manga_name
|
||||
|
||||
if args.verbose:
|
||||
print(f"\nProcessing manga: {manga_display_name}")
|
||||
|
||||
for volume_num in sorted(volumes[manga_name].keys()):
|
||||
chapters = volumes[manga_name][volume_num]
|
||||
|
||||
# Skip volumes with too few chapters
|
||||
if len(chapters) < args.min_chapters:
|
||||
if args.verbose:
|
||||
print(f"Skipping Volume {volume_num} - only has {len(chapters)} chapter(s) (minimum is {args.min_chapters})")
|
||||
ignored_count += 1
|
||||
continue
|
||||
|
||||
# List all chapters for debugging
|
||||
if args.extra_verbose:
|
||||
print(f"\nChapters for {manga_display_name} Volume {volume_num}:")
|
||||
for chapter in chapters:
|
||||
print(f" - Chapter {chapter['chapter_str']}: {os.path.basename(chapter['filename'])}")
|
||||
print(f" Exists: {os.path.exists(chapter['filename'])}")
|
||||
|
||||
success, message = create_volume_cbz(
|
||||
manga_display_name,
|
||||
volume_num,
|
||||
chapters,
|
||||
args.output,
|
||||
args.force,
|
||||
args.verbose,
|
||||
args.extra_verbose
|
||||
)
|
||||
|
||||
if success:
|
||||
if message == "Skipped (already exists)":
|
||||
skip_count += 1
|
||||
else:
|
||||
success_count += 1
|
||||
else:
|
||||
fail_count += 1
|
||||
print(f"Error creating Volume {volume_num} for {manga_display_name}: {message}")
|
||||
|
||||
print(f"\nVolume creation complete:")
|
||||
print(f" - {success_count} volumes created successfully")
|
||||
print(f" - {skip_count} volumes skipped (already exist)")
|
||||
print(f" - {ignored_count} volumes ignored (too few chapters)")
|
||||
print(f" - {fail_count} volumes failed")
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user