Python3 and QT5 upgrade (#109)
* Tweaked search string based on new comic vine search behavior Placated Beaufitul Soup by passing the parser * First cut at porting to Python 3 and PyQt5 * remove debug print * tweaked progress dialog handling for issues on ubuntu gui * Handle bad key more gracefullu * More integration of unrarlib into settings and rest of app * Better handling of "personal" unrar lib setting * PEP 440-compliant version string * Tuned linux rar help strings * Got setup working again * Attempts to build unrar on install * Some minimal desktop integration on various platforms * Fix wrong shortfile * More setup.py enhancements * Use proper temp file * Added comment block at top * Comment out desktop integration attempt for now * Updated some links and info * Fixed the html a bit * Repaired some images that caused libpng to complain * update readme re: py3qt5 branch changes * another note * #108 feat: try to simplify windows build using only pip and python3 * #108 feat: fix python location on appveyor (try 1) * #108 feat: use venv (try 2) * #108 feat: use venv (try 3) * #108 feat: update to latest pyinstaller develop branch * #108 feat: update to latest pyinstaller develop branch (again) * #108: add ssl libraries for windows packaging * #108: refresh env in win build to pick the right mingw * #108: change order of win build script operations * #113: fix subprocess usage in pyinstaller package * bump version
This commit is contained in:
parent
cf43513d52
commit
91f82fd6d3
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,3 +2,5 @@
|
||||
/nbproject/
|
||||
/dist
|
||||
*.pyc
|
||||
/.vscode
|
||||
venv
|
@ -1,4 +1,8 @@
|
||||
include README.txt
|
||||
include README.md
|
||||
include release_notes.txt
|
||||
include requirements.txt
|
||||
recursive-include scripts *.py *.txt
|
||||
include unrar/*
|
||||
recursive-include scripts *.py *.txt
|
||||
recursive-include desktop-integration *
|
||||
include windows/app.ico
|
||||
include mac/app.icns
|
||||
|
15
Makefile
15
Makefile
@ -1,4 +1,4 @@
|
||||
VERSION_STR := $(shell python -c 'import comictaggerlib.ctversion; print comictaggerlib.ctversion.version')
|
||||
VERSION_STR := $(shell python -c 'import comictaggerlib.ctversion; print( comictaggerlib.ctversion.version)')
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
APP_NAME=comictagger.exe
|
||||
@ -25,12 +25,17 @@ clean:
|
||||
$(MAKE) -C mac clean
|
||||
rm -rf build
|
||||
$(MAKE) -C unrar clean
|
||||
rm -f unrar/libunrar.so unrar/libunrar.a unrar/unrar
|
||||
rm -f comictaggerlib/libunrar.so
|
||||
rm -rf comictaggerlib/ui/__pycache__
|
||||
|
||||
pydist:
|
||||
mkdir -p release
|
||||
rm -f release/*.zip
|
||||
make clean
|
||||
mkdir -p piprelease
|
||||
rm -f comictagger-$(VERSION_STR).zip
|
||||
python setup.py sdist --formats=zip #,gztar
|
||||
mv dist/comictagger-$(VERSION_STR).zip release
|
||||
mv dist/comictagger-$(VERSION_STR).zip piprelease
|
||||
rm -rf comictagger.egg-info dist
|
||||
|
||||
upload:
|
||||
python setup.py register
|
||||
@ -47,4 +52,4 @@ endif
|
||||
|
||||
dist: unrar
|
||||
pyinstaller -y comictagger.spec
|
||||
mv dist/$(APP_NAME) dist/$(FINAL_NAME)
|
||||
mv dist/$(APP_NAME) dist/$(FINAL_NAME)
|
||||
|
66
README.md
66
README.md
@ -1,53 +1,17 @@
|
||||
This is a fork derived from google code:
|
||||
A fork from the primary dev branch at https://github.com/davide-romanini/comictagger
|
||||
|
||||
https://code.google.com/p/comictagger/
|
||||
Changes:
|
||||
- Ported to Python 3
|
||||
- Ported to PyQt5
|
||||
- Added more application and GUI awareness of the unrar library, and removed references to the old scheme that used the unrar executable.
|
||||
- Got setup.py working again to build sdist packages, suitable (I think) for PyPI. An install from the package will attempt to build unrar library. It should work on most Linux distros, and was tested on a Mac OSX system with dev tools from homebrew. If the library doesn't build, the GUI has instructions on where to download the library.
|
||||
- Removed/changes obsolete links to old Google code website.
|
||||
- Set a environment variable to scale the GUI on 4k displays
|
||||
|
||||
Notes:
|
||||
- I did some testing with the pyinstaller build, and it worked on both platforms. I did encounter two problems:
|
||||
- Mac build showed the wrong widget set. I found a solution here that seemed to work: https://stackoverflow.com/questions/48626999/packaging-with-pyinstaller-pyqt5-setstyle-ignored
|
||||
- Windows build had problems grabbing images from ComicVine using SSL. It think that some libraries are missing from the monolithic exe, but I couldn't figure out how to fix the problem.
|
||||
- In setup.py you can also find the remains of an attempt to do some desktop integration from a pip install. It does work, but can cause problems with wheel installs, and I don't know if it's worth the bother. I kept the commented-out code in place, just in case.
|
||||
|
||||
|
||||
Changes in this fork:
|
||||
- using different unrar library https://pypi.python.org/pypi/unrar/. The previous one used unrar.dll on windows and
|
||||
hackish wrapping of unrar command on linux, while this new one should use unrarlib on both platforms. From my tests
|
||||
it is more stable and faster. *Requires unrarlib availability, check unrar module documentation for more
|
||||
information*.
|
||||
- extracted core libraries in its own package comicapi, shared in a new repository using git subtree for better
|
||||
alignment with comicstreamer
|
||||
- support for *day of month* field in the GUI
|
||||
- merge of changes from fcanc fork
|
||||
|
||||
Todo:
|
||||
- more tests in non-linux platforms
|
||||
- repackage for simple user installation
|
||||
|
||||
Follows original readme:
|
||||
|
||||
ComicTagger is a multi-platform app for writing metadata to digital comics, written in Python and PyQt.
|
||||
|
||||
Features:
|
||||
|
||||
* Runs on Mac OSX, Microsoft Windows, and Linux systems
|
||||
* Communicates with an online database (Comic Vine) for acquiring metadata
|
||||
* Uses image processing to automatically match a given archive with the correct issue data
|
||||
* Batch processing in the GUI for tagging hundreds or more comics at a time
|
||||
* Reads and writes multiple tagging schemes ( ComicBookLover and ComicRack, with more planned).
|
||||
* Reads and writes RAR and Zip archives (external tools needed for writing RAR)
|
||||
* Command line interface (CLI) on all platforms (including Windows), which supports batch operations, and which can be
|
||||
used in native scripts for complex operations. For example, to recursively scrape and tag all archives in a folder
|
||||
comictagger.py -R -s -o -f -t cr -v -i --nooverwrite /path/to/comics/
|
||||
|
||||
For details, screen-shots, release notes, and more, visit http://code.google.com/p/comictagger/
|
||||
|
||||
Requires:
|
||||
|
||||
* python 2.6 or 2.7
|
||||
* configparser
|
||||
* python imaging (PIL) >= 1.1.6
|
||||
* beautifulsoup > 4.1
|
||||
|
||||
Optional requirement (for GUI):
|
||||
|
||||
* pyqt4
|
||||
|
||||
Install and run:
|
||||
|
||||
* ComicTagger can be run directly from this directory, using the launcher script "comictagger.py"
|
||||
|
||||
* To install on your system use: "python setup.py install". Take note in the output where comictagger.py goes!
|
||||
With Python 3, it's much easier to get the app working from scratch on a new distro, as all of the dependencies are available as wheels, including PyQt5, so just a simple "pip install comictagger.zip" is all that's needed.
|
||||
|
@ -19,8 +19,8 @@ import xml.etree.ElementTree as ET
|
||||
#from pprint import pprint
|
||||
#import zipfile
|
||||
|
||||
from genericmetadata import GenericMetadata
|
||||
import utils
|
||||
from .genericmetadata import GenericMetadata
|
||||
from . import utils
|
||||
|
||||
|
||||
class CoMet:
|
||||
@ -76,7 +76,7 @@ class CoMet:
|
||||
# helper func
|
||||
def assign(comet_entry, md_entry):
|
||||
if md_entry is not None:
|
||||
ET.SubElement(root, comet_entry).text = u"{0}".format(md_entry)
|
||||
ET.SubElement(root, comet_entry).text = "{0}".format(md_entry)
|
||||
|
||||
# title is manditory
|
||||
if md.title is None:
|
||||
@ -131,43 +131,43 @@ class CoMet:
|
||||
if credit['role'].lower() in set(self.writer_synonyms):
|
||||
ET.SubElement(
|
||||
root,
|
||||
'writer').text = u"{0}".format(
|
||||
'writer').text = "{0}".format(
|
||||
credit['person'])
|
||||
|
||||
if credit['role'].lower() in set(self.penciller_synonyms):
|
||||
ET.SubElement(
|
||||
root,
|
||||
'penciller').text = u"{0}".format(
|
||||
'penciller').text = "{0}".format(
|
||||
credit['person'])
|
||||
|
||||
if credit['role'].lower() in set(self.inker_synonyms):
|
||||
ET.SubElement(
|
||||
root,
|
||||
'inker').text = u"{0}".format(
|
||||
'inker').text = "{0}".format(
|
||||
credit['person'])
|
||||
|
||||
if credit['role'].lower() in set(self.colorist_synonyms):
|
||||
ET.SubElement(
|
||||
root,
|
||||
'colorist').text = u"{0}".format(
|
||||
'colorist').text = "{0}".format(
|
||||
credit['person'])
|
||||
|
||||
if credit['role'].lower() in set(self.letterer_synonyms):
|
||||
ET.SubElement(
|
||||
root,
|
||||
'letterer').text = u"{0}".format(
|
||||
'letterer').text = "{0}".format(
|
||||
credit['person'])
|
||||
|
||||
if credit['role'].lower() in set(self.cover_synonyms):
|
||||
ET.SubElement(
|
||||
root,
|
||||
'coverDesigner').text = u"{0}".format(
|
||||
'coverDesigner').text = "{0}".format(
|
||||
credit['person'])
|
||||
|
||||
if credit['role'].lower() in set(self.editor_synonyms):
|
||||
ET.SubElement(
|
||||
root,
|
||||
'editor').text = u"{0}".format(
|
||||
'editor').text = "{0}".format(
|
||||
credit['person'])
|
||||
|
||||
# self pretty-print
|
||||
|
@ -23,7 +23,7 @@ import subprocess
|
||||
import platform
|
||||
import ctypes
|
||||
import time
|
||||
import StringIO
|
||||
import io
|
||||
#import io
|
||||
#import locale
|
||||
#import shutil
|
||||
@ -65,12 +65,13 @@ try:
|
||||
self._data += chunk
|
||||
return 1
|
||||
rarfile._ReadIntoMemory._callback = _rar_cb
|
||||
except:
|
||||
print "WARNING: cannot find libunrar, rar support is disabled"
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("WARNING: cannot find libunrar, rar support is disabled")
|
||||
pass
|
||||
|
||||
if platform.system() == "Windows":
|
||||
import _subprocess
|
||||
#if platform.system() == "Windows":
|
||||
# import _subprocess
|
||||
|
||||
try:
|
||||
import Image
|
||||
@ -78,11 +79,11 @@ try:
|
||||
except ImportError:
|
||||
pil_available = False
|
||||
|
||||
from comicinfoxml import ComicInfoXml
|
||||
from comicbookinfo import ComicBookInfo
|
||||
from comet import CoMet
|
||||
from genericmetadata import GenericMetadata, PageType
|
||||
from filenameparser import FileNameParser
|
||||
from .comicinfoxml import ComicInfoXml
|
||||
from .comicbookinfo import ComicBookInfo
|
||||
from .comet import CoMet
|
||||
from .genericmetadata import GenericMetadata, PageType
|
||||
from .filenameparser import FileNameParser
|
||||
#from settings import ComicTaggerSettings
|
||||
|
||||
|
||||
@ -109,7 +110,10 @@ class ZipArchiver:
|
||||
return comment
|
||||
|
||||
def setArchiveComment(self, comment):
|
||||
return self.writeZipComment(self.path, comment)
|
||||
zf = zipfile.ZipFile(self.path, 'a')
|
||||
zf.comment = bytes(comment, 'utf-8')
|
||||
zf.close()
|
||||
return True
|
||||
|
||||
def readArchiveFile(self, archive_file):
|
||||
data = ""
|
||||
@ -118,14 +122,13 @@ class ZipArchiver:
|
||||
try:
|
||||
data = zf.read(archive_file)
|
||||
except zipfile.BadZipfile as e:
|
||||
print >> sys.stderr, u"bad zipfile [{0}]: {1} :: {2}".format(
|
||||
e, self.path, archive_file)
|
||||
print("bad zipfile [{0}]: {1} :: {2}".format(e, self.path, archive_file), file=sys.stderr)
|
||||
zf.close()
|
||||
raise IOError
|
||||
except Exception as e:
|
||||
zf.close()
|
||||
print >> sys.stderr, u"bad zipfile [{0}]: {1} :: {2}".format(
|
||||
e, self.path, archive_file)
|
||||
print("bad zipfile [{0}]: {1} :: {2}".format(
|
||||
e, self.path, archive_file), file=sys.stderr)
|
||||
raise IOError
|
||||
finally:
|
||||
zf.close()
|
||||
@ -164,8 +167,8 @@ class ZipArchiver:
|
||||
zf.close()
|
||||
return namelist
|
||||
except Exception as e:
|
||||
print >> sys.stderr, u"Unable to get zipfile list [{0}]: {1}".format(
|
||||
e, self.path)
|
||||
print("Unable to get zipfile list [{0}]: {1}".format(
|
||||
e, self.path), file=sys.stderr)
|
||||
return []
|
||||
|
||||
def rebuildZipFile(self, exclude_list):
|
||||
@ -220,7 +223,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
|
||||
@ -253,12 +256,12 @@ class ZipArchiver:
|
||||
fo.seek(pos + 2, 2)
|
||||
|
||||
# write out the comment itself
|
||||
fo.write(comment)
|
||||
fo.write(bytes(comment))
|
||||
fo.truncate()
|
||||
fo.close()
|
||||
else:
|
||||
raise Exception('Failed to write comment to zip file!')
|
||||
except:
|
||||
except Exception as e:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@ -280,8 +283,8 @@ class ZipArchiver:
|
||||
if not self.writeZipComment(self.path, comment):
|
||||
return False
|
||||
except Exception as e:
|
||||
print >> sys.stderr, u"Error while copying to {0}: {1}".format(
|
||||
self.path, e)
|
||||
print("Error while copying to {0}: {1}".format(
|
||||
self.path, e), file=sys.stderr)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@ -305,7 +308,7 @@ class RarArchiver:
|
||||
# windows only, keeps the cmd.exe from popping up
|
||||
if platform.system() == "Windows":
|
||||
self.startupinfo = subprocess.STARTUPINFO()
|
||||
self.startupinfo.dwFlags |= _subprocess.STARTF_USESHOWWINDOW
|
||||
self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
else:
|
||||
self.startupinfo = None
|
||||
|
||||
@ -314,37 +317,38 @@ class RarArchiver:
|
||||
pass
|
||||
|
||||
def getArchiveComment(self):
|
||||
|
||||
rarc = self.getRARObj()
|
||||
return rarc.comment
|
||||
|
||||
def setArchiveComment(self, comment):
|
||||
|
||||
if self.rar_exe_path is not None:
|
||||
try:
|
||||
# write comment to temp file
|
||||
tmp_fd, tmp_name = tempfile.mkstemp()
|
||||
f = os.fdopen(tmp_fd, 'w+b')
|
||||
f = os.fdopen(tmp_fd, 'w+')
|
||||
f.write(comment)
|
||||
f.close()
|
||||
|
||||
working_dir = os.path.dirname(os.path.abspath(self.path))
|
||||
|
||||
|
||||
# use external program to write comment to Rar archive
|
||||
subprocess.call([self.rar_exe_path,
|
||||
proc_args = [self.rar_exe_path,
|
||||
'c',
|
||||
'-w' + working_dir,
|
||||
'-c-',
|
||||
'-z' + tmp_name,
|
||||
self.path],
|
||||
self.path]
|
||||
subprocess.call(proc_args,
|
||||
startupinfo=self.startupinfo,
|
||||
stdout=RarArchiver.devnull)
|
||||
stdout=RarArchiver.devnull,
|
||||
stdin=RarArchiver.devnull,
|
||||
stderr=RarArchiver.devnull)
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
time.sleep(1)
|
||||
|
||||
os.remove(tmp_name)
|
||||
except:
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@ -376,26 +380,26 @@ class RarArchiver:
|
||||
#entries = rarc.read_files( archive_file )
|
||||
|
||||
if entries[0][0].file_size != len(entries[0][1]):
|
||||
print >> sys.stderr, u"readArchiveFile(): [file is not expected size: {0} vs {1}] {2}:{3} [attempt # {4}]".format(
|
||||
print("readArchiveFile(): [file is not expected size: {0} vs {1}] {2}:{3} [attempt # {4}]".format(
|
||||
entries[0][0].file_size, len(
|
||||
entries[0][1]), self.path, archive_file, tries)
|
||||
entries[0][1]), self.path, archive_file, tries), file=sys.stderr)
|
||||
continue
|
||||
|
||||
except (OSError, IOError) as e:
|
||||
print >> sys.stderr, u"readArchiveFile(): [{0}] {1}:{2} attempt#{3}".format(
|
||||
str(e), self.path, archive_file, tries)
|
||||
print("readArchiveFile(): [{0}] {1}:{2} attempt#{3}".format(
|
||||
str(e), self.path, archive_file, tries), file=sys.stderr)
|
||||
time.sleep(1)
|
||||
except Exception as e:
|
||||
print >> sys.stderr, u"Unexpected exception in readArchiveFile(): [{0}] for {1}:{2} attempt#{3}".format(
|
||||
str(e), self.path, archive_file, tries)
|
||||
print("Unexpected exception in readArchiveFile(): [{0}] for {1}:{2} attempt#{3}".format(
|
||||
str(e), self.path, archive_file, tries), file=sys.stderr)
|
||||
break
|
||||
|
||||
else:
|
||||
# Success"
|
||||
# entries is a list of of tuples: ( rarinfo, filedata)
|
||||
if tries > 1:
|
||||
print >> sys.stderr, u"Attempted read_files() {0} times".format(
|
||||
tries)
|
||||
print("Attempted read_files() {0} times".format(
|
||||
tries), file=sys.stderr)
|
||||
if (len(entries) == 1):
|
||||
return entries[0][1]
|
||||
else:
|
||||
@ -428,7 +432,9 @@ class RarArchiver:
|
||||
self.path,
|
||||
tmp_file],
|
||||
startupinfo=self.startupinfo,
|
||||
stdout=RarArchiver.devnull)
|
||||
stdout=RarArchiver.devnull,
|
||||
stdin=RarArchiver.devnull,
|
||||
stderr=RarArchiver.devnull)
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
time.sleep(1)
|
||||
@ -451,7 +457,9 @@ class RarArchiver:
|
||||
self.path,
|
||||
archive_file],
|
||||
startupinfo=self.startupinfo,
|
||||
stdout=RarArchiver.devnull)
|
||||
stdout=RarArchiver.devnull,
|
||||
stdin=RarArchiver.devnull,
|
||||
stderr=RarArchiver.devnull)
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
time.sleep(1)
|
||||
@ -479,8 +487,8 @@ class RarArchiver:
|
||||
namelist.append(item.filename)
|
||||
|
||||
except (OSError, IOError) as e:
|
||||
print >> sys.stderr, u"getArchiveFilenameList(): [{0}] {1} attempt#{2}".format(
|
||||
str(e), self.path, tries)
|
||||
print("getArchiveFilenameList(): [{0}] {1} attempt#{2}".format(
|
||||
str(e), self.path, tries), file=sys.stderr)
|
||||
time.sleep(1)
|
||||
|
||||
else:
|
||||
@ -497,8 +505,8 @@ class RarArchiver:
|
||||
rarc = rarfile.RarFile( self.path )
|
||||
|
||||
except (OSError, IOError) as e:
|
||||
print >> sys.stderr, u"getRARObj(): [{0}] {1} attempt#{2}".format(
|
||||
str(e), self.path, tries)
|
||||
print("getRARObj(): [{0}] {1} attempt#{2}".format(
|
||||
str(e), self.path, tries), file=sys.stderr)
|
||||
time.sleep(1)
|
||||
|
||||
else:
|
||||
@ -634,7 +642,7 @@ class ComicArchive:
|
||||
logo_data = None
|
||||
|
||||
class ArchiveType:
|
||||
Zip, Rar, Folder, Pdf, Unknown = range(5)
|
||||
Zip, Rar, Folder, Pdf, Unknown = list(range(5))
|
||||
|
||||
def __init__(self, path, rar_exe_path=None, default_image_path=None):
|
||||
self.path = path
|
||||
@ -729,7 +737,7 @@ class ComicArchive:
|
||||
if self.archive_type == self.ArchiveType.Unknown:
|
||||
return False
|
||||
|
||||
elif check_rar_status and self.isRar() and self.rar_exe_path is None:
|
||||
elif check_rar_status and self.isRar() and not self.rar_exe_path:
|
||||
return False
|
||||
|
||||
elif not os.access(self.path, os.W_OK):
|
||||
@ -817,7 +825,7 @@ class ComicArchive:
|
||||
try:
|
||||
image_data = self.archiver.readArchiveFile(filename)
|
||||
except IOError:
|
||||
print >> sys.stderr, u"Error reading in page. Substituting logo page."
|
||||
print("Error reading in page. Substituting logo page.", file=sys.stderr)
|
||||
image_data = ComicArchive.logo_data
|
||||
|
||||
return image_data
|
||||
@ -859,7 +867,7 @@ class ComicArchive:
|
||||
|
||||
# sort by most common
|
||||
sorted_buckets = sorted(
|
||||
length_buckets.iteritems(),
|
||||
iter(length_buckets.items()),
|
||||
key=lambda k_v: (
|
||||
k_v[1],
|
||||
k_v[0]),
|
||||
@ -1006,7 +1014,7 @@ class ComicArchive:
|
||||
try:
|
||||
raw_cix = self.archiver.readArchiveFile(self.ci_xml_filename)
|
||||
except IOError:
|
||||
print "Error reading in raw CIX!"
|
||||
print("Error reading in raw CIX!")
|
||||
raw_cix = ""
|
||||
return raw_cix
|
||||
|
||||
@ -1075,13 +1083,13 @@ class ComicArchive:
|
||||
|
||||
def readRawCoMet(self):
|
||||
if not self.hasCoMet():
|
||||
print >> sys.stderr, self.path, "doesn't have CoMet data!"
|
||||
print(self.path, "doesn't have CoMet data!", file=sys.stderr)
|
||||
return None
|
||||
|
||||
try:
|
||||
raw_comet = self.archiver.readArchiveFile(self.comet_filename)
|
||||
except IOError:
|
||||
print >> sys.stderr, u"Error reading in raw CoMet!"
|
||||
print("Error reading in raw CoMet!", file=sys.stderr)
|
||||
raw_comet = ""
|
||||
return raw_comet
|
||||
|
||||
@ -1136,7 +1144,7 @@ class ComicArchive:
|
||||
data = self.archiver.readArchiveFile(n)
|
||||
except:
|
||||
data = ""
|
||||
print >> sys.stderr, u"Error reading in Comet XML for validation!"
|
||||
print("Error reading in Comet XML for validation!", file=sys.stderr)
|
||||
if CoMet().validateString(data):
|
||||
# since we found it, save it!
|
||||
self.comet_filename = n
|
||||
@ -1156,7 +1164,7 @@ class ComicArchive:
|
||||
data = self.getPage(idx)
|
||||
if data is not None:
|
||||
try:
|
||||
im = Image.open(StringIO.StringIO(data))
|
||||
im = Image.open(io.StringIO(data))
|
||||
w, h = im.size
|
||||
|
||||
p['ImageSize'] = str(len(data))
|
||||
|
@ -18,8 +18,8 @@ import json
|
||||
from datetime import datetime
|
||||
#import zipfile
|
||||
|
||||
from genericmetadata import GenericMetadata
|
||||
import utils
|
||||
from .genericmetadata import GenericMetadata
|
||||
from . import utils
|
||||
#import ctversion
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ class ComicBookInfo:
|
||||
|
||||
def metadataFromString(self, string):
|
||||
|
||||
cbi_container = json.loads(unicode(string, 'utf-8'))
|
||||
cbi_container = json.loads(str(string, 'utf-8'))
|
||||
|
||||
metadata = GenericMetadata()
|
||||
|
||||
@ -109,7 +109,7 @@ class ComicBookInfo:
|
||||
# helper func
|
||||
def toInt(s):
|
||||
i = None
|
||||
if type(s) in [str, unicode, int]:
|
||||
if type(s) in [str, str, int]:
|
||||
try:
|
||||
i = int(s)
|
||||
except ValueError:
|
||||
|
@ -19,8 +19,8 @@ import xml.etree.ElementTree as ET
|
||||
#from pprint import pprint
|
||||
#import zipfile
|
||||
|
||||
from genericmetadata import GenericMetadata
|
||||
import utils
|
||||
from .genericmetadata import GenericMetadata
|
||||
from . import utils
|
||||
|
||||
|
||||
class ComicInfoXml:
|
||||
@ -54,7 +54,8 @@ class ComicInfoXml:
|
||||
header = '<?xml version="1.0"?>\n'
|
||||
|
||||
tree = self.convertMetadataToXML(self, metadata)
|
||||
return header + ET.tostring(tree.getroot())
|
||||
tree_str = ET.tostring(tree.getroot()).decode()
|
||||
return header + tree_str
|
||||
|
||||
def indent(self, elem, level=0):
|
||||
# for making the XML output readable
|
||||
@ -85,7 +86,7 @@ class ComicInfoXml:
|
||||
|
||||
def assign(cix_entry, md_entry):
|
||||
if md_entry is not None:
|
||||
ET.SubElement(root, cix_entry).text = u"{0}".format(md_entry)
|
||||
ET.SubElement(root, cix_entry).text = "{0}".format(md_entry)
|
||||
|
||||
assign('Title', md.title)
|
||||
assign('Series', md.series)
|
||||
|
@ -22,7 +22,7 @@ This should probably be re-written, but, well, it mostly works!
|
||||
|
||||
import re
|
||||
import os
|
||||
from urllib import unquote
|
||||
from urllib.parse import unquote
|
||||
|
||||
|
||||
class FileNameParser:
|
||||
|
@ -20,7 +20,7 @@ possible, however lossy it might be
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import utils
|
||||
from . import utils
|
||||
|
||||
|
||||
class PageType:
|
||||
@ -251,7 +251,7 @@ class GenericMetadata:
|
||||
return "No metadata"
|
||||
|
||||
def add_string(tag, val):
|
||||
if val is not None and u"{0}".format(val) != "":
|
||||
if val is not None and "{0}".format(val) != "":
|
||||
vals.append((tag, val))
|
||||
|
||||
def add_attr_string(tag):
|
||||
@ -314,7 +314,7 @@ class GenericMetadata:
|
||||
|
||||
# format the data nicely
|
||||
outstr = ""
|
||||
fmt_str = u"{0: <" + str(flen) + "} {1}\n"
|
||||
fmt_str = "{0: <" + str(flen) + "} {1}\n"
|
||||
for i in vals:
|
||||
outstr += fmt_str.format(i[0] + ":", i[1])
|
||||
|
||||
|
@ -44,7 +44,7 @@ class IssueString:
|
||||
if len(text) == 0:
|
||||
return
|
||||
|
||||
text = unicode(text)
|
||||
text = str(text)
|
||||
|
||||
# skip the minus sign if it's first
|
||||
if text[0] == '-':
|
||||
@ -119,7 +119,7 @@ class IssueString:
|
||||
|
||||
def asFloat(self):
|
||||
# return the float, with no suffix
|
||||
if self.suffix == u"½":
|
||||
if self.suffix == "½":
|
||||
if self.num is not None:
|
||||
return self.num + .5
|
||||
else:
|
||||
|
@ -55,20 +55,22 @@ def get_recursive_filelist(pathlist):
|
||||
# if path is a folder, walk it recursively, and all files underneath
|
||||
if isinstance(p, str):
|
||||
# make sure string is unicode
|
||||
p = p.decode(filename_encoding) # , 'replace')
|
||||
elif not isinstance(p, unicode):
|
||||
#p = p.decode(filename_encoding) # , 'replace')
|
||||
pass
|
||||
elif not isinstance(p, str):
|
||||
# it's probably a QString
|
||||
p = unicode(p)
|
||||
p = str(p)
|
||||
|
||||
if os.path.isdir(p):
|
||||
for root, dirs, files in os.walk(p):
|
||||
for f in files:
|
||||
if isinstance(f, str):
|
||||
# make sure string is unicode
|
||||
f = f.decode(filename_encoding, 'replace')
|
||||
elif not isinstance(f, unicode):
|
||||
#f = f.decode(filename_encoding, 'replace')
|
||||
pass
|
||||
elif not isinstance(f, str):
|
||||
# it's probably a QString
|
||||
f = unicode(f)
|
||||
f = str(f)
|
||||
filelist.append(os.path.join(root, f))
|
||||
else:
|
||||
filelist.append(p)
|
||||
@ -121,7 +123,7 @@ def which(program):
|
||||
|
||||
def removearticles(text):
|
||||
text = text.lower()
|
||||
articles = ['and', 'the', 'a', '&', 'issue']
|
||||
articles = ['and', 'a', '&', 'issue']
|
||||
newText = ''
|
||||
for word in text.split(' '):
|
||||
if word not in articles:
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
from comictaggerlib.main import ctmain
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -1,10 +1,22 @@
|
||||
# -*- mode: python -*-
|
||||
|
||||
import platform
|
||||
|
||||
block_cipher = None
|
||||
|
||||
binaries = [
|
||||
('./unrar/libunrar.so', './'),
|
||||
]
|
||||
|
||||
if platform.system() == "Windows":
|
||||
# add ssl qt libraries not discovered automatically
|
||||
binaries.extend([
|
||||
('./venv/Lib/site-packages/PyQt5/Qt/bin/libeay32.dll', './PyQt5/Qt/bin'),
|
||||
('./venv/Lib/site-packages/PyQt5/Qt/bin/ssleay32.dll', './PyQt5/Qt/bin')
|
||||
])
|
||||
|
||||
a = Analysis(['comictagger.py'],
|
||||
binaries=[('./unrar/libunrar.so', './')],
|
||||
binaries=binaries,
|
||||
datas=[('comictaggerlib/ui/*.ui', 'ui'), ('comictaggerlib/graphics', 'graphics')],
|
||||
hiddenimports=['PIL'],
|
||||
hookspath=[],
|
||||
|
@ -17,19 +17,19 @@
|
||||
import os
|
||||
#import sys
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
#from PyQt4.QtCore import QUrl, pyqtSignal, QByteArray
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
#from PyQt5.QtCore import QUrl, pyqtSignal, QByteArray
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from comicarchive import MetaDataStyle
|
||||
from coverimagewidget import CoverImageWidget
|
||||
from .settings import ComicTaggerSettings
|
||||
from .comicarchive import MetaDataStyle
|
||||
from .coverimagewidget import CoverImageWidget
|
||||
from comictaggerlib.ui.qtutils import reduceWidgetFontSize
|
||||
#from imagefetcher import ImageFetcher
|
||||
#from comicvinetalker import ComicVineTalker
|
||||
#import utils
|
||||
|
||||
|
||||
class AutoTagMatchWindow(QtGui.QDialog):
|
||||
class AutoTagMatchWindow(QtWidgets.QDialog):
|
||||
|
||||
volume_id = 0
|
||||
|
||||
@ -41,13 +41,13 @@ class AutoTagMatchWindow(QtGui.QDialog):
|
||||
|
||||
self.altCoverWidget = CoverImageWidget(
|
||||
self.altCoverContainer, CoverImageWidget.AltCoverMode)
|
||||
gridlayout = QtGui.QGridLayout(self.altCoverContainer)
|
||||
gridlayout = QtWidgets.QGridLayout(self.altCoverContainer)
|
||||
gridlayout.addWidget(self.altCoverWidget)
|
||||
gridlayout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.archiveCoverWidget = CoverImageWidget(
|
||||
self.archiveCoverContainer, CoverImageWidget.ArchiveMode)
|
||||
gridlayout = QtGui.QGridLayout(self.archiveCoverContainer)
|
||||
gridlayout = QtWidgets.QGridLayout(self.archiveCoverContainer)
|
||||
gridlayout.addWidget(self.archiveCoverWidget)
|
||||
gridlayout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
@ -58,10 +58,10 @@ class AutoTagMatchWindow(QtGui.QDialog):
|
||||
QtCore.Qt.WindowSystemMenuHint |
|
||||
QtCore.Qt.WindowMaximizeButtonHint)
|
||||
|
||||
self.skipButton = QtGui.QPushButton(self.tr("Skip to Next"))
|
||||
self.skipButton = QtWidgets.QPushButton(self.tr("Skip to Next"))
|
||||
self.buttonBox.addButton(
|
||||
self.skipButton, QtGui.QDialogButtonBox.ActionRole)
|
||||
self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setText(
|
||||
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
|
||||
self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
|
||||
"Accept and Write Tags")
|
||||
|
||||
self.match_set_list = match_set_list
|
||||
@ -83,8 +83,8 @@ class AutoTagMatchWindow(QtGui.QDialog):
|
||||
|
||||
if self.current_match_set_idx + 1 == len(self.match_set_list):
|
||||
self.buttonBox.button(
|
||||
QtGui.QDialogButtonBox.Cancel).setDisabled(True)
|
||||
# self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setText("Accept")
|
||||
QtWidgets.QDialogButtonBox.Cancel).setDisabled(True)
|
||||
# self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText("Accept")
|
||||
self.skipButton.setText(self.tr("Skip"))
|
||||
|
||||
self.setCoverImage()
|
||||
@ -94,7 +94,7 @@ class AutoTagMatchWindow(QtGui.QDialog):
|
||||
|
||||
path = self.current_match_set.ca.path
|
||||
self.setWindowTitle(
|
||||
u"Select correct match or skip ({0} of {1}): {2}".format(
|
||||
"Select correct match or skip ({0} of {1}): {2}".format(
|
||||
self.current_match_set_idx + 1,
|
||||
len(self.match_set_list),
|
||||
os.path.split(path)[1])
|
||||
@ -112,30 +112,30 @@ class AutoTagMatchWindow(QtGui.QDialog):
|
||||
self.twList.insertRow(row)
|
||||
|
||||
item_text = match['series']
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setData(QtCore.Qt.UserRole, (match,))
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 0, item)
|
||||
|
||||
if match['publisher'] is not None:
|
||||
item_text = u"{0}".format(match['publisher'])
|
||||
item_text = "{0}".format(match['publisher'])
|
||||
else:
|
||||
item_text = u"Unknown"
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item_text = "Unknown"
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 1, item)
|
||||
|
||||
month_str = u""
|
||||
year_str = u"????"
|
||||
month_str = ""
|
||||
year_str = "????"
|
||||
if match['month'] is not None:
|
||||
month_str = u"-{0:02d}".format(int(match['month']))
|
||||
month_str = "-{0:02d}".format(int(match['month']))
|
||||
if match['year'] is not None:
|
||||
year_str = u"{0}".format(match['year'])
|
||||
year_str = "{0}".format(match['year'])
|
||||
|
||||
item_text = year_str + month_str
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 2, item)
|
||||
@ -143,7 +143,7 @@ class AutoTagMatchWindow(QtGui.QDialog):
|
||||
item_text = match['issue_title']
|
||||
if item_text is None:
|
||||
item_text = ""
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 3, item)
|
||||
@ -180,7 +180,7 @@ class AutoTagMatchWindow(QtGui.QDialog):
|
||||
def currentMatch(self):
|
||||
row = self.twList.currentRow()
|
||||
match = self.twList.item(row, 0).data(
|
||||
QtCore.Qt.UserRole).toPyObject()[0]
|
||||
QtCore.Qt.UserRole)[0]
|
||||
return match
|
||||
|
||||
def accept(self):
|
||||
@ -190,7 +190,7 @@ class AutoTagMatchWindow(QtGui.QDialog):
|
||||
|
||||
if self.current_match_set_idx == len(self.match_set_list):
|
||||
# no more items
|
||||
QtGui.QDialog.accept(self)
|
||||
QtWidgets.QDialog.accept(self)
|
||||
else:
|
||||
self.updateData()
|
||||
|
||||
@ -199,22 +199,22 @@ class AutoTagMatchWindow(QtGui.QDialog):
|
||||
|
||||
if self.current_match_set_idx == len(self.match_set_list):
|
||||
# no more items
|
||||
QtGui.QDialog.reject(self)
|
||||
QtWidgets.QDialog.reject(self)
|
||||
else:
|
||||
self.updateData()
|
||||
|
||||
def reject(self):
|
||||
reply = QtGui.QMessageBox.question(
|
||||
reply = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
self.tr("Cancel Matching"),
|
||||
self.tr("Are you sure you wish to cancel the matching process?"),
|
||||
QtGui.QMessageBox.Yes,
|
||||
QtGui.QMessageBox.No)
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No)
|
||||
|
||||
if reply == QtGui.QMessageBox.No:
|
||||
if reply == QtWidgets.QMessageBox.No:
|
||||
return
|
||||
|
||||
QtGui.QDialog.reject(self)
|
||||
QtWidgets.QDialog.reject(self)
|
||||
|
||||
def saveMatch(self):
|
||||
|
||||
@ -228,18 +228,18 @@ class AutoTagMatchWindow(QtGui.QDialog):
|
||||
# now get the particular issue data
|
||||
cv_md = self.fetch_func(match)
|
||||
if cv_md is None:
|
||||
QtGui.QMessageBox.critical(self, self.tr("Network Issue"), self.tr(
|
||||
QtWidgets.QMessageBox.critical(self, self.tr("Network Issue"), self.tr(
|
||||
"Could not connect to Comic Vine to get issue details!"))
|
||||
return
|
||||
|
||||
QtGui.QApplication.setOverrideCursor(
|
||||
QtWidgets.QApplication.setOverrideCursor(
|
||||
QtGui.QCursor(QtCore.Qt.WaitCursor))
|
||||
md.overlay(cv_md)
|
||||
success = ca.writeMetadata(md, self.style)
|
||||
ca.loadCache([MetaDataStyle.CBI, MetaDataStyle.CIX])
|
||||
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
|
||||
if not success:
|
||||
QtGui.QMessageBox.warning(self, self.tr("Write Error"), self.tr(
|
||||
QtWidgets.QMessageBox.warning(self, self.tr("Write Error"), self.tr(
|
||||
"Saving the tags to the archive seemed to fail!"))
|
||||
|
@ -17,15 +17,15 @@
|
||||
#import sys
|
||||
#import os
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from coverimagewidget import CoverImageWidget
|
||||
from .settings import ComicTaggerSettings
|
||||
from .coverimagewidget import CoverImageWidget
|
||||
from comictaggerlib.ui.qtutils import reduceWidgetFontSize
|
||||
#import utils
|
||||
|
||||
|
||||
class AutoTagProgressWindow(QtGui.QDialog):
|
||||
class AutoTagProgressWindow(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent):
|
||||
super(AutoTagProgressWindow, self).__init__(parent)
|
||||
@ -35,13 +35,13 @@ class AutoTagProgressWindow(QtGui.QDialog):
|
||||
|
||||
self.archiveCoverWidget = CoverImageWidget(
|
||||
self.archiveCoverContainer, CoverImageWidget.DataMode, False)
|
||||
gridlayout = QtGui.QGridLayout(self.archiveCoverContainer)
|
||||
gridlayout = QtWidgets.QGridLayout(self.archiveCoverContainer)
|
||||
gridlayout.addWidget(self.archiveCoverWidget)
|
||||
gridlayout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.testCoverWidget = CoverImageWidget(
|
||||
self.testCoverContainer, CoverImageWidget.DataMode, False)
|
||||
gridlayout = QtGui.QGridLayout(self.testCoverContainer)
|
||||
gridlayout = QtWidgets.QGridLayout(self.testCoverContainer)
|
||||
gridlayout.addWidget(self.testCoverWidget)
|
||||
gridlayout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
@ -65,5 +65,5 @@ class AutoTagProgressWindow(QtGui.QDialog):
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
|
||||
def reject(self):
|
||||
QtGui.QDialog.reject(self)
|
||||
QtWidgets.QDialog.reject(self)
|
||||
self.isdone = True
|
||||
|
@ -16,15 +16,15 @@
|
||||
|
||||
#import os
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from .settings import ComicTaggerSettings
|
||||
#from settingswindow import SettingsWindow
|
||||
#from filerenamer import FileRenamer
|
||||
#import utils
|
||||
|
||||
|
||||
class AutoTagStartWindow(QtGui.QDialog):
|
||||
class AutoTagStartWindow(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent, settings, msg):
|
||||
super(AutoTagStartWindow, self).__init__(parent)
|
||||
@ -102,7 +102,7 @@ class AutoTagStartWindow(QtGui.QDialog):
|
||||
self.leSearchString.setEnabled(enable)
|
||||
|
||||
def accept(self):
|
||||
QtGui.QDialog.accept(self)
|
||||
QtWidgets.QDialog.accept(self)
|
||||
|
||||
self.autoSaveOnLow = self.cbxSaveOnLowConfidence.isChecked()
|
||||
self.dontUseYear = self.cbxDontUseYear.isChecked()
|
||||
@ -122,6 +122,6 @@ class AutoTagStartWindow(QtGui.QDialog):
|
||||
self.settings.wait_and_retry_on_rate_limit = self.waitAndRetryOnRateLimit
|
||||
|
||||
if self.cbxSpecifySearchString.isChecked():
|
||||
self.searchString = unicode(self.leSearchString.text())
|
||||
self.searchString = str(self.leSearchString.text())
|
||||
if len(self.searchString) == 0:
|
||||
self.searchString = None
|
||||
|
@ -29,15 +29,15 @@ import json
|
||||
|
||||
filename_encoding = sys.getfilesystemencoding()
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from options import Options
|
||||
from comicarchive import ComicArchive, MetaDataStyle
|
||||
from issueidentifier import IssueIdentifier
|
||||
from genericmetadata import GenericMetadata
|
||||
from comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from filerenamer import FileRenamer
|
||||
from cbltransformer import CBLTransformer
|
||||
import utils
|
||||
from .settings import ComicTaggerSettings
|
||||
from .options import Options
|
||||
from .comicarchive import ComicArchive, MetaDataStyle
|
||||
from .issueidentifier import IssueIdentifier
|
||||
from .genericmetadata import GenericMetadata
|
||||
from .comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from .filerenamer import FileRenamer
|
||||
from .cbltransformer import CBLTransformer
|
||||
from . import utils
|
||||
|
||||
|
||||
class MultipleMatch():
|
||||
@ -69,7 +69,7 @@ def actual_issue_data_fetch(match, settings, opts):
|
||||
cv_md = comicVine.fetchIssueData(
|
||||
match['volume_id'], match['issue_number'], settings)
|
||||
except ComicVineTalkerException:
|
||||
print >> sys.stderr, "Network error while getting issue details. Save aborted"
|
||||
print("Network error while getting issue details. Save aborted", file=sys.stderr)
|
||||
return None
|
||||
|
||||
if settings.apply_cbl_transform_on_cv_import:
|
||||
@ -83,39 +83,39 @@ def actual_metadata_save(ca, opts, md):
|
||||
if not opts.dryrun:
|
||||
# write out the new data
|
||||
if not ca.writeMetadata(md, opts.data_style):
|
||||
print >> sys.stderr, "The tag save seemed to fail!"
|
||||
print("The tag save seemed to fail!", file=sys.stderr)
|
||||
return False
|
||||
else:
|
||||
print >> sys.stderr, "Save complete."
|
||||
print("Save complete.", file=sys.stderr)
|
||||
else:
|
||||
if opts.terse:
|
||||
print >> sys.stderr, "dry-run option was set, so nothing was written"
|
||||
print("dry-run option was set, so nothing was written", file=sys.stderr)
|
||||
else:
|
||||
print >> sys.stderr, "dry-run option was set, so nothing was written, but here is the final set of tags:"
|
||||
print(u"{0}".format(md))
|
||||
print("dry-run option was set, so nothing was written, but here is the final set of tags:", file=sys.stderr)
|
||||
print(("{0}".format(md)))
|
||||
return True
|
||||
|
||||
|
||||
def display_match_set_for_choice(label, match_set, opts, settings):
|
||||
print(u"{0} -- {1}:".format(match_set.filename, label))
|
||||
print(("{0} -- {1}:".format(match_set.filename, label)))
|
||||
|
||||
# sort match list by year
|
||||
match_set.matches.sort(key=lambda k: k['year'])
|
||||
|
||||
for (counter, m) in enumerate(match_set.matches):
|
||||
counter += 1
|
||||
print(
|
||||
u" {0}. {1} #{2} [{3}] ({4}/{5}) - {6}".format(
|
||||
print((
|
||||
" {0}. {1} #{2} [{3}] ({4}/{5}) - {6}".format(
|
||||
counter,
|
||||
m['series'],
|
||||
m['issue_number'],
|
||||
m['publisher'],
|
||||
m['month'],
|
||||
m['year'],
|
||||
m['issue_title']))
|
||||
m['issue_title'])))
|
||||
if opts.interactive:
|
||||
while True:
|
||||
i = raw_input("Choose a match #, or 's' to skip: ")
|
||||
i = input("Choose a match #, or 's' to skip: ")
|
||||
if (i.isdigit() and int(i) in range(
|
||||
1, len(match_set.matches) + 1)) or i == 's':
|
||||
break
|
||||
@ -182,14 +182,14 @@ def post_process_matches(match_results, opts, settings):
|
||||
|
||||
def cli_mode(opts, settings):
|
||||
if len(opts.file_list) < 1:
|
||||
print >> sys.stderr, "You must specify at least one filename. Use the -h option for more info"
|
||||
print("You must specify at least one filename. Use the -h option for more info", file=sys.stderr)
|
||||
return
|
||||
|
||||
match_results = OnlineMatchResults()
|
||||
|
||||
for f in opts.file_list:
|
||||
if isinstance(f, str):
|
||||
f = f.decode(filename_encoding, 'replace')
|
||||
pass
|
||||
process_file_cli(f, opts, settings, match_results)
|
||||
sys.stdout.flush()
|
||||
|
||||
@ -225,19 +225,19 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
ComicTaggerSettings.getGraphic('nocover.png'))
|
||||
|
||||
if not os.path.lexists(filename):
|
||||
print >> sys.stderr, "Cannot find " + filename
|
||||
print("Cannot find " + filename, file=sys.stderr)
|
||||
return
|
||||
|
||||
if not ca.seemsToBeAComicArchive():
|
||||
print >> sys.stderr, "Sorry, but " + \
|
||||
filename + " is not a comic archive!"
|
||||
print("Sorry, but " + \
|
||||
filename + " is not a comic archive!", file=sys.stderr)
|
||||
return
|
||||
|
||||
# if not ca.isWritableForStyle(opts.data_style) and (opts.delete_tags or
|
||||
# opts.save_tags or opts.rename_file):
|
||||
if not ca.isWritable() and (
|
||||
opts.delete_tags or opts.copy_tags or opts.save_tags or opts.rename_file):
|
||||
print >> sys.stderr, "This archive is not writable for that tag type"
|
||||
print("This archive is not writable for that tag type", file=sys.stderr)
|
||||
return
|
||||
|
||||
has = [False, False, False]
|
||||
@ -256,7 +256,7 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
brief = ""
|
||||
|
||||
if batch_mode:
|
||||
brief = u"{0}: ".format(filename)
|
||||
brief = "{0}: ".format(filename)
|
||||
|
||||
if ca.isZip():
|
||||
brief += "ZIP archive "
|
||||
@ -280,24 +280,24 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
brief += "CoMet "
|
||||
brief += "]"
|
||||
|
||||
print brief
|
||||
print(brief)
|
||||
|
||||
if opts.terse:
|
||||
return
|
||||
|
||||
print
|
||||
print()
|
||||
|
||||
if opts.data_style is None or opts.data_style == MetaDataStyle.CIX:
|
||||
if has[MetaDataStyle.CIX]:
|
||||
print("--------- ComicRack tags ---------")
|
||||
if opts.raw:
|
||||
print(
|
||||
u"{0}".format(
|
||||
unicode(
|
||||
print((
|
||||
"{0}".format(
|
||||
str(
|
||||
ca.readRawCIX(),
|
||||
errors='ignore')))
|
||||
errors='ignore'))))
|
||||
else:
|
||||
print(u"{0}".format(ca.readCIX()))
|
||||
print(("{0}".format(ca.readCIX())))
|
||||
|
||||
if opts.data_style is None or opts.data_style == MetaDataStyle.CBI:
|
||||
if has[MetaDataStyle.CBI]:
|
||||
@ -305,43 +305,43 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
if opts.raw:
|
||||
pprint(json.loads(ca.readRawCBI()))
|
||||
else:
|
||||
print(u"{0}".format(ca.readCBI()))
|
||||
print(("{0}".format(ca.readCBI())))
|
||||
|
||||
if opts.data_style is None or opts.data_style == MetaDataStyle.COMET:
|
||||
if has[MetaDataStyle.COMET]:
|
||||
print("----------- CoMet tags -----------")
|
||||
if opts.raw:
|
||||
print(u"{0}".format(ca.readRawCoMet()))
|
||||
print(("{0}".format(ca.readRawCoMet())))
|
||||
else:
|
||||
print(u"{0}".format(ca.readCoMet()))
|
||||
print(("{0}".format(ca.readCoMet())))
|
||||
|
||||
elif opts.delete_tags:
|
||||
style_name = MetaDataStyle.name[opts.data_style]
|
||||
if has[opts.data_style]:
|
||||
if not opts.dryrun:
|
||||
if not ca.removeMetadata(opts.data_style):
|
||||
print(u"{0}: Tag removal seemed to fail!".format(filename))
|
||||
print(("{0}: Tag removal seemed to fail!".format(filename)))
|
||||
else:
|
||||
print(
|
||||
u"{0}: Removed {1} tags.".format(filename, style_name))
|
||||
print((
|
||||
"{0}: Removed {1} tags.".format(filename, style_name)))
|
||||
else:
|
||||
print(
|
||||
u"{0}: dry-run. {1} tags not removed".format(filename, style_name))
|
||||
print((
|
||||
"{0}: dry-run. {1} tags not removed".format(filename, style_name)))
|
||||
else:
|
||||
print(u"{0}: This archive doesn't have {1} tags to remove.".format(
|
||||
filename, style_name))
|
||||
print(("{0}: This archive doesn't have {1} tags to remove.".format(
|
||||
filename, style_name)))
|
||||
|
||||
elif opts.copy_tags:
|
||||
dst_style_name = MetaDataStyle.name[opts.data_style]
|
||||
if opts.no_overwrite and has[opts.data_style]:
|
||||
print(u"{0}: Already has {1} tags. Not overwriting.".format(
|
||||
filename, dst_style_name))
|
||||
print(("{0}: Already has {1} tags. Not overwriting.".format(
|
||||
filename, dst_style_name)))
|
||||
return
|
||||
if opts.copy_source == opts.data_style:
|
||||
print(
|
||||
u"{0}: Destination and source are same: {1}. Nothing to do.".format(
|
||||
print((
|
||||
"{0}: Destination and source are same: {1}. Nothing to do.".format(
|
||||
filename,
|
||||
dst_style_name))
|
||||
dst_style_name)))
|
||||
return
|
||||
|
||||
src_style_name = MetaDataStyle.name[opts.copy_source]
|
||||
@ -353,26 +353,26 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
md = CBLTransformer(md, settings).apply()
|
||||
|
||||
if not ca.writeMetadata(md, opts.data_style):
|
||||
print(u"{0}: Tag copy seemed to fail!".format(filename))
|
||||
print(("{0}: Tag copy seemed to fail!".format(filename)))
|
||||
else:
|
||||
print(u"{0}: Copied {1} tags to {2} .".format(
|
||||
filename, src_style_name, dst_style_name))
|
||||
print(("{0}: Copied {1} tags to {2} .".format(
|
||||
filename, src_style_name, dst_style_name)))
|
||||
else:
|
||||
print(
|
||||
u"{0}: dry-run. {1} tags not copied".format(filename, src_style_name))
|
||||
print((
|
||||
"{0}: dry-run. {1} tags not copied".format(filename, src_style_name)))
|
||||
else:
|
||||
print(u"{0}: This archive doesn't have {1} tags to copy.".format(
|
||||
filename, src_style_name))
|
||||
print(("{0}: This archive doesn't have {1} tags to copy.".format(
|
||||
filename, src_style_name)))
|
||||
|
||||
elif opts.save_tags:
|
||||
|
||||
if opts.no_overwrite and has[opts.data_style]:
|
||||
print(u"{0}: Already has {1} tags. Not overwriting.".format(
|
||||
filename, MetaDataStyle.name[opts.data_style]))
|
||||
print(("{0}: Already has {1} tags. Not overwriting.".format(
|
||||
filename, MetaDataStyle.name[opts.data_style])))
|
||||
return
|
||||
|
||||
if batch_mode:
|
||||
print(u"Processing {0}...".format(filename))
|
||||
print(("Processing {0}...".format(filename)))
|
||||
|
||||
md = create_local_metadata(opts, ca, has[opts.data_style])
|
||||
if md.issue is None or md.issue == "":
|
||||
@ -389,13 +389,13 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
cv_md = comicVine.fetchIssueDataByIssueID(
|
||||
opts.issue_id, settings)
|
||||
except ComicVineTalkerException:
|
||||
print >> sys.stderr, "Network error while getting issue details. Save aborted"
|
||||
print("Network error while getting issue details. Save aborted", file=sys.stderr)
|
||||
match_results.fetchDataFailures.append(filename)
|
||||
return
|
||||
|
||||
if cv_md is None:
|
||||
print >> sys.stderr, "No match for ID {0} was found.".format(
|
||||
opts.issue_id)
|
||||
print("No match for ID {0} was found.".format(
|
||||
opts.issue_id), file=sys.stderr)
|
||||
match_results.noMatches.append(filename)
|
||||
return
|
||||
|
||||
@ -405,7 +405,7 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
ii = IssueIdentifier(ca, settings)
|
||||
|
||||
if md is None or md.isEmpty:
|
||||
print >> sys.stderr, "No metadata given to search online with!"
|
||||
print("No metadata given to search online with!", file=sys.stderr)
|
||||
match_results.noMatches.append(filename)
|
||||
return
|
||||
|
||||
@ -444,22 +444,22 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
|
||||
if choices:
|
||||
if low_confidence:
|
||||
print >> sys.stderr, "Online search: Multiple low confidence matches. Save aborted"
|
||||
print("Online search: Multiple low confidence matches. Save aborted", file=sys.stderr)
|
||||
match_results.lowConfidenceMatches.append(
|
||||
MultipleMatch(filename, matches))
|
||||
return
|
||||
else:
|
||||
print >> sys.stderr, "Online search: Multiple good matches. Save aborted"
|
||||
print("Online search: Multiple good matches. Save aborted", file=sys.stderr)
|
||||
match_results.multipleMatches.append(
|
||||
MultipleMatch(filename, matches))
|
||||
return
|
||||
if low_confidence and opts.abortOnLowConfidence:
|
||||
print >> sys.stderr, "Online search: Low confidence match. Save aborted"
|
||||
print("Online search: Low confidence match. Save aborted", file=sys.stderr)
|
||||
match_results.lowConfidenceMatches.append(
|
||||
MultipleMatch(filename, matches))
|
||||
return
|
||||
if not found_match:
|
||||
print >> sys.stderr, "Online search: No match found. Save aborted"
|
||||
print("Online search: No match found. Save aborted", file=sys.stderr)
|
||||
match_results.noMatches.append(filename)
|
||||
return
|
||||
|
||||
@ -483,7 +483,7 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
|
||||
msg_hdr = ""
|
||||
if batch_mode:
|
||||
msg_hdr = u"{0}: ".format(filename)
|
||||
msg_hdr = "{0}: ".format(filename)
|
||||
|
||||
if opts.data_style is not None:
|
||||
use_tags = has[opts.data_style]
|
||||
@ -493,7 +493,7 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
md = create_local_metadata(opts, ca, use_tags)
|
||||
|
||||
if md.series is None:
|
||||
print >> sys.stderr, msg_hdr + "Can't rename without series name"
|
||||
print(msg_hdr + "Can't rename without series name", file=sys.stderr)
|
||||
return
|
||||
|
||||
new_ext = None # default
|
||||
@ -511,7 +511,7 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
new_name = renamer.determineName(filename, ext=new_ext)
|
||||
|
||||
if new_name == os.path.basename(filename):
|
||||
print >> sys.stderr, msg_hdr + "Filename is already good!"
|
||||
print(msg_hdr + "Filename is already good!", file=sys.stderr)
|
||||
return
|
||||
|
||||
folder = os.path.dirname(os.path.abspath(filename))
|
||||
@ -524,23 +524,23 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
else:
|
||||
suffix = " (dry-run, no change)"
|
||||
|
||||
print(
|
||||
u"renamed '{0}' -> '{1}' {2}".format(os.path.basename(filename), new_name, suffix))
|
||||
print((
|
||||
"renamed '{0}' -> '{1}' {2}".format(os.path.basename(filename), new_name, suffix)))
|
||||
|
||||
elif opts.export_to_zip:
|
||||
msg_hdr = ""
|
||||
if batch_mode:
|
||||
msg_hdr = u"{0}: ".format(filename)
|
||||
msg_hdr = "{0}: ".format(filename)
|
||||
|
||||
if not ca.isRar():
|
||||
print >> sys.stderr, msg_hdr + "Archive is not a RAR."
|
||||
print(msg_hdr + "Archive is not a RAR.", file=sys.stderr)
|
||||
return
|
||||
|
||||
rar_file = os.path.abspath(os.path.abspath(filename))
|
||||
new_file = os.path.splitext(rar_file)[0] + ".cbz"
|
||||
|
||||
if opts.abort_export_on_conflict and os.path.lexists(new_file):
|
||||
print msg_hdr + "{0} already exists in the that folder.".format(os.path.split(new_file)[1])
|
||||
print(msg_hdr + "{0} already exists in the that folder.".format(os.path.split(new_file)[1]))
|
||||
return
|
||||
|
||||
new_file = utils.unique_file(os.path.join(new_file))
|
||||
@ -554,8 +554,8 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
try:
|
||||
os.unlink(rar_file)
|
||||
except:
|
||||
print >> sys.stderr, msg_hdr + \
|
||||
"Error deleting original RAR after export"
|
||||
print(msg_hdr + \
|
||||
"Error deleting original RAR after export", file=sys.stderr)
|
||||
delete_success = False
|
||||
else:
|
||||
delete_success = True
|
||||
@ -565,20 +565,20 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
os.remove(new_file)
|
||||
else:
|
||||
msg = msg_hdr + \
|
||||
u"Dry-run: Would try to create {0}".format(
|
||||
"Dry-run: Would try to create {0}".format(
|
||||
os.path.split(new_file)[1])
|
||||
if opts.delete_rar_after_export:
|
||||
msg += u" and delete orginal."
|
||||
msg += " and delete orginal."
|
||||
print(msg)
|
||||
return
|
||||
|
||||
msg = msg_hdr
|
||||
if export_success:
|
||||
msg += u"Archive exported successfully to: {0}".format(
|
||||
msg += "Archive exported successfully to: {0}".format(
|
||||
os.path.split(new_file)[1])
|
||||
if opts.delete_rar_after_export and delete_success:
|
||||
msg += u" (Original deleted) "
|
||||
msg += " (Original deleted) "
|
||||
else:
|
||||
msg += u"Archive failed to export!"
|
||||
msg += "Archive failed to export!"
|
||||
|
||||
print(msg)
|
||||
|
@ -20,9 +20,9 @@ import datetime
|
||||
#import sys
|
||||
#from pprint import pprint
|
||||
|
||||
import ctversion
|
||||
from settings import ComicTaggerSettings
|
||||
import utils
|
||||
from . import ctversion
|
||||
from .settings import ComicTaggerSettings
|
||||
from . import utils
|
||||
|
||||
|
||||
class ComicVineCacher:
|
||||
@ -37,7 +37,7 @@ class ComicVineCacher:
|
||||
data = ""
|
||||
try:
|
||||
with open(self.version_file, 'rb') as f:
|
||||
data = f.read()
|
||||
data = f.read().decode("utf-8")
|
||||
f.close()
|
||||
except:
|
||||
pass
|
||||
@ -121,7 +121,7 @@ class ComicVineCacher:
|
||||
con = lite.connect(self.db_file)
|
||||
|
||||
with con:
|
||||
con.text_factory = unicode
|
||||
con.text_factory = str
|
||||
cur = con.cursor()
|
||||
|
||||
# remove all previous entries with this search term
|
||||
@ -161,7 +161,7 @@ class ComicVineCacher:
|
||||
results = list()
|
||||
con = lite.connect(self.db_file)
|
||||
with con:
|
||||
con.text_factory = unicode
|
||||
con.text_factory = str
|
||||
cur = con.cursor()
|
||||
|
||||
# purge stale search results
|
||||
@ -197,7 +197,7 @@ class ComicVineCacher:
|
||||
con = lite.connect(self.db_file)
|
||||
|
||||
with con:
|
||||
con.text_factory = unicode
|
||||
con.text_factory = str
|
||||
cur = con.cursor()
|
||||
|
||||
# remove all previous entries with this search term
|
||||
@ -217,7 +217,7 @@ class ComicVineCacher:
|
||||
con = lite.connect(self.db_file)
|
||||
with con:
|
||||
cur = con.cursor()
|
||||
con.text_factory = unicode
|
||||
con.text_factory = str
|
||||
|
||||
# purge stale issue info - probably issue data won't change
|
||||
# much....
|
||||
@ -300,7 +300,7 @@ class ComicVineCacher:
|
||||
con = lite.connect(self.db_file)
|
||||
with con:
|
||||
cur = con.cursor()
|
||||
con.text_factory = unicode
|
||||
con.text_factory = str
|
||||
|
||||
# purge stale volume info
|
||||
a_week_ago = datetime.datetime.today() - datetime.timedelta(days=7)
|
||||
@ -337,7 +337,7 @@ class ComicVineCacher:
|
||||
con = lite.connect(self.db_file)
|
||||
with con:
|
||||
cur = con.cursor()
|
||||
con.text_factory = unicode
|
||||
con.text_factory = str
|
||||
|
||||
# purge stale issue info - probably issue data won't change
|
||||
# much....
|
||||
@ -386,7 +386,7 @@ class ComicVineCacher:
|
||||
|
||||
with con:
|
||||
cur = con.cursor()
|
||||
con.text_factory = unicode
|
||||
con.text_factory = str
|
||||
timestamp = datetime.datetime.now()
|
||||
|
||||
data = {
|
||||
@ -403,7 +403,7 @@ class ComicVineCacher:
|
||||
con = lite.connect(self.db_file)
|
||||
with con:
|
||||
cur = con.cursor()
|
||||
con.text_factory = unicode
|
||||
con.text_factory = str
|
||||
|
||||
cur.execute(
|
||||
"SELECT super_url,thumb_url,cover_date,site_detail_url FROM Issues WHERE id=?",
|
||||
|
@ -15,8 +15,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import urllib2
|
||||
import urllib
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import re
|
||||
import time
|
||||
import datetime
|
||||
@ -28,8 +28,8 @@ import ssl
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
try:
|
||||
from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||
from PyQt4.QtCore import QUrl, pyqtSignal, QObject, QByteArray
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||
from PyQt5.QtCore import QUrl, pyqtSignal, QObject, QByteArray
|
||||
except ImportError:
|
||||
# No Qt, so define a few dummy QObjects to help us compile
|
||||
class QObject():
|
||||
@ -45,11 +45,11 @@ except ImportError:
|
||||
def emit(a, b, c):
|
||||
pass
|
||||
|
||||
import ctversion
|
||||
import utils
|
||||
from comicvinecacher import ComicVineCacher
|
||||
from genericmetadata import GenericMetadata
|
||||
from issuestring import IssueString
|
||||
from . import ctversion
|
||||
from . import utils
|
||||
from .comicvinecacher import ComicVineCacher
|
||||
from .genericmetadata import GenericMetadata
|
||||
from .issuestring import IssueString
|
||||
#from settings import ComicTaggerSettings
|
||||
|
||||
|
||||
@ -114,7 +114,7 @@ class ComicVineTalker(QObject):
|
||||
if self.log_func is None:
|
||||
# sys.stdout.write(text.encode(errors='replace'))
|
||||
# sys.stdout.flush()
|
||||
print >> sys.stderr, text
|
||||
print(text, file=sys.stderr)
|
||||
else:
|
||||
self.log_func(text)
|
||||
|
||||
@ -133,16 +133,19 @@ class ComicVineTalker(QObject):
|
||||
|
||||
def testKey(self, key):
|
||||
|
||||
test_url = self.api_base_url + "/issue/1/?api_key=" + \
|
||||
key + "&format=json&field_list=name"
|
||||
resp = urllib2.urlopen(test_url, context=self.ssl)
|
||||
content = resp.read()
|
||||
|
||||
cv_response = json.loads(content)
|
||||
|
||||
# Bogus request, but if the key is wrong, you get error 100: "Invalid
|
||||
# API Key"
|
||||
return cv_response['status_code'] != 100
|
||||
try:
|
||||
test_url = self.api_base_url + "/issue/1/?api_key=" + \
|
||||
key + "&format=json&field_list=name"
|
||||
resp = urllib.request.urlopen(test_url, context=self.ssl)
|
||||
content = resp.read()
|
||||
|
||||
cv_response = json.loads(content.decode('utf-8'))
|
||||
|
||||
# Bogus request, but if the key is wrong, you get error 100: "Invalid
|
||||
# API Key"
|
||||
return cv_response['status_code'] != 100
|
||||
except:
|
||||
return False
|
||||
|
||||
"""
|
||||
Get the contect from the CV server. If we're in "wait mode" and status code is a rate limit error
|
||||
@ -156,7 +159,7 @@ class ComicVineTalker(QObject):
|
||||
wait_times = [1, 2, 3, 4]
|
||||
while True:
|
||||
content = self.getUrlContent(url)
|
||||
cv_response = json.loads(content)
|
||||
cv_response = json.loads(content.decode('utf-8'))
|
||||
if self.wait_for_rate_limit and cv_response[
|
||||
'status_code'] == ComicVineTalkerException.RateLimit:
|
||||
self.writeLog(
|
||||
@ -188,9 +191,9 @@ class ComicVineTalker(QObject):
|
||||
# print "ATB---", url
|
||||
for tries in range(3):
|
||||
try:
|
||||
resp = urllib2.urlopen(url, context=self.ssl)
|
||||
resp = urllib.request.urlopen(url, context=self.ssl)
|
||||
return resp.read()
|
||||
except urllib2.HTTPError as e:
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.getcode() == 500:
|
||||
self.writeLog("Try #{0}: ".format(tries + 1))
|
||||
time.sleep(1)
|
||||
@ -223,19 +226,12 @@ class ComicVineTalker(QObject):
|
||||
|
||||
original_series_name = series_name
|
||||
|
||||
# We need to make the series name into an "AND"ed query list
|
||||
# Split and rejoin to remove extra internal spaces
|
||||
query_word_list = series_name.split()
|
||||
and_list = ['AND'] * (len(query_word_list) - 1)
|
||||
and_list.append('')
|
||||
# zipper up the two lists
|
||||
query_list = zip(query_word_list, and_list)
|
||||
# flatten the list
|
||||
query_list = [item for sublist in query_list for item in sublist]
|
||||
# convert back to a string
|
||||
query_string = " ".join(query_list).strip()
|
||||
# print "Query string = ", query_string
|
||||
query_string = " ".join( query_word_list ).strip()
|
||||
#print "Query string = ", query_string
|
||||
|
||||
query_string = urllib.quote_plus(query_string.encode("utf-8"))
|
||||
query_string = urllib.parse.quote_plus(query_string.encode("utf-8"))
|
||||
|
||||
search_url = self.api_base_url + "/search/?api_key=" + self.api_key + "&format=json&resources=volume&query=" + \
|
||||
query_string + \
|
||||
@ -369,7 +365,7 @@ class ComicVineTalker(QObject):
|
||||
year_filter = ",cover_date:{0}-1-1|{1}-1-1".format(
|
||||
year, int(year) + 1)
|
||||
|
||||
issue_number = urllib.quote_plus(unicode(issue_number).encode("utf-8"))
|
||||
issue_number = urllib.parse.quote_plus(str(issue_number).encode("utf-8"))
|
||||
|
||||
filter = "&filter=" + volume_filter + \
|
||||
year_filter + ",issue_number:" + issue_number
|
||||
@ -532,7 +528,7 @@ class ComicVineTalker(QObject):
|
||||
if string is None:
|
||||
return ""
|
||||
# find any tables
|
||||
soup = BeautifulSoup(string)
|
||||
soup = BeautifulSoup(string, "html.parser")
|
||||
tables = soup.findAll('table')
|
||||
|
||||
# remove all newlines first
|
||||
@ -592,7 +588,7 @@ class ComicVineTalker(QObject):
|
||||
for w in col_widths:
|
||||
fmtstr += " {{:{}}}|".format(w + 1)
|
||||
width = sum(col_widths) + len(col_widths) * 2
|
||||
print "width=", width
|
||||
print("width=", width)
|
||||
table_text = ""
|
||||
counter = 0
|
||||
for row in rows:
|
||||
@ -678,7 +674,7 @@ class ComicVineTalker(QObject):
|
||||
return url_list
|
||||
|
||||
# scrape the CV issue page URL to get the alternate cover URLs
|
||||
resp = urllib2.urlopen(issue_page_url, context=self.ssl)
|
||||
resp = urllib.request.urlopen(issue_page_url, context=self.ssl)
|
||||
content = resp.read()
|
||||
alt_cover_url_list = self.parseOutAltCoverUrls(content)
|
||||
|
||||
@ -688,23 +684,27 @@ class ComicVineTalker(QObject):
|
||||
return alt_cover_url_list
|
||||
|
||||
def parseOutAltCoverUrls(self, page_html):
|
||||
soup = BeautifulSoup(page_html)
|
||||
|
||||
soup = BeautifulSoup(page_html, "html.parser")
|
||||
|
||||
alt_cover_url_list = []
|
||||
|
||||
|
||||
# Using knowledge of the layout of the Comic Vine issue page here:
|
||||
# look for the divs that are in the classes 'content-pod' and
|
||||
# 'alt-cover'
|
||||
# look for the divs that are in the classes 'imgboxart' and
|
||||
# 'issue-cover'
|
||||
div_list = soup.find_all('div')
|
||||
covers_found = 0
|
||||
for d in div_list:
|
||||
if 'class' in d:
|
||||
if 'class' in d.attrs:
|
||||
c = d['class']
|
||||
if 'imgboxart' in c and 'issue-cover' in c:
|
||||
if ('imgboxart' in c and
|
||||
'issue-cover' in c and
|
||||
d.img['src'].startswith("http")
|
||||
):
|
||||
|
||||
covers_found += 1
|
||||
if covers_found != 1:
|
||||
alt_cover_url_list.append(d.img['src'])
|
||||
|
||||
alt_cover_url_list.append(d.img['src'])
|
||||
|
||||
return alt_cover_url_list
|
||||
|
||||
def fetchCachedAlternateCoverURLs(self, issue_id):
|
||||
@ -749,15 +749,15 @@ class ComicVineTalker(QObject):
|
||||
data = reply.readAll()
|
||||
|
||||
try:
|
||||
cv_response = json.loads(str(data))
|
||||
except:
|
||||
print >> sys.stderr, "Comic Vine query failed to get JSON data"
|
||||
print >> sys.stderr, str(data)
|
||||
cv_response = json.loads(bytes(data))
|
||||
except Exception as e:
|
||||
print("Comic Vine query failed to get JSON data", file=sys.stderr)
|
||||
print(str(data), file=sys.stderr)
|
||||
return
|
||||
|
||||
if cv_response['status_code'] != 1:
|
||||
print >> sys.stderr, "Comic Vine query failed with error: [{0}]. ".format(
|
||||
cv_response['error'])
|
||||
print("Comic Vine query failed with error: [{0}]. ".format(
|
||||
cv_response['error']), file=sys.stderr)
|
||||
return
|
||||
|
||||
image_url = cv_response['results']['image']['super_url']
|
||||
|
@ -1,4 +1,4 @@
|
||||
"""A PyQt4 widget to display cover images
|
||||
"""A PyQt5 widget to display cover images
|
||||
|
||||
Display cover images from either a local archive, or from Comic Vine.
|
||||
TODO: This should be re-factored using subclasses!
|
||||
@ -20,15 +20,16 @@ TODO: This should be re-factored using subclasses!
|
||||
|
||||
#import os
|
||||
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
from PyQt4 import uic
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtWidgets import *
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5 import uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from imagefetcher import ImageFetcher
|
||||
from pageloader import PageLoader
|
||||
from imagepopup import ImagePopup
|
||||
from .settings import ComicTaggerSettings
|
||||
from .comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from .imagefetcher import ImageFetcher
|
||||
from .pageloader import PageLoader
|
||||
from .imagepopup import ImagePopup
|
||||
from comictaggerlib.ui.qtutils import reduceWidgetFontSize, getQImageFromData
|
||||
#from genericmetadata import GenericMetadata, PageType
|
||||
#from comicarchive import MetaDataStyle
|
||||
|
@ -16,12 +16,12 @@
|
||||
|
||||
#import os
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from .settings import ComicTaggerSettings
|
||||
|
||||
|
||||
class CreditEditorWindow(QtGui.QDialog):
|
||||
class CreditEditorWindow(QtWidgets.QDialog):
|
||||
|
||||
ModeEdit = 0
|
||||
ModeNew = 1
|
||||
@ -90,7 +90,7 @@ class CreditEditorWindow(QtGui.QDialog):
|
||||
|
||||
def accept(self):
|
||||
if self.cbRole.currentText() == "" or self.leName.text() == "":
|
||||
QtGui.QMessageBox.warning(self, self.tr("Whoops"), self.tr(
|
||||
QtWidgets.QMessageBox.warning(self, self.tr("Whoops"), self.tr(
|
||||
"You need to enter both role and name for a credit."))
|
||||
else:
|
||||
QtGui.QDialog.accept(self)
|
||||
QtWidgets.QDialog.accept(self)
|
||||
|
@ -1,3 +1,3 @@
|
||||
# This file should contain only these comments, and the line below.
|
||||
# Used by packaging makefiles and app
|
||||
version = "1.1.20-SNAPSHOT"
|
||||
version = "1.1.30-rc1"
|
||||
|
@ -16,9 +16,9 @@
|
||||
|
||||
#import os
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from .settings import ComicTaggerSettings
|
||||
#from settingswindow import SettingsWindow
|
||||
#from filerenamer import FileRenamer
|
||||
#import utils
|
||||
@ -30,7 +30,7 @@ class ExportConflictOpts:
|
||||
createUnique = 3
|
||||
|
||||
|
||||
class ExportWindow(QtGui.QDialog):
|
||||
class ExportWindow(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent, settings, msg):
|
||||
super(ExportWindow, self).__init__(parent)
|
||||
@ -52,7 +52,7 @@ class ExportWindow(QtGui.QDialog):
|
||||
self.fileConflictBehavior = ExportConflictOpts.dontCreate
|
||||
|
||||
def accept(self):
|
||||
QtGui.QDialog.accept(self)
|
||||
QtWidgets.QDialog.accept(self)
|
||||
|
||||
self.deleteOriginal = self.cbxDeleteOriginal.isChecked()
|
||||
self.addToList = self.cbxAddToList.isChecked()
|
||||
|
@ -18,8 +18,8 @@ import os
|
||||
import re
|
||||
import datetime
|
||||
|
||||
import utils
|
||||
from issuestring import IssueString
|
||||
from . import utils
|
||||
from .issuestring import IssueString
|
||||
|
||||
|
||||
class FileRenamer:
|
||||
@ -49,7 +49,7 @@ class FileRenamer:
|
||||
return (word[0] == "%" and word[-1:] == "%")
|
||||
|
||||
if value is not None:
|
||||
return text.replace(token, unicode(value))
|
||||
return text.replace(token, str(value))
|
||||
else:
|
||||
if self.smart_cleanup:
|
||||
# smart cleanup means we want to remove anything appended to token if it's empty
|
||||
@ -81,7 +81,7 @@ class FileRenamer:
|
||||
new_name = self.replaceToken(new_name, md.volume, '%volume%')
|
||||
|
||||
if md.issue is not None:
|
||||
issue_str = u"{0}".format(
|
||||
issue_str = "{0}".format(
|
||||
IssueString(md.issue).asString(pad=self.issue_zero_padding))
|
||||
else:
|
||||
issue_str = None
|
||||
@ -98,8 +98,8 @@ class FileRenamer:
|
||||
md.month, int):
|
||||
if int(md.month) in range(1, 13):
|
||||
dt = datetime.datetime(1970, int(md.month), 1, 0, 0)
|
||||
month_name = dt.strftime(
|
||||
u"%B".encode(preferred_encoding)).decode(preferred_encoding)
|
||||
#month_name = dt.strftime("%B".encode(preferred_encoding)).decode(preferred_encoding)
|
||||
month_name = dt.strftime("%B")
|
||||
new_name = self.replaceToken(new_name, month_name, '%month_name%')
|
||||
|
||||
new_name = self.replaceToken(new_name, md.genre, '%genre%')
|
||||
@ -128,7 +128,7 @@ class FileRenamer:
|
||||
new_name = re.sub("\{\s*[-:]*\s*\}", "", new_name)
|
||||
|
||||
# remove duplicate spaces
|
||||
new_name = u" ".join(new_name.split())
|
||||
new_name = " ".join(new_name.split())
|
||||
|
||||
# remove remove duplicate -, _,
|
||||
new_name = re.sub("[-_]{2,}\s+", "-- ", new_name)
|
||||
@ -139,7 +139,7 @@ class FileRenamer:
|
||||
new_name = re.sub("[-]{1,2}\s*$", "", new_name)
|
||||
|
||||
# remove duplicate spaces (again!)
|
||||
new_name = u" ".join(new_name.split())
|
||||
new_name = " ".join(new_name.split())
|
||||
|
||||
if ext is None:
|
||||
ext = os.path.splitext(filename)[1]
|
||||
|
@ -1,5 +1,5 @@
|
||||
# coding=utf-8
|
||||
"""A PyQt4 widget for managing list of comic archive files"""
|
||||
"""A PyQt5 widget for managing list of comic archive files"""
|
||||
|
||||
# Copyright 2012-2014 Anthony Beville
|
||||
|
||||
@ -18,18 +18,19 @@
|
||||
import platform
|
||||
import os
|
||||
#import os
|
||||
#import sys
|
||||
import sys
|
||||
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
from PyQt4 import uic
|
||||
from PyQt4.QtCore import pyqtSignal
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5.QtWidgets import *
|
||||
from PyQt5 import uic
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from comicarchive import ComicArchive
|
||||
from optionalmsgdialog import OptionalMessageDialog
|
||||
from .settings import ComicTaggerSettings
|
||||
from .comicarchive import ComicArchive
|
||||
from .optionalmsgdialog import OptionalMessageDialog
|
||||
from comictaggerlib.ui.qtutils import reduceWidgetFontSize, centerWindowOnParent
|
||||
import utils
|
||||
from . import utils
|
||||
#from comicarchive import MetaDataStyle
|
||||
#from genericmetadata import GenericMetadata, PageType
|
||||
|
||||
@ -37,8 +38,10 @@ import utils
|
||||
class FileTableWidgetItem(QTableWidgetItem):
|
||||
|
||||
def __lt__(self, other):
|
||||
return (self.data(Qt.UserRole).toBool() <
|
||||
other.data(Qt.UserRole).toBool())
|
||||
#return (self.data(Qt.UserRole).toBool() <
|
||||
# other.data(Qt.UserRole).toBool())
|
||||
return (self.data(Qt.UserRole) <
|
||||
other.data(Qt.UserRole))
|
||||
|
||||
|
||||
class FileInfo():
|
||||
@ -139,7 +142,7 @@ class FileSelectionList(QWidget):
|
||||
|
||||
def getArchiveByRow(self, row):
|
||||
fi = self.twList.item(row, FileSelectionList.dataColNum).data(
|
||||
Qt.UserRole).toPyObject()
|
||||
Qt.UserRole)
|
||||
return fi.ca
|
||||
|
||||
def getCurrentArchive(self):
|
||||
@ -186,40 +189,46 @@ class FileSelectionList(QWidget):
|
||||
filelist = utils.get_recursive_filelist(pathlist)
|
||||
# we now have a list of files to add
|
||||
|
||||
progdialog = QProgressDialog("", "Cancel", 0, len(filelist), self)
|
||||
# Prog dialog on Linux flakes out for small range, so scale up
|
||||
progdialog = QProgressDialog("", "Cancel", 0, len(filelist), parent=self)
|
||||
progdialog.setWindowTitle("Adding Files")
|
||||
# progdialog.setWindowModality(Qt.WindowModal)
|
||||
progdialog.setWindowModality(Qt.ApplicationModal)
|
||||
progdialog.show()
|
||||
|
||||
progdialog.setMinimumDuration(300)
|
||||
centerWindowOnParent(progdialog)
|
||||
#QCoreApplication.processEvents()
|
||||
#progdialog.show()
|
||||
|
||||
QCoreApplication.processEvents()
|
||||
firstAdded = None
|
||||
self.twList.setSortingEnabled(False)
|
||||
for idx, f in enumerate(filelist):
|
||||
QCoreApplication.processEvents()
|
||||
if progdialog.wasCanceled():
|
||||
break
|
||||
progdialog.setValue(idx)
|
||||
progdialog.setValue(idx+1)
|
||||
progdialog.setLabelText(f)
|
||||
centerWindowOnParent(progdialog)
|
||||
QCoreApplication.processEvents()
|
||||
row = self.addPathItem(f)
|
||||
if firstAdded is None and row is not None:
|
||||
firstAdded = row
|
||||
|
||||
progdialog.hide()
|
||||
QCoreApplication.processEvents()
|
||||
|
||||
progdialog.close()
|
||||
if (self.settings.show_no_unrar_warning and
|
||||
self.settings.unrar_exe_path == "" and
|
||||
self.settings.rar_exe_path == "" and
|
||||
platform.system() != "Windows"):
|
||||
self.settings.unrar_lib_path == "" and
|
||||
not ComicTaggerSettings.haveOwnUnrarLib()):
|
||||
for f in filelist:
|
||||
ext = os.path.splitext(f)[1].lower()
|
||||
if ext == ".rar" or ext == ".cbr":
|
||||
checked = OptionalMessageDialog.msg(self, "No unrar tool",
|
||||
checked = OptionalMessageDialog.msg(self, "No UnRAR Ability",
|
||||
"""
|
||||
It looks like you've tried to open at least one CBR or RAR file.<br><br>
|
||||
In order for ComicTagger to read this kind of file, you will have to configure
|
||||
the location of the unrar tool in the settings. Until then, ComicTagger
|
||||
will not be able recognize these kind of files.
|
||||
the location of the unrar library in the settings. Until then, ComicTagger
|
||||
will not be able read these kind of files. See the "RAR Tools" tab in the
|
||||
settings/preferences for more info.
|
||||
"""
|
||||
)
|
||||
self.settings.show_no_unrar_warning = not checked
|
||||
@ -229,13 +238,19 @@ class FileSelectionList(QWidget):
|
||||
self.twList.selectRow(firstAdded)
|
||||
else:
|
||||
if len(pathlist) == 1 and os.path.isfile(pathlist[0]):
|
||||
QMessageBox.information(self, self.tr("File Open"), self.tr(
|
||||
"Selected file doesn't seem to be a comic archive."))
|
||||
ext = os.path.splitext(pathlist[0])[1].lower()
|
||||
if ext == ".rar" or ext == ".cbr" and self.settings.unrar_lib_path == "":
|
||||
QMessageBox.information(self, self.tr("File Open"), self.tr(
|
||||
"Selected file seems to be a rar file, "
|
||||
"and can't be read until the unrar library is configured."))
|
||||
else:
|
||||
QMessageBox.information(self, self.tr("File Open"), self.tr(
|
||||
"Selected file doesn't seem to be a comic archive."))
|
||||
else:
|
||||
QMessageBox.information(
|
||||
self,
|
||||
self.tr("File/Folder Open"),
|
||||
self.tr("No comic archives were found."))
|
||||
self.tr("No readable comic archives were found."))
|
||||
|
||||
self.twList.setSortingEnabled(True)
|
||||
|
||||
@ -271,7 +286,7 @@ class FileSelectionList(QWidget):
|
||||
return -1
|
||||
|
||||
def addPathItem(self, path):
|
||||
path = unicode(path)
|
||||
path = str(path)
|
||||
path = os.path.abspath(path)
|
||||
# print "processing", path
|
||||
|
||||
@ -327,7 +342,7 @@ class FileSelectionList(QWidget):
|
||||
|
||||
def updateRow(self, row):
|
||||
fi = self.twList.item(row, FileSelectionList.dataColNum).data(
|
||||
Qt.UserRole).toPyObject()
|
||||
Qt.UserRole) #.toPyObject()
|
||||
|
||||
filename_item = self.twList.item(row, FileSelectionList.fileColNum)
|
||||
folder_item = self.twList.item(row, FileSelectionList.folderColNum)
|
||||
@ -382,8 +397,8 @@ class FileSelectionList(QWidget):
|
||||
ca_list = []
|
||||
for r in range(self.twList.rowCount()):
|
||||
item = self.twList.item(r, FileSelectionList.dataColNum)
|
||||
if self.twList.isItemSelected(item):
|
||||
fi = item.data(Qt.UserRole).toPyObject()
|
||||
if item.isSelected():
|
||||
fi = item.data(Qt.UserRole)
|
||||
ca_list.append(fi.ca)
|
||||
|
||||
return ca_list
|
||||
@ -395,7 +410,7 @@ class FileSelectionList(QWidget):
|
||||
self.twList.setSortingEnabled(False)
|
||||
for r in range(self.twList.rowCount()):
|
||||
item = self.twList.item(r, FileSelectionList.dataColNum)
|
||||
if self.twList.isItemSelected(item):
|
||||
if item.isSelected():
|
||||
self.updateRow(r)
|
||||
self.twList.setSortingEnabled(True)
|
||||
|
||||
@ -425,7 +440,7 @@ class FileSelectionList(QWidget):
|
||||
return
|
||||
|
||||
fi = self.twList.item(new_idx, FileSelectionList.dataColNum).data(
|
||||
Qt.UserRole).toPyObject()
|
||||
Qt.UserRole) #.toPyObject()
|
||||
self.selectionChanged.emit(QVariant(fi))
|
||||
|
||||
def revertSelection(self):
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 13 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 4.2 KiB |
@ -19,14 +19,14 @@ import os
|
||||
import datetime
|
||||
import shutil
|
||||
import tempfile
|
||||
import urllib
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import ssl
|
||||
#import urllib2
|
||||
|
||||
try:
|
||||
from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||
from PyQt4.QtCore import QUrl, pyqtSignal, QObject, QByteArray
|
||||
from PyQt4 import QtGui
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||
from PyQt5.QtCore import QUrl, pyqtSignal, QObject, QByteArray
|
||||
from PyQt5 import QtGui
|
||||
except ImportError:
|
||||
# No Qt, so define a few dummy QObjects to help us compile
|
||||
class QObject():
|
||||
@ -45,7 +45,7 @@ except ImportError:
|
||||
def emit(a, b, c):
|
||||
pass
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from .settings import ComicTaggerSettings
|
||||
|
||||
|
||||
class ImageFetcherException(Exception):
|
||||
@ -87,11 +87,10 @@ class ImageFetcher(QObject):
|
||||
|
||||
# first look in the DB
|
||||
image_data = self.get_image_from_cache(url)
|
||||
|
||||
if blocking:
|
||||
if image_data is None:
|
||||
try:
|
||||
image_data = urllib.urlopen(url, context=self.ssl).read()
|
||||
image_data = urllib.request.urlopen(url, context=self.ssl).read()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
raise ImageFetcherException("Network Error!")
|
||||
|
@ -14,7 +14,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import StringIO
|
||||
import io
|
||||
import sys
|
||||
from functools import reduce
|
||||
|
||||
@ -40,9 +40,9 @@ class ImageHasher(object):
|
||||
if path is not None:
|
||||
self.image = Image.open(path)
|
||||
else:
|
||||
self.image = Image.open(StringIO.StringIO(data))
|
||||
except:
|
||||
print("Image data seems corrupted!")
|
||||
self.image = Image.open(io.BytesIO(data))
|
||||
except Exception as e:
|
||||
print("Image data seems corrupted! [{}]".format(e))
|
||||
# just generate a bogus image
|
||||
self.image = Image.new("L", (1, 1))
|
||||
|
||||
@ -52,8 +52,8 @@ class ImageHasher(object):
|
||||
(self.width, self.height), Image.ANTIALIAS).convert("L")
|
||||
except Exception as e:
|
||||
sys.exc_clear()
|
||||
print "average_hash error:", e
|
||||
return long(0)
|
||||
print("average_hash error:", e)
|
||||
return int(0)
|
||||
|
||||
pixels = list(image.getdata())
|
||||
avg = sum(pixels) / len(pixels)
|
||||
@ -61,7 +61,7 @@ class ImageHasher(object):
|
||||
def compare_value_to_avg(i):
|
||||
return (1 if i > avg else 0)
|
||||
|
||||
bitlist = map(compare_value_to_avg, pixels)
|
||||
bitlist = list(map(compare_value_to_avg, pixels))
|
||||
|
||||
# build up an int value from the bit list, one bit at a time
|
||||
def set_bit(x, idx_val):
|
||||
@ -178,13 +178,13 @@ class ImageHasher(object):
|
||||
|
||||
@staticmethod
|
||||
def hamming_distance(h1, h2):
|
||||
if isinstance(h1, long) or isinstance(h1, int):
|
||||
if isinstance(h1, int) or isinstance(h1, int):
|
||||
n1 = h1
|
||||
n2 = h2
|
||||
else:
|
||||
# convert hex strings to ints
|
||||
n1 = long(h1, 16)
|
||||
n2 = long(h2, 16)
|
||||
n1 = int(h1, 16)
|
||||
n2 = int(h2, 16)
|
||||
|
||||
# xor the two numbers
|
||||
n = n1 ^ n2
|
||||
|
@ -17,19 +17,19 @@
|
||||
#import sys
|
||||
#import os
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from .settings import ComicTaggerSettings
|
||||
|
||||
|
||||
class ImagePopup(QtGui.QDialog):
|
||||
class ImagePopup(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent, image_pixmap):
|
||||
super(ImagePopup, self).__init__(parent)
|
||||
|
||||
uic.loadUi(ComicTaggerSettings.getUIFile('imagepopup.ui'), self)
|
||||
|
||||
QtGui.QApplication.setOverrideCursor(
|
||||
QtWidgets.QApplication.setOverrideCursor(
|
||||
QtGui.QCursor(QtCore.Qt.WaitCursor))
|
||||
|
||||
# self.setWindowModality(QtCore.Qt.WindowModal)
|
||||
@ -38,15 +38,16 @@ class ImagePopup(QtGui.QDialog):
|
||||
|
||||
self.imagePixmap = image_pixmap
|
||||
|
||||
screen_size = QtGui.QDesktopWidget().screenGeometry()
|
||||
screen_size = QtWidgets.QDesktopWidget().screenGeometry()
|
||||
self.resize(screen_size.width(), screen_size.height())
|
||||
self.move(0, 0)
|
||||
|
||||
# This is a total hack. Uses a snapshot of the desktop, and overlays a
|
||||
# translucent screen over it. Probably can do it better by setting opacity of a
|
||||
# widget
|
||||
self.desktopBg = QtGui.QPixmap.grabWindow(
|
||||
QtGui.QApplication.desktop().winId(),
|
||||
screen = QtWidgets.QApplication.primaryScreen()
|
||||
self.desktopBg = screen.grabWindow(
|
||||
QtWidgets.QApplication.desktop().winId(),
|
||||
0,
|
||||
0,
|
||||
screen_size.width(),
|
||||
@ -59,7 +60,7 @@ class ImagePopup(QtGui.QDialog):
|
||||
self.applyImagePixmap()
|
||||
self.showFullScreen()
|
||||
self.raise_()
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
|
||||
def paintEvent(self, event):
|
||||
self.painter = QtGui.QPainter(self)
|
||||
|
@ -15,7 +15,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
import StringIO
|
||||
import io
|
||||
#import math
|
||||
#import urllib2
|
||||
#import urllib
|
||||
@ -27,12 +27,12 @@ try:
|
||||
except ImportError:
|
||||
pil_available = False
|
||||
|
||||
from genericmetadata import GenericMetadata
|
||||
from comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from imagehasher import ImageHasher
|
||||
from imagefetcher import ImageFetcher, ImageFetcherException
|
||||
from issuestring import IssueString
|
||||
import utils
|
||||
from .genericmetadata import GenericMetadata
|
||||
from .comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from .imagehasher import ImageHasher
|
||||
from .imagefetcher import ImageFetcher, ImageFetcherException
|
||||
from .issuestring import IssueString
|
||||
from . import utils
|
||||
#from settings import ComicTaggerSettings
|
||||
#from comicvinecacher import ComicVineCacher
|
||||
|
||||
@ -124,7 +124,7 @@ class IssueIdentifier:
|
||||
|
||||
def getAspectRatio(self, image_data):
|
||||
try:
|
||||
im = Image.open(StringIO.StringIO(image_data))
|
||||
im = Image.open(io.StringIO(image_data))
|
||||
w, h = im.size
|
||||
return float(h) / float(w)
|
||||
except:
|
||||
@ -132,17 +132,17 @@ class IssueIdentifier:
|
||||
|
||||
def cropCover(self, image_data):
|
||||
|
||||
im = Image.open(StringIO.StringIO(image_data))
|
||||
im = Image.open(io.StringIO(image_data))
|
||||
w, h = im.size
|
||||
|
||||
try:
|
||||
cropped_im = im.crop((int(w / 2), 0, w, h))
|
||||
except Exception as e:
|
||||
sys.exc_clear()
|
||||
print "cropCover() error:", e
|
||||
print("cropCover() error:", e)
|
||||
return None
|
||||
|
||||
output = StringIO.StringIO()
|
||||
output = io.StringIO()
|
||||
cropped_im.save(output, format="PNG")
|
||||
cropped_image_data = output.getvalue()
|
||||
output.close()
|
||||
@ -405,7 +405,7 @@ class IssueIdentifier:
|
||||
comicVine.setLogFunc(self.output_function)
|
||||
|
||||
# self.log_msg(("Searching for " + keys['series'] + "...")
|
||||
self.log_msg(u"Searching for {0} #{1} ...".format(
|
||||
self.log_msg("Searching for {0} #{1} ...".format(
|
||||
keys['series'], keys['issue_number']))
|
||||
try:
|
||||
cv_search_results = comicVine.searchForSeries(keys['series'])
|
||||
@ -492,11 +492,11 @@ class IssueIdentifier:
|
||||
break
|
||||
|
||||
if keys['year'] is None:
|
||||
self.log_msg(u"Found {0} series that have an issue #{1}".format(
|
||||
self.log_msg("Found {0} series that have an issue #{1}".format(
|
||||
len(shortlist), keys['issue_number']))
|
||||
else:
|
||||
self.log_msg(
|
||||
u"Found {0} series that have an issue #{1} from {2}".format(
|
||||
"Found {0} series that have an issue #{1} from {2}".format(
|
||||
len(shortlist),
|
||||
keys['issue_number'],
|
||||
keys['year']))
|
||||
@ -509,7 +509,7 @@ class IssueIdentifier:
|
||||
self.callback(counter, len(shortlist) * 3)
|
||||
counter += 1
|
||||
|
||||
self.log_msg(u"Examining covers for ID: {0} {1} ({2}) ...".format(
|
||||
self.log_msg("Examining covers for ID: {0} {1} ({2}) ...".format(
|
||||
series['id'],
|
||||
series['name'],
|
||||
series['start_year']), newline=False)
|
||||
@ -540,7 +540,7 @@ class IssueIdentifier:
|
||||
return self.match_list
|
||||
|
||||
match = dict()
|
||||
match['series'] = u"{0} ({1})".format(
|
||||
match['series'] = "{0} ({1})".format(
|
||||
series['name'], series['start_year'])
|
||||
match['distance'] = score_item['score']
|
||||
match['issue_number'] = keys['issue_number']
|
||||
@ -582,7 +582,7 @@ class IssueIdentifier:
|
||||
self.log_msg(str(l))
|
||||
|
||||
def print_match(item):
|
||||
self.log_msg(u"-----> {0} #{1} {2} ({3}/{4}) -- score: {5}".format(
|
||||
self.log_msg("-----> {0} #{1} {2} ({3}/{4}) -- score: {5}".format(
|
||||
item['series'],
|
||||
item['issue_number'],
|
||||
item['issue_title'],
|
||||
@ -613,7 +613,7 @@ class IssueIdentifier:
|
||||
self.callback(counter, len(self.match_list) * 3)
|
||||
counter += 1
|
||||
self.log_msg(
|
||||
u"Examining alternate covers for ID: {0} {1} ...".format(
|
||||
"Examining alternate covers for ID: {0} {1} ...".format(
|
||||
m['volume_id'],
|
||||
m['series']),
|
||||
newline=False)
|
||||
@ -640,18 +640,18 @@ class IssueIdentifier:
|
||||
if len(self.match_list) == 1:
|
||||
self.log_msg("No matching pages in the issue.")
|
||||
self.log_msg(
|
||||
u"--------------------------------------------------------------------------")
|
||||
"--------------------------------------------------------------------------")
|
||||
print_match(self.match_list[0])
|
||||
self.log_msg(
|
||||
u"--------------------------------------------------------------------------")
|
||||
"--------------------------------------------------------------------------")
|
||||
self.search_result = self.ResultFoundMatchButBadCoverScore
|
||||
else:
|
||||
self.log_msg(
|
||||
u"--------------------------------------------------------------------------")
|
||||
"--------------------------------------------------------------------------")
|
||||
self.log_msg(
|
||||
u"Multiple bad cover matches! Need to use other info...")
|
||||
"Multiple bad cover matches! Need to use other info...")
|
||||
self.log_msg(
|
||||
u"--------------------------------------------------------------------------")
|
||||
"--------------------------------------------------------------------------")
|
||||
self.search_result = self.ResultMultipleMatchesWithBadImageScores
|
||||
return self.match_list
|
||||
else:
|
||||
@ -695,28 +695,28 @@ class IssueIdentifier:
|
||||
|
||||
if len(self.match_list) == 1:
|
||||
self.log_msg(
|
||||
u"--------------------------------------------------------------------------")
|
||||
"--------------------------------------------------------------------------")
|
||||
print_match(self.match_list[0])
|
||||
self.log_msg(
|
||||
u"--------------------------------------------------------------------------")
|
||||
"--------------------------------------------------------------------------")
|
||||
self.search_result = self.ResultOneGoodMatch
|
||||
|
||||
elif len(self.match_list) == 0:
|
||||
self.log_msg(
|
||||
u"--------------------------------------------------------------------------")
|
||||
"--------------------------------------------------------------------------")
|
||||
self.log_msg("No matches found :(")
|
||||
self.log_msg(
|
||||
u"--------------------------------------------------------------------------")
|
||||
"--------------------------------------------------------------------------")
|
||||
self.search_result = self.ResultNoMatches
|
||||
else:
|
||||
# we've got multiple good matches:
|
||||
self.log_msg("More than one likely candidate.")
|
||||
self.search_result = self.ResultMultipleGoodMatches
|
||||
self.log_msg(
|
||||
u"--------------------------------------------------------------------------")
|
||||
"--------------------------------------------------------------------------")
|
||||
for item in self.match_list:
|
||||
print_match(item)
|
||||
self.log_msg(
|
||||
u"--------------------------------------------------------------------------")
|
||||
"--------------------------------------------------------------------------")
|
||||
|
||||
return self.match_list
|
||||
|
@ -18,29 +18,29 @@
|
||||
#import os
|
||||
#import re
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
#from PyQt4.QtCore import QUrl, pyqtSignal, QByteArray
|
||||
#from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
#from PyQt5.QtCore import QUrl, pyqtSignal, QByteArray
|
||||
#from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||
|
||||
from comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from settings import ComicTaggerSettings
|
||||
from issuestring import IssueString
|
||||
from coverimagewidget import CoverImageWidget
|
||||
from .comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from .settings import ComicTaggerSettings
|
||||
from .issuestring import IssueString
|
||||
from .coverimagewidget import CoverImageWidget
|
||||
from comictaggerlib.ui.qtutils import reduceWidgetFontSize
|
||||
#from imagefetcher import ImageFetcher
|
||||
#import utils
|
||||
|
||||
|
||||
class IssueNumberTableWidgetItem(QtGui.QTableWidgetItem):
|
||||
class IssueNumberTableWidgetItem(QtWidgets.QTableWidgetItem):
|
||||
|
||||
def __lt__(self, other):
|
||||
selfStr = self.data(QtCore.Qt.DisplayRole).toString()
|
||||
otherStr = other.data(QtCore.Qt.DisplayRole).toString()
|
||||
selfStr = self.data(QtCore.Qt.DisplayRole)
|
||||
otherStr = other.data(QtCore.Qt.DisplayRole)
|
||||
return (IssueString(selfStr).asFloat() <
|
||||
IssueString(otherStr).asFloat())
|
||||
|
||||
|
||||
class IssueSelectionWindow(QtGui.QDialog):
|
||||
class IssueSelectionWindow(QtWidgets.QDialog):
|
||||
|
||||
volume_id = 0
|
||||
|
||||
@ -52,7 +52,7 @@ class IssueSelectionWindow(QtGui.QDialog):
|
||||
|
||||
self.coverWidget = CoverImageWidget(
|
||||
self.coverImageContainer, CoverImageWidget.AltCoverMode)
|
||||
gridlayout = QtGui.QGridLayout(self.coverImageContainer)
|
||||
gridlayout = QtWidgets.QGridLayout(self.coverImageContainer)
|
||||
gridlayout.addWidget(self.coverWidget)
|
||||
gridlayout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
@ -85,15 +85,14 @@ class IssueSelectionWindow(QtGui.QDialog):
|
||||
self.twList.selectRow(0)
|
||||
else:
|
||||
for r in range(0, self.twList.rowCount()):
|
||||
issue_id, b = self.twList.item(
|
||||
r, 0).data(QtCore.Qt.UserRole).toInt()
|
||||
issue_id = self.twList.item(r, 0).data(QtCore.Qt.UserRole)
|
||||
if (issue_id == self.initial_id):
|
||||
self.twList.selectRow(r)
|
||||
break
|
||||
|
||||
def performQuery(self):
|
||||
|
||||
QtGui.QApplication.setOverrideCursor(
|
||||
QtWidgets.QApplication.setOverrideCursor(
|
||||
QtGui.QCursor(QtCore.Qt.WaitCursor))
|
||||
|
||||
try:
|
||||
@ -101,14 +100,14 @@ class IssueSelectionWindow(QtGui.QDialog):
|
||||
volume_data = comicVine.fetchVolumeData(self.series_id)
|
||||
self.issue_list = comicVine.fetchIssuesByVolume(self.series_id)
|
||||
except ComicVineTalkerException as e:
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
if e.code == ComicVineTalkerException.RateLimit:
|
||||
QtGui.QMessageBox.critical(
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
self.tr("Comic Vine Error"),
|
||||
ComicVineTalker.getRateLimitMessage())
|
||||
else:
|
||||
QtGui.QMessageBox.critical(
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
self.tr("Network Issue"),
|
||||
self.tr("Could not connect to Comic Vine to list issues!"))
|
||||
@ -139,7 +138,7 @@ class IssueSelectionWindow(QtGui.QDialog):
|
||||
if len(parts) > 1:
|
||||
item_text = parts[0] + "-" + parts[1]
|
||||
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 1, item)
|
||||
@ -147,7 +146,7 @@ class IssueSelectionWindow(QtGui.QDialog):
|
||||
item_text = record['name']
|
||||
if item_text is None:
|
||||
item_text = ""
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 2, item)
|
||||
@ -162,7 +161,7 @@ class IssueSelectionWindow(QtGui.QDialog):
|
||||
self.twList.setSortingEnabled(True)
|
||||
self.twList.sortItems(0, QtCore.Qt.AscendingOrder)
|
||||
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
|
||||
def cellDoubleClicked(self, r, c):
|
||||
self.accept()
|
||||
@ -174,8 +173,7 @@ class IssueSelectionWindow(QtGui.QDialog):
|
||||
if prev is not None and prev.row() == curr.row():
|
||||
return
|
||||
|
||||
self.issue_id, b = self.twList.item(
|
||||
curr.row(), 0).data(QtCore.Qt.UserRole).toInt()
|
||||
self.issue_id = self.twList.item(curr.row(), 0).data(QtCore.Qt.UserRole)
|
||||
|
||||
# list selection was changed, update the the issue cover
|
||||
for record in self.issue_list:
|
||||
|
@ -17,12 +17,12 @@
|
||||
#import sys
|
||||
#import os
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from .settings import ComicTaggerSettings
|
||||
|
||||
|
||||
class LogWindow(QtGui.QDialog):
|
||||
class LogWindow(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent):
|
||||
super(LogWindow, self).__init__(parent)
|
||||
@ -34,4 +34,8 @@ class LogWindow(QtGui.QDialog):
|
||||
QtCore.Qt.WindowMaximizeButtonHint)
|
||||
|
||||
def setText(self, text):
|
||||
try:
|
||||
text = text.decode()
|
||||
except:
|
||||
pass
|
||||
self.textEdit.setPlainText(text)
|
||||
|
@ -20,63 +20,80 @@ import signal
|
||||
import traceback
|
||||
import platform
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
|
||||
# setup libunrar
|
||||
if not os.environ.get("UNRAR_LIB_PATH", None):
|
||||
os.environ["UNRAR_LIB_PATH"] = ComicTaggerSettings.libunrarPath()
|
||||
from .settings import ComicTaggerSettings
|
||||
# Need to load setting before anything else
|
||||
SETTINGS = ComicTaggerSettings()
|
||||
|
||||
try:
|
||||
qt_available = True
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from taggerwindow import TaggerWindow
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from .taggerwindow import TaggerWindow
|
||||
except ImportError as e:
|
||||
qt_available = False
|
||||
|
||||
import utils
|
||||
import cli
|
||||
from options import Options
|
||||
from comicvinetalker import ComicVineTalker
|
||||
|
||||
from . import utils
|
||||
from . import cli
|
||||
from .options import Options
|
||||
from .comicvinetalker import ComicVineTalker
|
||||
|
||||
def ctmain():
|
||||
utils.fix_output_encoding()
|
||||
settings = ComicTaggerSettings()
|
||||
|
||||
opts = Options()
|
||||
opts.parseCmdLineArgs()
|
||||
|
||||
# manage the CV API key
|
||||
if opts.cv_api_key:
|
||||
if opts.cv_api_key != settings.cv_api_key:
|
||||
settings.cv_api_key = opts.cv_api_key
|
||||
settings.save()
|
||||
if opts.cv_api_key != SETTINGS.cv_api_key:
|
||||
SETTINGS.cv_api_key = opts.cv_api_key
|
||||
SETTINGS.save()
|
||||
if opts.only_set_key:
|
||||
print("Key set")
|
||||
return
|
||||
|
||||
ComicVineTalker.api_key = settings.cv_api_key
|
||||
ComicVineTalker.api_key = SETTINGS.cv_api_key
|
||||
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
if not qt_available and not opts.no_gui:
|
||||
opts.no_gui = True
|
||||
print >> sys.stderr, "PyQt4 is not available. ComicTagger is limited to command-line mode."
|
||||
print("PyQt5 is not available. ComicTagger is limited to command-line mode.", file=sys.stderr)
|
||||
|
||||
if opts.no_gui:
|
||||
cli.cli_mode(opts, settings)
|
||||
cli.cli_mode(opts, SETTINGS)
|
||||
else:
|
||||
app = QtGui.QApplication(sys.argv)
|
||||
|
||||
os.environ['QT_AUTO_SCREEN_SCALE_FACTOR'] = '1'
|
||||
|
||||
#if platform.system() == "Darwin":
|
||||
# QtWidgets.QApplication.setStyle("macintosh")
|
||||
#else:
|
||||
# QtWidgets.QApplication.setStyle("Fusion")
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
if platform.system() == "Darwin":
|
||||
# Set the MacOS dock icon
|
||||
app.setWindowIcon(
|
||||
QtGui.QIcon(ComicTaggerSettings.getGraphic('app.png')))
|
||||
|
||||
if platform.system() == "Windows":
|
||||
# For pure python, tell windows that we're not python,
|
||||
# so we can have our own taskbar icon
|
||||
import ctypes
|
||||
myappid = u'comictagger' # arbitrary string
|
||||
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
|
||||
|
||||
if platform.system() != "Linux":
|
||||
img = QtGui.QPixmap(ComicTaggerSettings.getGraphic('tags.png'))
|
||||
|
||||
splash = QtGui.QSplashScreen(img)
|
||||
splash = QtWidgets.QSplashScreen(img)
|
||||
splash.show()
|
||||
splash.raise_()
|
||||
app.processEvents()
|
||||
|
||||
try:
|
||||
tagger_window = TaggerWindow(opts.file_list, settings, opts=opts)
|
||||
tagger_window = TaggerWindow(opts.file_list, SETTINGS, opts=opts)
|
||||
tagger_window.setWindowIcon(
|
||||
QtGui.QIcon(ComicTaggerSettings.getGraphic('app.png')))
|
||||
tagger_window.show()
|
||||
|
||||
if platform.system() != "Linux":
|
||||
@ -84,8 +101,8 @@ def ctmain():
|
||||
|
||||
sys.exit(app.exec_())
|
||||
except Exception as e:
|
||||
QtGui.QMessageBox.critical(
|
||||
QtGui.QMainWindow(),
|
||||
QtWidgets.QMessageBox.critical(
|
||||
QtWidgets.QMainWindow(),
|
||||
"Error",
|
||||
"Unhandled exception in app:\n" +
|
||||
traceback.format_exc())
|
||||
|
@ -17,11 +17,11 @@
|
||||
import os
|
||||
#import sys
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
#from PyQt4.QtCore import QUrl, pyqtSignal, QByteArray
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
#from PyQt5.QtCore import QUrl, pyqtSignal, QByteArray
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from coverimagewidget import CoverImageWidget
|
||||
from .settings import ComicTaggerSettings
|
||||
from .coverimagewidget import CoverImageWidget
|
||||
from comictaggerlib.ui.qtutils import reduceWidgetFontSize
|
||||
#from imagefetcher import ImageFetcher
|
||||
#from comicarchive import MetaDataStyle
|
||||
@ -29,7 +29,7 @@ from comictaggerlib.ui.qtutils import reduceWidgetFontSize
|
||||
#import utils
|
||||
|
||||
|
||||
class MatchSelectionWindow(QtGui.QDialog):
|
||||
class MatchSelectionWindow(QtWidgets.QDialog):
|
||||
|
||||
volume_id = 0
|
||||
|
||||
@ -41,13 +41,13 @@ class MatchSelectionWindow(QtGui.QDialog):
|
||||
|
||||
self.altCoverWidget = CoverImageWidget(
|
||||
self.altCoverContainer, CoverImageWidget.AltCoverMode)
|
||||
gridlayout = QtGui.QGridLayout(self.altCoverContainer)
|
||||
gridlayout = QtWidgets.QGridLayout(self.altCoverContainer)
|
||||
gridlayout.addWidget(self.altCoverWidget)
|
||||
gridlayout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.archiveCoverWidget = CoverImageWidget(
|
||||
self.archiveCoverContainer, CoverImageWidget.ArchiveMode)
|
||||
gridlayout = QtGui.QGridLayout(self.archiveCoverContainer)
|
||||
gridlayout = QtWidgets.QGridLayout(self.archiveCoverContainer)
|
||||
gridlayout.addWidget(self.archiveCoverWidget)
|
||||
gridlayout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
@ -74,7 +74,7 @@ class MatchSelectionWindow(QtGui.QDialog):
|
||||
self.twList.selectRow(0)
|
||||
|
||||
path = self.comic_archive.path
|
||||
self.setWindowTitle(u"Select correct match: {0}".format(
|
||||
self.setWindowTitle("Select correct match: {0}".format(
|
||||
os.path.split(path)[1]))
|
||||
|
||||
def populateTable(self):
|
||||
@ -89,30 +89,30 @@ class MatchSelectionWindow(QtGui.QDialog):
|
||||
self.twList.insertRow(row)
|
||||
|
||||
item_text = match['series']
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setData(QtCore.Qt.UserRole, (match,))
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 0, item)
|
||||
|
||||
if match['publisher'] is not None:
|
||||
item_text = u"{0}".format(match['publisher'])
|
||||
item_text = "{0}".format(match['publisher'])
|
||||
else:
|
||||
item_text = u"Unknown"
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item_text = "Unknown"
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 1, item)
|
||||
|
||||
month_str = u""
|
||||
year_str = u"????"
|
||||
month_str = ""
|
||||
year_str = "????"
|
||||
if match['month'] is not None:
|
||||
month_str = u"-{0:02d}".format(int(match['month']))
|
||||
month_str = "-{0:02d}".format(int(match['month']))
|
||||
if match['year'] is not None:
|
||||
year_str = u"{0}".format(match['year'])
|
||||
year_str = "{0}".format(match['year'])
|
||||
|
||||
item_text = year_str + month_str
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 2, item)
|
||||
@ -120,7 +120,7 @@ class MatchSelectionWindow(QtGui.QDialog):
|
||||
item_text = match['issue_title']
|
||||
if item_text is None:
|
||||
item_text = ""
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 3, item)
|
||||
@ -156,5 +156,5 @@ class MatchSelectionWindow(QtGui.QDialog):
|
||||
def currentMatch(self):
|
||||
row = self.twList.currentRow()
|
||||
match = self.twList.item(row, 0).data(
|
||||
QtCore.Qt.UserRole).toPyObject()[0]
|
||||
QtCore.Qt.UserRole)[0]
|
||||
return match
|
||||
|
@ -1,4 +1,4 @@
|
||||
"""A PyQt4 dialog to show a message and let the user check a box
|
||||
"""A PyQt5 dialog to show a message and let the user check a box
|
||||
|
||||
Example usage:
|
||||
|
||||
@ -25,8 +25,9 @@ said_yes, checked = OptionalMessageDialog.question(self, "Question",
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5.QtWidgets import *
|
||||
|
||||
|
||||
StyleMessage = 0
|
||||
|
@ -25,11 +25,11 @@ try:
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from genericmetadata import GenericMetadata
|
||||
from comicarchive import MetaDataStyle
|
||||
from versionchecker import VersionChecker
|
||||
import ctversion
|
||||
import utils
|
||||
from .genericmetadata import GenericMetadata
|
||||
from .comicarchive import MetaDataStyle
|
||||
from .versionchecker import VersionChecker
|
||||
from . import ctversion
|
||||
from . import utils
|
||||
|
||||
|
||||
class Options:
|
||||
@ -144,7 +144,7 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
if msg is not None:
|
||||
print(msg)
|
||||
if show_help:
|
||||
print(self.help_text.format(appname))
|
||||
print((self.help_text.format(appname)))
|
||||
else:
|
||||
print("For more help, run with '--help'")
|
||||
sys.exit(code)
|
||||
@ -196,7 +196,7 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
# Map the dict to the metadata object
|
||||
for key in md_dict:
|
||||
if not hasattr(md, key):
|
||||
print("Warning: '{0}' is not a valid tag name".format(key))
|
||||
print(("Warning: '{0}' is not a valid tag name".format(key)))
|
||||
else:
|
||||
md.isEmpty = False
|
||||
setattr(md, key, md_dict[key])
|
||||
@ -216,7 +216,7 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
break
|
||||
sys.argv = script_args
|
||||
if not os.path.exists(scriptfile):
|
||||
print("Can't find {0}".format(scriptfile))
|
||||
print(("Can't find {0}".format(scriptfile)))
|
||||
else:
|
||||
# I *think* this makes sense:
|
||||
# assume the base name of the file is the module name
|
||||
@ -232,11 +232,11 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
if "main" in dir(script):
|
||||
script.main()
|
||||
else:
|
||||
print(
|
||||
"Can't find entry point \"main()\" in module \"{0}\"".format(module_name))
|
||||
print((
|
||||
"Can't find entry point \"main()\" in module \"{0}\"".format(module_name)))
|
||||
except Exception as e:
|
||||
print "Script raised an unhandled exception: ", e
|
||||
print(traceback.format_exc())
|
||||
print("Script raised an unhandled exception: ", e)
|
||||
print((traceback.format_exc()))
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
@ -340,8 +340,8 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
if o == "--only-set-cv-key":
|
||||
self.only_set_key = True
|
||||
if o == "--version":
|
||||
print(
|
||||
"ComicTagger {0}: Copyright (c) 2012-2014 Anthony Beville".format(ctversion.version))
|
||||
print((
|
||||
"ComicTagger {0}: Copyright (c) 2012-2014 Anthony Beville".format(ctversion.version)))
|
||||
print(
|
||||
"Distributed under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)")
|
||||
sys.exit(0)
|
||||
|
@ -18,13 +18,13 @@ import platform
|
||||
#import sys
|
||||
#import os
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from coverimagewidget import CoverImageWidget
|
||||
from .settings import ComicTaggerSettings
|
||||
from .coverimagewidget import CoverImageWidget
|
||||
|
||||
|
||||
class PageBrowserWindow(QtGui.QDialog):
|
||||
class PageBrowserWindow(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent, metadata):
|
||||
super(PageBrowserWindow, self).__init__(parent)
|
||||
@ -33,7 +33,7 @@ class PageBrowserWindow(QtGui.QDialog):
|
||||
|
||||
self.pageWidget = CoverImageWidget(
|
||||
self.pageContainer, CoverImageWidget.ArchiveMode)
|
||||
gridlayout = QtGui.QGridLayout(self.pageContainer)
|
||||
gridlayout = QtWidgets.QGridLayout(self.pageContainer)
|
||||
gridlayout.addWidget(self.pageWidget)
|
||||
gridlayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.pageWidget.showControls = False
|
||||
@ -47,7 +47,7 @@ class PageBrowserWindow(QtGui.QDialog):
|
||||
self.current_page_num = 0
|
||||
self.metadata = metadata
|
||||
|
||||
self.buttonBox.button(QtGui.QDialogButtonBox.Close).setDefault(True)
|
||||
self.buttonBox.button(QtWidgets.QDialogButtonBox.Close).setDefault(True)
|
||||
if platform.system() == "Darwin":
|
||||
self.btnPrev.setText("<<")
|
||||
self.btnNext.setText(">>")
|
||||
|
@ -1,4 +1,4 @@
|
||||
"""A PyQt4 widget for editing the page list info"""
|
||||
"""A PyQt5 widget for editing the page list info"""
|
||||
|
||||
# Copyright 2012-2014 Anthony Beville
|
||||
|
||||
@ -16,14 +16,15 @@
|
||||
|
||||
#import os
|
||||
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
from PyQt4 import uic
|
||||
from PyQt5.QtCore import *
|
||||
from PyQt5.QtGui import *
|
||||
from PyQt5.QtWidgets import *
|
||||
from PyQt5 import uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from genericmetadata import GenericMetadata, PageType
|
||||
from comicarchive import MetaDataStyle
|
||||
from coverimagewidget import CoverImageWidget
|
||||
from .settings import ComicTaggerSettings
|
||||
from .genericmetadata import GenericMetadata, PageType
|
||||
from .comicarchive import MetaDataStyle
|
||||
from .coverimagewidget import CoverImageWidget
|
||||
#from pageloader import PageLoader
|
||||
|
||||
|
||||
@ -171,7 +172,7 @@ class PageListEditor(QWidget):
|
||||
|
||||
#idx = int(str (self.listWidget.item(row).text()))
|
||||
idx = int(self.listWidget.item(row).data(
|
||||
Qt.UserRole).toPyObject()[0]['Image'])
|
||||
Qt.UserRole)[0]['Image'])
|
||||
|
||||
if self.comic_archive is not None:
|
||||
self.pageWidget.setArchive(self.comic_archive, idx)
|
||||
@ -180,7 +181,7 @@ class PageListEditor(QWidget):
|
||||
frontCover = 0
|
||||
for i in range(self.listWidget.count()):
|
||||
item = self.listWidget.item(i)
|
||||
page_dict = item.data(Qt.UserRole).toPyObject()[0]
|
||||
page_dict = item.data(Qt.UserRole)[0] #.toPyObject()[0]
|
||||
if 'Type' in page_dict and page_dict[
|
||||
'Type'] == PageType.FrontCover:
|
||||
frontCover = int(page_dict['Image'])
|
||||
@ -189,7 +190,7 @@ class PageListEditor(QWidget):
|
||||
|
||||
def getCurrentPageType(self):
|
||||
row = self.listWidget.currentRow()
|
||||
page_dict = self.listWidget.item(row).data(Qt.UserRole).toPyObject()[0]
|
||||
page_dict = self.listWidget.item(row).data(Qt.UserRole)[0] #.toPyObject()[0]
|
||||
if 'Type' in page_dict:
|
||||
return page_dict['Type']
|
||||
else:
|
||||
@ -197,7 +198,7 @@ class PageListEditor(QWidget):
|
||||
|
||||
def setCurrentPageType(self, t):
|
||||
row = self.listWidget.currentRow()
|
||||
page_dict = self.listWidget.item(row).data(Qt.UserRole).toPyObject()[0]
|
||||
page_dict = self.listWidget.item(row).data(Qt.UserRole)[0] #.toPyObject()[0]
|
||||
|
||||
if t == "":
|
||||
if 'Type' in page_dict:
|
||||
@ -239,7 +240,7 @@ class PageListEditor(QWidget):
|
||||
page_list = []
|
||||
for i in range(self.listWidget.count()):
|
||||
item = self.listWidget.item(i)
|
||||
page_list.append(item.data(Qt.UserRole).toPyObject()[0])
|
||||
page_list.append(item.data(Qt.UserRole)[0]) #.toPyObject()[0]
|
||||
return page_list
|
||||
|
||||
def emitFrontCoverChange(self):
|
||||
|
@ -14,8 +14,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt4.QtCore import pyqtSignal
|
||||
from PyQt5 import QtCore, QtGui, uic
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
|
||||
from comictaggerlib.ui.qtutils import getQImageFromData
|
||||
#from comicarchive import ComicArchive
|
||||
|
@ -1,4 +1,4 @@
|
||||
"""A PyQT4 dialog to show ID log and progress"""
|
||||
"""A PyQT5 dialog to show ID log and progress"""
|
||||
|
||||
# Copyright 2012-2014 Anthony Beville
|
||||
|
||||
@ -17,14 +17,14 @@
|
||||
#import sys
|
||||
#import os
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from comictaggerlib.ui.qtutils import reduceWidgetFontSize
|
||||
from settings import ComicTaggerSettings
|
||||
from .settings import ComicTaggerSettings
|
||||
#import utils
|
||||
|
||||
|
||||
class IDProgressWindow(QtGui.QDialog):
|
||||
class IDProgressWindow(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent):
|
||||
super(IDProgressWindow, self).__init__(parent)
|
||||
|
@ -16,16 +16,17 @@
|
||||
|
||||
import os
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from settingswindow import SettingsWindow
|
||||
from filerenamer import FileRenamer
|
||||
from comicarchive import MetaDataStyle
|
||||
import utils
|
||||
from .settings import ComicTaggerSettings
|
||||
from .settingswindow import SettingsWindow
|
||||
from .filerenamer import FileRenamer
|
||||
from .comicarchive import MetaDataStyle
|
||||
from comictaggerlib.ui.qtutils import centerWindowOnParent
|
||||
from . import utils
|
||||
|
||||
|
||||
class RenameWindow(QtGui.QDialog):
|
||||
class RenameWindow(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent, comic_archive_list, data_style, settings):
|
||||
super(RenameWindow, self).__init__(parent)
|
||||
@ -79,9 +80,9 @@ class RenameWindow(QtGui.QDialog):
|
||||
|
||||
row = self.twList.rowCount()
|
||||
self.twList.insertRow(row)
|
||||
folder_item = QtGui.QTableWidgetItem()
|
||||
old_name_item = QtGui.QTableWidgetItem()
|
||||
new_name_item = QtGui.QTableWidgetItem()
|
||||
folder_item = QtWidgets.QTableWidgetItem()
|
||||
old_name_item = QtWidgets.QTableWidgetItem()
|
||||
new_name_item = QtWidgets.QTableWidgetItem()
|
||||
|
||||
item_text = os.path.split(ca.path)[0]
|
||||
folder_item.setFlags(
|
||||
@ -128,23 +129,28 @@ class RenameWindow(QtGui.QDialog):
|
||||
|
||||
def accept(self):
|
||||
|
||||
progdialog = QtGui.QProgressDialog(
|
||||
progdialog = QtWidgets.QProgressDialog(
|
||||
"", "Cancel", 0, len(self.rename_list), self)
|
||||
progdialog.setWindowTitle("Renaming Archives")
|
||||
progdialog.setWindowModality(QtCore.Qt.WindowModal)
|
||||
progdialog.show()
|
||||
progdialog.setMinimumDuration(100)
|
||||
centerWindowOnParent(progdialog)
|
||||
#progdialog.show()
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
|
||||
for idx, item in enumerate(self.rename_list):
|
||||
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
if progdialog.wasCanceled():
|
||||
break
|
||||
progdialog.setValue(idx)
|
||||
idx += 1
|
||||
progdialog.setValue(idx)
|
||||
progdialog.setLabelText(item['new_name'])
|
||||
centerWindowOnParent(progdialog)
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
|
||||
if item['new_name'] == os.path.basename(item['archive'].path):
|
||||
print item['new_name'], "Filename is already good!"
|
||||
print(item['new_name'], "Filename is already good!")
|
||||
continue
|
||||
|
||||
if not item['archive'].isWritable(check_rar_status=False):
|
||||
@ -158,6 +164,7 @@ class RenameWindow(QtGui.QDialog):
|
||||
|
||||
item['archive'].rename(new_abs_path)
|
||||
|
||||
progdialog.close()
|
||||
progdialog.hide()
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
|
||||
QtGui.QDialog.accept(self)
|
||||
QtWidgets.QDialog.accept(self)
|
||||
|
@ -21,7 +21,7 @@ import platform
|
||||
import codecs
|
||||
import uuid
|
||||
|
||||
import utils
|
||||
from . import utils
|
||||
|
||||
|
||||
class ComicTaggerSettings:
|
||||
@ -34,13 +34,17 @@ class ComicTaggerSettings:
|
||||
else:
|
||||
folder = os.path.join(os.path.expanduser('~'), '.ComicTagger')
|
||||
if folder is not None:
|
||||
folder = folder.decode(filename_encoding)
|
||||
folder = folder
|
||||
return folder
|
||||
|
||||
@staticmethod
|
||||
def libunrarPath():
|
||||
def defaultLibunrarPath():
|
||||
return ComicTaggerSettings.baseDir() + "/libunrar.so"
|
||||
|
||||
@staticmethod
|
||||
def haveOwnUnrarLib():
|
||||
return os.path.exists(ComicTaggerSettings.defaultLibunrarPath())
|
||||
|
||||
@staticmethod
|
||||
def baseDir():
|
||||
if getattr(sys, 'frozen', None):
|
||||
@ -60,12 +64,11 @@ class ComicTaggerSettings:
|
||||
return os.path.join(ui_folder, filename)
|
||||
|
||||
def setDefaultValues(self):
|
||||
|
||||
# General Settings
|
||||
self.rar_exe_path = ""
|
||||
self.unrar_exe_path = ""
|
||||
self.unrar_lib_path = ""
|
||||
self.allow_cbi_in_rar = True
|
||||
self.check_for_new_version = True
|
||||
self.check_for_new_version = False
|
||||
self.send_usage_stats = False
|
||||
|
||||
# automatic settings
|
||||
@ -84,7 +87,7 @@ class ComicTaggerSettings:
|
||||
|
||||
# identifier settings
|
||||
self.id_length_delta_thresh = 5
|
||||
self.id_publisher_blacklist = "Panini Comics, Abril, Planeta DeAgostini, Editorial Televisa"
|
||||
self.id_publisher_blacklist = "Panini Comics, Abril, Planeta DeAgostini, Editorial Televisa, Dino Comics"
|
||||
|
||||
# Show/ask dialog flags
|
||||
self.ask_about_cbi_in_rar = True
|
||||
@ -148,7 +151,7 @@ class ComicTaggerSettings:
|
||||
else:
|
||||
self.load()
|
||||
|
||||
# take a crack at finding rar exes, if not set already
|
||||
# take a crack at finding rar exe, if not set already
|
||||
if self.rar_exe_path == "":
|
||||
if platform.system() == "Windows":
|
||||
# look in some likely places for Windows machines
|
||||
@ -162,19 +165,51 @@ class ComicTaggerSettings:
|
||||
self.rar_exe_path = utils.which("rar")
|
||||
if self.rar_exe_path != "":
|
||||
self.save()
|
||||
|
||||
if self.unrar_exe_path == "":
|
||||
if platform.system() != "Windows":
|
||||
# see if it's in the path of unix user
|
||||
if utils.which("unrar") is not None:
|
||||
self.unrar_exe_path = utils.which("unrar")
|
||||
if self.unrar_exe_path != "":
|
||||
if self.rar_exe_path != "":
|
||||
# make sure rar program is now in the path for the rar class
|
||||
utils.addtopath(os.path.dirname(self.rar_exe_path))
|
||||
|
||||
if self.haveOwnUnrarLib():
|
||||
# We have a 'personal' copy of the unrar lib in the basedir, so
|
||||
# don't search and change the setting
|
||||
# NOTE: a manual edit of the settings file overrides this below
|
||||
os.environ["UNRAR_LIB_PATH"] = self.defaultLibunrarPath()
|
||||
|
||||
elif self.unrar_lib_path == "":
|
||||
# Priority is for unrar lib search is:
|
||||
# 1. explicit setting in settings file
|
||||
# 2. UNRAR_LIB_PATH in environment
|
||||
# 3. check some likely platform specific places
|
||||
if "UNRAR_LIB_PATH" in os.environ:
|
||||
self.unrar_lib_path = os.environ["UNRAR_LIB_PATH"]
|
||||
else:
|
||||
# look in some platform specific places:
|
||||
if platform.system() == "Windows":
|
||||
# Default location for the RARLab DLL installer
|
||||
if (platform.architecture()[0] == '64bit' and
|
||||
os.path.exists("C:\\Program Files (x86)\\UnrarDLL\\x64\\UnRAR64.dll")
|
||||
):
|
||||
self.unrar_lib_path = "C:\\Program Files (x86)\\UnrarDLL\\x64\\UnRAR64.dll"
|
||||
elif (platform.architecture()[0] == '32bit' and
|
||||
os.path.exists("C:\\Program Files\\UnrarDLL\\UnRAR.dll")
|
||||
):
|
||||
self.unrar_lib_path = "C:\\Program Files\\UnrarDLL\\UnRAR.dll"
|
||||
elif platform.system() == "Darwin":
|
||||
# Look for the brew unrar library
|
||||
if os.path.exists("/usr/local/lib/libunrar.dylib"):
|
||||
self.unrar_lib_path = "/usr/local/lib/libunrar.dylib"
|
||||
elif platform.system() == "Linux":
|
||||
if os.path.exists("/usr/local/lib/libunrar.so"):
|
||||
self.unrar_lib_path = "/usr/local/lib/libunrar.so"
|
||||
elif os.path.exists("/usr/lib/libunrar.so"):
|
||||
self.unrar_lib_path = "/usr/lib/libunrar.so"
|
||||
|
||||
if self.unrar_lib_path != "":
|
||||
self.save()
|
||||
|
||||
# make sure unrar/rar programs are now in the path for the UnRAR class to
|
||||
# use
|
||||
utils.addtopath(os.path.dirname(self.unrar_exe_path))
|
||||
utils.addtopath(os.path.dirname(self.rar_exe_path))
|
||||
|
||||
if self.unrar_lib_path != "":
|
||||
# This needs to occur before the unrar module is loaded for the first time
|
||||
os.environ["UNRAR_LIB_PATH"] = self.unrar_lib_path
|
||||
|
||||
def reset(self):
|
||||
os.unlink(self.settings_file)
|
||||
@ -193,7 +228,8 @@ class ComicTaggerSettings:
|
||||
readline_generator(codecs.open(self.settings_file, "r", "utf8")))
|
||||
|
||||
self.rar_exe_path = self.config.get('settings', 'rar_exe_path')
|
||||
self.unrar_exe_path = self.config.get('settings', 'unrar_exe_path')
|
||||
if self.config.has_option('settings', 'unrar_lib_path'):
|
||||
self.unrar_lib_path = self.config.get('settings', 'unrar_lib_path')
|
||||
if self.config.has_option('settings', 'check_for_new_version'):
|
||||
self.check_for_new_version = self.config.getboolean(
|
||||
'settings', 'check_for_new_version')
|
||||
@ -353,7 +389,7 @@ class ComicTaggerSettings:
|
||||
self.config.set(
|
||||
'settings', 'check_for_new_version', self.check_for_new_version)
|
||||
self.config.set('settings', 'rar_exe_path', self.rar_exe_path)
|
||||
self.config.set('settings', 'unrar_exe_path', self.unrar_exe_path)
|
||||
self.config.set('settings', 'unrar_lib_path', self.unrar_lib_path)
|
||||
self.config.set('settings', 'send_usage_stats', self.send_usage_stats)
|
||||
|
||||
if not self.config.has_section('auto'):
|
||||
|
@ -16,42 +16,71 @@
|
||||
|
||||
import platform
|
||||
import os
|
||||
import sys
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from comicvinecacher import ComicVineCacher
|
||||
from comicvinetalker import ComicVineTalker
|
||||
from imagefetcher import ImageFetcher
|
||||
import utils
|
||||
from .settings import ComicTaggerSettings
|
||||
from .comicvinecacher import ComicVineCacher
|
||||
from .comicvinetalker import ComicVineTalker
|
||||
from .imagefetcher import ImageFetcher
|
||||
from . import utils
|
||||
|
||||
|
||||
windowsRarHelp = """
|
||||
<html><head/><body><p>In order to write to CBR/RAR archives,
|
||||
<html><head/><body><p>To write to CBR/RAR archives,
|
||||
you will need to have the tools from
|
||||
<a href="http://www.win-rar.com/download.html">
|
||||
<span style=" text-decoration: underline; color:#0000ff;">WinRAR</span>
|
||||
</a> installed. </p></body></html>
|
||||
<span style=" text-decoration: underline; color:#0000ff;">
|
||||
<a href="http://www.win-rar.com/download.html">WINRar</a></span>
|
||||
installed. (ComicTagger only uses the command-line rar tool,
|
||||
which is free to use.)</p></body></html>
|
||||
"""
|
||||
|
||||
linuxRarHelp = """
|
||||
<html><head/><body><p>In order to read/write to CBR/RAR archives,
|
||||
you will need to have the shareware tools from WinRar installed.
|
||||
Your package manager should have unrar, and probably rar.
|
||||
If not, download them <a href="http://www.win-rar.com/download.html">
|
||||
<span style=" text-decoration: underline; color:#0000ff;">here</span>
|
||||
</a>, and install in your path. </p></body></html>
|
||||
<html><head/><body><p>To write to CBR/RAR archives,
|
||||
you will need to have the shareware rar tool from RARLab installed.
|
||||
Your package manager should have rar (e.g. "apt-get install rar"). If not, download it
|
||||
<span style=" text-decoration: underline; color:#0000ff;">
|
||||
<a href="https://www.rarlab.com/download.htm">here</a></span>,
|
||||
and install in your path. </p></body></html>
|
||||
"""
|
||||
|
||||
macRarHelp = """
|
||||
<html><head/><body><p>In order to read/write to CBR/RAR archives,
|
||||
you will need the shareware tools from
|
||||
<a href="http://www.win-rar.com/download.html">
|
||||
<span style=" text-decoration: underline; color:#0000ff;">WinRAR</span>
|
||||
</a>. </p></body></html>
|
||||
<html><head/><body><p>To write to CBR/RAR archives,
|
||||
you will need the rar tool. The easiest way to get this is
|
||||
to install <span style=" text-decoration: underline; color:#0000ff;">
|
||||
<a href="https://brew.sh/">homebrew</a></span>.
|
||||
</p>Once homebrew is installed, run: <b>brew install caskroom/cask/rar</b></body></html>
|
||||
"""
|
||||
|
||||
windowsUnrarHelp = """
|
||||
<html><head/><body><p>To read CBR/RAR archives,
|
||||
you will need to have the unrar DLL from
|
||||
<span style=" text-decoration: underline; color:#0000ff;">
|
||||
<a href="https://www.rarlab.com/rar_add.htm">
|
||||
RARLab</a></span> installed. </p></body></html>
|
||||
"""
|
||||
|
||||
linuxUnrarHelp = """
|
||||
<html><head/><body><p>To read CBR/RAR archives,
|
||||
you will need to have the unrar library from RARLab installed.
|
||||
Look <span style=" text-decoration: underline; color:#0000ff;">
|
||||
<a href="https://github.com/beville/libunrar-binaries/releases">here</a></span>
|
||||
for pre-compiled binaries, or <span style=" text-decoration: underline; color:#0000ff;">
|
||||
<a href="https://www.rarlab.com/rar_add.htm">here</a></span>
|
||||
for the UnRAR source (which is easy to compile on Linux). </p></body></html>
|
||||
"""
|
||||
|
||||
macUnrarHelp = """
|
||||
<html><head/><body><p>To read CBR/RAR archives,
|
||||
you will need the unrar library. The easiest way to get this is
|
||||
to install <span style=" text-decoration: underline; color:#0000ff;">
|
||||
<a href="https://brew.sh/homebrew">homebrew</a></span>.
|
||||
</p>Once homebrew is installed, run: <b>brew install unrar</b></body></html>
|
||||
"""
|
||||
|
||||
|
||||
class SettingsWindow(QtGui.QDialog):
|
||||
class SettingsWindow(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent, settings):
|
||||
super(SettingsWindow, self).__init__(parent)
|
||||
@ -63,18 +92,28 @@ class SettingsWindow(QtGui.QDialog):
|
||||
|
||||
self.settings = settings
|
||||
self.name = "Settings"
|
||||
|
||||
self.priorUnrarLibPath = self.settings.unrar_lib_path
|
||||
|
||||
if self.settings.haveOwnUnrarLib():
|
||||
# We have our own unrarlib, so no need for this GUI
|
||||
self.grpBoxUnrar.hide()
|
||||
|
||||
if platform.system() == "Windows":
|
||||
self.lblUnrar.hide()
|
||||
self.leUnrarExePath.hide()
|
||||
self.btnBrowseUnrar.hide()
|
||||
self.lblRarHelp.setText(windowsRarHelp)
|
||||
self.lblUnrarHelp.setText(windowsUnrarHelp)
|
||||
|
||||
elif platform.system() == "Linux":
|
||||
self.lblRarHelp.setText(linuxRarHelp)
|
||||
self.lblUnrarHelp.setText(linuxUnrarHelp)
|
||||
|
||||
elif platform.system() == "Darwin":
|
||||
# Mac file dialog hides "/usr" and others, so allow user to type
|
||||
self.leUnrarLibPath.setReadOnly(False)
|
||||
self.leRarExePath.setReadOnly(False)
|
||||
|
||||
self.lblRarHelp.setText(macRarHelp)
|
||||
self.lblUnrarHelp.setText(macUnrarHelp)
|
||||
self.name = "Preferences"
|
||||
|
||||
self.setWindowTitle("ComicTagger " + self.name)
|
||||
@ -118,7 +157,7 @@ class SettingsWindow(QtGui.QDialog):
|
||||
|
||||
# Copy values from settings to form
|
||||
self.leRarExePath.setText(self.settings.rar_exe_path)
|
||||
self.leUnrarExePath.setText(self.settings.unrar_exe_path)
|
||||
self.leUnrarLibPath.setText(self.settings.unrar_lib_path)
|
||||
self.leNameLengthDeltaThresh.setText(
|
||||
str(self.settings.id_length_delta_thresh))
|
||||
self.tePublisherBlacklist.setPlainText(
|
||||
@ -171,12 +210,19 @@ class SettingsWindow(QtGui.QDialog):
|
||||
|
||||
# Copy values from form to settings and save
|
||||
self.settings.rar_exe_path = str(self.leRarExePath.text())
|
||||
self.settings.unrar_exe_path = str(self.leUnrarExePath.text())
|
||||
|
||||
# Don't accept the form info if we have our own unrar lib
|
||||
if not self.settings.haveOwnUnrarLib():
|
||||
self.settings.unrar_lib_path = str(self.leUnrarLibPath.text())
|
||||
|
||||
# make sure unrar/rar program is now in the path for the UnRAR class
|
||||
utils.addtopath(os.path.dirname(self.settings.unrar_exe_path))
|
||||
utils.addtopath(os.path.dirname(self.settings.rar_exe_path))
|
||||
# make sure rar program is now in the path for the rar class
|
||||
if self.settings.rar_exe_path:
|
||||
utils.addtopath(os.path.dirname(self.settings.rar_exe_path))
|
||||
|
||||
if self.settings.unrar_lib_path:
|
||||
os.environ["UNRAR_LIB_PATH"] = self.settings.unrar_lib_path
|
||||
# This doesn't do anything... we need to restart!
|
||||
|
||||
if not str(self.leNameLengthDeltaThresh.text()).isdigit():
|
||||
self.leNameLengthDeltaThresh.setText("0")
|
||||
|
||||
@ -195,8 +241,8 @@ class SettingsWindow(QtGui.QDialog):
|
||||
self.settings.use_series_start_as_volume = self.cbxUseSeriesStartAsVolume.isChecked()
|
||||
self.settings.clear_form_before_populating_from_cv = self.cbxClearFormBeforePopulating.isChecked()
|
||||
self.settings.remove_html_tables = self.cbxRemoveHtmlTables.isChecked()
|
||||
self.settings.cv_api_key = unicode(self.leKey.text())
|
||||
ComicVineTalker.api_key = self.settings.cv_api_key
|
||||
self.settings.cv_api_key = str(self.leKey.text())
|
||||
ComicVineTalker.api_key = self.settings.cv_api_key.strip()
|
||||
self.settings.assume_lone_credit_is_primary = self.cbxAssumeLoneCreditIsPrimary.isChecked()
|
||||
self.settings.copy_characters_to_tags = self.cbxCopyCharactersToTags.isChecked()
|
||||
self.settings.copy_teams_to_tags = self.cbxCopyTeamsToTags.isChecked()
|
||||
@ -214,32 +260,37 @@ class SettingsWindow(QtGui.QDialog):
|
||||
self.settings.rename_extension_based_on_archive = self.cbxChangeExtension.isChecked()
|
||||
|
||||
self.settings.save()
|
||||
QtGui.QDialog.accept(self)
|
||||
QtWidgets.QDialog.accept(self)
|
||||
|
||||
if self.priorUnrarLibPath != self.settings.unrar_lib_path:
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, "UnRar Library Change",
|
||||
"ComicTagger will need to be restarted for changes to take effect.")
|
||||
|
||||
def selectRar(self):
|
||||
self.selectFile(self.leRarExePath, "RAR")
|
||||
|
||||
def selectUnrar(self):
|
||||
self.selectFile(self.leUnrarExePath, "UnRAR")
|
||||
self.selectFile(self.leUnrarLibPath, "UnRAR")
|
||||
|
||||
def clearCache(self):
|
||||
ImageFetcher().clearCache()
|
||||
ComicVineCacher().clearCache()
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.name, "Cache has been cleared.")
|
||||
|
||||
def testAPIKey(self):
|
||||
if ComicVineTalker().testKey(unicode(self.leKey.text())):
|
||||
QtGui.QMessageBox.information(
|
||||
if ComicVineTalker().testKey(str(self.leKey.text()).strip()):
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, "API Key Test", "Key is valid!")
|
||||
else:
|
||||
QtGui.QMessageBox.warning(
|
||||
QtWidgets.QMessageBox.warning(
|
||||
self, "API Key Test", "Key is NOT valid.")
|
||||
|
||||
def resetSettings(self):
|
||||
self.settings.reset()
|
||||
self.settingsToForm()
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self,
|
||||
self.name,
|
||||
self.name +
|
||||
@ -247,14 +298,14 @@ class SettingsWindow(QtGui.QDialog):
|
||||
|
||||
def selectFile(self, control, name):
|
||||
|
||||
dialog = QtGui.QFileDialog(self)
|
||||
dialog.setFileMode(QtGui.QFileDialog.ExistingFile)
|
||||
dialog = QtWidgets.QFileDialog(self)
|
||||
dialog.setFileMode(QtWidgets.QFileDialog.ExistingFile)
|
||||
|
||||
if platform.system() == "Windows":
|
||||
if name == "RAR":
|
||||
filter = self.tr("Rar Program (Rar.exe)")
|
||||
else:
|
||||
filter = self.tr("Programs (*.exe)")
|
||||
filter = self.tr("Libraries (*.dll)")
|
||||
dialog.setNameFilter(filter)
|
||||
else:
|
||||
# QtCore.QDir.Executable | QtCore.QDir.Files)
|
||||
@ -262,8 +313,11 @@ class SettingsWindow(QtGui.QDialog):
|
||||
pass
|
||||
|
||||
dialog.setDirectory(os.path.dirname(str(control.text())))
|
||||
dialog.setWindowTitle("Find " + name + " program")
|
||||
|
||||
if name == "RAR":
|
||||
dialog.setWindowTitle("Find " + name + " program")
|
||||
else:
|
||||
dialog.setWindowTitle("Find " + name + " library")
|
||||
|
||||
if (dialog.exec_()):
|
||||
fileList = dialog.selectedFiles()
|
||||
control.setText(str(fileList[0]))
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
import sys
|
||||
import locale
|
||||
import functools
|
||||
import platform
|
||||
import os
|
||||
import pprint
|
||||
@ -26,38 +27,38 @@ import re
|
||||
import pickle
|
||||
#import signal
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt4 import QtNetwork
|
||||
from PyQt4.QtCore import QString, QUrl
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
from PyQt5 import QtNetwork
|
||||
from PyQt5.QtCore import QUrl
|
||||
|
||||
#from comicarchive import ComicArchive
|
||||
#from pageloader import PageLoader
|
||||
from volumeselectionwindow import VolumeSelectionWindow
|
||||
from comicarchive import MetaDataStyle
|
||||
from comicinfoxml import ComicInfoXml
|
||||
from genericmetadata import GenericMetadata
|
||||
from comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from crediteditorwindow import CreditEditorWindow
|
||||
from settingswindow import SettingsWindow
|
||||
from settings import ComicTaggerSettings
|
||||
from pagebrowser import PageBrowserWindow
|
||||
from filenameparser import FileNameParser
|
||||
from logwindow import LogWindow
|
||||
from optionalmsgdialog import OptionalMessageDialog
|
||||
from pagelisteditor import PageListEditor
|
||||
from fileselectionlist import FileSelectionList
|
||||
from cbltransformer import CBLTransformer
|
||||
from renamewindow import RenameWindow
|
||||
from exportwindow import ExportWindow, ExportConflictOpts
|
||||
from issueidentifier import IssueIdentifier
|
||||
from autotagstartwindow import AutoTagStartWindow
|
||||
from autotagprogresswindow import AutoTagProgressWindow
|
||||
from autotagmatchwindow import AutoTagMatchWindow
|
||||
from coverimagewidget import CoverImageWidget
|
||||
from versionchecker import VersionChecker
|
||||
from .volumeselectionwindow import VolumeSelectionWindow
|
||||
from .comicarchive import MetaDataStyle
|
||||
from .comicinfoxml import ComicInfoXml
|
||||
from .genericmetadata import GenericMetadata
|
||||
from .comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from .crediteditorwindow import CreditEditorWindow
|
||||
from .settingswindow import SettingsWindow
|
||||
from .settings import ComicTaggerSettings
|
||||
from .pagebrowser import PageBrowserWindow
|
||||
from .filenameparser import FileNameParser
|
||||
from .logwindow import LogWindow
|
||||
from .optionalmsgdialog import OptionalMessageDialog
|
||||
from .pagelisteditor import PageListEditor
|
||||
from .fileselectionlist import FileSelectionList
|
||||
from .cbltransformer import CBLTransformer
|
||||
from .renamewindow import RenameWindow
|
||||
from .exportwindow import ExportWindow, ExportConflictOpts
|
||||
from .issueidentifier import IssueIdentifier
|
||||
from .autotagstartwindow import AutoTagStartWindow
|
||||
from .autotagprogresswindow import AutoTagProgressWindow
|
||||
from .autotagmatchwindow import AutoTagMatchWindow
|
||||
from .coverimagewidget import CoverImageWidget
|
||||
from .versionchecker import VersionChecker
|
||||
from comictaggerlib.ui.qtutils import reduceWidgetFontSize, centerWindowOnParent
|
||||
import utils
|
||||
import ctversion
|
||||
from . import utils
|
||||
from . import ctversion
|
||||
|
||||
|
||||
class OnlineMatchResults():
|
||||
@ -78,14 +79,14 @@ class MultipleMatch():
|
||||
self.matches = match_list
|
||||
|
||||
|
||||
class TaggerWindow(QtGui.QMainWindow):
|
||||
class TaggerWindow(QtWidgets.QMainWindow):
|
||||
|
||||
appName = "ComicTagger"
|
||||
version = ctversion.version
|
||||
|
||||
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
|
||||
|
||||
@ -95,14 +96,14 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
socket.connectToServer(settings.install_id)
|
||||
alive = socket.waitForConnected(3000)
|
||||
if alive:
|
||||
print(
|
||||
print((
|
||||
"Another application with key [{}] is already running".format(
|
||||
settings.install_id))
|
||||
settings.install_id)))
|
||||
# send file list to other instance
|
||||
if len(file_list) > 0:
|
||||
socket.write(pickle.dumps(file_list))
|
||||
if not socket.waitForBytesWritten(3000):
|
||||
print(socket.errorString().toLatin1())
|
||||
print((socket.errorString().toLatin1()))
|
||||
socket.disconnectFromServer()
|
||||
sys.exit()
|
||||
else:
|
||||
@ -118,28 +119,28 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
self.socketServer.removeServer(settings.install_id)
|
||||
ok = self.socketServer.listen(settings.install_id)
|
||||
if not ok:
|
||||
print(
|
||||
print((
|
||||
"Cannot start local socket with key [{}]. Reason: %s ".format(
|
||||
settings.install_id,
|
||||
self.socketServer.errorString()))
|
||||
self.socketServer.errorString())))
|
||||
sys.exit()
|
||||
#print("Registering as single instance with key [{}]".format(settings.install_id))
|
||||
#----------------------------------
|
||||
|
||||
self.archiveCoverWidget = CoverImageWidget(
|
||||
self.coverImageContainer, CoverImageWidget.ArchiveMode)
|
||||
gridlayout = QtGui.QGridLayout(self.coverImageContainer)
|
||||
gridlayout = QtWidgets.QGridLayout(self.coverImageContainer)
|
||||
gridlayout.addWidget(self.archiveCoverWidget)
|
||||
gridlayout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.pageListEditor = PageListEditor(self.tabPages)
|
||||
gridlayout = QtGui.QGridLayout(self.tabPages)
|
||||
gridlayout = QtWidgets.QGridLayout(self.tabPages)
|
||||
gridlayout.addWidget(self.pageListEditor)
|
||||
|
||||
#---------------------------
|
||||
self.fileSelectionList = FileSelectionList(
|
||||
self.widgetListHolder, self.settings)
|
||||
gridlayout = QtGui.QGridLayout(self.widgetListHolder)
|
||||
gridlayout = QtWidgets.QGridLayout(self.widgetListHolder)
|
||||
gridlayout.addWidget(self.fileSelectionList)
|
||||
|
||||
self.fileSelectionList.selectionChanged.connect(
|
||||
@ -153,7 +154,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
# walk through all the lablels in the main form, and make them
|
||||
# a smidge smaller
|
||||
for child in self.scrollAreaWidgetContents.children():
|
||||
if (isinstance(child, QtGui.QLabel)):
|
||||
if (isinstance(child, QtWidgets.QLabel)):
|
||||
f = child.font()
|
||||
if f.pointSize() > 10:
|
||||
f.setPointSize(f.pointSize() - 2)
|
||||
@ -264,20 +265,6 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
)
|
||||
self.settings.show_disclaimer = not checked
|
||||
|
||||
if self.settings.ask_about_usage_stats:
|
||||
reply = QtGui.QMessageBox.question(
|
||||
self,
|
||||
self.tr("Anonymous Stats"),
|
||||
self.tr(
|
||||
"Is it okay if ComicTagger occasionally sends some anonymous usage statistics? Nothing nefarious, "
|
||||
"just trying to get a better idea of how the app is being used.\n\nThanks for your support!"),
|
||||
QtGui.QMessageBox.Yes | QtGui.QMessageBox.Default,
|
||||
QtGui.QMessageBox.No)
|
||||
|
||||
if reply == QtGui.QMessageBox.Yes:
|
||||
self.settings.send_usage_stats = True
|
||||
self.settings.ask_about_usage_stats = False
|
||||
|
||||
if self.settings.check_for_new_version:
|
||||
#self.checkLatestVersionOnline()
|
||||
pass
|
||||
@ -304,6 +291,9 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
|
||||
def updateAppTitle(self):
|
||||
|
||||
self.setWindowIcon(
|
||||
QtGui.QIcon(ComicTaggerSettings.getGraphic('app.png')))
|
||||
|
||||
if self.comic_archive is None:
|
||||
self.setWindowTitle(self.appName)
|
||||
else:
|
||||
@ -456,7 +446,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
rar_count += 1
|
||||
|
||||
if rar_count == 0:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self,
|
||||
self.tr("Export as Zip Archive"),
|
||||
self.tr("No RAR archives selected!"))
|
||||
@ -478,11 +468,13 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
if not dlg.exec_():
|
||||
return
|
||||
|
||||
progdialog = QtGui.QProgressDialog(
|
||||
progdialog = QtWidgets.QProgressDialog(
|
||||
"", "Cancel", 0, rar_count, self)
|
||||
progdialog.setWindowTitle("Exporting as ZIP")
|
||||
progdialog.setWindowModality(QtCore.Qt.ApplicationModal)
|
||||
progdialog.show()
|
||||
progdialog.setMinimumDuration(300)
|
||||
centerWindowOnParent(progdialog)
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
prog_idx = 0
|
||||
|
||||
new_archives_to_add = []
|
||||
@ -496,8 +488,8 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
if progdialog.wasCanceled():
|
||||
break
|
||||
progdialog.setValue(prog_idx)
|
||||
prog_idx += 1
|
||||
progdialog.setValue(prog_idx)
|
||||
progdialog.setLabelText(ca.path)
|
||||
centerWindowOnParent(progdialog)
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
@ -528,23 +520,23 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
if os.path.lexists(export_name):
|
||||
os.remove(export_name)
|
||||
|
||||
progdialog.close()
|
||||
|
||||
progdialog.hide()
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
self.fileSelectionList.addPathList(new_archives_to_add)
|
||||
self.fileSelectionList.removeArchiveList(archives_to_remove)
|
||||
|
||||
summary = u"Successfully created {0} Zip archive(s).".format(
|
||||
summary = "Successfully created {0} Zip archive(s).".format(
|
||||
success_count)
|
||||
if len(skipped_list) > 0:
|
||||
summary += u"\n\nThe following {0} RAR archive(s) were skipped due to file name conflicts:\n".format(
|
||||
summary += "\n\nThe following {0} RAR archive(s) were skipped due to file name conflicts:\n".format(
|
||||
len(skipped_list))
|
||||
for f in skipped_list:
|
||||
summary += u"\t{0}\n".format(f)
|
||||
summary += "\t{0}\n".format(f)
|
||||
if len(failed_list) > 0:
|
||||
summary += u"\n\nThe following {0} RAR archive(s) failed to export due to read/write errors:\n".format(
|
||||
summary += "\n\nThe following {0} RAR archive(s) failed to export due to read/write errors:\n".format(
|
||||
len(failed_list))
|
||||
for f in failed_list:
|
||||
summary += u"\t{0}\n".format(f)
|
||||
summary += "\t{0}\n".format(f)
|
||||
|
||||
dlg = LogWindow(self)
|
||||
dlg.setText(summary)
|
||||
@ -553,12 +545,12 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
|
||||
def aboutApp(self):
|
||||
|
||||
website = "http://code.google.com/p/comictagger"
|
||||
website = "https://github.com/davide-romanini/comictagger"
|
||||
email = "comictagger@gmail.com"
|
||||
license_link = "http://www.apache.org/licenses/LICENSE-2.0"
|
||||
license_name = "Apache License 2.0"
|
||||
|
||||
msgBox = QtGui.QMessageBox()
|
||||
msgBox = QtWidgets.QMessageBox()
|
||||
msgBox.setWindowTitle(self.tr("About " + self.appName))
|
||||
msgBox.setTextFormat(QtCore.Qt.RichText)
|
||||
msgBox.setIconPixmap(
|
||||
@ -568,12 +560,12 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
" v" +
|
||||
self.version +
|
||||
"<br>" +
|
||||
"©2014 Anthony Beville<br><br>" +
|
||||
"©2014-2018 ComicTagger Devs<br><br>" +
|
||||
"<a href='{0}'>{0}</a><br><br>".format(website) +
|
||||
"<a href='mailto:{0}'>{0}</a><br><br>".format(email) +
|
||||
"License: <a href='{0}'>{1}</a>".format(license_link, license_name))
|
||||
|
||||
msgBox.setStandardButtons(QtGui.QMessageBox.Ok)
|
||||
msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
msgBox.exec_()
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
@ -723,11 +715,11 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
|
||||
tag_info = ""
|
||||
if ca.hasCIX():
|
||||
tag_info = u"• ComicRack tags"
|
||||
tag_info = "• ComicRack tags"
|
||||
if ca.hasCBI():
|
||||
if tag_info != "":
|
||||
tag_info += "\n"
|
||||
tag_info += u"• ComicBookLover tags"
|
||||
tag_info += "• ComicBookLover tags"
|
||||
|
||||
self.lblTagList.setText(tag_info)
|
||||
|
||||
@ -749,13 +741,13 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
|
||||
def connectChildDirtyFlagSignals(self, widget):
|
||||
|
||||
if (isinstance(widget, QtGui.QLineEdit)):
|
||||
if (isinstance(widget, QtWidgets.QLineEdit)):
|
||||
widget.textEdited.connect(self.setDirtyFlag)
|
||||
if (isinstance(widget, QtGui.QTextEdit)):
|
||||
if (isinstance(widget, QtWidgets.QTextEdit)):
|
||||
widget.textChanged.connect(self.setDirtyFlag)
|
||||
if (isinstance(widget, QtGui.QComboBox)):
|
||||
if (isinstance(widget, QtWidgets.QComboBox)):
|
||||
widget.currentIndexChanged.connect(self.setDirtyFlag)
|
||||
if (isinstance(widget, QtGui.QCheckBox)):
|
||||
if (isinstance(widget, QtWidgets.QCheckBox)):
|
||||
widget.stateChanged.connect(self.setDirtyFlag)
|
||||
|
||||
# recursive call on chillun
|
||||
@ -781,14 +773,14 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
|
||||
def clearChildren(self, widget):
|
||||
|
||||
if (isinstance(widget, QtGui.QLineEdit) or
|
||||
isinstance(widget, QtGui.QTextEdit)):
|
||||
if (isinstance(widget, QtWidgets.QLineEdit) or
|
||||
isinstance(widget, QtWidgets.QTextEdit)):
|
||||
widget.setText("")
|
||||
if (isinstance(widget, QtGui.QComboBox)):
|
||||
if (isinstance(widget, QtWidgets.QComboBox)):
|
||||
widget.setCurrentIndex(0)
|
||||
if (isinstance(widget, QtGui.QCheckBox)):
|
||||
if (isinstance(widget, QtWidgets.QCheckBox)):
|
||||
widget.setChecked(False)
|
||||
if (isinstance(widget, QtGui.QTableWidget)):
|
||||
if (isinstance(widget, QtWidgets.QTableWidget)):
|
||||
while widget.rowCount() > 0:
|
||||
widget.removeRow(0)
|
||||
|
||||
@ -802,7 +794,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
# helper func
|
||||
def assignText(field, value):
|
||||
if value is not None:
|
||||
field.setText(unicode(value))
|
||||
field.setText(str(value))
|
||||
|
||||
md = self.metadata
|
||||
|
||||
@ -893,18 +885,18 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
self.twCredits.insertRow(row)
|
||||
|
||||
item_text = role
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
self.twCredits.setItem(row, 1, item)
|
||||
|
||||
item_text = name
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twCredits.setItem(row, 2, item)
|
||||
|
||||
item = QtGui.QTableWidgetItem("")
|
||||
item = QtWidgets.QTableWidgetItem("")
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twCredits.setItem(row, 0, item)
|
||||
self.updateCreditPrimaryFlag(row, primary_flag)
|
||||
@ -923,7 +915,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
|
||||
# helper func
|
||||
def xlate(data, type_str):
|
||||
s = u"{0}".format(data).strip()
|
||||
s = "{0}".format(data).strip()
|
||||
if s == "":
|
||||
return None
|
||||
elif type_str == "str":
|
||||
@ -964,12 +956,10 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
md.format = xlate(self.cbFormat.currentText(), "str")
|
||||
md.country = xlate(self.cbCountry.currentText(), "str")
|
||||
|
||||
langiso = self.cbLanguage.itemData(
|
||||
self.cbLanguage.currentIndex()).toString()
|
||||
langiso = self.cbLanguage.itemData(self.cbLanguage.currentIndex())
|
||||
md.language = xlate(langiso, "str")
|
||||
|
||||
manga_code = self.cbManga.itemData(
|
||||
self.cbManga.currentIndex()).toString()
|
||||
manga_code = self.cbManga.itemData(self.cbManga.currentIndex())
|
||||
md.manga = xlate(manga_code, "str")
|
||||
|
||||
# Make a list from the coma delimited tags string
|
||||
@ -989,8 +979,8 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
md.credits = list()
|
||||
row = 0
|
||||
while row < self.twCredits.rowCount():
|
||||
role = u"{0}".format(self.twCredits.item(row, 1).text())
|
||||
name = u"{0}".format(self.twCredits.item(row, 2).text())
|
||||
role = "{0}".format(self.twCredits.item(row, 1).text())
|
||||
name = "{0}".format(self.twCredits.item(row, 2).text())
|
||||
primary_flag = self.twCredits.item(row, 0).text() != ""
|
||||
|
||||
md.addCredit(name, role, bool(primary_flag))
|
||||
@ -1013,15 +1003,15 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
|
||||
def selectFile(self, folder_mode=False):
|
||||
|
||||
dialog = QtGui.QFileDialog(self)
|
||||
dialog = QtWidgets.QFileDialog(self)
|
||||
if folder_mode:
|
||||
dialog.setFileMode(QtGui.QFileDialog.Directory)
|
||||
dialog.setFileMode(QtWidgets.QFileDialog.Directory)
|
||||
else:
|
||||
dialog.setFileMode(QtGui.QFileDialog.ExistingFiles)
|
||||
dialog.setFileMode(QtWidgets.QFileDialog.ExistingFiles)
|
||||
|
||||
if self.settings.last_opened_folder is not None:
|
||||
dialog.setDirectory(self.settings.last_opened_folder)
|
||||
# dialog.setFileMode(QtGui.QFileDialog.Directory)
|
||||
# dialog.setFileMode(QtWidgets.QFileDialog.Directory)
|
||||
|
||||
if not folder_mode:
|
||||
if platform.system() != "Windows" and utils.which("unrar") is None:
|
||||
@ -1042,7 +1032,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
|
||||
def autoIdentifySearch(self):
|
||||
if self.comic_archive is None:
|
||||
QtGui.QMessageBox.warning(
|
||||
QtWidgets.QMessageBox.warning(
|
||||
self,
|
||||
self.tr("Automatic Identify Search"),
|
||||
self.tr("You need to load a comic first!"))
|
||||
@ -1052,19 +1042,19 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
|
||||
def queryOnline(self, autoselect=False):
|
||||
|
||||
issue_number = unicode(self.leIssueNum.text()).strip()
|
||||
issue_number = str(self.leIssueNum.text()).strip()
|
||||
|
||||
if autoselect and issue_number == "":
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self,
|
||||
"Automatic Identify Search",
|
||||
"Can't auto-identify without an issue number (yet!)")
|
||||
return
|
||||
|
||||
if unicode(self.leSeries.text()).strip() != "":
|
||||
series_name = unicode(self.leSeries.text()).strip()
|
||||
if str(self.leSeries.text()).strip() != "":
|
||||
series_name = str(self.leSeries.text()).strip()
|
||||
else:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self,
|
||||
self.tr("Online Search"),
|
||||
self.tr("Need to enter a series name to search."))
|
||||
@ -1098,7 +1088,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
|
||||
if selector.result():
|
||||
# we should now have a volume ID
|
||||
QtGui.QApplication.setOverrideCursor(
|
||||
QtWidgets.QApplication.setOverrideCursor(
|
||||
QtGui.QCursor(QtCore.Qt.WaitCursor))
|
||||
|
||||
# copy the form onto metadata object
|
||||
@ -1109,19 +1099,19 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
new_metadata = comicVine.fetchIssueData(
|
||||
selector.volume_id, selector.issue_number, self.settings)
|
||||
except ComicVineTalkerException as e:
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
if e.code == ComicVineTalkerException.RateLimit:
|
||||
QtGui.QMessageBox.critical(
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
self.tr("Comic Vine Error"),
|
||||
ComicVineTalker.getRateLimitMessage())
|
||||
else:
|
||||
QtGui.QMessageBox.critical(
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
self.tr("Network Issue"),
|
||||
self.tr("Could not connect to Comic Vine to get issue details.!"))
|
||||
else:
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
if new_metadata is not None:
|
||||
|
||||
if self.settings.apply_cbl_transform_on_cv_import:
|
||||
@ -1135,7 +1125,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
# Now push the new combined data into the edit controls
|
||||
self.metadataToForm()
|
||||
else:
|
||||
QtGui.QMessageBox.critical(
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self, self.tr("Search"), self.tr(
|
||||
"Could not find an issue {0} for that series".format(
|
||||
selector.issue_number)))
|
||||
@ -1143,7 +1133,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
def commitMetadata(self):
|
||||
|
||||
if (self.metadata is not None and self.comic_archive is not None):
|
||||
reply = QtGui.QMessageBox.question(
|
||||
reply = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
self.tr("Save Tags"),
|
||||
self.tr(
|
||||
@ -1151,11 +1141,11 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
MetaDataStyle.name[
|
||||
self.save_data_style] +
|
||||
" tags to this archive?"),
|
||||
QtGui.QMessageBox.Yes,
|
||||
QtGui.QMessageBox.No)
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No)
|
||||
|
||||
if reply == QtGui.QMessageBox.Yes:
|
||||
QtGui.QApplication.setOverrideCursor(
|
||||
if reply == QtWidgets.QMessageBox.Yes:
|
||||
QtWidgets.QApplication.setOverrideCursor(
|
||||
QtGui.QCursor(QtCore.Qt.WaitCursor))
|
||||
self.formToMetadata()
|
||||
|
||||
@ -1163,10 +1153,10 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
self.metadata, self.save_data_style)
|
||||
self.comic_archive.loadCache(
|
||||
[MetaDataStyle.CBI, MetaDataStyle.CIX])
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
|
||||
if not success:
|
||||
QtGui.QMessageBox.warning(
|
||||
QtWidgets.QMessageBox.warning(
|
||||
self,
|
||||
self.tr("Save failed"),
|
||||
self.tr("The tag save operation seemed to fail!"))
|
||||
@ -1174,18 +1164,18 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
self.clearDirtyFlag()
|
||||
self.updateInfoBox()
|
||||
self.updateMenus()
|
||||
#QtGui.QMessageBox.information(self, self.tr("Yeah!"), self.tr("File written."))
|
||||
#QtWidgets.QMessageBox.information(self, self.tr("Yeah!"), self.tr("File written."))
|
||||
self.fileSelectionList.updateCurrentRow()
|
||||
|
||||
else:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Whoops!"), self.tr("No data to commit!"))
|
||||
|
||||
def setLoadDataStyle(self, s):
|
||||
if self.dirtyFlagVerification(
|
||||
"Change Tag Read Style",
|
||||
"If you change read tag style now, data in the form will be lost. Are you sure?"):
|
||||
self.load_data_style, b = self.cbLoadDataStyle.itemData(s).toInt()
|
||||
self.load_data_style = self.cbLoadDataStyle.itemData(s)
|
||||
self.settings.last_selected_load_data_style = self.load_data_style
|
||||
self.updateMenus()
|
||||
if self.comic_archive is not None:
|
||||
@ -1198,16 +1188,21 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
self.setLoadDataStyle)
|
||||
|
||||
def setSaveDataStyle(self, s):
|
||||
self.save_data_style, b = self.cbSaveDataStyle.itemData(s).toInt()
|
||||
self.save_data_style = self.cbSaveDataStyle.itemData(s)
|
||||
|
||||
self.settings.last_selected_save_data_style = self.save_data_style
|
||||
self.updateStyleTweaks()
|
||||
self.updateMenus()
|
||||
|
||||
def updateCreditColors(self):
|
||||
#!!!ATB qt5 porting TODO
|
||||
#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)
|
||||
|
||||
cix_credits = ComicInfoXml().getParseableCredits()
|
||||
|
||||
@ -1218,19 +1213,19 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
if str(self.twCredits.item(r, 1).text()
|
||||
).lower() not in cix_credits:
|
||||
self.twCredits.item(
|
||||
r, 1).setBackgroundColor(inactive_color)
|
||||
r, 1).setBackground(inactive_brush)
|
||||
else:
|
||||
self.twCredits.item(r, 1).setBackgroundColor(active_color)
|
||||
self.twCredits.item(r, 1).setBackground(active_brush)
|
||||
# turn off entire primary column
|
||||
self.twCredits.item(r, 0).setBackgroundColor(inactive_color)
|
||||
self.twCredits.item(r, 0).setBackground(inactive_brush)
|
||||
r = r + 1
|
||||
|
||||
if self.save_data_style == MetaDataStyle.CBI:
|
||||
# loop over credit table, make all active color
|
||||
r = 0
|
||||
while r < self.twCredits.rowCount():
|
||||
self.twCredits.item(r, 0).setBackgroundColor(active_color)
|
||||
self.twCredits.item(r, 1).setBackgroundColor(active_color)
|
||||
self.twCredits.item(r, 0).setBackground(active_brush)
|
||||
self.twCredits.item(r, 1).setBackground(active_brush)
|
||||
r = r + 1
|
||||
|
||||
def updateStyleTweaks(self):
|
||||
@ -1259,18 +1254,18 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
if enable:
|
||||
item.setPalette(active_palette)
|
||||
item.setAutoFillBackground(False)
|
||||
if isinstance(item, QtGui.QCheckBox):
|
||||
if isinstance(item, QtWidgets.QCheckBox):
|
||||
item.setEnabled(True)
|
||||
elif isinstance(item, QtGui.QComboBox):
|
||||
elif isinstance(item, QtWidgets.QComboBox):
|
||||
item.setEnabled(True)
|
||||
else:
|
||||
item.setReadOnly(False)
|
||||
else:
|
||||
item.setAutoFillBackground(True)
|
||||
if isinstance(item, QtGui.QCheckBox):
|
||||
if isinstance(item, QtWidgets.QCheckBox):
|
||||
item.setPalette(inactive_palette2)
|
||||
item.setEnabled(False)
|
||||
elif isinstance(item, QtGui.QComboBox):
|
||||
elif isinstance(item, QtWidgets.QComboBox):
|
||||
item.setPalette(inactive_palette3)
|
||||
item.setEnabled(False)
|
||||
else:
|
||||
@ -1364,7 +1359,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
ok_to_mod = True
|
||||
if self.isDupeCredit(new_role, new_name):
|
||||
# delete the dupe credit from list
|
||||
reply = QtGui.QMessageBox.question(
|
||||
reply = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
self.tr("Duplicate Credit!"),
|
||||
self.tr(
|
||||
@ -1420,7 +1415,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
self.settings.last_main_window_width,
|
||||
self.settings.last_main_window_height)
|
||||
else:
|
||||
screen = QtGui.QDesktopWidget().screenGeometry()
|
||||
screen = QtWidgets.QDesktopWidget().screenGeometry()
|
||||
size = self.frameGeometry()
|
||||
self.move((screen.width() - size.width()) / 2,
|
||||
(screen.height() - size.height()) / 2)
|
||||
@ -1459,7 +1454,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
# Add the entries to the language combobox
|
||||
self.cbLanguage.addItem("", "")
|
||||
lang_dict = utils.getLanguageDict()
|
||||
for key in sorted(lang_dict, cmp=locale.strcoll, key=lang_dict.get):
|
||||
for key in sorted(lang_dict, key=functools.cmp_to_key(locale.strcoll)):
|
||||
self.cbLanguage.addItem(lang_dict[key], key)
|
||||
|
||||
# Add the entries to the manga combobox
|
||||
@ -1558,7 +1553,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
has_md_count += 1
|
||||
|
||||
if has_md_count == 0:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Remove Tags"), self.tr(
|
||||
"No archives with {0} tags selected!".format(
|
||||
MetaDataStyle.name[style])))
|
||||
@ -1570,22 +1565,26 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
return
|
||||
|
||||
if has_md_count != 0:
|
||||
reply = QtGui.QMessageBox.question(
|
||||
reply = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
self.tr("Remove Tags"),
|
||||
self.tr(
|
||||
"Are you sure you wish to remove the {0} tags from {1} archive(s)?".format(
|
||||
MetaDataStyle.name[style],
|
||||
has_md_count)),
|
||||
QtGui.QMessageBox.Yes,
|
||||
QtGui.QMessageBox.No)
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No)
|
||||
|
||||
if reply == QtGui.QMessageBox.Yes:
|
||||
progdialog = QtGui.QProgressDialog(
|
||||
if reply == QtWidgets.QMessageBox.Yes:
|
||||
progdialog = QtWidgets.QProgressDialog(
|
||||
"", "Cancel", 0, has_md_count, self)
|
||||
progdialog.setWindowTitle("Removing Tags")
|
||||
progdialog.setWindowModality(QtCore.Qt.ApplicationModal)
|
||||
progdialog.show()
|
||||
progdialog.setMinimumDuration(300)
|
||||
centerWindowOnParent(progdialog)
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
#progdialog.show()
|
||||
|
||||
prog_idx = 0
|
||||
|
||||
failed_list = []
|
||||
@ -1595,8 +1594,8 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
if progdialog.wasCanceled():
|
||||
break
|
||||
progdialog.setValue(prog_idx)
|
||||
prog_idx += 1
|
||||
progdialog.setValue(prog_idx)
|
||||
progdialog.setLabelText(ca.path)
|
||||
centerWindowOnParent(progdialog)
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
@ -1608,18 +1607,19 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
success_count += 1
|
||||
ca.loadCache([MetaDataStyle.CBI, MetaDataStyle.CIX])
|
||||
|
||||
progdialog.close()
|
||||
progdialog.hide()
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
self.fileSelectionList.updateSelectedRows()
|
||||
self.updateInfoBox()
|
||||
self.updateMenus()
|
||||
|
||||
summary = u"Successfully removed tags in {0} archive(s).".format(
|
||||
summary = "Successfully removed tags in {0} archive(s).".format(
|
||||
success_count)
|
||||
if len(failed_list) > 0:
|
||||
summary += u"\n\nThe remove operation failed in the following {0} archive(s):\n".format(
|
||||
summary += "\n\nThe remove operation failed in the following {0} archive(s):\n".format(
|
||||
len(failed_list))
|
||||
for f in failed_list:
|
||||
summary += u"\t{0}\n".format(f)
|
||||
summary += "\t{0}\n".format(f)
|
||||
|
||||
dlg = LogWindow(self)
|
||||
dlg.setText(summary)
|
||||
@ -1636,7 +1636,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
dest_style = self.save_data_style
|
||||
|
||||
if src_style == dest_style:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self,
|
||||
self.tr("Copy Tags"),
|
||||
self.tr(
|
||||
@ -1649,7 +1649,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
has_src_count += 1
|
||||
|
||||
if has_src_count == 0:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Copy Tags"), self.tr(
|
||||
"No archives with {0} tags selected!".format(
|
||||
MetaDataStyle.name[src_style])))
|
||||
@ -1661,7 +1661,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
return
|
||||
|
||||
if has_src_count != 0:
|
||||
reply = QtGui.QMessageBox.question(
|
||||
reply = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
self.tr("Copy Tags"),
|
||||
self.tr(
|
||||
@ -1669,15 +1669,17 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
MetaDataStyle.name[src_style],
|
||||
MetaDataStyle.name[dest_style],
|
||||
has_src_count)),
|
||||
QtGui.QMessageBox.Yes,
|
||||
QtGui.QMessageBox.No)
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No)
|
||||
|
||||
if reply == QtGui.QMessageBox.Yes:
|
||||
progdialog = QtGui.QProgressDialog(
|
||||
if reply == QtWidgets.QMessageBox.Yes:
|
||||
progdialog = QtWidgets.QProgressDialog(
|
||||
"", "Cancel", 0, has_src_count, self)
|
||||
progdialog.setWindowTitle("Copying Tags")
|
||||
progdialog.setWindowModality(QtCore.Qt.ApplicationModal)
|
||||
progdialog.show()
|
||||
progdialog.setMinimumDuration(300)
|
||||
centerWindowOnParent(progdialog)
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
prog_idx = 0
|
||||
|
||||
failed_list = []
|
||||
@ -1687,8 +1689,8 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
if progdialog.wasCanceled():
|
||||
break
|
||||
progdialog.setValue(prog_idx)
|
||||
prog_idx += 1
|
||||
progdialog.setValue(prog_idx)
|
||||
progdialog.setLabelText(ca.path)
|
||||
centerWindowOnParent(progdialog)
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
@ -1706,18 +1708,19 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
|
||||
ca.loadCache([MetaDataStyle.CBI, MetaDataStyle.CIX])
|
||||
|
||||
progdialog.close()
|
||||
progdialog.hide()
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
self.fileSelectionList.updateSelectedRows()
|
||||
self.updateInfoBox()
|
||||
self.updateMenus()
|
||||
|
||||
summary = u"Successfully copied tags in {0} archive(s).".format(
|
||||
summary = "Successfully copied tags in {0} archive(s).".format(
|
||||
success_count)
|
||||
if len(failed_list) > 0:
|
||||
summary += u"\n\nThe copy operation failed in the following {0} archive(s):\n".format(
|
||||
summary += "\n\nThe copy operation failed in the following {0} archive(s):\n".format(
|
||||
len(failed_list))
|
||||
for f in failed_list:
|
||||
summary += u"\t{0}\n".format(f)
|
||||
summary += "\t{0}\n".format(f)
|
||||
|
||||
dlg = LogWindow(self)
|
||||
dlg.setText(summary)
|
||||
@ -1728,7 +1731,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
|
||||
# now get the particular issue data
|
||||
cv_md = None
|
||||
QtGui.QApplication.setOverrideCursor(
|
||||
QtWidgets.QApplication.setOverrideCursor(
|
||||
QtGui.QCursor(QtCore.Qt.WaitCursor))
|
||||
|
||||
try:
|
||||
@ -1743,7 +1746,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
if self.settings.apply_cbl_transform_on_cv_import:
|
||||
cv_md = CBLTransformer(cv_md, self.settings).apply()
|
||||
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
|
||||
return cv_md
|
||||
|
||||
@ -1861,7 +1864,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
style = self.save_data_style
|
||||
|
||||
if len(ca_list) == 0:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Auto-Tag"), self.tr("No archives selected!"))
|
||||
return
|
||||
|
||||
@ -1891,9 +1894,9 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
self.atprogdialog.setWindowTitle("Auto-Tagging")
|
||||
|
||||
self.autoTagLog(
|
||||
u"==========================================================================\n")
|
||||
"==========================================================================\n")
|
||||
self.autoTagLog(
|
||||
u"Auto-Tagging Started for {0} items\n".format(len(ca_list)))
|
||||
"Auto-Tagging Started for {0} items\n".format(len(ca_list)))
|
||||
|
||||
prog_idx = 0
|
||||
|
||||
@ -1901,10 +1904,10 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
archives_to_remove = []
|
||||
for ca in ca_list:
|
||||
self.autoTagLog(
|
||||
u"==========================================================================\n")
|
||||
"==========================================================================\n")
|
||||
self.autoTagLog(
|
||||
u"Auto-Tagging {0} of {1}\n".format(prog_idx + 1, len(ca_list)))
|
||||
self.autoTagLog(u"{0}\n".format(ca.path))
|
||||
"Auto-Tagging {0} of {1}\n".format(prog_idx + 1, len(ca_list)))
|
||||
self.autoTagLog("{0}\n".format(ca.path))
|
||||
cover_idx = ca.readMetadata(style).getCoverPageIndexList()[0]
|
||||
image_data = ca.getPage(cover_idx)
|
||||
self.atprogdialog.setArchiveImage(image_data)
|
||||
@ -1935,24 +1938,24 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
self.loadArchive(self.fileSelectionList.getCurrentArchive())
|
||||
self.atprogdialog = None
|
||||
|
||||
summary = u""
|
||||
summary += u"Successfully tagged archives: {0}\n".format(
|
||||
summary = ""
|
||||
summary += "Successfully tagged archives: {0}\n".format(
|
||||
len(match_results.goodMatches))
|
||||
|
||||
if len(match_results.multipleMatches) > 0:
|
||||
summary += u"Archives with multiple matches: {0}\n".format(
|
||||
summary += "Archives with multiple matches: {0}\n".format(
|
||||
len(match_results.multipleMatches))
|
||||
if len(match_results.lowConfidenceMatches) > 0:
|
||||
summary += u"Archives with one or more low-confidence matches: {0}\n".format(
|
||||
summary += "Archives with one or more low-confidence matches: {0}\n".format(
|
||||
len(match_results.lowConfidenceMatches))
|
||||
if len(match_results.noMatches) > 0:
|
||||
summary += u"Archives with no matches: {0}\n".format(
|
||||
summary += "Archives with no matches: {0}\n".format(
|
||||
len(match_results.noMatches))
|
||||
if len(match_results.fetchDataFailures) > 0:
|
||||
summary += u"Archives that failed due to data fetch errors: {0}\n".format(
|
||||
summary += "Archives that failed due to data fetch errors: {0}\n".format(
|
||||
len(match_results.fetchDataFailures))
|
||||
if len(match_results.writeFailures) > 0:
|
||||
summary += u"Archives that failed due to file writing errors: {0}\n".format(
|
||||
summary += "Archives that failed due to file writing errors: {0}\n".format(
|
||||
len(match_results.writeFailures))
|
||||
|
||||
self.autoTagLog(summary)
|
||||
@ -1960,18 +1963,18 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
sum_selectable = len(
|
||||
match_results.multipleMatches) + len(match_results.lowConfidenceMatches)
|
||||
if sum_selectable > 0:
|
||||
summary += u"\n\nDo you want to manually select the ones with multiple matches and/or low-confidence matches now?"
|
||||
summary += "\n\nDo you want to manually select the ones with multiple matches and/or low-confidence matches now?"
|
||||
|
||||
reply = QtGui.QMessageBox.question(
|
||||
reply = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
self.tr(u"Auto-Tag Summary"),
|
||||
self.tr("Auto-Tag Summary"),
|
||||
self.tr(summary),
|
||||
QtGui.QMessageBox.Yes,
|
||||
QtGui.QMessageBox.No)
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No)
|
||||
|
||||
match_results.multipleMatches.extend(
|
||||
match_results.lowConfidenceMatches)
|
||||
if reply == QtGui.QMessageBox.Yes:
|
||||
if reply == QtWidgets.QMessageBox.Yes:
|
||||
matchdlg = AutoTagMatchWindow(
|
||||
self,
|
||||
match_results.multipleMatches,
|
||||
@ -1983,19 +1986,19 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
self.loadArchive(self.fileSelectionList.getCurrentArchive())
|
||||
|
||||
else:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Auto-Tag Summary"), self.tr(summary))
|
||||
|
||||
def dirtyFlagVerification(self, title, desc):
|
||||
if self.dirtyFlag:
|
||||
reply = QtGui.QMessageBox.question(
|
||||
reply = QtWidgets.QMessageBox.question(
|
||||
self,
|
||||
self.tr(title),
|
||||
self.tr(desc),
|
||||
QtGui.QMessageBox.Yes,
|
||||
QtGui.QMessageBox.No)
|
||||
QtWidgets.QMessageBox.Yes,
|
||||
QtWidgets.QMessageBox.No)
|
||||
|
||||
if reply != QtGui.QMessageBox.Yes:
|
||||
if reply != QtWidgets.QMessageBox.Yes:
|
||||
return False
|
||||
return True
|
||||
|
||||
@ -2046,10 +2049,10 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
dlg.exec_()
|
||||
|
||||
def showWiki(self):
|
||||
webbrowser.open("http://code.google.com/p/comictagger/wiki/Home?tm=6")
|
||||
webbrowser.open("https://github.com/davide-romanini/comictagger/wiki")
|
||||
|
||||
def reportBug(self):
|
||||
webbrowser.open("http://code.google.com/p/comictagger/issues/list")
|
||||
webbrowser.open("https://github.com/davide-romanini/comictagger/issues")
|
||||
|
||||
def showForum(self):
|
||||
webbrowser.open("http://comictagger.forumotion.com/")
|
||||
@ -2070,7 +2073,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
ca_list = self.fileSelectionList.getSelectedArchiveList()
|
||||
|
||||
if len(ca_list) == 0:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, self.tr("Rename"), self.tr("No archives selected!"))
|
||||
return
|
||||
|
||||
@ -2086,7 +2089,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
self.loadArchive(self.comic_archive)
|
||||
|
||||
def fileListSelectionChanged(self, qvarFI):
|
||||
fi = qvarFI.toPyObject()
|
||||
fi = qvarFI #.toPyObject()
|
||||
self.loadArchive(fi.ca)
|
||||
|
||||
def loadArchive(self, comic_archive):
|
||||
@ -2183,7 +2186,7 @@ class TaggerWindow(QtGui.QMainWindow):
|
||||
win32gui.SetWindowPos(
|
||||
hwnd, win32con.HWND_NOTOPMOST, x, y, w, h, 0)
|
||||
except Exception as e:
|
||||
print "Whoops", e
|
||||
print("Whoops", e)
|
||||
elif platform.system() == "Darwin":
|
||||
self.raise_()
|
||||
self.showNormal()
|
||||
|
@ -8,7 +8,7 @@ from comictaggerlib.settings import ComicTaggerSettings
|
||||
|
||||
|
||||
try:
|
||||
from PyQt4 import QtGui
|
||||
from PyQt5 import QtGui
|
||||
qt_available = True
|
||||
except ImportError:
|
||||
qt_available = False
|
||||
@ -64,7 +64,7 @@ if qt_available:
|
||||
try:
|
||||
from PIL import Image
|
||||
from PIL import WebPImagePlugin
|
||||
import StringIO
|
||||
import io
|
||||
pil_available = True
|
||||
except ImportError:
|
||||
pil_available = False
|
||||
@ -78,8 +78,8 @@ if qt_available:
|
||||
# Qt doesn't understand the format, but maybe PIL does
|
||||
# so try to convert the image data to uncompressed tiff
|
||||
# format
|
||||
im = Image.open(StringIO.StringIO(image_data))
|
||||
output = StringIO.StringIO()
|
||||
im = Image.open(io.StringIO(image_data))
|
||||
output = io.StringIO()
|
||||
im.save(output, format="PNG")
|
||||
success = img.loadFromData(output.getvalue())
|
||||
except Exception as e:
|
||||
|
@ -116,118 +116,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="grpBoxRar">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QLabel" name="lblRarHelp">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>In order to read/write to CBR/RAR archives, you will need to have the shareware tools from <a href="www.win-rar.com/download.html"><span style=" text-decoration: underline; color:#0000ff;">WinRAR</span></a> installed. </p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>RAR program</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="leRarExePath">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="btnBrowseRar">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="lblUnrar">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>UnRAR program</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="leUnrarExePath">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QPushButton" name="btnBrowseUnrar">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
@ -529,7 +417,7 @@
|
||||
<x>11</x>
|
||||
<y>21</y>
|
||||
<width>251</width>
|
||||
<height>192</height>
|
||||
<height>199</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_7">
|
||||
@ -688,6 +576,172 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_7">
|
||||
<attribute name="title">
|
||||
<string>RAR Tools</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="grpBoxUnrar">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lblUnrar">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>UnRAR library</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="leUnrarLibPath">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="btnBrowseUnrar">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QLabel" name="lblUnrarHelp">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>In order to read CBR/RAR archives, you will need to have the unrar library from <a href="www.win-rar.com/download.html"><span style=" text-decoration: underline; color:#0000ff;">WinRAR</span></a> installed. </p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="grpBoxRar">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>RAR program</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="leRarExePath">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="btnBrowseRar">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QLabel" name="lblRarHelp">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>In order to write to CBR/RAR archives, you will need to have the shareware tools from <a href="www.win-rar.com/download.html"><span style=" text-decoration: underline; color:#0000ff;">WinRAR</span></a> installed. </p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -16,13 +16,13 @@
|
||||
|
||||
import sys
|
||||
import platform
|
||||
import urllib2
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
#import os
|
||||
#import urllib
|
||||
|
||||
try:
|
||||
from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
||||
from PyQt4.QtCore import QUrl, pyqtSignal, QObject, QByteArray
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
||||
from PyQt5.QtCore import QUrl, pyqtSignal, QObject, QByteArray
|
||||
except ImportError:
|
||||
# No Qt, so define a few dummy QObjects to help us compile
|
||||
class QObject():
|
||||
@ -38,7 +38,7 @@ except ImportError:
|
||||
def emit(a, b, c):
|
||||
pass
|
||||
|
||||
import ctversion
|
||||
from . import ctversion
|
||||
|
||||
|
||||
class VersionChecker(QObject):
|
||||
@ -67,7 +67,7 @@ class VersionChecker(QObject):
|
||||
def getLatestVersion(self, uuid, use_stats=True):
|
||||
|
||||
try:
|
||||
resp = urllib2.urlopen(self.getRequestUrl(uuid, use_stats))
|
||||
resp = urllib.request.urlopen(self.getRequestUrl(uuid, use_stats))
|
||||
new_version = resp.read()
|
||||
except Exception as e:
|
||||
return None
|
||||
|
@ -18,20 +18,20 @@
|
||||
#import time
|
||||
#import os
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt4.QtCore import QUrl, pyqtSignal
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
from PyQt5.QtCore import QUrl, pyqtSignal
|
||||
#from PyQt4.QtCore import QObject
|
||||
#from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest
|
||||
|
||||
from comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from issueselectionwindow import IssueSelectionWindow
|
||||
from issueidentifier import IssueIdentifier
|
||||
from genericmetadata import GenericMetadata
|
||||
from progresswindow import IDProgressWindow
|
||||
from settings import ComicTaggerSettings
|
||||
from matchselectionwindow import MatchSelectionWindow
|
||||
from coverimagewidget import CoverImageWidget
|
||||
from comictaggerlib.ui.qtutils import reduceWidgetFontSize
|
||||
from .comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from .issueselectionwindow import IssueSelectionWindow
|
||||
from .issueidentifier import IssueIdentifier
|
||||
from .genericmetadata import GenericMetadata
|
||||
from .progresswindow import IDProgressWindow
|
||||
from .settings import ComicTaggerSettings
|
||||
from .matchselectionwindow import MatchSelectionWindow
|
||||
from .coverimagewidget import CoverImageWidget
|
||||
from comictaggerlib.ui.qtutils import reduceWidgetFontSize, centerWindowOnParent
|
||||
#from imagefetcher import ImageFetcher
|
||||
#import utils
|
||||
|
||||
@ -90,7 +90,7 @@ class IdentifyThread(QtCore.QThread):
|
||||
self.identifyComplete.emit()
|
||||
|
||||
|
||||
class VolumeSelectionWindow(QtGui.QDialog):
|
||||
class VolumeSelectionWindow(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent, series_name, issue_number, year, issue_count,
|
||||
cover_index_list, comic_archive, settings, autoselect=False):
|
||||
@ -101,7 +101,7 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
|
||||
self.imageWidget = CoverImageWidget(
|
||||
self.imageContainer, CoverImageWidget.URLMode)
|
||||
gridlayout = QtGui.QGridLayout(self.imageContainer)
|
||||
gridlayout = QtWidgets.QGridLayout(self.imageContainer)
|
||||
gridlayout.addWidget(self.imageWidget)
|
||||
gridlayout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
@ -113,6 +113,7 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
QtCore.Qt.WindowMaximizeButtonHint)
|
||||
|
||||
self.settings = settings
|
||||
self.parent = parent
|
||||
self.series_name = series_name
|
||||
self.issue_number = issue_number
|
||||
self.year = year
|
||||
@ -144,7 +145,7 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
self.btnRequery.setEnabled(enabled)
|
||||
self.btnIssues.setEnabled(enabled)
|
||||
self.btnAutoSelect.setEnabled(enabled)
|
||||
self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(enabled)
|
||||
self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(enabled)
|
||||
|
||||
def requery(self,):
|
||||
self.performQuery(refresh=True)
|
||||
@ -153,12 +154,12 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
def autoSelect(self):
|
||||
|
||||
if self.comic_archive is None:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, "Auto-Select", "You need to load a comic first!")
|
||||
return
|
||||
|
||||
if self.issue_number is None or self.issue_number == "":
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self,
|
||||
"Auto-Select",
|
||||
"Can't auto-select without an issue number (yet!)")
|
||||
@ -192,7 +193,7 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
self.iddialog.exec_()
|
||||
|
||||
def logIDOutput(self, text):
|
||||
print unicode(text),
|
||||
print(str(text), end=' ')
|
||||
self.iddialog.textEdit.ensureCursorVisible()
|
||||
self.iddialog.textEdit.insertPlainText(text)
|
||||
|
||||
@ -212,22 +213,22 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
found_match = None
|
||||
choices = False
|
||||
if result == self.ii.ResultNoMatches:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, "Auto-Select Result", " No matches found :-(")
|
||||
elif result == self.ii.ResultFoundMatchButBadCoverScore:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self,
|
||||
"Auto-Select Result",
|
||||
" Found a match, but cover doesn't seem the same. Verify before commiting!")
|
||||
found_match = matches[0]
|
||||
elif result == self.ii.ResultFoundMatchButNotFirstPage:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self,
|
||||
"Auto-Select Result",
|
||||
" Found a match, but not with the first page of the archive.")
|
||||
found_match = matches[0]
|
||||
elif result == self.ii.ResultMultipleMatchesWithBadImageScores:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self,
|
||||
"Auto-Select Result",
|
||||
" Found some possibilities, but no confidence. Proceed manually.")
|
||||
@ -235,7 +236,7 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
elif result == self.ii.ResultOneGoodMatch:
|
||||
found_match = matches[0]
|
||||
elif result == self.ii.ResultMultipleGoodMatches:
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self,
|
||||
"Auto-Select Result",
|
||||
" Found multiple likely matches. Please select.")
|
||||
@ -264,7 +265,7 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
for record in self.cv_search_results:
|
||||
if record['id'] == self.volume_id:
|
||||
title = record['name']
|
||||
title += " (" + unicode(record['start_year']) + ")"
|
||||
title += " (" + str(record['start_year']) + ")"
|
||||
title += " - "
|
||||
break
|
||||
|
||||
@ -279,26 +280,24 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
|
||||
def selectByID(self):
|
||||
for r in range(0, self.twList.rowCount()):
|
||||
volume_id, b = self.twList.item(
|
||||
r, 0).data(QtCore.Qt.UserRole).toInt()
|
||||
volume_id = self.twList.item(r, 0).data(QtCore.Qt.UserRole)
|
||||
if (volume_id == self.volume_id):
|
||||
self.twList.selectRow(r)
|
||||
break
|
||||
|
||||
def performQuery(self, refresh=False):
|
||||
|
||||
self.progdialog = QtGui.QProgressDialog(
|
||||
self.progdialog = QtWidgets.QProgressDialog(
|
||||
"Searching Online", "Cancel", 0, 100, self)
|
||||
self.progdialog.setWindowTitle("Online Search")
|
||||
self.progdialog.canceled.connect(self.searchCanceled)
|
||||
self.progdialog.setModal(True)
|
||||
|
||||
self.progdialog.setMinimumDuration(300)
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
self.search_thread = SearchThread(self.series_name, refresh)
|
||||
self.search_thread.searchComplete.connect(self.searchComplete)
|
||||
self.search_thread.progressUpdate.connect(self.searchProgressUpdate)
|
||||
self.search_thread.start()
|
||||
|
||||
# QtCore.QCoreApplication.processEvents()
|
||||
self.progdialog.exec_()
|
||||
|
||||
def searchCanceled(self):
|
||||
@ -315,18 +314,20 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
|
||||
def searchProgressUpdate(self, current, total):
|
||||
self.progdialog.setMaximum(total)
|
||||
self.progdialog.setValue(current)
|
||||
self.progdialog.setValue(current+1)
|
||||
|
||||
def searchComplete(self):
|
||||
self.progdialog.accept()
|
||||
del self.progdialog
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
if self.search_thread.cv_error:
|
||||
if self.search_thread.error_code == ComicVineTalkerException.RateLimit:
|
||||
QtGui.QMessageBox.critical(
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
self.tr("Comic Vine Error"),
|
||||
ComicVineTalker.getRateLimitMessage())
|
||||
else:
|
||||
QtGui.QMessageBox.critical(
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
self.tr("Network Issue"),
|
||||
self.tr("Could not connect to Comic Vine to search for series!"))
|
||||
@ -345,20 +346,20 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
self.twList.insertRow(row)
|
||||
|
||||
item_text = record['name']
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setData(QtCore.Qt.UserRole, record['id'])
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 0, item)
|
||||
|
||||
item_text = str(record['start_year'])
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 1, item)
|
||||
|
||||
item_text = record['count_of_issues']
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item.setData(QtCore.Qt.DisplayRole, record['count_of_issues'])
|
||||
item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
@ -367,7 +368,7 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
if record['publisher'] is not None:
|
||||
item_text = record['publisher']['name']
|
||||
item.setData(QtCore.Qt.ToolTipRole, item_text)
|
||||
item = QtGui.QTableWidgetItem(item_text)
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
item.setFlags(
|
||||
QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.twList.setItem(row, 3, item)
|
||||
@ -382,7 +383,7 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
|
||||
if len(self.cv_search_results) == 0:
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
QtGui.QMessageBox.information(
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, "Search Result", "No matches found!")
|
||||
|
||||
if self.immediate_autoselect and len(self.cv_search_results) > 0:
|
||||
@ -404,9 +405,8 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
if prev is not None and prev.row() == curr.row():
|
||||
return
|
||||
|
||||
self.volume_id, b = self.twList.item(
|
||||
curr.row(), 0).data(QtCore.Qt.UserRole).toInt()
|
||||
|
||||
self.volume_id = self.twList.item(curr.row(), 0).data(QtCore.Qt.UserRole)
|
||||
|
||||
# list selection was changed, update the info on the volume
|
||||
for record in self.cv_search_results:
|
||||
if record['id'] == self.volume_id:
|
||||
|
11
desktop-integration/linux/ComicTagger.desktop
Normal file
11
desktop-integration/linux/ComicTagger.desktop
Normal file
@ -0,0 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Name=ComicTagger
|
||||
GenericName=Comic Metadata Editor
|
||||
Comment=A cross-platform GUI/CLI app for writing metadata to comic archives
|
||||
Exec=%%CTSCRIPT%% %F
|
||||
Icon=/usr/local/share/comictagger/app.png
|
||||
Terminal=false
|
||||
Type=Application
|
||||
MimeType=text/plain;
|
||||
Categories=Application;
|
4
desktop-integration/mac/ComicTagger
Normal file
4
desktop-integration/mac/ComicTagger
Normal file
@ -0,0 +1,4 @@
|
||||
This file is a placeholder that will automaticlly be replaced with a symlink to
|
||||
the local machine's Python framework python binary.
|
||||
|
||||
When pip does an uninstall, it will remove the link.
|
32
desktop-integration/mac/Info.plist
Normal file
32
desktop-integration/mac/Info.plist
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>main.sh</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>app.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.comictagger.comictagger</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>ComicTagger</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>%%CTVERSION%%</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>%%CTVERSION%%</string>
|
||||
<key>NSAppleScriptEnabled</key>
|
||||
<string>YES</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
17
desktop-integration/mac/main.sh
Executable file
17
desktop-integration/mac/main.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This is a lot of hoop-jumping to get the absolute path
|
||||
# of this script, so that we can use the Symlinked python
|
||||
# binary to call the CT script. This is all so that the
|
||||
# Mac menu doesn't say "Python".
|
||||
|
||||
realpath()
|
||||
{
|
||||
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
|
||||
}
|
||||
|
||||
CTSCRIPT=%%CTSCRIPT%%
|
||||
|
||||
THIS=$(realpath $0)
|
||||
THIS_FOLDER=$(dirname $THIS)
|
||||
"$THIS_FOLDER/ComicTagger" "$CTSCRIPT"
|
4
desktop-integration/windows/ComicTagger-pip.lnk
Normal file
4
desktop-integration/windows/ComicTagger-pip.lnk
Normal file
@ -0,0 +1,4 @@
|
||||
This file is a placeholder that will automatically be replaced with a Windows
|
||||
shortcut on the user's desktop.
|
||||
|
||||
When pip does an uninstall, it will remove the shortcut file.
|
@ -3,5 +3,6 @@ beautifulsoup4 >= 4.1
|
||||
unrar==0.3
|
||||
natsort==3.5.2
|
||||
PyPDF2==1.24
|
||||
Pillow==4.3.0
|
||||
PyInstaller==3.3.1
|
||||
pillow>=4.3.0
|
||||
PyQt5>=5.10.1
|
||||
git+https://github.com/pyinstaller/pyinstaller@develop
|
243
setup.py
243
setup.py
@ -1,42 +1,227 @@
|
||||
#!/usr/bin/env python
|
||||
# Setup file for comictagger python source (no wheels yet)
|
||||
#
|
||||
# The install process will attempt to compile the unrar lib from source.
|
||||
# If it succeeds, the unrar lib binary will be installed with the python
|
||||
# source. If it fails, install will just continue. On most Linux systems it
|
||||
# should just work. (Tested on a Mac system with homebrew, as well)
|
||||
#
|
||||
# An entry point script called "comictagger" will be created
|
||||
#
|
||||
# Currently commented out, an experiment at desktop integration.
|
||||
# It seems that post installation tweaks are broken by wheel files.
|
||||
# Kept here for further research
|
||||
|
||||
from __future__ import print_function
|
||||
from setuptools import setup
|
||||
from setuptools import dist
|
||||
from setuptools import Command
|
||||
import setuptools.command.build_py
|
||||
import setuptools.command.install
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import platform
|
||||
import tempfile
|
||||
import comictaggerlib.ctversion
|
||||
|
||||
python_requires='>=3',
|
||||
|
||||
|
||||
with open('requirements.txt') as f:
|
||||
required = f.read().splitlines()
|
||||
# Always require PyQt5 on Windows and Mac
|
||||
if platform.system() in [ "Windows", "Darwin" ]:
|
||||
required.append("PyQt5")
|
||||
|
||||
platform_data_files = []
|
||||
|
||||
"""
|
||||
if platform.system() in [ "Windows" ]:
|
||||
required.append("winshell")
|
||||
|
||||
# Some files to install on different platforms
|
||||
|
||||
if platform.system() == "Linux":
|
||||
linux_desktop_shortcut = "/usr/local/share/applications/ComicTagger.desktop"
|
||||
platform_data_files = [("/usr/local/share/applications",
|
||||
["desktop-integration/linux/ComicTagger.desktop"]),
|
||||
("/usr/local/share/comictagger",
|
||||
["comictaggerlib/graphics/app.png"]),
|
||||
]
|
||||
|
||||
if platform.system() == "Windows":
|
||||
win_desktop_folder = os.path.join(os.environ["USERPROFILE"], "Desktop")
|
||||
win_appdata_folder = os.path.join(os.environ["APPDATA"], "comictagger")
|
||||
win_desktop_shortcut = os.path.join(win_desktop_folder, "ComicTagger-pip.lnk")
|
||||
platform_data_files = [(win_desktop_folder,
|
||||
["desktop-integration/windows/ComicTagger-pip.lnk"]),
|
||||
(win_appdata_folder,
|
||||
["windows/app.ico"]),
|
||||
]
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
mac_app_folder = "/Applications"
|
||||
ct_app_name = "ComicTagger-pip.app"
|
||||
mac_app_infoplist = os.path.join(mac_app_folder, ct_app_name, "Contents", "Info.plist")
|
||||
mac_app_main = os.path.join(mac_app_folder, ct_app_name, "MacOS", "main.sh")
|
||||
mac_python_link = os.path.join(mac_app_folder, ct_app_name, "MacOS", "ComicTagger")
|
||||
platform_data_files = [(os.path.join(mac_app_folder, ct_app_name, "Contents"),
|
||||
["desktop-integration/mac/Info.plist"]),
|
||||
(os.path.join(mac_app_folder, ct_app_name, "Contents/Resources"),
|
||||
["mac/app.icns"]),
|
||||
(os.path.join(mac_app_folder, ct_app_name, "Contents/MacOS"),
|
||||
["desktop-integration/mac/main.sh",
|
||||
"desktop-integration/mac/ComicTagger"]),
|
||||
]
|
||||
|
||||
def fileTokenReplace(filename, token, replacement):
|
||||
with open(filename, "rt") as fin:
|
||||
fd, tmpfile = tempfile.mkstemp()
|
||||
with open(tmpfile, "wt") as fout:
|
||||
for line in fin:
|
||||
fout.write(line.replace('%%{}%%'.format(token), replacement))
|
||||
os.close(fd)
|
||||
# fix permissions of temp file
|
||||
os.chmod(tmpfile, 420) #Octal 0o644
|
||||
os.rename(tmpfile, filename)
|
||||
|
||||
def postInstall(scripts_folder):
|
||||
entry_point_script = os.path.join(scripts_folder, "comictagger")
|
||||
|
||||
if platform.system() == "Windows":
|
||||
# doctor the shortcut for this windows system after deployment
|
||||
import winshell
|
||||
winshell.CreateShortcut(
|
||||
Path=os.path.abspath(win_desktop_shortcut),
|
||||
Target=entry_point_script + ".exe",
|
||||
Icon=(os.path.join(win_appdata_folder, 'app.ico'), 0),
|
||||
Description="Launch ComicTagger as installed by PIP"
|
||||
)
|
||||
|
||||
if platform.system() == "Linux":
|
||||
# doctor the script path in the desktop file
|
||||
fileTokenReplace(linux_desktop_shortcut,
|
||||
"CTSCRIPT",
|
||||
entry_point_script)
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
# doctor the plist app version
|
||||
fileTokenReplace(mac_app_infoplist,
|
||||
"CTVERSION",
|
||||
comictaggerlib.ctversion.version)
|
||||
# doctor the script path in main.sh
|
||||
fileTokenReplace(mac_app_main,
|
||||
"CTSCRIPT",
|
||||
entry_point_script)
|
||||
# Make the launcher script executable
|
||||
os.chmod(mac_app_main, 509) #Octal 0o775
|
||||
|
||||
# Final install step: create a symlink to Python OS X application
|
||||
punt = False
|
||||
pythonpath,top = os.path.split(os.path.realpath(sys.executable))
|
||||
while top:
|
||||
if 'Resources' in pythonpath:
|
||||
pass
|
||||
elif os.path.exists(os.path.join(pythonpath,'Resources')):
|
||||
break
|
||||
pythonpath,top = os.path.split(pythonpath)
|
||||
else:
|
||||
print("Failed to find a Resources directory associated with ", str(sys.executable))
|
||||
punt = True
|
||||
|
||||
if not punt:
|
||||
pythonapp = os.path.join(pythonpath, 'Resources','Python.app','Contents','MacOS','Python')
|
||||
if not os.path.exists(pythonapp):
|
||||
print("Failed to find a Python app in ", str(pythonapp))
|
||||
punt = True
|
||||
|
||||
# remove the placeholder
|
||||
os.remove(mac_python_link)
|
||||
if not punt:
|
||||
os.symlink(pythonapp, mac_python_link)
|
||||
else:
|
||||
# We failed, but we can still be functional
|
||||
os.symlink(sys.executable, mac_python_link)
|
||||
"""
|
||||
|
||||
class BuildUnrarCommand(Command):
|
||||
description = 'build unrar library'
|
||||
user_options = []
|
||||
|
||||
def initialize_options(self):
|
||||
pass
|
||||
def finalize_options(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
if not os.path.exists("comictaggerlib/libunrar.so"):
|
||||
if not os.path.exists("unrar/libunrar.so"):
|
||||
print("Building C++ unrar library....")
|
||||
subprocess.call(['make', '-C', 'unrar', 'lib'])
|
||||
print("Copying .so file to comictaggerlib folder")
|
||||
shutil.copyfile("unrar/libunrar.so", "comictaggerlib/libunrar.so")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("WARNING ---- Unrar library build/deploy failed. User will have to self-install libunrar.")
|
||||
|
||||
|
||||
class BuildPyCommand(setuptools.command.build_py.build_py):
|
||||
"""Custom build command."""
|
||||
|
||||
def run(self):
|
||||
self.run_command('build_unrar')
|
||||
setuptools.command.build_py.build_py.run(self)
|
||||
|
||||
class customInstall(setuptools.command.install.install):
|
||||
"""Custom install command."""
|
||||
|
||||
def run(self):
|
||||
|
||||
# Do the standard install
|
||||
setuptools.command.install.install.run(self)
|
||||
|
||||
# Custom post install
|
||||
#postInstall(self.install_scripts)
|
||||
|
||||
#----------------------------------------------------
|
||||
setup(name="comictagger",
|
||||
install_requires=required,
|
||||
cmdclass={
|
||||
'build_unrar': BuildUnrarCommand,
|
||||
'build_py': BuildPyCommand,
|
||||
'install': customInstall,
|
||||
},
|
||||
version=comictaggerlib.ctversion.version,
|
||||
description="A cross-platform GUI/CLI app for writing metadata to comic archives",
|
||||
author="Anthony Beville",
|
||||
author="ComicTagger team",
|
||||
author_email="comictagger@gmail.com",
|
||||
url="http://code.google.com/p/comictagger/",
|
||||
url="https://github.com/davide-romanini/comictagger",
|
||||
download_url="https://pypi.python.org/packages/source/c/comictagger/comictagger-{0}.zip".format(comictaggerlib.ctversion.version),
|
||||
packages=["comictaggerlib", "comicapi", "comicapi/UnRAR2"],
|
||||
packages=["comictaggerlib", "comicapi"],
|
||||
package_data={
|
||||
'comictaggerlib': ['ui/*.ui', 'graphics/*'],
|
||||
'comicapi/UnRAR2': ['UnRARDLL/*.*', 'UnRARDLL/x64/*.*'],
|
||||
'comictaggerlib': ['ui/*', 'graphics/*', '*.so'],
|
||||
},
|
||||
scripts=["comictagger.py"],
|
||||
entry_points=dict(console_scripts=['comictagger=comictaggerlib.main:ctmain']),
|
||||
data_files=platform_data_files,
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
"Environment :: Win32 (MS Windows)",
|
||||
"Environment :: MacOS X",
|
||||
"Environment :: X11 Applications :: Qt",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Natural Language :: English",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 2.6",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Topic :: Utilities",
|
||||
"Topic :: Other/Nonlisted Topic",
|
||||
"Topic :: Multimedia :: Graphics"
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
"Environment :: Win32 (MS Windows)",
|
||||
"Environment :: MacOS X",
|
||||
"Environment :: X11 Applications :: Qt",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Natural Language :: English",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Topic :: Utilities",
|
||||
"Topic :: Other/Nonlisted Topic",
|
||||
"Topic :: Multimedia :: Graphics"
|
||||
],
|
||||
keywords=['comictagger', 'comics', 'comic', 'metadata', 'tagging', 'tagger'],
|
||||
license="Apache License 2.0",
|
||||
|
||||
long_description="""
|
||||
@ -48,19 +233,9 @@ Features:
|
||||
* Communicates with an online database (Comic Vine) for acquiring metadata
|
||||
* Uses image processing to automatically match a given archive with the correct issue data
|
||||
* Batch processing in the GUI for tagging hundreds or more comics at a time
|
||||
* Reads and writes multiple tagging schemes ( ComicBookLover and ComicRack, with more planned).
|
||||
* Reads and writes multiple tagging schemes ( ComicBookLover and ComicRack).
|
||||
* Reads and writes RAR and Zip archives (external tools needed for writing RAR)
|
||||
* Command line interface (CLI) on all platforms (including Windows), which supports batch operations, and which can be used in native scripts for complex operations.
|
||||
|
||||
Requires:
|
||||
|
||||
* python 2.6 or 2.7
|
||||
* configparser
|
||||
* python imaging (PIL) >= 1.1.6
|
||||
* beautifulsoup > 4.1
|
||||
|
||||
Optional requirement (for GUI):
|
||||
|
||||
* pyqt4
|
||||
* Can run without PyQt5 installed
|
||||
"""
|
||||
)
|
||||
|
@ -1,9 +1,8 @@
|
||||
# Script to be run inside appveyor for a full build
|
||||
$env:PATH="C:\tools\mingw64\bin;C:\Miniconda-x64;C:\Miniconda-x64\Scripts;$env:path"
|
||||
choco install -y mingw
|
||||
C:\Miniconda-x64\Scripts\conda create -y --name comictagger python=2
|
||||
C:\Miniconda-x64\Scripts\activate comictagger
|
||||
C:\Miniconda-x64\Scripts\conda install -y pyqt=4
|
||||
C:\Miniconda-x64\Scripts\pip install -r .\requirements.txt
|
||||
refreshenv
|
||||
$env:PATH="C:\Python36-x64;$env:path"
|
||||
python -m venv venv
|
||||
.\venv\Scripts\Activate.ps1
|
||||
pip install -r .\requirements.txt
|
||||
mingw32-make dist
|
||||
objdump -af unrar/libunrar.so
|
||||
|
Loading…
x
Reference in New Issue
Block a user