Skip to content

Commit

Permalink
Metron updates (#26)
Browse files Browse the repository at this point in the history
- Update with new MetronInfo format statuses
 - Tidy up main
 - Unlink THEN move when converting archives
  • Loading branch information
Buried-In-Code authored Aug 5, 2024
1 parent 54eb48e commit 8b6355a
Show file tree
Hide file tree
Showing 14 changed files with 109 additions and 135 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.2
rev: v0.5.6
hooks:
- id: ruff-format
- id: ruff
Expand Down
2 changes: 1 addition & 1 deletion perdoo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"get_data_root",
"setup_logging",
]
__version__ = "0.2.1"
__version__ = "0.3.0"

import logging
import os
Expand Down
187 changes: 81 additions & 106 deletions perdoo/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from pathlib import Path
from platform import python_version
from tempfile import TemporaryDirectory
from typing import cast

from pydantic import ValidationError
from rich.prompt import Prompt
Expand Down Expand Up @@ -39,8 +40,7 @@ def convert_collection(path: Path, output: OutputFormat) -> None:
OutputFormat.CB7: (".cb7", CB7Archive),
OutputFormat.CBT: (".cbt", CBTArchive),
}.get(output, (".cbz", CBZArchive))
formats = list(ARCHIVE_EXTENSIONS)
formats.remove(format_)
formats = [ext for ext in ARCHIVE_EXTENSIONS if ext != format_]
for file in list_files(path, *formats):
with CONSOLE.status(
f"Converting {file.name} to {output.name}", spinner="simpleDotsScrolling"
Expand All @@ -57,110 +57,92 @@ def read_meta_file(cls: type[InfoModel], filename: str) -> InfoModel | None:
return cls.from_bytes(content=archive.read_file(filename=filename))
return None

# region Read Metadata
try:
metadata = read_meta_file(cls=Metadata, filename="/Metadata.xml") or read_meta_file(
cls=Metadata, filename="Metadata.xml"
metadata = read_meta_file(Metadata, "/Metadata.xml") or read_meta_file(
Metadata, "Metadata.xml"
)
if metadata:
metadata = cast(Metadata, metadata)
meta = metadata.meta
details = Details(
series=Identifications(
search=metadata.issue.series.title,
comicvine=get_metadata_id(
resources=metadata.issue.series.resources, source=Source.COMICVINE
),
comicvine=get_metadata_id(metadata.issue.series.resources, Source.COMICVINE),
league=get_metadata_id(
resources=metadata.issue.series.resources,
source=Source.LEAGUE_OF_COMIC_GEEKS,
),
marvel=get_metadata_id(
resources=metadata.issue.series.resources, source=Source.MARVEL
),
metron=get_metadata_id(
resources=metadata.issue.series.resources, source=Source.METRON
metadata.issue.series.resources, Source.LEAGUE_OF_COMIC_GEEKS
),
marvel=get_metadata_id(metadata.issue.series.resources, Source.MARVEL),
metron=get_metadata_id(metadata.issue.series.resources, Source.METRON),
),
issue=Identifications(
search=metadata.issue.number,
comicvine=get_metadata_id(
resources=metadata.issue.resources, source=Source.COMICVINE
),
league=get_metadata_id(
resources=metadata.issue.resources, source=Source.LEAGUE_OF_COMIC_GEEKS
),
marvel=get_metadata_id(
resources=metadata.issue.resources, source=Source.MARVEL
),
metron=get_metadata_id(
resources=metadata.issue.resources, source=Source.METRON
),
comicvine=get_metadata_id(metadata.issue.resources, Source.COMICVINE),
league=get_metadata_id(metadata.issue.resources, Source.LEAGUE_OF_COMIC_GEEKS),
marvel=get_metadata_id(metadata.issue.resources, Source.MARVEL),
metron=get_metadata_id(metadata.issue.resources, Source.METRON),
),
)
return meta, details
except ValidationError:
LOGGER.error("%s contains an invalid Metadata file", archive.path.name) # noqa: TRY400
# endregion

# region Read MetronInfo
try:
metron_info = read_meta_file(cls=MetronInfo, filename="/MetronInfo.xml") or read_meta_file(
cls=MetronInfo, filename="MetronInfo.xml"
metron_info = read_meta_file(MetronInfo, "/MetronInfo.xml") or read_meta_file(
MetronInfo, "MetronInfo.xml"
)
if metron_info:
metron_info = cast(MetronInfo, metron_info)
series_id = metron_info.series.id if metron_info.id else None
issue_id = metron_info.id.primary.value if metron_info.id else None
details = Details(
series=Identifications(
search=metron_info.series.name,
comicvine=metron_info.series.id
if metron_info.id and metron_info.id.source == InformationSource.COMIC_VINE
comicvine=series_id
if metron_info.id.primary.source == InformationSource.COMIC_VINE
else None,
league=metron_info.series.id
if metron_info.id
and metron_info.id.source == InformationSource.LEAGUE_OF_COMIC_GEEKS
league=series_id
if metron_info.id.primary.source == InformationSource.LEAGUE_OF_COMIC_GEEKS
else None,
marvel=metron_info.series.id
if metron_info.id and metron_info.id.source == InformationSource.MARVEL
marvel=series_id
if metron_info.id.primary.source == InformationSource.MARVEL
else None,
metron=metron_info.series.id
if metron_info.id and metron_info.id.source == InformationSource.METRON
metron=series_id
if metron_info.id.primary.source == InformationSource.METRON
else None,
),
issue=Identifications(
search=metron_info.number,
comicvine=metron_info.id.value
if metron_info.id and metron_info.id.source == InformationSource.COMIC_VINE
comicvine=issue_id
if metron_info.id.primary.source == InformationSource.COMIC_VINE
else None,
league=metron_info.id.value
if metron_info.id
and metron_info.id.source == InformationSource.LEAGUE_OF_COMIC_GEEKS
league=issue_id
if metron_info.id.primary.source == InformationSource.LEAGUE_OF_COMIC_GEEKS
else None,
marvel=metron_info.id.value
if metron_info.id and metron_info.id.source == InformationSource.MARVEL
marvel=issue_id
if metron_info.id.primary.source == InformationSource.MARVEL
else None,
metron=metron_info.id.value
if metron_info.id and metron_info.id.source == InformationSource.METRON
metron=issue_id
if metron_info.id.primary.source == InformationSource.METRON
else None,
),
)
return Meta(date_=date.today(), tool=Tool(value="MetronInfo")), details
except ValidationError:
LOGGER.error("%s contains an invalid MetronInfo file", archive.path.name) # noqa: TRY400
# endregion

# region Read ComicInfo
try:
comic_info = read_meta_file(cls=ComicInfo, filename="/ComicInfo.xml") or read_meta_file(
cls=ComicInfo, filename="ComicInfo.xml"
comic_info = read_meta_file(ComicInfo, "/ComicInfo.xml") or read_meta_file(
ComicInfo, "ComicInfo.xml"
)
if comic_info:
comic_info = cast(ComicInfo, comic_info)
details = Details(
series=Identifications(search=comic_info.series),
issue=Identifications(search=comic_info.number),
)
return Meta(date_=date.today(), tool=Tool(value="ComicInfo")), details
except ValidationError:
LOGGER.error("%s contains an invalid ComicInfo file", archive.path.name) # noqa: TRY400
# endregion

return None, None

Expand All @@ -173,67 +155,58 @@ def load_archives(
archive = get_archive(path=file)
LOGGER.debug("Reading %s", file.stem)
meta, details = read_meta(archive=archive)
if not meta or not details:
archives.append((file, archive, details))
continue
difference = abs(date.today() - meta.date_)
if force or meta.tool != Tool() or difference.days >= 28:
if (
not meta
or not details
or force
or meta.tool != Tool()
or abs(date.today() - meta.date_).days >= 28
):
archives.append((file, archive, details))
continue
return archives


def fetch_from_services(
settings: Settings, details: Details
) -> tuple[Metadata | None, MetronInfo | None, ComicInfo | None]:
marvel = None
if settings.marvel and settings.marvel.public_key and settings.marvel.private_key:
marvel = Marvel(settings=settings.marvel)
metron = None
if settings.metron and settings.metron.username and settings.metron.password:
metron = Metron(settings=settings.metron)
comicvine = None
if settings.comicvine and settings.comicvine.api_key:
comicvine = Comicvine(settings.comicvine)
league = None
if (
settings.league_of_comic_geeks
services = {
Service.COMICVINE: Comicvine(settings.comicvine)
if settings.comicvine and settings.comicvine.api_key
else None,
Service.LEAGUE_OF_COMIC_GEEKS: League(settings.league_of_comic_geeks)
if settings.league_of_comic_geeks
and settings.league_of_comic_geeks.client_id
and settings.league_of_comic_geeks.client_secret
):
league = League(settings.league_of_comic_geeks)
if not marvel and not metron and not comicvine and not league:
LOGGER.warning("No external services configured")
return None, None, None

services = {
Service.COMICVINE: comicvine,
Service.LEAGUE_OF_COMIC_GEEKS: league,
Service.MARVEL: marvel,
Service.METRON: metron,
else None,
Service.MARVEL: Marvel(settings.marvel)
if settings.marvel and settings.marvel.public_key and settings.marvel.private_key
else None,
Service.METRON: Metron(settings.metron)
if settings.metron and settings.metron.username and settings.metron.password
else None,
}

for service_name in settings.service_order:
service = services[service_name]
if not service:
continue
LOGGER.info("Fetching details from %s", type(service).__name__)
metadata, metron_info, comic_info = service.fetch(details=details)
if metadata and metron_info and comic_info:
return metadata, metron_info, comic_info
if service:
LOGGER.info("Fetching details from %s", type(service).__name__)
metadata, metron_info, comic_info = service.fetch(details=details)
if metadata and metron_info and comic_info:
return metadata, metron_info, comic_info
LOGGER.warning("No external services configured or data incomplete")
return None, None, None


def generate_filename(root: Path, extension: str, metadata: Metadata) -> Path:
publisher_filename = metadata.issue.series.publisher.title
series_filename = (
publisher_filename = sanitize(metadata.issue.series.publisher.title)
series_filename = sanitize(
f"{metadata.issue.series.title} v{metadata.issue.series.volume}"
if metadata.issue.series.volume > 1
else metadata.issue.series.title
)

number_str = (
f"_#{metadata.issue.number.zfill(3 if metadata.issue.format == Format.COMIC else 2)}"
f"_#{metadata.issue.number.zfill(3 if metadata.issue.format == Format.SINGLE_ISSUE else 2)}"
if metadata.issue.number
else ""
)
Expand All @@ -244,19 +217,18 @@ def generate_filename(root: Path, extension: str, metadata: Metadata) -> Path:
Format.HARDCOVER: "_HC",
Format.TRADE_PAPERBACK: "_TP",
}.get(metadata.issue.format, "")
if metadata.issue.format in {Format.ANNUAL, Format.DIGITAL_CHAPTER}:
issue_filename = sanitize(value=series_filename) + format_str + number_str
elif metadata.issue.format in {Format.GRAPHIC_NOVEL, Format.HARDCOVER, Format.TRADE_PAPERBACK}:
issue_filename = sanitize(value=series_filename) + number_str + format_str

if metadata.issue.format in {
Format.GRAPHIC_NOVEL,
Format.HARDCOVER,
Format.TRADE_PAPERBACK,
Format.OMNIBUS,
}:
issue_filename = f"{series_filename}{number_str}{format_str}"
else:
issue_filename = sanitize(value=series_filename) + number_str
issue_filename = f"{series_filename}{format_str}{number_str}"

return (
root
/ sanitize(value=publisher_filename)
/ sanitize(value=series_filename)
/ f"{issue_filename}.{extension}"
)
return root / publisher_filename / series_filename / f"{issue_filename}.{extension}"


def rename_images(folder: Path, filename: str) -> None:
Expand All @@ -276,7 +248,7 @@ def process_pages(
from perdoo.models.metadata import Page as MetadataPage
from perdoo.models.metron_info import Page as MetronPage

rename_images(folder=folder, filename=filename)
rename_images(folder, filename)
image_list = list_files(folder, *IMAGE_EXTENSIONS)
metadata_pages = set()
metron_info_pages = set()
Expand Down Expand Up @@ -304,7 +276,6 @@ def process_pages(

def start(settings: Settings, force: bool = False) -> None:
LOGGER.info("Starting Perdoo")

convert_collection(path=settings.input_folder, output=settings.output.format)

with CONSOLE.status(
Expand All @@ -325,6 +296,7 @@ def start(settings: Settings, force: bool = False) -> None:
if not metadata:
LOGGER.warning("Not enough information to organize and rename this comic, skipping")
continue

new_file = generate_filename(
root=settings.output_folder, extension=settings.output.format.value, metadata=metadata
)
Expand All @@ -344,6 +316,7 @@ def start(settings: Settings, force: bool = False) -> None:
filename=new_file.stem,
)
metadata.meta = Meta(date_=date.today())

files = list_files(temp_folder, *IMAGE_EXTENSIONS)
if settings.output.create_metadata:
metadata_file = temp_folder / "Metadata.xml"
Expand All @@ -357,6 +330,7 @@ def start(settings: Settings, force: bool = False) -> None:
comic_info_file = temp_folder / "ComicInfo.xml"
comic_info.to_file(file=comic_info_file)
files.append(comic_info_file)

status.update(f"Archiving {new_file.stem}")
archive_file = archive.archive_files(
src=temp_folder, output_name=archive.path.stem, files=files
Expand All @@ -366,6 +340,7 @@ def start(settings: Settings, force: bool = False) -> None:
continue
archive.path.unlink(missing_ok=True)
shutil.move(archive_file, archive.path)

if file.relative_to(settings.input_folder) != new_file.relative_to(settings.output_folder):
LOGGER.info(
"Organizing comic, moving file to %s", new_file.relative_to(settings.output_folder)
Expand Down
2 changes: 1 addition & 1 deletion perdoo/archives/cb7.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,6 @@ def convert(old_archive: BaseArchive) -> CB7Archive | None:
if not archive_file:
return None
new_filepath = old_archive.path.parent / f"{old_archive.path.stem}.cb7"
shutil.move(archive_file, new_filepath)
old_archive.path.unlink(missing_ok=True)
shutil.move(archive_file, new_filepath)
return CB7Archive(path=new_filepath)
2 changes: 1 addition & 1 deletion perdoo/archives/cbt.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,6 @@ def convert(old_archive: BaseArchive) -> CBTArchive | None:
if not archive_file:
return None
new_filepath = old_archive.path.parent / f"{old_archive.path.stem}.cbt"
shutil.move(archive_file, new_filepath)
old_archive.path.unlink(missing_ok=True)
shutil.move(archive_file, new_filepath)
return CBTArchive(path=new_filepath)
2 changes: 1 addition & 1 deletion perdoo/archives/cbz.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,6 @@ def convert(old_archive: BaseArchive) -> CBZArchive | None:
if not archive_file:
return None
new_filepath = old_archive.path.parent / f"{old_archive.path.stem}.cbz"
shutil.move(archive_file, new_filepath)
old_archive.path.unlink(missing_ok=True)
shutil.move(archive_file, new_filepath)
return CBZArchive(path=new_filepath)
Loading

0 comments on commit 8b6355a

Please sign in to comment.