Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
ac1bdf2f9c | |||
c840724c9c | |||
220606a046 | |||
223269cc2e | |||
31b96fdbb9 | |||
908a500e7e | |||
ae20a2eec8 | |||
287c5f39c1 | |||
cfd2489228 | |||
86a83021a6 | |||
d7595f5ca1 | |||
5a2bb66d5b | |||
5de2ce65a4 | |||
95d167561d | |||
7d2702c3b6 | |||
d0f96b6511 | |||
ba71e61d87 | |||
191d72554c |
@ -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 7zip file [{0}]: {1} :: {2}".format(e, self.path,
|
||||
archive_file), file=sys.stderr)
|
||||
raise IOError
|
||||
except Exception as e:
|
||||
print("bad 7zip file [{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)
|
||||
|
||||
@ -786,7 +902,7 @@ class ComicArchive:
|
||||
def keyfunc(k):
|
||||
return k.lower()
|
||||
|
||||
files = natsort.natsorted(files, alg=natsort.ns.IC | natsort.ns.I)
|
||||
files = natsort.natsorted(files, alg=natsort.ns.IC | natsort.ns.I | natsort.ns.U)
|
||||
|
||||
# make a sub-list of image files
|
||||
self.page_list = []
|
||||
@ -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
|
||||
|
@ -51,12 +51,9 @@ class ComicInfoXml:
|
||||
return self.convertXMLToMetadata(tree)
|
||||
|
||||
def stringFromMetadata(self, metadata, xml=None):
|
||||
|
||||
header = '<?xml version="1.0"?>\n'
|
||||
|
||||
tree = self.convertMetadataToXML(self, metadata, xml)
|
||||
tree_str = ET.tostring(tree.getroot()).decode()
|
||||
return header + tree_str
|
||||
tree_str = ET.tostring(tree.getroot(), encoding="utf-8", xml_declaration=True).decode()
|
||||
return tree_str
|
||||
|
||||
def indent(self, elem, level=0):
|
||||
# for making the XML output readable
|
||||
@ -90,7 +87,6 @@ class ComicInfoXml:
|
||||
|
||||
def assign(cix_entry, md_entry):
|
||||
if md_entry is not None:
|
||||
print(cix_entry, md_entry)
|
||||
et_entry = root.find(cix_entry)
|
||||
if et_entry is not None:
|
||||
et_entry.text = "{0}".format(md_entry)
|
||||
@ -278,7 +274,7 @@ class ComicInfoXml:
|
||||
|
||||
tree = self.convertMetadataToXML(self, metadata, xml)
|
||||
# ET.dump(tree)
|
||||
tree.write(filename, encoding='utf-8')
|
||||
tree.write(filename, encoding="utf-8", xml_declaration=True)
|
||||
|
||||
def readFromExternalFile(self, filename):
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
import localefix
|
||||
from comictaggerlib.main import ctmain
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -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"
|
||||
|
@ -630,7 +630,6 @@ class ComicVineTalker(QObject):
|
||||
for w in col_widths:
|
||||
fmtstr += " {{:{}}}|".format(w + 1)
|
||||
width = sum(col_widths) + len(col_widths) * 2
|
||||
print("width=", width)
|
||||
table_text = ""
|
||||
counter = 0
|
||||
for row in rows:
|
||||
@ -741,14 +740,15 @@ class ComicVineTalker(QObject):
|
||||
for d in div_list:
|
||||
if 'class' in d.attrs:
|
||||
c = d['class']
|
||||
if ('imgboxart' in c and
|
||||
'issue-cover' in c and
|
||||
d.img['src'].startswith("http")
|
||||
):
|
||||
|
||||
covers_found += 1
|
||||
if covers_found != 1:
|
||||
if 'imgboxart' in c and 'issue-cover' in c:
|
||||
if d.img['src'].startswith("http"):
|
||||
covers_found += 1
|
||||
if covers_found != 1:
|
||||
alt_cover_url_list.append(d.img['src'])
|
||||
elif d.img['data-src'].startswith("http"):
|
||||
covers_found += 1
|
||||
if covers_found != 1:
|
||||
alt_cover_url_list.append(d.img['data-src'])
|
||||
|
||||
return alt_cover_url_list
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
||||
|
44
localefix.py
Normal file
44
localefix.py
Normal file
@ -0,0 +1,44 @@
|
||||
import locale
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def _lang_code_mac():
|
||||
"""
|
||||
stolen from https://github.com/mu-editor/mu
|
||||
Returns the user's language preference as defined in the Language & Region
|
||||
preference pane in macOS's System Preferences.
|
||||
"""
|
||||
|
||||
# Uses the shell command `defaults read -g AppleLocale` that prints out a
|
||||
# language code to standard output. Assumptions about the command:
|
||||
# - It exists and is in the shell's PATH.
|
||||
# - It accepts those arguments.
|
||||
# - It returns a usable language code.
|
||||
#
|
||||
# Reference documentation:
|
||||
# - The man page for the `defaults` command on macOS.
|
||||
# - The macOS underlying API:
|
||||
# https://developer.apple.com/documentation/foundation/nsuserdefaults.
|
||||
|
||||
LANG_DETECT_COMMAND = 'defaults read -g AppleLocale'
|
||||
|
||||
status, output = subprocess.getstatusoutput(LANG_DETECT_COMMAND)
|
||||
if status == 0:
|
||||
# Command was successful.
|
||||
lang_code = output
|
||||
else:
|
||||
_logger.warning('Language detection command failed: %r', output)
|
||||
lang_code = ''
|
||||
|
||||
return lang_code
|
||||
|
||||
if sys.platform == "darwin" and "LANG" not in os.environ:
|
||||
code = _lang_code_mac()
|
||||
if code != "":
|
||||
os.environ["LANG"] = f"{code}.utf-8"
|
||||
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
sys.stdout.reconfigure(encoding=sys.getdefaultencoding())
|
||||
sys.stderr.reconfigure(encoding=sys.getdefaultencoding())
|
||||
sys.stdin.reconfigure(encoding=sys.getdefaultencoding())
|
@ -1,5 +1,6 @@
|
||||
beautifulsoup4 >= 4.1
|
||||
configparser
|
||||
natsort
|
||||
natsort >=8.1.0
|
||||
pillow>=4.3.0
|
||||
requests
|
||||
py7zr
|
||||
|
@ -1,4 +1,4 @@
|
||||
pyinstaller==4.3
|
||||
pyinstaller>=4.10
|
||||
setuptools>=42
|
||||
setuptools_scm[toml]>=3.4
|
||||
wheel
|
||||
|
Reference in New Issue
Block a user