Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/publishing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v7
- name: Setup Python
run: uv python install 3.13
run: uv python install 3.14
- name: Build wheel and source tarball
run: uv build
- uses: actions/upload-artifact@v4
Expand All @@ -35,7 +35,7 @@ jobs:
path: dist/
- uses: astral-sh/setup-uv@v7
- name: Setup Python
run: uv python install 3.13
run: uv python install 3.14
- name: Validate dist
run: uv run --with twine twine check dist/*

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rich-codex.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v7
- name: Setup Python
run: uv python install '3.13'
run: uv python install 3.14
- name: Install project
run: uv sync --dev

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/testing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
- '3.11'
- '3.12'
- '3.13'
- '3.14'
os:
- ubuntu-latest
- macos-latest
Expand Down
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.13.1
rev: v0.13.2
hooks:
- id: ruff-format
- id: ruff-check
Expand Down
10 changes: 4 additions & 6 deletions perdoo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@
"get_state_root",
"setup_logging",
]
__version__ = "0.6.0"
__version__ = "0.7.0"

import logging
import os
from functools import cache
from pathlib import Path

from rich.logging import RichHandler
from rich.traceback import install

from perdoo.console import CONSOLE

Expand Down Expand Up @@ -52,15 +51,14 @@ def get_state_root() -> Path:


def setup_logging(debug: bool = False) -> None:
install(show_locals=debug, max_frames=3, console=CONSOLE)

console_handler = RichHandler(
rich_tracebacks=True,
tracebacks_show_locals=True,
tracebacks_show_locals=debug,
tracebacks_max_frames=3,
omit_repeated_times=False,
show_level=True,
show_time=False,
show_path=True,
show_path=debug,
console=CONSOLE,
)
console_handler.setLevel(logging.DEBUG if debug else logging.INFO)
Expand Down
24 changes: 11 additions & 13 deletions perdoo/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ def load(value: str) -> "SyncOption":
return entry
raise ValueError(f"'{value}' isn't a valid SyncOption")

def __str__(self) -> str:
return self.value


@app.callback(invoke_without_command=True)
def common(
Expand Down Expand Up @@ -109,30 +106,30 @@ def _create_search_from_metron(metron_info: MetronInfo) -> Search:
)


def _create_search_from_comic_info(comic_info: ComicInfo) -> Search:
def _create_search_from_comic_info(comic_info: ComicInfo, filename: str) -> Search:
volume = comic_info.volume if comic_info.volume else None
year = volume if volume and volume > 1900 else None
volume = volume if volume and volume < 1900 else None
return Search(
series=SeriesSearch(name=comic_info.series, volume=volume, year=year),
series=SeriesSearch(name=comic_info.series or filename, volume=volume, year=year),
issue=IssueSearch(number=comic_info.number),
)


def _create_search_from_filename(fallback_title: str) -> Search:
series_name = comicfn2dict(fallback_title).get("series", fallback_title).replace("-", " ")
def _create_search_from_filename(filename: str) -> Search:
series_name = comicfn2dict(filename).get("series", filename).replace("-", " ")
return Search(series=SeriesSearch(name=series_name), issue=IssueSearch())


def get_search_details(
metadata: tuple[MetronInfo | None, ComicInfo | None], fallback_title: str
metadata: tuple[MetronInfo | None, ComicInfo | None], filename: str
) -> Search:
metron_info, comic_info = metadata
if metron_info and metron_info.series and metron_info.series.name:
return _create_search_from_metron(metron_info)
return _create_search_from_metron(metron_info=metron_info)
if comic_info and comic_info.series:
return _create_search_from_comic_info(comic_info)
return _create_search_from_filename(fallback_title)
return _create_search_from_comic_info(comic_info=comic_info, filename=filename)
return _create_search_from_filename(filename=filename)


def load_page_info(entry: Comic, comic_info: ComicInfo) -> list[Page]:
Expand Down Expand Up @@ -181,7 +178,7 @@ def run(
case_sensitive=False,
help="Sync ComicInfo/MetronInfo with online services.",
),
] = SyncOption.OUTDATED.value,
] = SyncOption.OUTDATED,
skip_clean: Annotated[
bool,
Option(
Expand Down Expand Up @@ -251,7 +248,8 @@ def run(
metadata: tuple[MetronInfo | None, ComicInfo | None] = (entry.metron_info, entry.comic_info)

if sync != SyncOption.SKIP:
search = get_search_details(metadata=metadata, fallback_title=entry.path.stem)
search = get_search_details(metadata=metadata, filename=entry.path.stem)
search.filename = entry.path.stem
last_modified = date(1900, 1, 1)
if sync == SyncOption.OUTDATED:
metron_info, _ = metadata
Expand Down
24 changes: 14 additions & 10 deletions perdoo/comic.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
ComicMetadataError,
MetadataFormat,
)
from natsort import humansorted, ns

from perdoo.metadata import ComicInfo, MetronInfo
from perdoo.settings import Naming
Expand Down Expand Up @@ -101,17 +102,17 @@ def read_metadata(self, metadata_format: MetadataFormat) -> None:
raise ComicMetadataError(f"Unsupported metadata format: {metadata_format}")

def convert(self, extension: Literal["cbt", "cbz"]) -> None:
check, archiver = {
"cbt": (self.is_cbt, TarArchiver),
"cbz": (self.is_cbz, ZipArchiver),
}.get(extension)
check, archiver = {"cbt": (self.is_cbt, TarArchiver), "cbz": (self.is_cbz, ZipArchiver)}[
extension
]
if check():
return
output_file = self.path.with_suffix(f".{extension}")
with self.archive as source, archiver(path=output_file) as destination:
LOGGER.debug("Converting '%s' to '%s'", source.path.name, destination.path.name)
if destination.copy_from_archive(other_archive=source):
self._archiver = destination
source.path.unlink()

def clean_archive(self) -> None:
with self.archive as source:
Expand Down Expand Up @@ -148,11 +149,14 @@ def _get_filepath_from_metadata(self, naming: Naming) -> str | None:

def _rename_images(self, base_name: str) -> None:
with self.archive as source:
files = [
x
for x in source.get_filename_list()
if Path(x).suffix.lower() in SUPPORTED_IMAGE_EXTENSIONS
]
files = humansorted(
[
x
for x in source.get_filename_list()
if Path(x).suffix.lower() in SUPPORTED_IMAGE_EXTENSIONS
],
alg=ns.NA | ns.G | ns.P,
)
pad_count = len(str(len(files))) if files else 1
for idx, filename in enumerate(files):
img_file = Path(filename)
Expand All @@ -171,7 +175,7 @@ def rename(self, naming: Naming, output_folder: Path) -> None:
new_filepath = new_filepath.lstrip("/")

output = output_folder / f"{new_filepath}.cbz"
if output == self.path:
if output.relative_to(output_folder) == self.path.resolve().relative_to(output_folder):
return
if output.exists():
LOGGER.warning("'%s' already exists, skipping", output.relative_to(output_folder))
Expand Down
4 changes: 2 additions & 2 deletions perdoo/metadata/comic_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,12 +343,12 @@ def get_filename(self, settings: Naming) -> str:
"series-id": lambda _: None,
"series-name": lambda x: x.series,
"series-sort-name": lambda _: None,
"series-year": lambda x: x.volume if x.volume > 1900 else None,
"series-year": lambda x: x.volume if x.volume and x.volume > 1900 else None,
"store-date": lambda _: None,
"store-day": lambda _: None,
"store-month": lambda _: None,
"store-year": lambda _: None,
"title": lambda x: x.title,
"upc": lambda _: None,
"volume": lambda x: x.volume if x.volume < 1900 else None,
"volume": lambda x: x.volume if x.volume and x.volume < 1900 else None,
}
12 changes: 6 additions & 6 deletions perdoo/services/_base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__all__ = ["BaseService"]

from abc import abstractmethod
from abc import ABC, abstractmethod
from typing import Generic, TypeVar

from perdoo.metadata import ComicInfo, MetronInfo
Expand All @@ -10,20 +10,20 @@
C = TypeVar("C")


class BaseService(Generic[S, C]):
class BaseService(ABC, Generic[S, C]):
@abstractmethod
def _search_series(
self, name: str | None, volume: int | None, year: int | None
self, name: str | None, volume: int | None, year: int | None, filename: str
) -> int | None: ...

@abstractmethod
def fetch_series(self, search: SeriesSearch) -> S | None: ...
def fetch_series(self, search: SeriesSearch, filename: str) -> S | None: ...

@abstractmethod
def _search_issue(self, series_id: int, number: str | None) -> int | None: ...
def _search_issue(self, series_id: int, number: str | None, filename: str) -> int | None: ...

@abstractmethod
def fetch_issue(self, series_id: int, search: IssueSearch) -> C | None: ...
def fetch_issue(self, series_id: int, search: IssueSearch, filename: str) -> C | None: ...

@abstractmethod
def _process_metron_info(self, series: S, issue: C) -> MetronInfo | None: ...
Expand Down
Loading
Loading