99from tempfile import TemporaryDirectory
1010
1111from pydantic import ValidationError
12+ from rich .prompt import Prompt
1213
1314from perdoo import ARCHIVE_EXTENSIONS , IMAGE_EXTENSIONS , __version__ , setup_logging
1415from perdoo .archives import BaseArchive , CB7Archive , CBTArchive , CBZArchive , get_archive
1516from perdoo .console import CONSOLE
1617from perdoo .models import ComicInfo , Metadata , MetronInfo
17- from perdoo .models .metadata import Format , Meta , Tool
18+ from perdoo .models ._base import InfoModel
19+ from perdoo .models .metadata import Format , Meta , Source , Tool
20+ from perdoo .models .metron_info import InformationSource
1821from perdoo .services import Comicvine , League , Marvel , Metron
1922from perdoo .settings import OutputFormat , Settings
20- from perdoo .utils import (
21- comic_to_metadata ,
22- create_metadata ,
23- list_files ,
24- metadata_to_comic ,
25- metadata_to_metron ,
26- metron_to_metadata ,
27- sanitize ,
28- )
23+ from perdoo .utils import Details , Identifications , get_metadata_id , list_files , sanitize
2924
3025LOGGER = logging .getLogger ("perdoo" )
3126
@@ -52,54 +47,120 @@ def convert_collection(path: Path, output: OutputFormat) -> None:
5247 archive_type .convert (old_archive = archive )
5348
5449
55- def read_archive (archive : BaseArchive ) -> tuple [Metadata , MetronInfo , ComicInfo ]:
50+ def read_meta (archive : BaseArchive ) -> tuple [Meta , Details ]:
5651 filenames = archive .list_filenames ()
57- metadata = None
52+
53+ def read_meta_file (cls : type [InfoModel ], filename : str ) -> InfoModel | None :
54+ if filename in filenames :
55+ return cls .from_bytes (content = archive .read_file (filename = filename ))
56+ return None
57+
5858 try :
59- if "/Metadata.xml" in filenames :
60- metadata = Metadata .from_bytes (content = archive .read_file (filename = "/Metadata.xml" ))
61- elif "Metadata.xml" in filenames :
62- metadata = Metadata .from_bytes (content = archive .read_file (filename = "Metadata.xml" ))
59+ metadata = read_meta_file (cls = Metadata , filename = "/Metadata.xml" ) or read_meta_file (
60+ cls = Metadata , filename = "Metadata.xml"
61+ )
62+ if metadata :
63+ meta = metadata .meta
64+ details = Details (
65+ series = Identifications (
66+ search = metadata .issue .series .title ,
67+ comicvine = get_metadata_id (
68+ resources = metadata .issue .series .resources , source = Source .COMICVINE
69+ ),
70+ league = get_metadata_id (
71+ resources = metadata .issue .series .resources ,
72+ source = Source .LEAGUE_OF_COMIC_GEEKS ,
73+ ),
74+ marvel = get_metadata_id (
75+ resources = metadata .issue .series .resources , source = Source .MARVEL
76+ ),
77+ metron = get_metadata_id (
78+ resources = metadata .issue .series .resources , source = Source .METRON
79+ ),
80+ ),
81+ issue = Identifications (
82+ search = metadata .issue .number ,
83+ comicvine = get_metadata_id (
84+ resources = metadata .issue .resources , source = Source .COMICVINE
85+ ),
86+ league = get_metadata_id (
87+ resources = metadata .issue .resources , source = Source .LEAGUE_OF_COMIC_GEEKS
88+ ),
89+ marvel = get_metadata_id (
90+ resources = metadata .issue .resources , source = Source .MARVEL
91+ ),
92+ metron = get_metadata_id (
93+ resources = metadata .issue .resources , source = Source .METRON
94+ ),
95+ ),
96+ )
97+ return meta , details
6398 except ValidationError :
6499 LOGGER .error ("%s contains an invalid Metadata file" , archive .path .name ) # noqa: TRY400
65- metron_info = None
66100 try :
67- if "/MetronInfo.xml" in filenames :
68- metron_info = MetronInfo .from_bytes (
69- content = archive .read_file (filename = "/MetronInfo.xml" )
70- )
71- elif "MetronInfo.xml" in filenames :
72- metron_info = MetronInfo .from_bytes (
73- content = archive .read_file (filename = "MetronInfo.xml" )
101+ metron_info = read_meta_file (cls = MetronInfo , filename = "/MetronInfo.xml" ) or read_meta_file (
102+ cls = MetronInfo , filename = "MetronInfo.xml"
103+ )
104+ if metron_info :
105+ details = Details (
106+ series = Identifications (
107+ search = metron_info .series .name ,
108+ comicvine = metron_info .series .id
109+ if metron_info .id and metron_info .id .source == InformationSource .COMIC_VINE
110+ else None ,
111+ league = metron_info .series .id
112+ if metron_info .id
113+ and metron_info .id .source == InformationSource .LEAGUE_OF_COMIC_GEEKS
114+ else None ,
115+ marvel = metron_info .series .id
116+ if metron_info .id and metron_info .id .source == InformationSource .MARVEL
117+ else None ,
118+ metron = metron_info .series .id
119+ if metron_info .id and metron_info .id .source == InformationSource .METRON
120+ else None ,
121+ ),
122+ issue = Identifications (
123+ search = metron_info .number ,
124+ comicvine = metron_info .id .value
125+ if metron_info .id and metron_info .id .source == InformationSource .COMIC_VINE
126+ else None ,
127+ league = metron_info .id .value
128+ if metron_info .id
129+ and metron_info .id .source == InformationSource .LEAGUE_OF_COMIC_GEEKS
130+ else None ,
131+ marvel = metron_info .id .value
132+ if metron_info .id and metron_info .id .source == InformationSource .MARVEL
133+ else None ,
134+ metron = metron_info .id .value
135+ if metron_info .id and metron_info .id .source == InformationSource .METRON
136+ else None ,
137+ ),
74138 )
139+ return Meta (date_ = date .today (), tool = Tool (value = "MetronInfo" )), details
75140 except ValidationError :
76141 LOGGER .error ("%s contains an invalid MetronInfo file" , archive .path .name ) # noqa: TRY400
77- comic_info = None
78142 try :
79- if "/ComicInfo.xml" in filenames :
80- comic_info = ComicInfo .from_bytes (content = archive .read_file (filename = "/ComicInfo.xml" ))
81- elif "ComicInfo.xml" in filenames :
82- comic_info = ComicInfo .from_bytes (content = archive .read_file (filename = "ComicInfo.xml" ))
143+ comic_info = read_meta_file (cls = ComicInfo , filename = "/ComicInfo.xml" ) or read_meta_file (
144+ cls = ComicInfo , filename = "ComicInfo.xml"
145+ )
146+ if comic_info :
147+ details = Details (
148+ series = Identifications (search = comic_info .series ),
149+ issue = Identifications (search = comic_info .number ),
150+ )
151+ return Meta (date_ = date .today (), tool = Tool (value = "ComicInfo" )), details
83152 except ValidationError :
84153 LOGGER .error ("%s contains an invalid ComicInfo file" , archive .path .name ) # noqa: TRY400
85154
86- if not metadata :
87- if metron_info :
88- metadata = metron_to_metadata (metron_info = metron_info )
89- elif comic_info :
90- metadata = comic_to_metadata (comic_info = comic_info )
91- else :
92- metadata = create_metadata (archive = archive )
93- if not metron_info :
94- metron_info = metadata_to_metron (metadata = metadata )
95- if not comic_info :
96- comic_info = metadata_to_comic (metadata = metadata )
97- return metadata , metron_info , comic_info
155+ return Meta (date_ = date .today (), tool = Tool (value = "Manual" )), Details (
156+ series = Identifications (search = Prompt .ask ("Series title" , console = CONSOLE )),
157+ issue = Identifications (),
158+ )
98159
99160
100161def fetch_from_services (
101- settings : Settings , metainfo : tuple [ Metadata , MetronInfo , ComicInfo ]
102- ) -> None :
162+ settings : Settings , details : Details
163+ ) -> tuple [ Metadata | None , MetronInfo | None , ComicInfo | None ] :
103164 marvel = None
104165 if settings .marvel and settings .marvel .public_key and settings .marvel .private_key :
105166 marvel = Marvel (settings = settings .marvel )
@@ -118,13 +179,13 @@ def fetch_from_services(
118179 league = League (settings .league_of_comic_geeks )
119180 if not marvel and not metron and not comicvine and not league :
120181 LOGGER .warning ("No external services configured" )
121- return
182+ return None , None , None
122183
123- success = any (
124- service and service .fetch (* metainfo ) for service in ( marvel , metron , comicvine , league )
125- )
126- if not success :
127- LOGGER . warning ( "Unable to fetch information fron any service" )
184+ for service in ( marvel , metron , comicvine , league ):
185+ metadata , metron_info , comic_info = service .fetch (details = details )
186+ if metadata and metron_info and comic_info :
187+ return metadata , metron_info , comic_info
188+ return None , None , None
128189
129190
130191def generate_filename (root : Path , extension : str , metadata : Metadata ) -> Path :
@@ -169,7 +230,7 @@ def rename_images(folder: Path, filename: str) -> None:
169230 new_filename = f"{ filename } _{ str (index ).zfill (pad_count )} { img_file .suffix } "
170231 if img_file .name != new_filename :
171232 LOGGER .info ("Renamed %s to %s" , img_file .name , new_filename )
172- img_file . rename ( folder / f" { filename } - { str ( index ). zfill ( pad_count ) } { img_file . suffix } " )
233+ shutil . move ( img_file , folder / new_filename )
173234
174235
175236def process_pages (
@@ -210,16 +271,16 @@ def start(settings: Settings, force: bool = False) -> None:
210271 convert_collection (path = settings .collection_folder , output = settings .output .format )
211272 for file in list_files (settings .collection_folder , f".{ settings .output .format } " ):
212273 archive = get_archive (path = file )
213- metadata , metron_info , comic_info = read_archive (archive = archive )
274+ CONSOLE .rule (file .stem )
275+ LOGGER .info ("Processing %s" , file .stem )
276+ meta , details = read_meta (archive = archive )
214277
215278 if not force :
216- difference = abs (date .today () - metadata . meta .date_ )
217- if metadata . meta .tool == Tool () and difference .days < 28 :
279+ difference = abs (date .today () - meta .date_ )
280+ if meta .tool == Tool () and difference .days < 28 :
218281 continue
219282
220- CONSOLE .rule (file .stem )
221- LOGGER .info ("Processing %s" , file .name )
222- fetch_from_services (settings = settings , metainfo = (metadata , metron_info , comic_info ))
283+ metadata , metron_info , comic_info = fetch_from_services (settings = settings , details = details )
223284 new_file = generate_filename (
224285 root = settings .collection_folder ,
225286 extension = settings .output .format .value ,
@@ -229,6 +290,7 @@ def start(settings: Settings, force: bool = False) -> None:
229290 temp_folder = Path (temp_str )
230291 if not archive .extract_files (destination = temp_folder ):
231292 return
293+ LOGGER .info ("Processing %s pages" , file .stem )
232294 process_pages (
233295 folder = temp_folder ,
234296 metadata = metadata ,
0 commit comments