From ba71e61d873aef4b6e9114d401c40f61953d0481 Mon Sep 17 00:00:00 2001 From: MichaelFitzurka Date: Fri, 18 Mar 2022 15:14:42 -0400 Subject: [PATCH 1/3] Added 7zip support thru py7zr. Tweaked save of archive file and images in comicarchive. --- comicapi/comicarchive.py | 137 ++++++++++++++++++++++++++-- comictaggerlib/cli.py | 8 +- comictaggerlib/fileselectionlist.py | 8 +- comictaggerlib/renamewindow.py | 4 +- comictaggerlib/taggerwindow.py | 16 ++-- requirements.txt | 1 + scripts/name_fixer.py | 4 +- scripts/remove_ads.py | 2 +- 8 files changed, 156 insertions(+), 24 deletions(-) diff --git a/comicapi/comicarchive.py b/comicapi/comicarchive.py index f5b654a..4f10a2e 100644 --- a/comicapi/comicarchive.py +++ b/comicapi/comicarchive.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import py7zr import zipfile import os import struct @@ -50,6 +51,109 @@ class MetaDataStyle: COMET = 2 name = ['ComicBookLover', 'ComicRack', 'CoMet'] +class SevenZipArchiver: + + """7Z implementation""" + + def __init__(self, path): + self.path = path + + # @todo: Implement Comment? + def getArchiveComment(self): + return "" + + def setArchiveComment(self, comment): + return False + + def readArchiveFile(self, archive_file): + data = "" + try: + with py7zr.SevenZipFile(self.path, 'r') as zf: + data = zf.read(archive_file)[archive_file].read() + except py7zr.Bad7zFile as e: + print("bad zipfile [{0}]: {1} :: {2}".format(e, self.path, + archive_file), file=sys.stderr) + raise IOError + except Exception as e: + print("bad zipfile [{0}]: {1} :: {2}".format(e, self.path, + archive_file), file=sys.stderr) + raise IOError + + return data + + def removeArchiveFile(self, archive_file): + try: + self.rebuildSevenZipFile([archive_file]) + except: + return False + else: + return True + + def writeArchiveFile(self, archive_file, data): + # At the moment, no other option but to rebuild the whole + # zip archive w/o the indicated file. Very sucky, but maybe + # another solution can be found + try: + files = self.getArchiveFilenameList() + if archive_file in files: + self.rebuildSevenZipFile([archive_file]) + + # now just add the archive file as a new one + with py7zr.SevenZipFile(self.path, 'a') as zf: + zf.writestr(data, archive_file) + return True + except: + return False + + def getArchiveFilenameList(self): + try: + with py7zr.SevenZipFile(self.path, 'r') as zf: + namelist = zf.getnames() + + return namelist + except Exception as e: + print("Unable to get zipfile list [{0}]: {1}".format( + e, self.path), file=sys.stderr) + return [] + + def rebuildSevenZipFile(self, exclude_list): + """Zip helper func + + This recompresses the zip archive, without the files in the exclude_list + """ + tmp_fd, tmp_name = tempfile.mkstemp(dir=os.path.dirname(self.path)) + os.close(tmp_fd) + + try: + with py7zr.SevenZipFile(self.path, 'r') as zip: + targets = [f for f in zip.getnames() if f not in exclude_list] + with py7zr.SevenZipFile(self.path, 'r') as zin: + with py7zr.SevenZipFile(tmp_name, 'w') as zout: + for fname, bio in zin.read(targets).items(): + zout.writef(bio, fname) + except Exception as e: + print("Exception[{0}]: {1}".format(e, self.path)) + return [] + + # replace with the new file + os.remove(self.path) + os.rename(tmp_name, self.path) + + def copyFromArchive(self, otherArchive): + """Replace the current zip with one copied from another archive""" + try: + with py7zr.SevenZipFile(self.path, 'w') as zout: + for fname in otherArchive.getArchiveFilenameList(): + data = otherArchive.readArchiveFile(fname) + if data is not None: + zout.writestr(data, fname) + except Exception as e: + print("Error while copying to {0}: {1}".format( + self.path, e), file=sys.stderr) + return False + else: + return True + class ZipArchiver: """ZIP implementation""" @@ -101,7 +205,9 @@ class ZipArchiver: # zip archive w/o the indicated file. Very sucky, but maybe # another solution can be found try: - self.rebuildZipFile([archive_file]) + files = self.getArchiveFilenameList() + if archive_file in files: + self.rebuildZipFile([archive_file]) # now just add the archive file as a new one zf = zipfile.ZipFile( @@ -173,7 +279,7 @@ class ZipArchiver: 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 @@ -270,7 +376,7 @@ class RarArchiver: f.close() working_dir = os.path.dirname(os.path.abspath(self.path)) - + # use external program to write comment to Rar archive proc_args = [self.rar_exe_path, 'c', @@ -304,7 +410,7 @@ class RarArchiver: while tries < 7: try: tries = tries + 1 - data = rarc.open(archive_file).read() + data = rarc.open(archive_file).read() entries = [(rarc.getinfo(archive_file), data)] if entries[0][0].file_size != len(entries[0][1]): @@ -530,7 +636,7 @@ class UnknownArchiver: class ComicArchive: logo_data = None class ArchiveType: - Zip, Rar, Folder, Pdf, Unknown = list(range(5)) + SevenZip, Zip, Rar, Folder, Pdf, Unknown = list(range(6)) def __init__(self, path, rar_exe_path=None, default_image_path=None): self.path = path @@ -558,7 +664,11 @@ class ComicArchive: self.archive_type = self.ArchiveType.Zip self.archiver = ZipArchiver(self.path) else: - if self.zipTest(): + if self.sevenZipTest(): + self.archive_type = self.ArchiveType.SevenZip + self.archiver = SevenZipArchiver(self.path) + + elif self.zipTest(): self.archive_type = self.ArchiveType.Zip self.archiver = ZipArchiver(self.path) @@ -595,6 +705,9 @@ class ComicArchive: self.path = path self.archiver.path = path + def sevenZipTest(self): + return py7zr.is_7zfile(self.path) + def zipTest(self): return zipfile.is_zipfile(self.path) @@ -604,6 +717,9 @@ class ComicArchive: except: return False + def isSevenZip(self): + return self.archive_type == self.ArchiveType.SevenZip + def isZip(self): return self.archive_type == self.ArchiveType.Zip @@ -645,7 +761,7 @@ class ComicArchive: if ( # or self.isFolder() ) - (self.isZip() or self.isRar()) + (self.isSevenZip() or self.isZip() or self.isRar()) and (self.getNumberOfPages() > 0) @@ -826,7 +942,7 @@ class ComicArchive: def hasCBI(self): if self.has_cbi is None: - # if ( not ( self.isZip() or self.isRar()) or not + # if ( not (self.isSevenZip() or self.isZip() or self.isRar()) or not # self.seemsToBeAComicArchive() ): if not self.seemsToBeAComicArchive(): self.has_cbi = False @@ -1037,7 +1153,10 @@ class ComicArchive: data = self.getPage(idx) if data is not None: try: - im = Image.open(io.StringIO(data)) + if isinstance(data, bytes): + im = Image.open(io.BytesIO(data)) + else: + im = Image.open(io.StringIO(data)) w, h = im.size p['ImageSize'] = str(len(data)) diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py index 688907d..a58ecbd 100644 --- a/comictaggerlib/cli.py +++ b/comictaggerlib/cli.py @@ -258,7 +258,9 @@ def process_file_cli(filename, opts, settings, match_results): if batch_mode: brief = "{0}: ".format(filename) - if ca.isZip(): + if ca.isSevenZip(): + brief += "7Z archive " + elif ca.isZip(): brief += "ZIP archive " elif ca.isRar(): brief += "RAR archive " @@ -498,7 +500,9 @@ def process_file_cli(filename, opts, settings, match_results): new_ext = None # default if settings.rename_extension_based_on_archive: - if ca.isZip(): + if ca.isSevenZip(): + new_ext = ".cb7" + elif ca.isZip(): new_ext = ".cbz" elif ca.isRar(): new_ext = ".cbr" diff --git a/comictaggerlib/fileselectionlist.py b/comictaggerlib/fileselectionlist.py index 8960935..9be4689 100644 --- a/comictaggerlib/fileselectionlist.py +++ b/comictaggerlib/fileselectionlist.py @@ -197,7 +197,7 @@ class FileSelectionList(QWidget): centerWindowOnParent(progdialog) #QCoreApplication.processEvents() #progdialog.show() - + QCoreApplication.processEvents() firstAdded = None self.twList.setSortingEnabled(False) @@ -212,7 +212,7 @@ class FileSelectionList(QWidget): row = self.addPathItem(f) if firstAdded is None and row is not None: firstAdded = row - + progdialog.hide() QCoreApplication.processEvents() @@ -335,7 +335,9 @@ class FileSelectionList(QWidget): filename_item.setText(item_text) filename_item.setData(Qt.ToolTipRole, item_text) - if fi.ca.isZip(): + if fi.ca.isSevenZip(): + item_text = "7Z" + elif fi.ca.isZip(): item_text = "ZIP" elif fi.ca.isRar(): item_text = "RAR" diff --git a/comictaggerlib/renamewindow.py b/comictaggerlib/renamewindow.py index 9d1e2e1..abbd334 100644 --- a/comictaggerlib/renamewindow.py +++ b/comictaggerlib/renamewindow.py @@ -67,7 +67,9 @@ class RenameWindow(QtWidgets.QDialog): new_ext = None # default if self.settings.rename_extension_based_on_archive: - if ca.isZip(): + if ca.isSevenZip(): + new_ext = ".cb7" + elif ca.isZip(): new_ext = ".cbz" elif ca.isRar(): new_ext = ".cbr" diff --git a/comictaggerlib/taggerwindow.py b/comictaggerlib/taggerwindow.py index f78fa7c..945889c 100644 --- a/comictaggerlib/taggerwindow.py +++ b/comictaggerlib/taggerwindow.py @@ -87,7 +87,7 @@ class TaggerWindow(QtWidgets.QMainWindow): def __init__(self, file_list, settings, parent=None, opts=None): super(TaggerWindow, self).__init__(parent) - + uic.loadUi(ComicTaggerSettings.getUIFile('taggerwindow.ui'), self) self.settings = settings @@ -294,7 +294,7 @@ class TaggerWindow(QtWidgets.QMainWindow): self.setWindowIcon( QtGui.QIcon(ComicTaggerSettings.getGraphic('app.png'))) - + if self.comic_archive is None: self.setWindowTitle(self.appName) else: @@ -584,7 +584,7 @@ class TaggerWindow(QtWidgets.QMainWindow): event.accept() def getUrlFromLocalFileID(self, localFileID): - return localFileID.toLocalFile() + return localFileID.toLocalFile() def dropEvent(self, event): # if self.dirtyFlagVerification("Open Archive", @@ -675,7 +675,9 @@ class TaggerWindow(QtWidgets.QMainWindow): self.lblFilename.setText(filename) - if ca.isZip(): + if ca.isSevenZip(): + self.lblArchiveType.setText("7Z archive") + elif ca.isZip(): self.lblArchiveType.setText("ZIP archive") elif ca.isRar(): self.lblArchiveType.setText("RAR archive") @@ -985,7 +987,7 @@ class TaggerWindow(QtWidgets.QMainWindow): dialog.setDirectory(self.settings.last_opened_folder) if not folder_mode: - archive_filter = "Comic archive files (*.cbz *.zip *.cbr *.rar)" + archive_filter = "Comic archive files (*.cb7 *.7z *.cbz *.zip *.cbr *.rar)" filters = [ archive_filter, "Any files (*)" @@ -1164,11 +1166,11 @@ class TaggerWindow(QtWidgets.QMainWindow): def updateCreditColors(self): #!!!ATB qt5 porting TODO - #return + #return inactive_color = QtGui.QColor(255, 170, 150) active_palette = self.leSeries.palette() active_color = active_palette.color(QtGui.QPalette.Base) - + inactive_brush = QtGui.QBrush(inactive_color) active_brush = QtGui.QBrush(active_color) diff --git a/requirements.txt b/requirements.txt index 61bc2f0..feccf16 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ configparser natsort pillow>=4.3.0 requests +py7zr diff --git a/scripts/name_fixer.py b/scripts/name_fixer.py index f105614..7b097ac 100755 --- a/scripts/name_fixer.py +++ b/scripts/name_fixer.py @@ -54,7 +54,9 @@ def calculate_rename(ca, md, settings): new_ext = None # default if settings.rename_extension_based_on_archive: - if ca.isZip(): + if ca.isSevenZip(): + new_ext = ".cb7" + elif ca.isZip(): new_ext = ".cbz" elif ca.isRar(): new_ext = ".cbr" diff --git a/scripts/remove_ads.py b/scripts/remove_ads.py index 54d08c8..08e1a2f 100755 --- a/scripts/remove_ads.py +++ b/scripts/remove_ads.py @@ -51,7 +51,7 @@ def main(): for filename in filelist: ca = ComicArchive(filename, settings.rar_exe_path) - if (ca.isZip or ca.isRar()) and ca.hasMetadata(style): + if (ca.isSevenZip() or ca.isZip() or ca.isRar()) and ca.hasMetadata(style): md = ca.readMetadata(style) if len(md.pages) != 0: for p in md.pages: From ae20a2eec8d9390d4a70d3e53b842b314e2d63cc Mon Sep 17 00:00:00 2001 From: MichaelFitzurka Date: Sat, 26 Mar 2022 12:42:33 -0400 Subject: [PATCH 2/3] Updates as requested. --- comicapi/comicarchive.py | 9 +++------ scripts/name_fixer.py | 4 +--- scripts/remove_ads.py | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/comicapi/comicarchive.py b/comicapi/comicarchive.py index 4f10a2e..685aaef 100644 --- a/comicapi/comicarchive.py +++ b/comicapi/comicarchive.py @@ -71,11 +71,11 @@ class SevenZipArchiver: with py7zr.SevenZipFile(self.path, 'r') as zf: data = zf.read(archive_file)[archive_file].read() except py7zr.Bad7zFile as e: - print("bad zipfile [{0}]: {1} :: {2}".format(e, self.path, + print("bad 7zip file [{0}]: {1} :: {2}".format(e, self.path, archive_file), file=sys.stderr) raise IOError except Exception as e: - print("bad zipfile [{0}]: {1} :: {2}".format(e, self.path, + print("bad 7zip file [{0}]: {1} :: {2}".format(e, self.path, archive_file), file=sys.stderr) raise IOError @@ -1153,10 +1153,7 @@ class ComicArchive: data = self.getPage(idx) if data is not None: try: - if isinstance(data, bytes): - im = Image.open(io.BytesIO(data)) - else: - im = Image.open(io.StringIO(data)) + im = Image.open(io.StringIO(data)) w, h = im.size p['ImageSize'] = str(len(data)) diff --git a/scripts/name_fixer.py b/scripts/name_fixer.py index 7b097ac..f105614 100755 --- a/scripts/name_fixer.py +++ b/scripts/name_fixer.py @@ -54,9 +54,7 @@ def calculate_rename(ca, md, settings): new_ext = None # default if settings.rename_extension_based_on_archive: - if ca.isSevenZip(): - new_ext = ".cb7" - elif ca.isZip(): + if ca.isZip(): new_ext = ".cbz" elif ca.isRar(): new_ext = ".cbr" diff --git a/scripts/remove_ads.py b/scripts/remove_ads.py index 08e1a2f..8e2d687 100755 --- a/scripts/remove_ads.py +++ b/scripts/remove_ads.py @@ -51,7 +51,7 @@ def main(): for filename in filelist: ca = ComicArchive(filename, settings.rar_exe_path) - if (ca.isSevenZip() or ca.isZip() or ca.isRar()) and ca.hasMetadata(style): + if (ca.isZip() or ca.isRar()) and ca.hasMetadata(style): md = ca.readMetadata(style) if len(md.pages) != 0: for p in md.pages: From 908a500e7e3385accf0c1a41a2b71a9a4e8ba02d Mon Sep 17 00:00:00 2001 From: MichaelFitzurka Date: Sat, 26 Mar 2022 12:45:33 -0400 Subject: [PATCH 3/3] One more. --- scripts/remove_ads.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/remove_ads.py b/scripts/remove_ads.py index 8e2d687..54d08c8 100755 --- a/scripts/remove_ads.py +++ b/scripts/remove_ads.py @@ -51,7 +51,7 @@ def main(): for filename in filelist: ca = ComicArchive(filename, settings.rar_exe_path) - if (ca.isZip() or ca.isRar()) and ca.hasMetadata(style): + if (ca.isZip or ca.isRar()) and ca.hasMetadata(style): md = ca.readMetadata(style) if len(md.pages) != 0: for p in md.pages: