buried-in-code / perdoo Goto Github PK
View Code? Open in Web Editor NEWPerdoo's goal is to help sort and organize your comic collection by using the information stored in metadata files inside the comic archives.
License: MIT License
Perdoo's goal is to help sort and organize your comic collection by using the information stored in metadata files inside the comic archives.
License: MIT License
It should be possible to define an input directory that is different than where the files ultimately get stored with the default being the same as it is now. If both the input and output directories are defined as the same, the current behavior would not change.
One of the eventual goals of this change would be to have the option to leave the input directory untouched and to only copy the files instead of moving/re-arranging the original folder structure.
Tracking issue for:
I have not tested other API's (as I do not have access to the others at this time), but, this issue is happening with the Comicvine API.
When asked to "Try again" and selecting "n" on comic identification, the program exits with exit code 1 and the error AttributeError: 'NoneType' object has no attribute 'issue'
.
The full log is pasted below:
Perdoo v0.2.0
Python v3.12.4
INFO Starting Perdoo
────────── Descender Compendium (2024) (Digital) ──────────
INFO Processing Descender Compendium (2024) (Digital)
Series title: Descender
INFO Fetching details from Comicvine
+------------------------------ Comicvine Series ------------------------------+
| 1: 80426 | Image | Descender (2015) |
| 2: 106862 | Image | Descender Deluxe Edition (2017) |
| 3: 90103 | Image | Descender: Machine Moon (2016) |
| 4: 102251 | Image | Descender: Orbital Mechanics (2017) |
| 5: 107643 | Image | Descender: Rise of the Robots (2018) |
| 6: 96656 | Image | Descender: Singularities (2016) |
| 7: 113701 | Image | Descender: The Machine War (2018) |
| 8: 84421 | Image | Descender: Tin Stars (2015) |
| 9: 86365 | Image | Image Firsts: Descender (2015) |
| 10: 105168 | Splitter | Descender (2015) |
| 0: None of the Above |
+------------------------------------------------------------------------------+
Select (0): 0
Try Again [y/n]: n
╭───────────────────── Traceback (most recent call last) ──────────────────────╮
│ /home/d/Development/Python/Perdoo/perdoo/__main__.py:396 in <module> │
│ │
│ 393 │
│ 394 │
│ 395 if __name__ == "__main__": │
│ ❱ 396 │ main() │
│ 397 │
│ │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │ annotations = _Feature((3, 7, 0, 'beta', 1), None, 16777216) │ │
│ │ ARCHIVE_EXTENSIONS = ('.cb7', '.cbr', '.cbt', '.cbz') │ │
│ │ ArgumentParser = <class 'argparse.ArgumentParser'> │ │
│ │ BaseArchive = <class 'perdoo.archives._base.BaseArchive'> │ │
│ │ CB7Archive = <class 'perdoo.archives.cb7.CB7Archive'> │ │
│ │ CBTArchive = <class 'perdoo.archives.cbt.CBTArchive'> │ │
│ │ CBZArchive = <class 'perdoo.archives.cbz.CBZArchive'> │ │
│ │ ComicInfo = <class 'perdoo.models.comic_info.ComicInfo'> │ │
│ │ Comicvine = <class 'perdoo.services.comicvine.Comicvine'> │ │
│ │ CONSOLE = <console width=80 None> │ │
│ │ convert_collection = <function convert_collection at 0x7e28f467f380> │ │
│ │ date = <class 'datetime.date'> │ │
│ │ Details = <class 'perdoo.utils.Details'> │ │
│ │ fetch_from_services = <function fetch_from_services at 0x7e28f1f2f7e0> │ │
│ │ Format = <enum 'Format'> │ │
│ │ generate_filename = <function generate_filename at 0x7e28f1cc8040> │ │
│ │ get_archive = <function get_archive at 0x7e28f3f47600> │ │
│ │ get_metadata_id = <function get_metadata_id at 0x7e28f286f560> │ │
│ │ Identifications = <class 'perdoo.utils.Identifications'> │ │
│ │ IMAGE_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.webp') │ │
│ │ InfoModel = <class 'perdoo.models._base.InfoModel'> │ │
│ │ InformationSource = <enum 'InformationSource'> │ │
│ │ League = <class 'perdoo.services.league.League'> │ │
│ │ list_files = <function list_files at 0x7e28f39eae80> │ │
│ │ load_archives = <function load_archives at 0x7e28f1f2f740> │ │
│ │ LOGGER = <Logger perdoo (DEBUG)> │ │
│ │ logging = <module 'logging' from │ │
│ │ '/usr/lib/python3.12/logging/__init__.py'> │ │
│ │ main = <function main at 0x7e28f1b50900> │ │
│ │ Marvel = <class 'perdoo.services.marvel.Marvel'> │ │
│ │ Meta = <class 'perdoo.models.metadata.Meta'> │ │
│ │ Metadata = <class 'perdoo.models.metadata.Metadata'> │ │
│ │ Metron = <class 'perdoo.services.metron.Metron'> │ │
│ │ MetronInfo = <class 'perdoo.models.metron_info.MetronInfo'> │ │
│ │ Namespace = <class 'argparse.Namespace'> │ │
│ │ OutputFormat = <enum 'OutputFormat'> │ │
│ │ parse_arguments = <function parse_arguments at 0x7e28f4dee200> │ │
│ │ Path = <class 'pathlib.Path'> │ │
│ │ process_pages = <function process_pages at 0x7e28f1b50b80> │ │
│ │ Prompt = <class 'rich.prompt.Prompt'> │ │
│ │ python_version = <function python_version at 0x7e28f4751b20> │ │
│ │ read_meta = <function read_meta at 0x7e28f2605f80> │ │
│ │ rename_images = <function rename_images at 0x7e28f1b50ae0> │ │
│ │ sanitize = <function sanitize at 0x7e28f286f4c0> │ │
│ │ Service = <enum 'Service'> │ │
│ │ Settings = <class 'perdoo.settings.Settings'> │ │
│ │ setup_logging = <function setup_logging at 0x7e28f3b234c0> │ │
│ │ shutil = <module 'shutil' from │ │
│ │ '/usr/lib/python3.12/shutil.py'> │ │
│ │ Source = <enum 'Source'> │ │
│ │ start = <function start at 0x7e28f1b50a40> │ │
│ │ TemporaryDirectory = <class 'tempfile.TemporaryDirectory'> │ │
│ │ Tool = <class 'perdoo.models.metadata.Tool'> │ │
│ │ ValidationError = <class │ │
│ │ 'pydantic_core._pydantic_core.ValidationError'> │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│ │
│ /home/d/Development/Python/Perdoo/perdoo/__main__.py:390 in main │
│ │
│ 387 │ │ settings = Settings.load().save() │
│ 388 │ │ if args.debug: │
│ 389 │ │ │ CONSOLE.print(f"Settings: {settings}") │
│ ❱ 390 │ │ start(settings=settings, force=args.force) │
│ 391 │ except KeyboardInterrupt: │
│ 392 │ │ pass │
│ 393 │
│ │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │ args = Namespace(force=False, debug=False) │ │
│ │ settings = Settings( │ │
│ │ │ collection_folder=PosixPath('/home/d/Comics'), │ │
│ │ │ comicvine=Comicvine( │ │
│ │ │ │ api_key='<snip>' │ │
│ │ │ ), │ │
│ │ │ league_of_comic_geeks=LeagueofComicGeeks( │ │
│ │ │ │ client_id='', │ │
│ │ │ │ client_secret='', │ │
│ │ │ │ access_token='' │ │
│ │ │ ), │ │
│ │ │ marvel=Marvel(public_key='', private_key=''), │ │
│ │ │ metron=Metron(password='', username=''), │ │
│ │ │ service_order=[ │ │
│ │ │ │ <Service.METRON: 'Metron'>, │ │
│ │ │ │ <Service.MARVEL: 'Marvel'>, │ │
│ │ │ │ <Service.COMICVINE: 'Comicvine'>, │ │
│ │ │ │ <Service.LEAGUE_OF_COMIC_GEEKS: 'League of Comic │ │
│ │ Geeks'> │ │
│ │ │ ], │ │
│ │ │ output=Output( │ │
│ │ │ │ create_comic_info=True, │ │
│ │ │ │ create_metron_info=True, │ │
│ │ │ │ create_metadata=True, │ │
│ │ │ │ format=<OutputFormat.CBZ: 'cbz'> │ │
│ │ │ ) │ │
│ │ ) │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│ │
│ /home/d/Development/Python/Perdoo/perdoo/__main__.py:321 in start │
│ │
│ 318 │ │ ) │
│ 319 │ │ │
│ 320 │ │ metadata, metron_info, comic_info = fetch_from_services(settin │
│ ❱ 321 │ │ new_file = generate_filename( │
│ 322 │ │ │ root=settings.collection_folder, │
│ 323 │ │ │ extension=settings.output.format.value, │
│ 324 │ │ │ metadata=metadata, │
│ │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │ archive = <perdoo.archives.cbz.CBZArchive object at 0x7e28f1b01610> │ │
│ │ archives = [ │ │
│ │ │ ( │ │
│ │ │ │ PosixPath('/home/d/Comics/Descender Compendium │ │
│ │ (2024) (Digital).cbz'), │ │
│ │ │ │ <perdoo.archives.cbz.CBZArchive object at │ │
│ │ 0x7e28f1b01610>, │ │
│ │ │ │ None │ │
│ │ │ ), │ │
│ │ │ ( │ │
│ │ │ │ PosixPath('/home/d/Comics/Dog Biscuits - Alex │ │
│ │ Graham.cbz'), │ │
│ │ │ │ <perdoo.archives.cbr.CBRArchive object at │ │
│ │ 0x7e28f1c2f920>, │ │
│ │ │ │ None │ │
│ │ │ ), │ │
│ │ │ ( │ │
│ │ │ │ PosixPath('/home/d/Comics/Joker-Harley - Criminal │ │
│ │ Sanity.cbz'), │ │
│ │ │ │ <perdoo.archives.cbz.CBZArchive object at │ │
│ │ 0x7e28f1ec03b0>, │ │
│ │ │ │ None │ │
│ │ │ ), │ │
│ │ │ ( │ │
│ │ │ │ PosixPath('/home/d/Comics/Memetic (2015) (Digital) │ │
│ │ .cbz'), │ │
│ │ │ │ <perdoo.archives.cbz.CBZArchive object at │ │
│ │ 0x7e28f2220410>, │ │
│ │ │ │ None │ │
│ │ │ ) │ │
│ │ ] │ │
│ │ comic_info = None │ │
│ │ details = Details( │ │
│ │ │ series=Identifications( │ │
│ │ │ │ search='Descender', │ │
│ │ │ │ comicvine=None, │ │
│ │ │ │ league=None, │ │
│ │ │ │ marvel=None, │ │
│ │ │ │ metron=None │ │
│ │ │ ), │ │
│ │ │ issue=Identifications( │ │
│ │ │ │ search=None, │ │
│ │ │ │ comicvine=None, │ │
│ │ │ │ league=None, │ │
│ │ │ │ marvel=None, │ │
│ │ │ │ metron=None │ │
│ │ │ ) │ │
│ │ ) │ │
│ │ file = PosixPath('/home/d/Comics/Descender Compendium (2024) │ │
│ │ (Digital).cbz') │ │
│ │ force = False │ │
│ │ metadata = None │ │
│ │ metron_info = None │ │
│ │ settings = Settings( │ │
│ │ │ collection_folder=PosixPath('/home/d/Comics'), │ │
│ │ │ comicvine=Comicvine( │ │
│ │ │ │ api_key='<snip>' │ │
│ │ │ ), │ │
│ │ │ league_of_comic_geeks=LeagueofComicGeeks( │ │
│ │ │ │ client_id='', │ │
│ │ │ │ client_secret='', │ │
│ │ │ │ access_token='' │ │
│ │ │ ), │ │
│ │ │ marvel=Marvel(public_key='', private_key=''), │ │
│ │ │ metron=Metron(password='', username=''), │ │
│ │ │ service_order=[ │ │
│ │ │ │ <Service.METRON: 'Metron'>, │ │
│ │ │ │ <Service.MARVEL: 'Marvel'>, │ │
│ │ │ │ <Service.COMICVINE: 'Comicvine'>, │ │
│ │ │ │ <Service.LEAGUE_OF_COMIC_GEEKS: 'League of Comic │ │
│ │ Geeks'> │ │
│ │ │ ], │ │
│ │ │ output=Output( │ │
│ │ │ │ create_comic_info=True, │ │
│ │ │ │ create_metron_info=True, │ │
│ │ │ │ create_metadata=True, │ │
│ │ │ │ format=<OutputFormat.CBZ: 'cbz'> │ │
│ │ │ ) │ │
│ │ ) │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│ │
│ /home/d/Development/Python/Perdoo/perdoo/__main__.py:227 in │
│ generate_filename │
│ │
│ 224 │
│ 225 │
│ 226 def generate_filename(root: Path, extension: str, metadata: Metadata) │
│ ❱ 227 │ publisher_filename = metadata.issue.series.publisher.title │
│ 228 │ series_filename = ( │
│ 229 │ │ f"{metadata.issue.series.title} v{metadata.issue.series.volume │
│ 230 │ │ if metadata.issue.series.volume > 1 │
│ │
│ ╭──────────────── locals ─────────────────╮ │
│ │ extension = 'cbz' │ │
│ │ metadata = None │ │
│ │ root = PosixPath('/home/d/Comics') │ │
│ ╰─────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────╯
AttributeError: 'NoneType' object has no attribute 'issue'
Process finished with exit code 1
Please let me know if you would like any more information.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.