Skip to content

Commit 2a3ccd0

Browse files
Fix URL naming, closes #84
Add timezone to last_modified using local tz as default Fix ComicInfo pages
1 parent a594647 commit 2a3ccd0

File tree

3 files changed

+40
-46
lines changed

3 files changed

+40
-46
lines changed

perdoo/__main__.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
from datetime import date
33
from enum import Enum
4+
from io import BytesIO
45
from pathlib import Path
56
from platform import python_version
67
from typing import Annotated
@@ -129,17 +130,37 @@ def get_search_details(
129130

130131

131132
def load_page_info(entry: Comic, comic_info: ComicInfo) -> list[Page]:
133+
from PIL import Image # noqa: PLC0415
134+
135+
from perdoo.metadata.comic_info import PageType # noqa: PLC0415
136+
132137
pages = set()
133138
image_files = [
134139
x
135140
for x in entry.archive.get_filename_list()
136141
if Path(x).suffix.lower() in SUPPORTED_IMAGE_EXTENSIONS
137142
]
138143
for idx, file in enumerate(image_files):
139-
img_file = Path(file)
140-
is_final_page = idx == len(image_files) - 1
141144
page = next((x for x in comic_info.pages if x.image == idx), None)
142-
pages.add(Page.from_path(file=img_file, index=idx, is_final_page=is_final_page, page=page))
145+
if page:
146+
page_type = page.type
147+
elif idx == 0:
148+
page_type = PageType.FRONT_COVER
149+
elif idx == len(image_files) - 1:
150+
page_type = PageType.BACK_COVER
151+
else:
152+
page_type = PageType.STORY
153+
if not page:
154+
page = Page(image=idx)
155+
page.type = page_type
156+
page_bytes = entry.archive.read_file(file)
157+
page.image_size = len(page_bytes)
158+
with Image.open(BytesIO(page_bytes)) as page_data:
159+
width, height = page_data.size
160+
page.double_page = width >= height
161+
page.image_height = height
162+
page.image_width = width
163+
pages.add(page)
143164
return sorted(pages)
144165

145166

perdoo/metadata/comic_info.py

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,14 @@
44
from collections.abc import Callable
55
from datetime import date
66
from enum import Enum
7-
from pathlib import Path
87

98
from natsort import humansorted, ns
10-
from PIL import Image
119
from pydantic import HttpUrl, NonNegativeFloat
1210
from pydantic_xml import attr, computed_attr, element, wrapped
1311

1412
from perdoo.metadata._base import PascalModel
1513
from perdoo.settings import Naming
1614

17-
try:
18-
from typing import Self # Python >= 3.11
19-
except ImportError:
20-
from typing_extensions import Self # Python < 3.11
21-
22-
2315
LOGGER = logging.getLogger(__name__)
2416

2517

@@ -41,7 +33,7 @@ class YesNo(Enum):
4133
YES = "Yes"
4234

4335
@staticmethod
44-
def load(value: str) -> Self:
36+
def load(value: str) -> "YesNo":
4537
for entry in YesNo:
4638
if entry.value.replace(" ", "").casefold() == value.replace(" ", "").casefold():
4739
return entry
@@ -59,7 +51,7 @@ class Manga(Enum):
5951
YES_AND_RIGHT_TO_LEFT = "YesAndRightToLeft"
6052

6153
@staticmethod
62-
def load(value: str) -> Self:
54+
def load(value: str) -> "Manga":
6355
for entry in Manga:
6456
if entry.value.replace(" ", "").casefold() == value.replace(" ", "").casefold():
6557
return entry
@@ -88,7 +80,7 @@ class AgeRating(Enum):
8880
X18 = "X18+"
8981

9082
@staticmethod
91-
def load(value: str) -> Self:
83+
def load(value: str) -> "AgeRating":
9284
for entry in AgeRating:
9385
if entry.value.replace(" ", "").casefold() == value.replace(" ", "").casefold():
9486
return entry
@@ -113,7 +105,7 @@ class PageType(Enum):
113105
DELETED = "Deleted"
114106

115107
@staticmethod
116-
def load(value: str) -> Self:
108+
def load(value: str) -> "PageType":
117109
for entry in PageType:
118110
if entry.value.replace(" ", "").casefold() == value.replace(" ", "").casefold():
119111
return entry
@@ -147,27 +139,6 @@ def __eq__(self, other) -> bool: # noqa: ANN001
147139
def __hash__(self) -> int:
148140
return hash((type(self), self.image))
149141

150-
@staticmethod
151-
def from_path(file: Path, index: int, is_final_page: bool, page: Self | None) -> Self:
152-
if page:
153-
page_type = page.type
154-
elif index == 0:
155-
page_type = PageType.FRONT_COVER
156-
elif is_final_page:
157-
page_type = PageType.BACK_COVER
158-
else:
159-
page_type = PageType.STORY
160-
with Image.open(file) as img:
161-
width, height = img.size
162-
return Page(
163-
image=index,
164-
type=page_type,
165-
double_page=width >= height,
166-
image_size=file.stat().st_size,
167-
image_height=height,
168-
image_width=width,
169-
)
170-
171142

172143
class ComicInfo(PascalModel):
173144
age_rating: AgeRating = element(default=AgeRating.UNKNOWN)

perdoo/metadata/metron_info.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,12 @@
2424
from enum import Enum
2525
from typing import Generic, TypeVar
2626

27-
from pydantic import HttpUrl, NonNegativeInt, PositiveInt
27+
from pydantic import HttpUrl, NonNegativeInt, PositiveInt, field_validator
2828
from pydantic_xml import attr, computed_attr, element, wrapped
2929

3030
from perdoo.metadata._base import PascalModel
3131
from perdoo.settings import Naming
3232

33-
try:
34-
from typing import Self # Python >= 3.11
35-
except ImportError:
36-
from typing_extensions import Self # Python < 3.11
37-
3833
LOGGER = logging.getLogger(__name__)
3934
T = TypeVar("T")
4035

@@ -49,7 +44,7 @@ class AgeRating(Enum):
4944
ADULT = "Adult"
5045

5146
@staticmethod
52-
def load(value: str) -> Self:
47+
def load(value: str) -> "AgeRating":
5348
for entry in AgeRating:
5449
if entry.value.replace(" ", "").casefold() == value.replace(" ", "").casefold():
5550
return entry
@@ -142,7 +137,7 @@ class Role(Enum):
142137
OTHER = "Other"
143138

144139
@staticmethod
145-
def load(value: str) -> Self:
140+
def load(value: str) -> "Role":
146141
for entry in Role:
147142
if entry.value.replace(" ", "").casefold() == value.replace(" ", "").casefold():
148143
return entry
@@ -191,7 +186,7 @@ class InformationSource(Enum):
191186
LEAGUE_OF_COMIC_GEEKS = "League of Comic Geeks"
192187

193188
@staticmethod
194-
def load(value: str) -> Self:
189+
def load(value: str) -> "InformationSource":
195190
for entry in InformationSource:
196191
if entry.value.replace(" ", "").casefold() == value.replace(" ", "").casefold():
197192
return entry
@@ -366,7 +361,7 @@ class MetronInfo(PascalModel):
366361
universes: list[Universe] = wrapped(
367362
path="Universes", entity=element(tag="Universe", default_factory=list)
368363
)
369-
urls: list[Url] = wrapped(path="URLS", entity=element(tag="URLs", default_factory=list))
364+
urls: list[Url] = wrapped(path="URLs", entity=element(tag="URL", default_factory=list))
370365

371366
@computed_attr(ns="xsi", name="noNamespaceSchemaLocation")
372367
def schema_location(self) -> str:
@@ -389,6 +384,13 @@ def get_filename(self, settings: Naming) -> str:
389384
seperator=settings.seperator,
390385
)
391386

387+
@field_validator("last_modified", mode="before")
388+
def ensure_timezone(cls, v: str | datetime | None) -> str | datetime | None:
389+
if isinstance(v, datetime) and v.tzinfo is None:
390+
timezone = datetime.now().astimezone().tzinfo
391+
return v.replace(tzinfo=timezone)
392+
return v
393+
392394

393395
PATTERN_MAP: dict[str, Callable[[MetronInfo], str | int | None]] = {
394396
"cover-date": lambda x: x.cover_date,

0 commit comments

Comments
 (0)