From 5923904c1a38501de097d58f6d5b5853a6342f69 Mon Sep 17 00:00:00 2001 From: dickloraine Date: Sun, 13 Sep 2020 18:48:03 +0200 Subject: [PATCH] added calibre 5 compatibility --- .gitignore | 143 +++++++++++++++++++++++++++++++++++++++++++++ __init__.py | 2 +- comicbookinfo.py | 5 ++ comicinfoxml.py | 10 ++++ comicmetadata.py | 93 ++++++----------------------- config.py | 12 +++- genericmetadata.py | 4 +- 7 files changed, 191 insertions(+), 78 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fc91d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,143 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +/.vscode + +EmbedComicMetadata.zip +build.bat diff --git a/__init__.py b/__init__.py index d3db358..2cb1f8d 100644 --- a/__init__.py +++ b/__init__.py @@ -23,7 +23,7 @@ class EmbedComicMetadataBase(InterfaceActionBase): description = 'Embeds calibres metadata into cbz comic archieves' supported_platforms = ['windows', 'osx', 'linux'] author = 'dloraine' - version = (1, 4, 0) + version = (1, 5, 0) minimum_calibre_version = (3, 1, 1) #: This field defines the GUI plugin class that contains all the code diff --git a/comicbookinfo.py b/comicbookinfo.py index a05ff1d..8081aaa 100644 --- a/comicbookinfo.py +++ b/comicbookinfo.py @@ -22,6 +22,11 @@ from calibre.utils.localization import calibre_langcode_to_name, canonicalize_lang, lang_as_iso639_1 from calibre_plugins.EmbedComicMetadata.genericmetadata import GenericMetadata +import sys + +if sys.version_info[0] > 2: + unicode = str + class ComicBookInfo: diff --git a/comicinfoxml.py b/comicinfoxml.py index 517d02d..ac59cee 100644 --- a/comicinfoxml.py +++ b/comicinfoxml.py @@ -22,6 +22,14 @@ import xml.etree.ElementTree as ET from calibre_plugins.EmbedComicMetadata.genericmetadata import GenericMetadata +import sys + +if sys.version_info[0] > 2: + python3 = True + unicode = str +else: + python3 = False + class ComicInfoXml: @@ -54,6 +62,8 @@ def stringFromMetadata(self, metadata): header = '\n' tree = self.convertMetadataToXML(self, metadata) + if python3: + return header + ET.tostring(tree.getroot(), "unicode") return header + ET.tostring(tree.getroot()) def indent(self, elem, level=0): diff --git a/comicmetadata.py b/comicmetadata.py index 5dca518..780047d 100644 --- a/comicmetadata.py +++ b/comicmetadata.py @@ -14,6 +14,10 @@ from calibre_plugins.EmbedComicMetadata.comicinfoxml import ComicInfoXml from calibre_plugins.EmbedComicMetadata.comicbookinfo import ComicBookInfo +import sys + +python3 = sys.version_info[0] > 2 + # synonyms for artists WRITER = ['writer', 'plotter', 'scripter'] @@ -105,14 +109,16 @@ def embed_cix_metadata(self): # ensure we have a temp file self.make_temp_cbz_file() + if not python3: + cix_string = cix_string.decode('utf-8', 'ignore') # use the safe_replace function from calibre to prevent coruption if self.zipinfo is not None: with open(self.file, 'r+b') as zf: - safe_replace(zf, self.zipinfo, StringIO(cix_string.decode('utf-8', 'ignore'))) + safe_replace(zf, self.zipinfo, StringIO(cix_string)) # save the metadata in the file else: zf = ZipFile(self.file, "a") - zf.writestr("ComicInfo.xml", cix_string.decode('utf-8', 'ignore')) + zf.writestr("ComicInfo.xml", cix_string) zf.close() def embed_cbi_metadata(self): @@ -124,7 +130,10 @@ def embed_cbi_metadata(self): # ensure we have a temp file self.make_temp_cbz_file() # save the metadata in the comment - writeZipComment(self.file, cbi_string) + zf = ZipFile(self.file, 'a') + zf.comment = cbi_string.encode("utf-8") + zf._didModify = True + zf.close() def convert_calibre_md_to_comic_md(self): ''' @@ -234,7 +243,7 @@ def convert_comic_md_to_calibre_md(self, comic_metadata): # issue if co.issue: try: - if isinstance(co.issue, unicode): + if not python3 and isinstance(co.issue, unicode): mi.series_index = unicodedata.numeric(co.issue) else: mi.series_index = float(co.issue) @@ -300,10 +309,9 @@ def convert_cbr_to_cbz(self): with TemporaryFile("comic.cbz") as tf: zf = ZipFile(tf, "w") zf.add_dir(tdir) - zf.close() - # write comment if comments: - writeZipComment(tf, comments) + zf.comment = comment.encode("utf-8") + zf.close() # add the cbz format to calibres library self.db.add_format(self.book_id, "cbz", tf) self.format = "cbz" @@ -333,6 +341,7 @@ def update_cover(self): if name.rsplit(".", 1)[0] == "00000000_cover": cover_info = name break + zf.close() # delete previous cover if cover_info != "": @@ -354,13 +363,14 @@ def count_pages(self): for name in zf.namelist(): if name.lower().rpartition('.')[-1] in IMG_EXTENSIONS: pages += 1 + zf.close() return pages def action_count_pages(self): pages = self.count_pages() if pages == 0: return False - update_custom_column(prefs['pages_column'], pages, self.calibre_metadata, + update_custom_column(prefs['pages_column'], str(pages), self.calibre_metadata, self.db.field_metadata.custom_field_metadata()) self.db.set_metadata(self.book_id, self.calibre_metadata) return True @@ -387,6 +397,7 @@ def get_picture_size(self): if size_x < size_y: break index += 1 + zf.close() size = round(size_x * size_y / 1000000, 2) return size @@ -549,69 +560,3 @@ def ensure_int(value, func, *args): func(*args) except (ValueError, TypeError): pass - - -def writeZipComment(filename, comment): - ''' - This is a custom function for writing a comment to a zip file, - since the built-in one doesn't seem to work on Windows and Mac OS/X - - Fortunately, the zip comment is at the end of the file, and it's - easy to manipulate. See this website for more info: - see: http://en.wikipedia.org/wiki/Zip_(file_format)#Structure - ''' - from os import stat - from struct import pack - - # get file size - statinfo = stat(filename) - file_length = statinfo.st_size - - try: - fo = open(filename, "r+b") - - # the starting position, relative to EOF - pos = -4 - - found = False - value = bytearray() - - # walk backwards to find the "End of Central Directory" record - while (not found) and (-pos != file_length): - # seek, relative to EOF - fo.seek(pos, 2) - - value = fo.read(4) - - # look for the end of central directory signature - if bytearray(value) == bytearray([0x50, 0x4b, 0x05, 0x06]): - found = True - else: - # not found, step back another byte - pos = pos - 1 - # print pos,"{1} int: {0:x}".format(bytearray(value)[0], value) - - if found: - - # now skip forward 20 bytes to the comment length word - pos += 20 - fo.seek(pos, 2) - - # Pack the length of the comment string - format = "H" # one 2-byte integer - comment_length = pack(format, len(comment)) # pack integer in a binary string - - # write out the length - fo.write(comment_length) - fo.seek(pos + 2, 2) - - # write out the comment itself - fo.write(comment) - fo.truncate() - fo.close() - else: - raise Exception('Failed to write comment to zip file!') - except: - return False - else: - return True diff --git a/config.py b/config.py index 9041fa1..a76357e 100644 --- a/config.py +++ b/config.py @@ -18,6 +18,16 @@ from calibre_plugins.EmbedComicMetadata.ini import ( get_configuration, CONFIG_NAME, CONFIG_TITLE, CONFIG_DEFAULT, CONFIG_COLUMN_TYPE) +import sys + +python3 = sys.version_info[0] > 2 + +# python 2/3 compatibility +def iteritems(d): + if python3: + return iter(d.items()) + return iter(d.iteritems()) + # This is where all preferences for this plugin will be stored # Remember that this name (i.e. plugins/interface_demo) is also @@ -143,7 +153,7 @@ def get_custom_columns(self, column_type): ''' custom_columns = self.ia.gui.library_view.model().custom_columns available_columns = {} - for key, column in custom_columns.iteritems(): + for key, column in iteritems(custom_columns): if (column["datatype"] in column_type["datatype"] and bool(column["is_multiple"]) == column_type["is_multiple"] and column['display'].get('is_names', False) == column_type['is_names']): diff --git a/genericmetadata.py b/genericmetadata.py index 8bc2dbf..0e4b344 100644 --- a/genericmetadata.py +++ b/genericmetadata.py @@ -174,7 +174,7 @@ def overlayCredits(self, new_credits, overwrite=True): self.credits.remove(r) for c in new_credits: - if c.has_key('primary') and c['primary']: + if 'primary' in c and c['primary']: primary = True else: primary = False @@ -295,7 +295,7 @@ def add_attr_string(tag): for c in self.credits: primary = "" - if c.has_key('primary') and c['primary']: + if 'primary' in c and c['primary']: primary = " [P]" add_string("credit", c['role'] + ": " + c['person'] + primary)