Compare commits
27 Commits
1.1.12-bet
...
1.1.15-bet
Author | SHA1 | Date | |
---|---|---|---|
bf9ab71fd9 | |||
33b00ad323 | |||
301ff084f1 | |||
0c146bb245 | |||
08cc4a1acb | |||
f97a1653d9 | |||
d9dbab301a | |||
3d93197101 | |||
752a1d8923 | |||
68002daffa | |||
ad5062c582 | |||
2680468f34 | |||
6156fc296a | |||
0feed294d4 | |||
e57736b955 | |||
70fcdc0129 | |||
9a64195ebd | |||
b0f229f851 | |||
877a5ccd85 | |||
c0f2e2f771 | |||
0adfc9beb3 | |||
d0bc41d7ee | |||
fa46a065a4 | |||
8fcd5ba7d6 | |||
759cdc6b40 | |||
1405d9ff0e | |||
d8fcbbad0a |
1
Makefile
1
Makefile
@ -53,6 +53,7 @@ upload:
|
||||
#$(UPLOAD_TOOL) -p comictagger -s "ComicTagger $(VERSION_STR) Mac OS X" -l Featured,Type-Archive -u beville -w $(PASSWORD) "release/ComicTagger-$(VERSION_STR).dmg"
|
||||
#$(UPLOAD_TOOL) -p comictagger -s "ComicTagger $(VERSION_STR) Windows" -l Featured,Type-Installer -u beville -w $(PASSWORD) "release/ComicTagger v$(VERSION_STR).exe"
|
||||
python setup.py register
|
||||
python setup.py sdist --formats=zip upload
|
||||
|
||||
svn_tag:
|
||||
svn copy https://comictagger.googlecode.com/svn/trunk \
|
||||
|
@ -48,6 +48,19 @@ class AutoTagStartWindow(QtGui.QDialog):
|
||||
self.cbxSpecifySearchString.setCheckState( QtCore.Qt.Unchecked )
|
||||
self.leNameLengthMatchTolerance.setText( str(self.settings.id_length_delta_thresh) )
|
||||
self.leSearchString.setEnabled( False )
|
||||
|
||||
if self.settings.save_on_low_confidence:
|
||||
self.cbxSaveOnLowConfidence.setCheckState( QtCore.Qt.Checked)
|
||||
if self.settings.dont_use_year_when_identifying:
|
||||
self.cbxDontUseYear.setCheckState( QtCore.Qt.Checked)
|
||||
if self.settings.assume_1_if_no_issue_num:
|
||||
self.cbxAssumeIssueOne.setCheckState( QtCore.Qt.Checked)
|
||||
if self.settings.ignore_leading_numbers_in_filename:
|
||||
self.cbxIgnoreLeadingDigitsInFilename.setCheckState( QtCore.Qt.Checked)
|
||||
if self.settings.remove_archive_after_successful_match:
|
||||
self.cbxRemoveAfterSuccess.setCheckState( QtCore.Qt.Checked)
|
||||
if self.settings.wait_and_retry_on_rate_limit:
|
||||
self.cbxWaitForRateLimit.setCheckState( QtCore.Qt.Checked)
|
||||
|
||||
nlmtTip = (
|
||||
""" <html>The <b>Name Length Match Tolerance</b> is for eliminating automatic
|
||||
@ -79,6 +92,7 @@ class AutoTagStartWindow(QtGui.QDialog):
|
||||
self.assumeIssueOne = False
|
||||
self.ignoreLeadingDigitsInFilename = False
|
||||
self.removeAfterSuccess = False
|
||||
self.waitAndRetryOnRateLimit = False
|
||||
self.searchString = None
|
||||
self.nameLengthMatchTolerance = self.settings.id_length_delta_thresh
|
||||
|
||||
@ -96,6 +110,15 @@ class AutoTagStartWindow(QtGui.QDialog):
|
||||
self.ignoreLeadingDigitsInFilename = self.cbxIgnoreLeadingDigitsInFilename.isChecked()
|
||||
self.removeAfterSuccess = self.cbxRemoveAfterSuccess.isChecked()
|
||||
self.nameLengthMatchTolerance = int(self.leNameLengthMatchTolerance.text())
|
||||
self.waitAndRetryOnRateLimit = self.cbxWaitForRateLimit.isChecked()
|
||||
|
||||
#persist some settings
|
||||
self.settings.save_on_low_confidence = self.autoSaveOnLow
|
||||
self.settings.dont_use_year_when_identifying = self.dontUseYear
|
||||
self.settings.assume_1_if_no_issue_num = self.assumeIssueOne
|
||||
self.settings.ignore_leading_numbers_in_filename = self.ignoreLeadingDigitsInFilename
|
||||
self.settings.remove_archive_after_successful_match = self.removeAfterSuccess
|
||||
self.settings.wait_and_retry_on_rate_limit = self.waitAndRetryOnRateLimit
|
||||
|
||||
if self.cbxSpecifySearchString.isChecked():
|
||||
self.searchString = unicode(self.leSearchString.text())
|
||||
|
@ -74,6 +74,9 @@ class CBLTransformer:
|
||||
|
||||
if self.settings.copy_locations_to_tags:
|
||||
add_string_list_to_tags( self.metadata.locations )
|
||||
|
||||
if self.settings.copy_storyarcs_to_tags:
|
||||
add_string_list_to_tags( self.metadata.storyArc )
|
||||
|
||||
if self.settings.copy_notes_to_comments:
|
||||
if self.metadata.notes is not None:
|
||||
|
@ -60,11 +60,13 @@ class OnlineMatchResults():
|
||||
|
||||
#-----------------------------
|
||||
|
||||
def actual_issue_data_fetch( match, settings ):
|
||||
def actual_issue_data_fetch( match, settings, opts ):
|
||||
|
||||
# now get the particular issue data
|
||||
try:
|
||||
cv_md = ComicVineTalker().fetchIssueData( match['volume_id'], match['issue_number'], settings )
|
||||
comicVine = ComicVineTalker()
|
||||
comicVine.wait_for_rate_limit = opts.wait_and_retry_on_rate_limit
|
||||
cv_md = comicVine.fetchIssueData( match['volume_id'], match['issue_number'], settings )
|
||||
except ComicVineTalkerException:
|
||||
print >> sys.stderr, "Network error while getting issue details. Save aborted"
|
||||
return None
|
||||
@ -115,9 +117,9 @@ def display_match_set_for_choice( label, match_set, opts, settings ):
|
||||
i = int(i) - 1
|
||||
# save the data!
|
||||
# we know at this point, that the file is all good to go
|
||||
ca = ComicArchive( match_set.filename, settings )
|
||||
ca = ComicArchive( match_set.filename, settings.rar_exe_path )
|
||||
md = create_local_metadata( opts, ca, ca.hasMetadata(opts.data_style) )
|
||||
cv_md = actual_issue_data_fetch(match_set.matches[int(i)], settings)
|
||||
cv_md = actual_issue_data_fetch(match_set.matches[int(i)], settings, opts)
|
||||
md.overlay( cv_md )
|
||||
actual_metadata_save( ca, opts, md )
|
||||
|
||||
@ -209,7 +211,7 @@ def process_file_cli( filename, opts, settings, match_results ):
|
||||
|
||||
batch_mode = len( opts.file_list ) > 1
|
||||
|
||||
ca = ComicArchive(filename, settings)
|
||||
ca = ComicArchive(filename, settings.rar_exe_path)
|
||||
|
||||
if not os.path.lexists( filename ):
|
||||
print >> sys.stderr,"Cannot find "+ filename
|
||||
@ -346,7 +348,9 @@ def process_file_cli( filename, opts, settings, match_results ):
|
||||
if opts.issue_id is not None:
|
||||
# we were given the actual ID to search with
|
||||
try:
|
||||
cv_md = ComicVineTalker().fetchIssueDataByIssueID( opts.issue_id, settings )
|
||||
comicVine = ComicVineTalker()
|
||||
comicVine.wait_for_rate_limit = opts.wait_and_retry_on_rate_limit
|
||||
cv_md = comicVine.fetchIssueDataByIssueID( opts.issue_id, settings )
|
||||
except ComicVineTalkerException:
|
||||
print >> sys.stderr,"Network error while getting issue details. Save aborted"
|
||||
match_results.fetchDataFailures.append(filename)
|
||||
@ -374,6 +378,7 @@ def process_file_cli( filename, opts, settings, match_results ):
|
||||
# use our overlayed MD struct to search
|
||||
ii.setAdditionalMetadata( md )
|
||||
ii.onlyUseAdditionalMetaData = True
|
||||
ii.waitAndRetryOnRateLimit = opts.wait_and_retry_on_rate_limit
|
||||
ii.setOutputFunction( myoutput )
|
||||
ii.cover_page_index = md.getCoverPageIndexList()[0]
|
||||
matches = ii.search()
|
||||
@ -421,7 +426,7 @@ def process_file_cli( filename, opts, settings, match_results ):
|
||||
# we got here, so we have a single match
|
||||
|
||||
# now get the particular issue data
|
||||
cv_md = actual_issue_data_fetch(matches[0], settings)
|
||||
cv_md = actual_issue_data_fetch(matches[0], settings, opts)
|
||||
if cv_md is None:
|
||||
match_results.fetchDataFailures.append(filename)
|
||||
return
|
||||
|
@ -42,12 +42,12 @@ sys.path.insert(0, os.path.abspath(".") )
|
||||
import UnRAR2
|
||||
from UnRAR2.rar_exceptions import *
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
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
|
||||
|
||||
class MetaDataStyle:
|
||||
CBI = 0
|
||||
@ -239,9 +239,9 @@ class ZipArchiver:
|
||||
class RarArchiver:
|
||||
|
||||
devnull = None
|
||||
def __init__( self, path, settings ):
|
||||
def __init__( self, path, rar_exe_path ):
|
||||
self.path = path
|
||||
self.settings = settings
|
||||
self.rar_exe_path = rar_exe_path
|
||||
|
||||
if RarArchiver.devnull is None:
|
||||
RarArchiver.devnull = open(os.devnull, "w")
|
||||
@ -264,7 +264,7 @@ class RarArchiver:
|
||||
|
||||
def setArchiveComment( self, comment ):
|
||||
|
||||
if self.settings.rar_exe_path is not None:
|
||||
if self.rar_exe_path is not None:
|
||||
try:
|
||||
# write comment to temp file
|
||||
tmp_fd, tmp_name = tempfile.mkstemp()
|
||||
@ -275,7 +275,7 @@ class RarArchiver:
|
||||
working_dir = os.path.dirname( os.path.abspath( self.path ) )
|
||||
|
||||
# use external program to write comment to Rar archive
|
||||
subprocess.call([self.settings.rar_exe_path, 'c', '-w' + working_dir , '-c-', '-z' + tmp_name, self.path],
|
||||
subprocess.call([self.rar_exe_path, 'c', '-w' + working_dir , '-c-', '-z' + tmp_name, self.path],
|
||||
startupinfo=self.startupinfo,
|
||||
stdout=RarArchiver.devnull)
|
||||
|
||||
@ -333,7 +333,7 @@ class RarArchiver:
|
||||
|
||||
def writeArchiveFile( self, archive_file, data ):
|
||||
|
||||
if self.settings.rar_exe_path is not None:
|
||||
if self.rar_exe_path is not None:
|
||||
try:
|
||||
tmp_folder = tempfile.mkdtemp()
|
||||
|
||||
@ -348,7 +348,7 @@ class RarArchiver:
|
||||
f.close()
|
||||
|
||||
# use external program to write file to Rar archive
|
||||
subprocess.call([self.settings.rar_exe_path, 'a', '-w' + working_dir ,'-c-', '-ep', self.path, tmp_file],
|
||||
subprocess.call([self.rar_exe_path, 'a', '-w' + working_dir ,'-c-', '-ep', self.path, tmp_file],
|
||||
startupinfo=self.startupinfo,
|
||||
stdout=RarArchiver.devnull)
|
||||
|
||||
@ -364,10 +364,10 @@ class RarArchiver:
|
||||
return False
|
||||
|
||||
def removeArchiveFile( self, archive_file ):
|
||||
if self.settings.rar_exe_path is not None:
|
||||
if self.rar_exe_path is not None:
|
||||
try:
|
||||
# use external program to remove file from Rar archive
|
||||
subprocess.call([self.settings.rar_exe_path, 'd','-c-', self.path, archive_file],
|
||||
subprocess.call([self.rar_exe_path, 'd','-c-', self.path, archive_file],
|
||||
startupinfo=self.startupinfo,
|
||||
stdout=RarArchiver.devnull)
|
||||
|
||||
@ -515,17 +515,17 @@ class ComicArchive:
|
||||
class ArchiveType:
|
||||
Zip, Rar, Folder, Unknown = range(4)
|
||||
|
||||
def __init__( self, path, settings ):
|
||||
def __init__( self, path, rar_exe_path=None ):
|
||||
self.path = path
|
||||
|
||||
self.rar_exe_path = rar_exe_path
|
||||
self.ci_xml_filename = 'ComicInfo.xml'
|
||||
self.comet_default_filename = 'CoMet.xml'
|
||||
self.resetCache()
|
||||
self.settings = settings
|
||||
|
||||
if self.rarTest():
|
||||
self.archive_type = self.ArchiveType.Rar
|
||||
self.archiver = RarArchiver( self.path, settings )
|
||||
self.archiver = RarArchiver( self.path, rar_exe_path=self.rar_exe_path )
|
||||
|
||||
elif self.zipTest():
|
||||
self.archive_type = self.ArchiveType.Zip
|
||||
@ -588,7 +588,7 @@ class ComicArchive:
|
||||
if self.archive_type == self.ArchiveType.Unknown :
|
||||
return False
|
||||
|
||||
elif check_rar_status and self.isRar() and self.settings.rar_exe_path is None:
|
||||
elif check_rar_status and self.isRar() and self.rar_exe_path is None:
|
||||
return False
|
||||
|
||||
elif not os.access(self.path, os.W_OK):
|
||||
@ -764,7 +764,7 @@ class ComicArchive:
|
||||
# make a sub-list of image files
|
||||
self.page_list = []
|
||||
for name in files:
|
||||
if ( name[-4:].lower() in [ ".jpg", "jpeg", ".png", ".gif" ] and os.path.basename(name)[0] != "." ):
|
||||
if ( name[-4:].lower() in [ ".jpg", "jpeg", ".png", ".gif", "webp" ] and os.path.basename(name)[0] != "." ):
|
||||
self.page_list.append(name)
|
||||
|
||||
return self.page_list
|
||||
@ -1016,7 +1016,7 @@ class ComicArchive:
|
||||
|
||||
|
||||
|
||||
def metadataFromFilename( self ):
|
||||
def metadataFromFilename( self , parse_scan_info=True):
|
||||
|
||||
metadata = GenericMetadata()
|
||||
|
||||
@ -1033,7 +1033,7 @@ class ComicArchive:
|
||||
metadata.year = fnp.year
|
||||
if fnp.issue_count != "":
|
||||
metadata.issueCount = fnp.issue_count
|
||||
if self.settings.parse_scan_info:
|
||||
if parse_scan_info:
|
||||
if fnp.remainder != "":
|
||||
metadata.scanInfo = fnp.remainder
|
||||
|
||||
|
@ -55,19 +55,50 @@ class CVTypeID:
|
||||
Issue = "4000"
|
||||
|
||||
class ComicVineTalkerException(Exception):
|
||||
pass
|
||||
Unknown = -1
|
||||
Network = -2
|
||||
InvalidKey = 100
|
||||
RateLimit = 107
|
||||
|
||||
def __init__(self, code=-1, desc=""):
|
||||
self.desc = desc
|
||||
self.code = code
|
||||
|
||||
def __str__(self):
|
||||
if (self.code == ComicVineTalkerException.Unknown or
|
||||
self.code == ComicVineTalkerException.Network):
|
||||
return self.desc
|
||||
else:
|
||||
return "CV error #{0}: [{1}]. \n".format( self.code, self.desc )
|
||||
|
||||
|
||||
|
||||
class ComicVineTalker(QObject):
|
||||
|
||||
logo_url = "http://static.comicvine.com/bundles/comicvinesite/images/logo.png"
|
||||
api_key = ""
|
||||
|
||||
def __init__(self, api_key=""):
|
||||
@staticmethod
|
||||
def getRateLimitMessage():
|
||||
if ComicVineTalker.api_key == "":
|
||||
return "Comic Vine rate limit exceeded. You should configue your own Comic Vine API key."
|
||||
else:
|
||||
return "Comic Vine rate limit exceeded. Please wait a bit."
|
||||
|
||||
|
||||
def __init__(self):
|
||||
QObject.__init__(self)
|
||||
|
||||
self.api_base_url = "http://www.comicvine.com/api"
|
||||
self.wait_for_rate_limit = False
|
||||
|
||||
# key that is registered to comictagger
|
||||
self.api_key = '27431e6787042105bd3e47e169a624521f89f3a4'
|
||||
default_api_key = '27431e6787042105bd3e47e169a624521f89f3a4'
|
||||
|
||||
if ComicVineTalker.api_key == "":
|
||||
self.api_key = default_api_key
|
||||
else:
|
||||
self.api_key = ComicVineTalker.api_key
|
||||
|
||||
self.log_func = None
|
||||
|
||||
@ -95,9 +126,9 @@ class ComicVineTalker(QObject):
|
||||
day = parts[2]
|
||||
return day, month, year
|
||||
|
||||
def testKey( self ):
|
||||
def testKey( self, key ):
|
||||
|
||||
test_url = self.api_base_url + "/issue/1/?api_key=" + self.api_key + "&format=json&field_list=name"
|
||||
test_url = self.api_base_url + "/issue/1/?api_key=" + key + "&format=json&field_list=name"
|
||||
resp = urllib2.urlopen( test_url )
|
||||
content = resp.read()
|
||||
|
||||
@ -106,6 +137,36 @@ class ComicVineTalker(QObject):
|
||||
# Bogus request, but if the key is wrong, you get error 100: "Invalid API Key"
|
||||
return cv_response[ 'status_code' ] != 100
|
||||
|
||||
"""
|
||||
Get the contect from the CV server. If we're in "wait mode" and status code is a rate limit error
|
||||
sleep for a bit and retry.
|
||||
"""
|
||||
def getCVContent(self, url):
|
||||
total_time_waited = 0
|
||||
limit_wait_time = 1
|
||||
counter = 0
|
||||
wait_times = [1,2,3,4]
|
||||
while True:
|
||||
content = self.getUrlContent(url)
|
||||
cv_response = json.loads(content)
|
||||
if self.wait_for_rate_limit and cv_response[ 'status_code' ] == ComicVineTalkerException.RateLimit:
|
||||
self.writeLog( "Rate limit encountered. Waiting for {0} minutes\n".format(limit_wait_time))
|
||||
time.sleep(limit_wait_time * 60)
|
||||
total_time_waited += limit_wait_time
|
||||
limit_wait_time = wait_times[counter]
|
||||
if counter < 3:
|
||||
counter += 1
|
||||
# don't wait much more than 20 minutes
|
||||
if total_time_waited < 20:
|
||||
continue
|
||||
if cv_response[ 'status_code' ] != 1:
|
||||
self.writeLog( "Comic Vine query failed with error #{0}: [{1}]. \n".format( cv_response[ 'status_code' ], cv_response[ 'error' ] ))
|
||||
raise ComicVineTalkerException(cv_response[ 'status_code' ], cv_response[ 'error' ] )
|
||||
else:
|
||||
# it's all good
|
||||
break
|
||||
return cv_response
|
||||
|
||||
def getUrlContent( self, url ):
|
||||
# connect to server:
|
||||
# if there is a 500 error, try a few more times before giving up
|
||||
@ -126,9 +187,9 @@ class ComicVineTalker(QObject):
|
||||
|
||||
except Exception as e:
|
||||
self.writeLog( str(e) + "\n" )
|
||||
raise ComicVineTalkerException("Network Error!")
|
||||
raise ComicVineTalkerException(ComicVineTalkerException.Network, "Network Error!")
|
||||
|
||||
raise ComicVineTalkerException("Error on Comic Vine server")
|
||||
raise ComicVineTalkerException(ComicVineTalkerException.Unknown, "Error on Comic Vine server")
|
||||
|
||||
def searchForSeries( self, series_name , callback=None, refresh_cache=False ):
|
||||
|
||||
@ -161,14 +222,8 @@ class ComicVineTalker(QObject):
|
||||
query_string = urllib.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 + "&field_list=name,id,start_year,publisher,image,description,count_of_issues"
|
||||
content = self.getUrlContent(search_url + "&page=1")
|
||||
|
||||
cv_response = json.loads(content)
|
||||
|
||||
if cv_response[ 'status_code' ] != 1:
|
||||
self.writeLog( "Comic Vine query failed with error: [{0}]. \n".format( cv_response[ 'error' ] ))
|
||||
return None
|
||||
|
||||
cv_response = self.getCVContent(search_url + "&page=1")
|
||||
|
||||
search_results = list()
|
||||
|
||||
# see http://api.comicvine.com/documentation/#handling_responses
|
||||
@ -190,14 +245,9 @@ class ComicVineTalker(QObject):
|
||||
if callback is None:
|
||||
self.writeLog("getting another page of results {0} of {1}...\n".format( current_result_count, total_result_count))
|
||||
page += 1
|
||||
|
||||
cv_response = self.getCVContent(search_url + "&page="+str(page))
|
||||
|
||||
content = self.getUrlContent(search_url + "&page="+str(page))
|
||||
|
||||
cv_response = json.loads(content)
|
||||
|
||||
if cv_response[ 'status_code' ] != 1:
|
||||
self.writeLog( "Comic Vine query failed with error: [{0}]. \n".format( cv_response[ 'error' ] ))
|
||||
return None
|
||||
search_results.extend( cv_response['results'])
|
||||
current_result_count += cv_response['number_of_page_results']
|
||||
|
||||
@ -229,12 +279,7 @@ class ComicVineTalker(QObject):
|
||||
|
||||
volume_url = self.api_base_url + "/volume/" + CVTypeID.Volume + "-" + str(series_id) + "/?api_key=" + self.api_key + "&field_list=name,id,start_year,publisher,count_of_issues&format=json"
|
||||
|
||||
content = self.getUrlContent(volume_url)
|
||||
cv_response = json.loads(content)
|
||||
|
||||
if cv_response[ 'status_code' ] != 1:
|
||||
print >> sys.stderr, "Comic Vine query failed with error: [{0}]. ".format( cv_response[ 'error' ] )
|
||||
return None
|
||||
cv_response = self.getCVContent(volume_url)
|
||||
|
||||
volume_results = cv_response['results']
|
||||
|
||||
@ -254,12 +299,8 @@ class ComicVineTalker(QObject):
|
||||
|
||||
#---------------------------------
|
||||
issues_url = self.api_base_url + "/issues/" + "?api_key=" + self.api_key + "&filter=volume:" + str(series_id) + "&field_list=id,volume,issue_number,name,image,cover_date,site_detail_url,description&format=json"
|
||||
content = self.getUrlContent(issues_url)
|
||||
cv_response = json.loads(content)
|
||||
|
||||
if cv_response[ 'status_code' ] != 1:
|
||||
print >> sys.stderr, "Comic Vine query failed with error: [{0}]. ".format( cv_response[ 'error' ] )
|
||||
return None
|
||||
cv_response = self.getCVContent(issues_url)
|
||||
|
||||
#------------------------------------
|
||||
|
||||
limit = cv_response['limit']
|
||||
@ -279,12 +320,8 @@ class ComicVineTalker(QObject):
|
||||
offset += cv_response['number_of_page_results']
|
||||
|
||||
#print issues_url+ "&offset="+str(offset)
|
||||
content = self.getUrlContent(issues_url + "&offset="+str(offset))
|
||||
cv_response = json.loads(content)
|
||||
|
||||
if cv_response[ 'status_code' ] != 1:
|
||||
self.writeLog( "Comic Vine query failed with error: [{0}]. \n".format( cv_response[ 'error' ] ))
|
||||
return None
|
||||
cv_response = self.getCVContent(issues_url + "&offset="+str(offset))
|
||||
|
||||
volume_issues_result.extend( cv_response['results'])
|
||||
current_result_count += cv_response['number_of_page_results']
|
||||
|
||||
@ -310,12 +347,8 @@ class ComicVineTalker(QObject):
|
||||
|
||||
issues_url = self.api_base_url + "/issues/" + "?api_key=" + self.api_key + filter + "&field_list=id,volume,issue_number,name,image,cover_date,site_detail_url,description&format=json"
|
||||
|
||||
content = self.getUrlContent(issues_url)
|
||||
cv_response = json.loads(content)
|
||||
|
||||
if cv_response[ 'status_code' ] != 1:
|
||||
print >> sys.stderr, "Comic Vine query failed with error: [{0}]. ".format( cv_response[ 'error' ] )
|
||||
return None
|
||||
cv_response = self.getCVContent(issues_url)
|
||||
|
||||
#------------------------------------
|
||||
|
||||
limit = cv_response['limit']
|
||||
@ -335,12 +368,8 @@ class ComicVineTalker(QObject):
|
||||
offset += cv_response['number_of_page_results']
|
||||
|
||||
#print issues_url+ "&offset="+str(offset)
|
||||
content = self.getUrlContent(issues_url + "&offset="+str(offset))
|
||||
cv_response = json.loads(content)
|
||||
|
||||
if cv_response[ 'status_code' ] != 1:
|
||||
self.writeLog( "Comic Vine query failed with error: [{0}]. \n".format( cv_response[ 'error' ] ))
|
||||
return None
|
||||
cv_response = self.getCVContent(issues_url + "&offset="+str(offset))
|
||||
|
||||
filtered_issues_result.extend( cv_response['results'])
|
||||
current_result_count += cv_response['number_of_page_results']
|
||||
|
||||
@ -366,11 +395,7 @@ class ComicVineTalker(QObject):
|
||||
if (found):
|
||||
issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + str(record['id']) + "/?api_key=" + self.api_key + "&format=json"
|
||||
|
||||
content = self.getUrlContent(issue_url)
|
||||
cv_response = json.loads(content)
|
||||
if cv_response[ 'status_code' ] != 1:
|
||||
print >> sys.stderr, "Comic Vine query failed with error: [{0}]. ".format( cv_response[ 'error' ] )
|
||||
return None
|
||||
cv_response = self.getCVContent(issue_url)
|
||||
issue_results = cv_response['results']
|
||||
|
||||
else:
|
||||
@ -382,11 +407,7 @@ class ComicVineTalker(QObject):
|
||||
def fetchIssueDataByIssueID( self, issue_id, settings ):
|
||||
|
||||
issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + str(issue_id) + "/?api_key=" + self.api_key + "&format=json"
|
||||
content = self.getUrlContent(issue_url)
|
||||
cv_response = json.loads(content)
|
||||
if cv_response[ 'status_code' ] != 1:
|
||||
print >> sys.stderr, "Comic Vine query failed with error: [{0}]. ".format( cv_response[ 'error' ] )
|
||||
return None
|
||||
cv_response = self.getCVContent(issue_url)
|
||||
|
||||
issue_results = cv_response['results']
|
||||
|
||||
@ -412,7 +433,7 @@ class ComicVineTalker(QObject):
|
||||
metadata.day, metadata.month, metadata.year = self.parseDateStr( issue_results['cover_date'] )
|
||||
|
||||
#metadata.issueCount = volume_results['count_of_issues']
|
||||
metadata.comments = self.cleanup_html(issue_results['description'])
|
||||
metadata.comments = self.cleanup_html(issue_results['description'], settings.remove_html_tables)
|
||||
if settings.use_series_start_as_volume:
|
||||
metadata.volume = volume_results['start_year']
|
||||
|
||||
@ -458,11 +479,25 @@ class ComicVineTalker(QObject):
|
||||
metadata.storyArc = utils.listToString(arc_list)
|
||||
|
||||
return metadata
|
||||
|
||||
def cleanup_html( self, string):
|
||||
def cleanup_html( self, string, remove_html_tables):
|
||||
"""
|
||||
converter = html2text.HTML2Text()
|
||||
#converter.emphasis_mark = '*'
|
||||
#converter.ignore_links = True
|
||||
converter.body_width = 0
|
||||
|
||||
print html2text.html2text(string)
|
||||
return string
|
||||
#return converter.handle(string)
|
||||
"""
|
||||
|
||||
|
||||
if string is None:
|
||||
return ""
|
||||
# find any tables
|
||||
soup = BeautifulSoup(string)
|
||||
tables = soup.findAll('table')
|
||||
|
||||
# remove all newlines first
|
||||
string = string.replace("\n", "")
|
||||
|
||||
@ -471,6 +506,14 @@ class ComicVineTalker(QObject):
|
||||
string = string.replace("</p>", "\n\n")
|
||||
string = string.replace("<h4>", "*")
|
||||
string = string.replace("</h4>", "*\n")
|
||||
|
||||
#remove the tables
|
||||
p = re.compile(r'<table[^<]*?>.*?<\/table>')
|
||||
if remove_html_tables:
|
||||
string = p.sub('',string)
|
||||
string = string.replace("*List of covers and their creators:*","")
|
||||
else:
|
||||
string = p.sub('{}',string)
|
||||
|
||||
# now strip all other tags
|
||||
p = re.compile(r'<[^<]*?>')
|
||||
@ -480,6 +523,57 @@ class ComicVineTalker(QObject):
|
||||
newstring = newstring.replace('&','&')
|
||||
|
||||
newstring = newstring.strip()
|
||||
|
||||
if not remove_html_tables:
|
||||
# now rebuild the tables into text from BSoup
|
||||
try:
|
||||
table_strings = []
|
||||
for table in tables:
|
||||
rows = []
|
||||
hdrs = []
|
||||
col_widths = []
|
||||
for hdr in table.findAll('th'):
|
||||
item = hdr.string.strip()
|
||||
hdrs.append(item)
|
||||
col_widths.append(len(item))
|
||||
rows.append(hdrs)
|
||||
|
||||
for row in table.findAll('tr'):
|
||||
cols = []
|
||||
col = row.findAll('td')
|
||||
i = 0
|
||||
for c in col:
|
||||
item = c.string.strip()
|
||||
cols.append(item)
|
||||
if len(item) > col_widths[i]:
|
||||
col_widths[i] = len(item)
|
||||
i += 1
|
||||
if len(cols) != 0:
|
||||
rows.append(cols)
|
||||
# now we have the data, make it into text
|
||||
fmtstr =""
|
||||
for w in col_widths:
|
||||
fmtstr += " {{:{}}}|".format(w+1)
|
||||
width = sum(col_widths) + len(col_widths)*2
|
||||
print "width=" , width
|
||||
table_text = ""
|
||||
counter = 0
|
||||
for row in rows:
|
||||
table_text += fmtstr.format(*row) + "\n"
|
||||
if counter == 0 and len(hdrs)!= 0:
|
||||
table_text += "-" * width + "\n"
|
||||
counter += 1
|
||||
|
||||
table_strings.append(table_text)
|
||||
|
||||
newstring = newstring.format(*table_strings)
|
||||
except:
|
||||
# we caught an error rebuilding the table.
|
||||
# just bail and remove the formatting
|
||||
print "table parse error"
|
||||
newstring.replace("{}", "")
|
||||
|
||||
|
||||
return newstring
|
||||
|
||||
def fetchIssueDate( self, issue_id ):
|
||||
@ -504,19 +598,14 @@ class ComicVineTalker(QObject):
|
||||
|
||||
issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + str(issue_id) + "/?api_key=" + self.api_key + "&format=json&field_list=image,cover_date,site_detail_url"
|
||||
|
||||
content = self.getUrlContent(issue_url)
|
||||
|
||||
details = dict()
|
||||
details['image_url'] = None
|
||||
details['thumb_image_url'] = None
|
||||
details['cover_date'] = None
|
||||
details['site_detail_url'] = None
|
||||
|
||||
cv_response = json.loads(content)
|
||||
if cv_response[ 'status_code' ] != 1:
|
||||
print >> sys.stderr, "Comic Vine query failed with error: [{0}]. ".format( cv_response[ 'error' ] )
|
||||
return details
|
||||
|
||||
cv_response = self.getCVContent(issue_url)
|
||||
|
||||
details['image_url'] = cv_response['results']['image']['super_url']
|
||||
details['thumb_image_url'] = cv_response['results']['image']['thumb_url']
|
||||
details['cover_date'] = cv_response['results']['cover_date']
|
||||
@ -665,4 +754,4 @@ class ComicVineTalker(QObject):
|
||||
issue['image'] = dict()
|
||||
issue['image']['super_url'] = ComicVineTalker.logo_url
|
||||
issue['image']['thumb_url'] = ComicVineTalker.logo_url
|
||||
|
||||
|
||||
|
@ -237,8 +237,7 @@ class CoverImageWidget(QWidget):
|
||||
|
||||
# called when the image is done loading from internet
|
||||
def coverRemoteFetchComplete( self, image_data, issue_id ):
|
||||
img = QImage()
|
||||
img.loadFromData( image_data )
|
||||
img = utils.getQImageFromData( image_data)
|
||||
self.current_pixmap = QPixmap(img)
|
||||
self.setDisplayPixmap( 0, 0)
|
||||
#print "ATB cover fetch complete!"
|
||||
|
@ -1,3 +1,3 @@
|
||||
# This file should contan only these comments, and the line below.
|
||||
# Used by packaging makefiles and app
|
||||
version="1.1.12-beta"
|
||||
version="1.1.15-beta"
|
||||
|
@ -31,7 +31,10 @@ from settings import ComicTaggerSettings
|
||||
from comicarchive import ComicArchive
|
||||
from comicarchive import MetaDataStyle
|
||||
from genericmetadata import GenericMetadata, PageType
|
||||
from optionalmsgdialog import OptionalMessageDialog
|
||||
import utils
|
||||
import platform
|
||||
import os
|
||||
|
||||
class FileTableWidget( QTableWidget ):
|
||||
|
||||
@ -172,7 +175,6 @@ class FileSelectionList(QWidget):
|
||||
def addPathList( self, pathlist ):
|
||||
|
||||
filelist = utils.get_recursive_filelist( pathlist )
|
||||
|
||||
# we now have a list of files to add
|
||||
|
||||
progdialog = QProgressDialog("", "Cancel", 0, len(filelist), self)
|
||||
@ -196,6 +198,24 @@ class FileSelectionList(QWidget):
|
||||
firstAdded = row
|
||||
|
||||
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"):
|
||||
for f in filelist:
|
||||
ext = os.path.splitext(f)[1].lower()
|
||||
if ext == ".rar" or ext ==".cbr":
|
||||
checked = OptionalMessageDialog.msg( self, "No unrar tool",
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
)
|
||||
self.settings.show_no_unrar_warning = not checked
|
||||
break
|
||||
|
||||
if firstAdded is not None:
|
||||
self.twList.selectRow(firstAdded)
|
||||
else:
|
||||
@ -226,7 +246,17 @@ class FileSelectionList(QWidget):
|
||||
return True
|
||||
r = r + 1
|
||||
|
||||
return False
|
||||
return False
|
||||
|
||||
def getCurrentListRow( self, path ):
|
||||
r = 0
|
||||
while r < self.twList.rowCount():
|
||||
ca = self.getArchiveByRow( r )
|
||||
if ca.path == path:
|
||||
return r
|
||||
r = r + 1
|
||||
|
||||
return -1
|
||||
|
||||
def addPathItem( self, path):
|
||||
path = unicode( path )
|
||||
@ -234,9 +264,9 @@ class FileSelectionList(QWidget):
|
||||
#print "processing", path
|
||||
|
||||
if self.isListDupe(path):
|
||||
return None
|
||||
return self.getCurrentListRow(path)
|
||||
|
||||
ca = ComicArchive( path, self.settings )
|
||||
ca = ComicArchive( path, self.settings.rar_exe_path )
|
||||
|
||||
if ca.seemsToBeAComicArchive() :
|
||||
row = self.twList.rowCount()
|
||||
|
@ -31,7 +31,7 @@ class PageType:
|
||||
InnerCover = "InnerCover"
|
||||
Roundup = "Roundup"
|
||||
Story = "Story"
|
||||
Advertisment = "Advertisement"
|
||||
Advertisement = "Advertisement"
|
||||
Editorial = "Editorial"
|
||||
Letters = "Letters"
|
||||
Preview = "Preview"
|
||||
|
@ -21,7 +21,8 @@ import StringIO
|
||||
import sys
|
||||
|
||||
try:
|
||||
import Image
|
||||
from PIL import Image
|
||||
from PIL import WebPImagePlugin
|
||||
pil_available = True
|
||||
except ImportError:
|
||||
pil_available = False
|
||||
|
@ -23,7 +23,8 @@ import math
|
||||
import urllib2, urllib
|
||||
import StringIO
|
||||
try:
|
||||
import Image
|
||||
from PIL import Image
|
||||
from PIL import WebPImagePlugin
|
||||
pil_available = True
|
||||
except ImportError:
|
||||
pil_available = False
|
||||
@ -83,6 +84,7 @@ class IssueIdentifier:
|
||||
self.search_result = self.ResultNoMatches
|
||||
self.cover_page_index = 0
|
||||
self.cancel = False
|
||||
self.waitAndRetryOnRateLimit = False
|
||||
|
||||
def setScoreMinThreshold( self, thresh ):
|
||||
self.min_score_thresh = thresh
|
||||
@ -377,8 +379,9 @@ class IssueIdentifier:
|
||||
self.log_msg( "\tMonth : " + str(keys['month']) )
|
||||
|
||||
#self.log_msg("Publisher Blacklist: " + str(self.publisher_blacklist))
|
||||
|
||||
comicVine = ComicVineTalker( )
|
||||
comicVine.wait_for_rate_limit = self.waitAndRetryOnRateLimit
|
||||
|
||||
comicVine.setLogFunc( self.output_function )
|
||||
|
||||
#self.log_msg( ( "Searching for " + keys['series'] + "...")
|
||||
@ -393,6 +396,9 @@ class IssueIdentifier:
|
||||
if self.cancel == True:
|
||||
return []
|
||||
|
||||
if cv_search_results == None:
|
||||
return []
|
||||
|
||||
series_second_round_list = []
|
||||
|
||||
#self.log_msg( "Removing results with too long names, banned publishers, or future start dates" )
|
||||
@ -443,6 +449,9 @@ class IssueIdentifier:
|
||||
self.log_msg( "Network issue while searching for series details. Aborting...")
|
||||
return []
|
||||
|
||||
if issue_list is None:
|
||||
return []
|
||||
|
||||
shortlist = list()
|
||||
#now re-associate the issues and volumes
|
||||
for issue in issue_list:
|
||||
|
@ -92,12 +92,15 @@ class IssueSelectionWindow(QtGui.QDialog):
|
||||
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
|
||||
|
||||
try:
|
||||
comicVine = ComicVineTalker( )
|
||||
comicVine = ComicVineTalker()
|
||||
volume_data = comicVine.fetchVolumeData( self.series_id )
|
||||
self.issue_list = comicVine.fetchIssuesByVolume( self.series_id )
|
||||
except ComicVineTalkerException:
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
QtGui.QMessageBox.critical(self, self.tr("Network Issue"), self.tr("Could not connect to ComicVine to list issues!"))
|
||||
except ComicVineTalkerException as e:
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
if e.code == ComicVineTalkerException.RateLimit:
|
||||
QtGui.QMessageBox.critical(self, self.tr("Comic Vine Error"), ComicVineTalker.getRateLimitMessage())
|
||||
else:
|
||||
QtGui.QMessageBox.critical(self, self.tr("Network Issue"), self.tr("Could not connect to ComicVine to list issues!"))
|
||||
return
|
||||
|
||||
while self.twList.rowCount() > 0:
|
||||
|
@ -43,6 +43,12 @@ class IssueString:
|
||||
|
||||
if text is None:
|
||||
return
|
||||
|
||||
if type(text) == int:
|
||||
text = str(text)
|
||||
|
||||
if len(text) == 0:
|
||||
return
|
||||
|
||||
text = unicode(text)
|
||||
|
||||
|
@ -28,6 +28,7 @@ import utils
|
||||
import cli
|
||||
from settings import ComicTaggerSettings
|
||||
from options import Options
|
||||
from comicvinetalker import ComicVineTalker
|
||||
|
||||
try:
|
||||
qt_available = True
|
||||
@ -40,9 +41,20 @@ except ImportError as e:
|
||||
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.only_set_key:
|
||||
print "Key set"
|
||||
return
|
||||
|
||||
ComicVineTalker.api_key = settings.cv_api_key
|
||||
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
@ -64,7 +76,7 @@ def ctmain():
|
||||
app.processEvents()
|
||||
|
||||
try:
|
||||
tagger_window = TaggerWindow( opts.file_list, settings )
|
||||
tagger_window = TaggerWindow( opts.file_list, settings, opts=opts )
|
||||
tagger_window.show()
|
||||
|
||||
if platform.system() != "Linux":
|
||||
|
@ -60,7 +60,7 @@ class OptionalMessageDialog(QDialog):
|
||||
if style == StyleQuestion:
|
||||
check_text = "Remember this answer"
|
||||
else:
|
||||
check_text = "Don't show this dialog again"
|
||||
check_text = "Don't show this message again"
|
||||
|
||||
self.theCheckBox = QCheckBox(check_text)
|
||||
|
||||
|
@ -42,50 +42,53 @@ A utility for reading and writing metadata to comic archives.
|
||||
|
||||
If no options are given, {0} will run in windowed mode
|
||||
|
||||
-p, --print Print out tag info from file. Specify type
|
||||
(via -t) to get only info of that tag type
|
||||
--raw With -p, will print out the raw tag block(s)
|
||||
from the file
|
||||
-d, --delete Deletes the tag block of specified type (via -t)
|
||||
-c, --copy=SOURCE Copy the specified source tag block to destination style
|
||||
specified via via -t (potentially lossy operation)
|
||||
-s, --save Save out tags as specified type (via -t)
|
||||
Must specify also at least -o, -p, or -m
|
||||
--nooverwrite Don't modify tag block if it already exists ( relevent for -s or -c )
|
||||
-1, --assume-issue-one Assume issue number is 1 if not found ( relevent for -s )
|
||||
-n, --dryrun Don't actually modify file (only relevent for -d, -s, or -r)
|
||||
-t, --type=TYPE Specify TYPE as either "CR", "CBL", or "COMET" (as either
|
||||
ComicRack, ComicBookLover, or CoMet style tags, respectivly)
|
||||
-f, --parsefilename Parse the filename to get some info, specifically
|
||||
series name, issue number, volume, and publication
|
||||
year
|
||||
-i, --interactive Interactively query the user when there are multiple matches for
|
||||
an online search
|
||||
--nosummary Suppress the default summary after a save operation
|
||||
-o, --online Search online and attempt to identify file using
|
||||
existing metadata and images in archive. May be used
|
||||
in conjuntion with -f and -m
|
||||
--id=ID Use the issue ID when searching online. Overrides all other metadata
|
||||
-m, --metadata=LIST Explicity define, as a list, some tags to be used
|
||||
e.g. "series=Plastic Man , publisher=Quality Comics"
|
||||
"series=Kickers^, Inc., issue=1, year=1986"
|
||||
Name-Value pairs are comma separated. Use a "^" to
|
||||
escape an "=" or a ",", as shown in the example above
|
||||
Some names that can be used:
|
||||
series, issue, issueCount, year, publisher, title
|
||||
-r, --rename Rename the file based on specified tag style.
|
||||
--noabort Don't abort save operation when online match is of low confidence
|
||||
-e, --export-to-zip Export RAR archive to Zip format
|
||||
--delete-rar Delete original RAR archive after successful export to Zip
|
||||
--abort-on-conflict Don't export to zip if intended new filename exists (Otherwise, creates
|
||||
a new unique filename)
|
||||
-S, --script=FILE Run an "add-on" python script that uses the comictagger library for custom
|
||||
processing. Script arguments can follow the script name
|
||||
-R, --recursive Recursively include files in sub-folders
|
||||
-v, --verbose Be noisy when doing what it does
|
||||
--terse Don't say much (for print mode)
|
||||
--version Display version
|
||||
-h, --help Display this message
|
||||
-p, --print Print out tag info from file. Specify type
|
||||
(via -t) to get only info of that tag type
|
||||
--raw With -p, will print out the raw tag block(s)
|
||||
from the file
|
||||
-d, --delete Deletes the tag block of specified type (via -t)
|
||||
-c, --copy=SOURCE Copy the specified source tag block to destination style
|
||||
specified via via -t (potentially lossy operation)
|
||||
-s, --save Save out tags as specified type (via -t)
|
||||
Must specify also at least -o, -p, or -m
|
||||
--nooverwrite Don't modify tag block if it already exists ( relevent for -s or -c )
|
||||
-1, --assume-issue-one Assume issue number is 1 if not found ( relevent for -s )
|
||||
-n, --dryrun Don't actually modify file (only relevent for -d, -s, or -r)
|
||||
-t, --type=TYPE Specify TYPE as either "CR", "CBL", or "COMET" (as either
|
||||
ComicRack, ComicBookLover, or CoMet style tags, respectivly)
|
||||
-f, --parsefilename Parse the filename to get some info, specifically
|
||||
series name, issue number, volume, and publication
|
||||
year
|
||||
-i, --interactive Interactively query the user when there are multiple matches for
|
||||
an online search
|
||||
--nosummary Suppress the default summary after a save operation
|
||||
-o, --online Search online and attempt to identify file using
|
||||
existing metadata and images in archive. May be used
|
||||
in conjuntion with -f and -m
|
||||
--id=ID Use the issue ID when searching online. Overrides all other metadata
|
||||
-m, --metadata=LIST Explicity define, as a list, some tags to be used
|
||||
e.g. "series=Plastic Man , publisher=Quality Comics"
|
||||
"series=Kickers^, Inc., issue=1, year=1986"
|
||||
Name-Value pairs are comma separated. Use a "^" to
|
||||
escape an "=" or a ",", as shown in the example above
|
||||
Some names that can be used:
|
||||
series, issue, issueCount, year, publisher, title
|
||||
-r, --rename Rename the file based on specified tag style.
|
||||
--noabort Don't abort save operation when online match is of low confidence
|
||||
-e, --export-to-zip Export RAR archive to Zip format
|
||||
--delete-rar Delete original RAR archive after successful export to Zip
|
||||
--abort-on-conflict Don't export to zip if intended new filename exists (Otherwise, creates
|
||||
a new unique filename)
|
||||
-S, --script=FILE Run an "add-on" python script that uses the comictagger library for custom
|
||||
processing. Script arguments can follow the script name
|
||||
-R, --recursive Recursively include files in sub-folders
|
||||
--cv-api-key=KEY Use the given Comic Vine API Key (persisted in settings)
|
||||
--only-set-cv-key Only set the Comiv Vine API key and quit
|
||||
-w, --wait-on-cv-rate-limit When encountering a Comic Vine rate limit error, wait and retry query
|
||||
-v, --verbose Be noisy when doing what it does
|
||||
--terse Don't say much (for print mode)
|
||||
--version Display version
|
||||
-h, --help Display this message
|
||||
|
||||
For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
"""
|
||||
@ -111,6 +114,8 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
self.parse_filename = False
|
||||
self.show_save_summary = True
|
||||
self.raw = False
|
||||
self.cv_api_key = None
|
||||
self.only_set_key = False
|
||||
self.rename_file = False
|
||||
self.no_overwrite = False
|
||||
self.interactive = False
|
||||
@ -118,8 +123,10 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
self.recursive = False
|
||||
self.run_script = False
|
||||
self.script = None
|
||||
self.wait_and_retry_on_rate_limit = False
|
||||
self.assume_issue_is_one_if_not_set = False
|
||||
self.file_list = []
|
||||
|
||||
|
||||
def display_msg_and_quit( self, msg, code, show_help=False ):
|
||||
appname = os.path.basename(sys.argv[0])
|
||||
@ -235,11 +242,12 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
# parse command line options
|
||||
try:
|
||||
opts, args = getopt.getopt( input_args,
|
||||
"hpdt:fm:vonsrc:ieRS:1",
|
||||
"hpdt:fm:vownsrc:ieRS:1",
|
||||
[ "help", "print", "delete", "type=", "copy=", "parsefilename", "metadata=", "verbose",
|
||||
"online", "dryrun", "save", "rename" , "raw", "noabort", "terse", "nooverwrite",
|
||||
"interactive", "nosummary", "version", "id=" , "recursive", "script=",
|
||||
"export-to-zip", "delete-rar", "abort-on-conflict", "assume-issue-one" ] )
|
||||
"export-to-zip", "delete-rar", "abort-on-conflict", "assume-issue-one",
|
||||
"cv-api-key=", "only-set-cv-key", "wait-on-cv-rate-limit" ] )
|
||||
|
||||
except getopt.GetoptError as err:
|
||||
self.display_msg_and_quit( str(err), 2 )
|
||||
@ -289,6 +297,8 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
self.abort_export_on_conflict = True
|
||||
if o in ("-f", "--parsefilename"):
|
||||
self.parse_filename = True
|
||||
if o in ("-w", "--wait-on-cv-rate-limit"):
|
||||
self.wait_and_retry_on_rate_limit = True
|
||||
if o == "--id":
|
||||
self.issue_id = a
|
||||
if o == "--raw":
|
||||
@ -303,6 +313,10 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
self.assume_issue_is_one_if_not_set = True
|
||||
if o == "--nooverwrite":
|
||||
self.no_overwrite = True
|
||||
if o == "--cv-api-key":
|
||||
self.cv_api_key = a
|
||||
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 "Distributed under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)"
|
||||
@ -322,7 +336,7 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
else:
|
||||
self.display_msg_and_quit( "Invalid tag type", 1 )
|
||||
|
||||
if self.print_tags or self.delete_tags or self.save_tags or self.copy_tags or self.rename_file or self.export_to_zip:
|
||||
if self.print_tags or self.delete_tags or self.save_tags or self.copy_tags or self.rename_file or self.export_to_zip or self.only_set_key:
|
||||
self.no_gui = True
|
||||
|
||||
count = 0
|
||||
@ -333,9 +347,10 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
if self.copy_tags: count += 1
|
||||
if self.rename_file: count += 1
|
||||
if self.export_to_zip: count +=1
|
||||
if self.only_set_key: count +=1
|
||||
|
||||
if count > 1:
|
||||
self.display_msg_and_quit( "Must choose only one action of print, delete, save, copy, rename, export, or run script", 1 )
|
||||
self.display_msg_and_quit( "Must choose only one action of print, delete, save, copy, rename, export, set key, or run script", 1 )
|
||||
|
||||
if self.script is not None:
|
||||
self.launch_script( self.script )
|
||||
@ -352,8 +367,11 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
else:
|
||||
self.filename = args[0]
|
||||
self.file_list = args
|
||||
|
||||
if self.only_set_key and self.cv_api_key == None:
|
||||
self.display_msg_and_quit( "Key not given!", 1 )
|
||||
|
||||
if self.no_gui and self.filename is None:
|
||||
if (self.only_set_key == False) and self.no_gui and (self.filename is None):
|
||||
self.display_msg_and_quit( "Command requires at least one filename!", 1 )
|
||||
|
||||
if self.delete_tags and self.data_style is None:
|
||||
|
@ -64,7 +64,7 @@ class PageListEditor(QWidget):
|
||||
pageTypeNames = {
|
||||
PageType.FrontCover: "Front Cover",
|
||||
PageType.InnerCover: "Inner Cover",
|
||||
PageType.Advertisment: "Advertisment",
|
||||
PageType.Advertisement: "Advertisement",
|
||||
PageType.Roundup: "Roundup",
|
||||
PageType.Story: "Story",
|
||||
PageType.Editorial: "Editorial",
|
||||
@ -92,7 +92,7 @@ class PageListEditor(QWidget):
|
||||
self.comboBox.addItem( "", "" )
|
||||
self.comboBox.addItem( self.pageTypeNames[ PageType.FrontCover], PageType.FrontCover )
|
||||
self.comboBox.addItem( self.pageTypeNames[ PageType.InnerCover], PageType.InnerCover )
|
||||
self.comboBox.addItem( self.pageTypeNames[ PageType.Advertisment], PageType.Advertisment )
|
||||
self.comboBox.addItem( self.pageTypeNames[ PageType.Advertisement], PageType.Advertisement )
|
||||
self.comboBox.addItem( self.pageTypeNames[ PageType.Roundup], PageType.Roundup )
|
||||
self.comboBox.addItem( self.pageTypeNames[ PageType.Story], PageType.Story )
|
||||
self.comboBox.addItem( self.pageTypeNames[ PageType.Editorial], PageType.Editorial )
|
||||
|
@ -22,6 +22,7 @@ from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt4.QtCore import pyqtSignal
|
||||
|
||||
from comicarchive import ComicArchive
|
||||
import utils
|
||||
|
||||
"""
|
||||
This class holds onto a reference of each instance in a list
|
||||
@ -66,8 +67,7 @@ class PageLoader( QtCore.QThread ):
|
||||
return
|
||||
|
||||
if image_data is not None:
|
||||
img = QtGui.QImage()
|
||||
img.loadFromData( image_data )
|
||||
img = utils.getQImageFromData( image_data)
|
||||
|
||||
if self.abandoned:
|
||||
return
|
||||
|
@ -72,7 +72,7 @@ class RenameWindow(QtGui.QDialog):
|
||||
|
||||
md = ca.readMetadata(self.data_style)
|
||||
if md.isEmpty:
|
||||
md = ca.metadataFromFilename()
|
||||
md = ca.metadataFromFilename(self.settings.parse_scan_info)
|
||||
self.renamer.setMetadata( md )
|
||||
new_name = self.renamer.determineName( ca.path, ext=new_ext )
|
||||
|
||||
|
@ -32,11 +32,15 @@ class ComicTaggerSettings:
|
||||
|
||||
@staticmethod
|
||||
def getSettingsFolder():
|
||||
filename_encoding = sys.getfilesystemencoding()
|
||||
if platform.system() == "Windows":
|
||||
return os.path.join( os.environ['APPDATA'], 'ComicTagger' )
|
||||
folder = os.path.join( os.environ['APPDATA'], 'ComicTagger' )
|
||||
else:
|
||||
return os.path.join( os.path.expanduser('~') , '.ComicTagger')
|
||||
|
||||
folder = os.path.join( os.path.expanduser('~') , '.ComicTagger')
|
||||
if folder is not None:
|
||||
folder = folder.decode(filename_encoding)
|
||||
return folder
|
||||
|
||||
frozen_win_exe_path = None
|
||||
|
||||
@staticmethod
|
||||
@ -94,12 +98,16 @@ class ComicTaggerSettings:
|
||||
self.show_disclaimer = True
|
||||
self.dont_notify_about_this_version = ""
|
||||
self.ask_about_usage_stats = True
|
||||
self.show_no_unrar_warning = True
|
||||
|
||||
#filename parsing settings
|
||||
self.parse_scan_info = True
|
||||
|
||||
# Comic Vine settings
|
||||
self.use_series_start_as_volume = False
|
||||
self.clear_form_before_populating_from_cv = False
|
||||
self.remove_html_tables = False
|
||||
self.cv_api_key = ""
|
||||
|
||||
# CBL Tranform settings
|
||||
|
||||
@ -107,6 +115,7 @@ class ComicTaggerSettings:
|
||||
self.copy_characters_to_tags = False
|
||||
self.copy_teams_to_tags = False
|
||||
self.copy_locations_to_tags = False
|
||||
self.copy_storyarcs_to_tags = False
|
||||
self.copy_notes_to_comments = False
|
||||
self.copy_weblink_to_comments = False
|
||||
self.apply_cbl_transform_on_cv_import = False
|
||||
@ -117,6 +126,14 @@ class ComicTaggerSettings:
|
||||
self.rename_issue_number_padding = 3
|
||||
self.rename_use_smart_string_cleanup = True
|
||||
self.rename_extension_based_on_archive = True
|
||||
|
||||
#Auto-tag stickies
|
||||
self.save_on_low_confidence = False
|
||||
self.dont_use_year_when_identifying = False
|
||||
self.assume_1_if_no_issue_num = False
|
||||
self.ignore_leading_numbers_in_filename = False
|
||||
self.remove_archive_after_successful_match = False
|
||||
self.wait_and_retry_on_rate_limit = False
|
||||
|
||||
def __init__(self):
|
||||
|
||||
@ -228,9 +245,17 @@ class ComicTaggerSettings:
|
||||
self.dont_notify_about_this_version = self.config.get( 'dialogflags', 'dont_notify_about_this_version' )
|
||||
if self.config.has_option('dialogflags', 'ask_about_usage_stats'):
|
||||
self.ask_about_usage_stats = self.config.getboolean( 'dialogflags', 'ask_about_usage_stats' )
|
||||
if self.config.has_option('dialogflags', 'show_no_unrar_warning'):
|
||||
self.show_no_unrar_warning = self.config.getboolean( 'dialogflags', 'show_no_unrar_warning' )
|
||||
|
||||
if self.config.has_option('comicvine', 'use_series_start_as_volume'):
|
||||
self.use_series_start_as_volume = self.config.getboolean( 'comicvine', 'use_series_start_as_volume' )
|
||||
if self.config.has_option('comicvine', 'clear_form_before_populating_from_cv'):
|
||||
self.clear_form_before_populating_from_cv = self.config.getboolean( 'comicvine', 'clear_form_before_populating_from_cv' )
|
||||
if self.config.has_option('comicvine', 'remove_html_tables'):
|
||||
self.remove_html_tables = self.config.getboolean( 'comicvine', 'remove_html_tables' )
|
||||
if self.config.has_option('comicvine', 'cv_api_key'):
|
||||
self.cv_api_key = self.config.get( 'comicvine', 'cv_api_key' )
|
||||
|
||||
if self.config.has_option('cbl_transform', 'assume_lone_credit_is_primary'):
|
||||
self.assume_lone_credit_is_primary = self.config.getboolean( 'cbl_transform', 'assume_lone_credit_is_primary' )
|
||||
@ -241,7 +266,9 @@ class ComicTaggerSettings:
|
||||
if self.config.has_option('cbl_transform', 'copy_locations_to_tags'):
|
||||
self.copy_locations_to_tags = self.config.getboolean( 'cbl_transform', 'copy_locations_to_tags' )
|
||||
if self.config.has_option('cbl_transform', 'copy_notes_to_comments'):
|
||||
self.copy_notes_to_comments = self.config.getboolean( 'cbl_transform', 'copy_notes_to_comments' )
|
||||
self.copy_notes_to_comments = self.config.getboolean( 'cbl_transform', 'copy_notes_to_comments' )
|
||||
if self.config.has_option('cbl_transform', 'copy_storyarcs_to_tags'):
|
||||
self.copy_storyarcs_to_tags = self.config.getboolean( 'cbl_transform', 'copy_storyarcs_to_tags' )
|
||||
if self.config.has_option('cbl_transform', 'copy_weblink_to_comments'):
|
||||
self.copy_weblink_to_comments = self.config.getboolean( 'cbl_transform', 'copy_weblink_to_comments' )
|
||||
if self.config.has_option('cbl_transform', 'apply_cbl_transform_on_cv_import'):
|
||||
@ -257,7 +284,20 @@ class ComicTaggerSettings:
|
||||
self.rename_use_smart_string_cleanup = self.config.getboolean( 'rename', 'rename_use_smart_string_cleanup' )
|
||||
if self.config.has_option('rename', 'rename_extension_based_on_archive'):
|
||||
self.rename_extension_based_on_archive = self.config.getboolean( 'rename', 'rename_extension_based_on_archive' )
|
||||
|
||||
|
||||
if self.config.has_option('autotag', 'save_on_low_confidence'):
|
||||
self.save_on_low_confidence = self.config.getboolean( 'autotag', 'save_on_low_confidence' )
|
||||
if self.config.has_option('autotag', 'dont_use_year_when_identifying'):
|
||||
self.dont_use_year_when_identifying = self.config.getboolean( 'autotag', 'dont_use_year_when_identifying' )
|
||||
if self.config.has_option('autotag', 'assume_1_if_no_issue_num'):
|
||||
self.assume_1_if_no_issue_num = self.config.getboolean( 'autotag', 'assume_1_if_no_issue_num' )
|
||||
if self.config.has_option('autotag', 'ignore_leading_numbers_in_filename'):
|
||||
self.ignore_leading_numbers_in_filename = self.config.getboolean( 'autotag', 'ignore_leading_numbers_in_filename' )
|
||||
if self.config.has_option('autotag', 'remove_archive_after_successful_match'):
|
||||
self.remove_archive_after_successful_match = self.config.getboolean( 'autotag', 'remove_archive_after_successful_match' )
|
||||
if self.config.has_option('autotag', 'wait_and_retry_on_rate_limit'):
|
||||
self.wait_and_retry_on_rate_limit = self.config.getboolean( 'autotag', 'wait_and_retry_on_rate_limit' )
|
||||
|
||||
def save( self ):
|
||||
|
||||
if not self.config.has_section( 'settings' ):
|
||||
@ -297,6 +337,7 @@ class ComicTaggerSettings:
|
||||
self.config.set( 'dialogflags', 'show_disclaimer', self.show_disclaimer )
|
||||
self.config.set( 'dialogflags', 'dont_notify_about_this_version', self.dont_notify_about_this_version )
|
||||
self.config.set( 'dialogflags', 'ask_about_usage_stats', self.ask_about_usage_stats )
|
||||
self.config.set( 'dialogflags', 'show_no_unrar_warning', self.show_no_unrar_warning )
|
||||
|
||||
if not self.config.has_section( 'filenameparser' ):
|
||||
self.config.add_section( 'filenameparser' )
|
||||
@ -307,6 +348,9 @@ class ComicTaggerSettings:
|
||||
self.config.add_section( 'comicvine' )
|
||||
|
||||
self.config.set( 'comicvine', 'use_series_start_as_volume', self.use_series_start_as_volume )
|
||||
self.config.set( 'comicvine', 'clear_form_before_populating_from_cv', self.clear_form_before_populating_from_cv )
|
||||
self.config.set( 'comicvine', 'remove_html_tables', self.remove_html_tables )
|
||||
self.config.set( 'comicvine', 'cv_api_key', self.cv_api_key )
|
||||
|
||||
if not self.config.has_section( 'cbl_transform' ):
|
||||
self.config.add_section( 'cbl_transform' )
|
||||
@ -315,6 +359,7 @@ class ComicTaggerSettings:
|
||||
self.config.set( 'cbl_transform', 'copy_characters_to_tags', self.copy_characters_to_tags )
|
||||
self.config.set( 'cbl_transform', 'copy_teams_to_tags', self.copy_teams_to_tags )
|
||||
self.config.set( 'cbl_transform', 'copy_locations_to_tags', self.copy_locations_to_tags )
|
||||
self.config.set( 'cbl_transform', 'copy_storyarcs_to_tags', self.copy_storyarcs_to_tags )
|
||||
self.config.set( 'cbl_transform', 'copy_notes_to_comments', self.copy_notes_to_comments )
|
||||
self.config.set( 'cbl_transform', 'copy_weblink_to_comments', self.copy_weblink_to_comments )
|
||||
self.config.set( 'cbl_transform', 'apply_cbl_transform_on_cv_import', self.apply_cbl_transform_on_cv_import )
|
||||
@ -327,6 +372,15 @@ class ComicTaggerSettings:
|
||||
self.config.set( 'rename', 'rename_issue_number_padding', self.rename_issue_number_padding )
|
||||
self.config.set( 'rename', 'rename_use_smart_string_cleanup', self.rename_use_smart_string_cleanup )
|
||||
self.config.set( 'rename', 'rename_extension_based_on_archive', self.rename_extension_based_on_archive )
|
||||
|
||||
if not self.config.has_section( 'autotag' ):
|
||||
self.config.add_section( 'autotag' )
|
||||
self.config.set( 'autotag', 'save_on_low_confidence', self.save_on_low_confidence )
|
||||
self.config.set( 'autotag', 'dont_use_year_when_identifying', self.dont_use_year_when_identifying )
|
||||
self.config.set( 'autotag', 'assume_1_if_no_issue_num', self.assume_1_if_no_issue_num )
|
||||
self.config.set( 'autotag', 'ignore_leading_numbers_in_filename', self.ignore_leading_numbers_in_filename )
|
||||
self.config.set( 'autotag', 'remove_archive_after_successful_match', self.remove_archive_after_successful_match )
|
||||
self.config.set( 'autotag', 'wait_and_retry_on_rate_limit', self.wait_and_retry_on_rate_limit )
|
||||
|
||||
with codecs.open( self.settings_file, 'wb', 'utf8') as configfile:
|
||||
self.config.write(configfile)
|
||||
|
@ -24,6 +24,7 @@ from PyQt4 import QtCore, QtGui, uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from comicvinecacher import ComicVineCacher
|
||||
from comicvinetalker import ComicVineTalker
|
||||
from imagefetcher import ImageFetcher
|
||||
import utils
|
||||
|
||||
@ -110,6 +111,7 @@ class SettingsWindow(QtGui.QDialog):
|
||||
self.btnBrowseUnrar.clicked.connect(self.selectUnrar)
|
||||
self.btnClearCache.clicked.connect(self.clearCache)
|
||||
self.btnResetSettings.clicked.connect(self.resetSettings)
|
||||
self.btnTestKey.clicked.connect(self.testAPIKey)
|
||||
|
||||
def settingsToForm( self ):
|
||||
|
||||
@ -127,6 +129,11 @@ class SettingsWindow(QtGui.QDialog):
|
||||
|
||||
if self.settings.use_series_start_as_volume:
|
||||
self.cbxUseSeriesStartAsVolume.setCheckState( QtCore.Qt.Checked)
|
||||
if self.settings.clear_form_before_populating_from_cv:
|
||||
self.cbxClearFormBeforePopulating.setCheckState( QtCore.Qt.Checked)
|
||||
if self.settings.remove_html_tables:
|
||||
self.cbxRemoveHtmlTables.setCheckState( QtCore.Qt.Checked)
|
||||
self.leKey.setText( str(self.settings.cv_api_key) )
|
||||
|
||||
if self.settings.assume_lone_credit_is_primary:
|
||||
self.cbxAssumeLoneCreditIsPrimary.setCheckState( QtCore.Qt.Checked)
|
||||
@ -136,6 +143,8 @@ class SettingsWindow(QtGui.QDialog):
|
||||
self.cbxCopyTeamsToTags.setCheckState( QtCore.Qt.Checked)
|
||||
if self.settings.copy_locations_to_tags:
|
||||
self.cbxCopyLocationsToTags.setCheckState( QtCore.Qt.Checked)
|
||||
if self.settings.copy_storyarcs_to_tags:
|
||||
self.cbxCopyStoryArcsToTags.setCheckState( QtCore.Qt.Checked)
|
||||
if self.settings.copy_notes_to_comments:
|
||||
self.cbxCopyNotesToComments.setCheckState( QtCore.Qt.Checked)
|
||||
if self.settings.copy_weblink_to_comments:
|
||||
@ -177,11 +186,15 @@ class SettingsWindow(QtGui.QDialog):
|
||||
self.settings.parse_scan_info = self.cbxParseScanInfo.isChecked()
|
||||
|
||||
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.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()
|
||||
self.settings.copy_locations_to_tags = self.cbxCopyLocationsToTags.isChecked()
|
||||
self.settings.copy_storyarcs_to_tags = self.cbxCopyStoryArcsToTags.isChecked()
|
||||
self.settings.copy_notes_to_comments = self.cbxCopyNotesToComments.isChecked()
|
||||
self.settings.copy_weblink_to_comments = self.cbxCopyWebLinkToComments.isChecked()
|
||||
self.settings.apply_cbl_transform_on_cv_import = self.cbxApplyCBLTransformOnCVIMport.isChecked()
|
||||
@ -206,6 +219,12 @@ class SettingsWindow(QtGui.QDialog):
|
||||
ComicVineCacher( ).clearCache()
|
||||
QtGui.QMessageBox.information(self, self.name, "Cache has been cleared.")
|
||||
|
||||
def testAPIKey( self ):
|
||||
if ComicVineTalker().testKey( unicode(self.leKey.text()) ):
|
||||
QtGui.QMessageBox.information(self, "API Key Test", "Key is valid!")
|
||||
else:
|
||||
QtGui.QMessageBox.warning(self, "API Key Test", "Key is NOT valid.")
|
||||
|
||||
|
||||
def resetSettings( self ):
|
||||
self.settings.reset()
|
||||
|
@ -21,7 +21,9 @@ limitations under the License.
|
||||
|
||||
from PyQt4 import QtCore, QtGui, uic
|
||||
from PyQt4.QtCore import QUrl,pyqtSignal
|
||||
from PyQt4 import QtNetwork
|
||||
|
||||
import sys
|
||||
import signal
|
||||
import locale
|
||||
import platform
|
||||
@ -30,6 +32,7 @@ import pprint
|
||||
import json
|
||||
import webbrowser
|
||||
import re
|
||||
import pickle
|
||||
|
||||
from volumeselectionwindow import VolumeSelectionWindow
|
||||
from comicarchive import MetaDataStyle
|
||||
@ -79,12 +82,43 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
appName = "ComicTagger"
|
||||
version = ctversion.version
|
||||
|
||||
def __init__(self, file_list, settings, parent = None):
|
||||
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
|
||||
|
||||
#----------------------------------
|
||||
# prevent multiple instances
|
||||
socket = QtNetwork.QLocalSocket(self)
|
||||
socket.connectToServer(settings.install_id)
|
||||
alive = socket.waitForConnected(3000)
|
||||
if alive:
|
||||
print "Another application with key [{}] is already running".format( 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()
|
||||
socket.disconnectFromServer()
|
||||
sys.exit()
|
||||
else:
|
||||
# listen on a socket to prevent multiple instances
|
||||
self.socketServer = QtNetwork.QLocalServer(self)
|
||||
self.socketServer.newConnection.connect(self.onIncomingSocketConnection)
|
||||
ok = self.socketServer.listen(settings.install_id)
|
||||
if not ok:
|
||||
if self.socketServer.serverError() == QtNetwork.QAbstractSocket.AddressInUseError:
|
||||
#print "Resetting unresponsive socket with key [{}]".format(settings.install_id)
|
||||
self.socketServer.removeServer(settings.install_id)
|
||||
ok = self.socketServer.listen(settings.install_id)
|
||||
if not ok:
|
||||
print "Cannot start local socket with key [{}]. Reason: %s ".format(settings.install_id, 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.addWidget( self.archiveCoverWidget )
|
||||
@ -119,6 +153,11 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
|
||||
self.setWindowIcon(QtGui.QIcon( ComicTaggerSettings.getGraphic('app.png')))
|
||||
|
||||
if opts is not None and opts.data_style is not None and opts.data_style != MetaDataStyle.COMET:
|
||||
#respect the command line option tag type
|
||||
settings.last_selected_save_data_style = opts.data_style
|
||||
settings.last_selected_load_data_style = opts.data_style
|
||||
|
||||
self.save_data_style = settings.last_selected_save_data_style
|
||||
self.load_data_style = settings.last_selected_load_data_style
|
||||
|
||||
@ -477,7 +516,7 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
+ "<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.exec_()
|
||||
|
||||
@ -503,7 +542,7 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
|
||||
def actualLoadCurrentArchive( self ):
|
||||
if self.metadata.isEmpty:
|
||||
self.metadata = self.comic_archive.metadataFromFilename( )
|
||||
self.metadata = self.comic_archive.metadataFromFilename( self.settings.parse_scan_info)
|
||||
if len(self.metadata.pages) == 0:
|
||||
self.metadata.setDefaultPageList( self.comic_archive.getNumberOfPages() )
|
||||
|
||||
@ -869,7 +908,7 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
if self.comic_archive is not None:
|
||||
#copy the form onto metadata object
|
||||
self.formToMetadata()
|
||||
new_metadata = self.comic_archive.metadataFromFilename( )
|
||||
new_metadata = self.comic_archive.metadataFromFilename(self.settings.parse_scan_info)
|
||||
if new_metadata is not None:
|
||||
self.metadata.overlay( new_metadata )
|
||||
self.metadataToForm()
|
||||
@ -953,17 +992,23 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
self.formToMetadata()
|
||||
|
||||
try:
|
||||
comicVine = ComicVineTalker( )
|
||||
comicVine = ComicVineTalker()
|
||||
new_metadata = comicVine.fetchIssueData( selector.volume_id, selector.issue_number, self.settings )
|
||||
except ComicVineTalkerException:
|
||||
except ComicVineTalkerException as e:
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
QtGui.QMessageBox.critical(self, self.tr("Network Issue"), self.tr("Could not connect to ComicVine to get issue details!"))
|
||||
if e.code == ComicVineTalkerException.RateLimit:
|
||||
QtGui.QMessageBox.critical(self, self.tr("Comic Vine Error"), ComicVineTalker.getRateLimitMessage())
|
||||
else:
|
||||
QtGui.QMessageBox.critical(self, self.tr("Network Issue"), self.tr("Could not connect to ComicVine to get issue details.!"))
|
||||
else:
|
||||
QtGui.QApplication.restoreOverrideCursor()
|
||||
if new_metadata is not None:
|
||||
|
||||
if self.settings.apply_cbl_transform_on_cv_import:
|
||||
new_metadata = CBLTransformer( new_metadata, self.settings ).apply()
|
||||
|
||||
if self.settings.clear_form_before_populating_from_cv:
|
||||
self.clearForm()
|
||||
|
||||
self.metadata.overlay( new_metadata )
|
||||
# Now push the new combined data into the edit controls
|
||||
@ -1512,7 +1557,9 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
|
||||
|
||||
try:
|
||||
cv_md = ComicVineTalker().fetchIssueData( match['volume_id'], match['issue_number'], self.settings )
|
||||
comicVine = ComicVineTalker( )
|
||||
comicVine.wait_for_rate_limit = self.settings.wait_and_retry_on_rate_limit
|
||||
cv_md = comicVine.fetchIssueData( match['volume_id'], match['issue_number'], self.settings )
|
||||
except ComicVineTalkerException:
|
||||
print "Network error while getting issue details. Save aborted"
|
||||
|
||||
@ -1540,7 +1587,7 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
# read in metadata, and parse file name if not there
|
||||
md = ca.readMetadata( self.save_data_style )
|
||||
if md.isEmpty:
|
||||
md = ca.metadataFromFilename()
|
||||
md = ca.metadataFromFilename(self.settings.parse_scan_info)
|
||||
if dlg.ignoreLeadingDigitsInFilename and md.series is not None:
|
||||
#remove all leading numbers
|
||||
md.series = re.sub( "([\d.]*)(.*)", "\\2", md.series)
|
||||
@ -1559,6 +1606,7 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
md.issue = "1"
|
||||
ii.setAdditionalMetadata( md )
|
||||
ii.onlyUseAdditionalMetaData = True
|
||||
ii.waitAndRetryOnRateLimit = dlg.waitAndRetryOnRateLimit
|
||||
ii.setOutputFunction( self.autoTagLog )
|
||||
ii.cover_page_index = md.getCoverPageIndexList()[0]
|
||||
ii.setCoverURLCallback( self.atprogdialog.setTestImage )
|
||||
@ -1640,6 +1688,7 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
atstartdlg = AutoTagStartWindow( self, self.settings,
|
||||
self.tr("You have selected {0} archive(s) to automatically identify and write {1} tags to.\n\n".format(len(ca_list), MetaDataStyle.name[style]) +
|
||||
"Please choose options below, and select OK to Auto-Tag.\n" ))
|
||||
|
||||
atstartdlg.adjustSize( )
|
||||
atstartdlg.setModal( True )
|
||||
if not atstartdlg.exec_():
|
||||
@ -1875,4 +1924,53 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
"Don't tell me about this version again")
|
||||
if checked:
|
||||
self.settings.dont_notify_about_this_version = new_version
|
||||
|
||||
|
||||
def onIncomingSocketConnection(self):
|
||||
# accept connection from other instance.
|
||||
# read in the file list if they're giving it,
|
||||
# and add to our own list
|
||||
localSocket = self.socketServer.nextPendingConnection()
|
||||
if localSocket.waitForReadyRead(3000):
|
||||
byteArray = localSocket.readAll()
|
||||
if len(byteArray) > 0:
|
||||
obj = pickle.loads(byteArray)
|
||||
localSocket.disconnectFromServer()
|
||||
if type(obj) is list:
|
||||
self.fileSelectionList.addPathList( obj )
|
||||
else:
|
||||
#print localSocket.errorString().toLatin1()
|
||||
pass
|
||||
|
||||
self.bringToTop()
|
||||
|
||||
def bringToTop(self):
|
||||
if platform.system() == "Windows":
|
||||
self.showNormal()
|
||||
self.raise_()
|
||||
self.activateWindow()
|
||||
try:
|
||||
import win32con
|
||||
import win32gui
|
||||
hwnd = self.effectiveWinId()
|
||||
rect = win32gui.GetWindowRect(hwnd)
|
||||
x = rect[0]
|
||||
y = rect[1]
|
||||
w = rect[2] - x
|
||||
h = rect[3] - y
|
||||
# mark it "always on top", just for a moment, to force it to the top
|
||||
win32gui.SetWindowPos(hwnd,win32con.HWND_TOPMOST, x, y, w, h, 0)
|
||||
win32gui.SetWindowPos(hwnd,win32con.HWND_NOTOPMOST, x, y, w, h, 0)
|
||||
except Exception as e:
|
||||
print "Whoops", e
|
||||
elif platform.system() == "Darwin":
|
||||
self.raise_()
|
||||
self.showNormal()
|
||||
self.activateWindow()
|
||||
else:
|
||||
flags = self.windowFlags()
|
||||
self.setWindowFlags( flags | QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.X11BypassWindowManagerHint)
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
#self.show()
|
||||
self.setWindowFlags( flags )
|
||||
self.show()
|
||||
|
@ -9,8 +9,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>607</width>
|
||||
<height>319</height>
|
||||
<width>519</width>
|
||||
<height>378</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -25,183 +25,180 @@
|
||||
<property name="modal">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="cbxSpecifySearchString">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
<string>Specify series search string for all selected archives:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetFixedSize</enum>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="cbxSaveOnLowConfidence">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
<property name="text">
|
||||
<string>Save on low confidence match</string>
|
||||
</property>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxSaveOnLowConfidence">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save on low confidence match</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxDontUseYear">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Don't use publication year in indentification process</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxAssumeIssueOne">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>If no issue number, assume "1"</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxIgnoreLeadingDigitsInFilename">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ignore leading (sequence) numbers in filename</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxRemoveAfterSuccess">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove archives from list after successful tagging</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxSpecifySearchString">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Specify series search string for all selected archives</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="leSearchString">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QLineEdit" name="leNameLengthMatchTolerance">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Adjust Name Length Match Tolerance:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="cbxIgnoreLeadingDigitsInFilename">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
<property name="text">
|
||||
<string>Ignore leading (sequence) numbers in filename</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="cbxRemoveAfterSuccess">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove archives from list after successful tagging</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="cbxWaitForRateLimit">
|
||||
<property name="text">
|
||||
<string>Wait and retry when Comic Vine rate limit is exceeded (experimental)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="cbxDontUseYear">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Don't use publication year in indentification process</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="cbxAssumeIssueOne">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>If no issue number, assume "1"</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLineEdit" name="leNameLengthMatchTolerance">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLineEdit" name="leSearchString">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Adjust Name Length Match Tolerance:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>674</width>
|
||||
<height>428</height>
|
||||
<width>702</width>
|
||||
<height>432</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -21,6 +21,12 @@
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@ -336,19 +342,156 @@
|
||||
<attribute name="title">
|
||||
<string>Comic Vine</string>
|
||||
</attribute>
|
||||
<widget class="QCheckBox" name="cbxUseSeriesStartAsVolume">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>30</y>
|
||||
<width>240</width>
|
||||
<height>25</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Series Start Date as Volume</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="grpBoxCVTop">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbxUseSeriesStartAsVolume">
|
||||
<property name="text">
|
||||
<string>Use Series Start Date as Volume</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbxClearFormBeforePopulating">
|
||||
<property name="text">
|
||||
<string>Clear Form Before Importing Comic Vine data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbxRemoveHtmlTables">
|
||||
<property name="text">
|
||||
<string>Remove HTML tables from CV summary field</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="grpBoxKey">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_8">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="leKey">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<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>Comic Vine API Key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QLabel" name="lblKeyHelp">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>A personal API key from <a href="http://www.comicvine.com/api/"><span style=" text-decoration: underline; color:#0000ff;">Comic Vine</span></a> is recommended in order to search for tag data. Login (or create a new account) there to get your key, and enter it below.</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="2">
|
||||
<widget class="QPushButton" name="btnTestKey">
|
||||
<property name="text">
|
||||
<string>Tesk Key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<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 class="QWidget" name="tab_4">
|
||||
<attribute name="title">
|
||||
@ -385,11 +528,18 @@
|
||||
<rect>
|
||||
<x>11</x>
|
||||
<y>21</y>
|
||||
<width>246</width>
|
||||
<height>182</height>
|
||||
<width>251</width>
|
||||
<height>192</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_7">
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="cbxCopyLocationsToTags">
|
||||
<property name="text">
|
||||
<string>Copy Locations to Generic Tags</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="cbxAssumeLoneCreditIsPrimary">
|
||||
<property name="text">
|
||||
@ -411,27 +561,27 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="cbxCopyLocationsToTags">
|
||||
<property name="text">
|
||||
<string>Copy Locations to Generic Tags</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="cbxCopyNotesToComments">
|
||||
<property name="text">
|
||||
<string>Copy Notes to Comments</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="cbxCopyWebLinkToComments">
|
||||
<property name="text">
|
||||
<string>Copy Web Link to Comments</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="cbxCopyStoryArcsToTags">
|
||||
<property name="text">
|
||||
<string>Copy Story Arcs to Generic Tags</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
|
@ -25,13 +25,14 @@ import re
|
||||
import platform
|
||||
import locale
|
||||
import codecs
|
||||
from settings import ComicTaggerSettings
|
||||
|
||||
class UtilsVars:
|
||||
already_fixed_encoding = False
|
||||
|
||||
def get_actual_preferred_encoding():
|
||||
preferred_encoding = locale.getpreferredencoding()
|
||||
if getattr(sys, 'frozen', None) and platform.system() == "Darwin":
|
||||
if platform.system() == "Darwin":
|
||||
preferred_encoding = "utf-8"
|
||||
return preferred_encoding
|
||||
|
||||
@ -629,4 +630,34 @@ if qt_available:
|
||||
# And vertical position the same, but with the height dimensions
|
||||
vpos = ( main_window_size.height() - window.height() ) / 2
|
||||
# And the move call repositions the window
|
||||
window.move(hpos + main_window_size.left(), vpos + main_window_size.top())
|
||||
window.move(hpos + main_window_size.left(), vpos + main_window_size.top())
|
||||
|
||||
try:
|
||||
from PIL import Image
|
||||
from PIL import WebPImagePlugin
|
||||
import StringIO
|
||||
pil_available = True
|
||||
except ImportError:
|
||||
pil_available = False
|
||||
|
||||
def getQImageFromData(image_data):
|
||||
img = QtGui.QImage()
|
||||
success = img.loadFromData( image_data )
|
||||
if not success:
|
||||
try:
|
||||
if pil_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.save(output, format="TIFF")
|
||||
img.loadFromData( output.getvalue() )
|
||||
success = True
|
||||
except Exception as e:
|
||||
pass
|
||||
# if still nothing, go with default image
|
||||
if not success:
|
||||
img.load(ComicTaggerSettings.getGraphic('nocover.png'))
|
||||
return img
|
||||
|
||||
|
||||
|
@ -46,15 +46,18 @@ class SearchThread( QtCore.QThread):
|
||||
QtCore.QThread.__init__(self)
|
||||
self.series_name = series_name
|
||||
self.refresh = refresh
|
||||
self.error_code = None
|
||||
|
||||
def run(self):
|
||||
comicVine = ComicVineTalker( )
|
||||
comicVine = ComicVineTalker()
|
||||
try:
|
||||
self.cv_error = False
|
||||
self.cv_search_results = comicVine.searchForSeries( self.series_name, callback=self.prog_callback, refresh_cache=self.refresh )
|
||||
except ComicVineTalkerException:
|
||||
except ComicVineTalkerException as e:
|
||||
self.cv_search_results = []
|
||||
self.cv_error = True
|
||||
self.error_code = e.code
|
||||
|
||||
finally:
|
||||
self.searchComplete.emit()
|
||||
|
||||
@ -293,7 +296,10 @@ class VolumeSelectionWindow(QtGui.QDialog):
|
||||
def searchComplete( self ):
|
||||
self.progdialog.accept()
|
||||
if self.search_thread.cv_error:
|
||||
QtGui.QMessageBox.critical(self, self.tr("Network Issue"), self.tr("Could not connect to ComicVine to search for series!"))
|
||||
if self.search_thread.error_code == ComicVineTalkerException.RateLimit:
|
||||
QtGui.QMessageBox.critical(self, self.tr("Comic Vine Error"), ComicVineTalker.getRateLimitMessage())
|
||||
else:
|
||||
QtGui.QMessageBox.critical(self, self.tr("Network Issue"), self.tr("Could not connect to ComicVine to search for series!"))
|
||||
return
|
||||
|
||||
self.cv_search_results = self.search_thread.cv_search_results
|
||||
|
@ -1,5 +1,6 @@
|
||||
#PYINSTALLER_CMD := VERSIONER_PYTHON_PREFER_32_BIT=yes arch -i386 python $(HOME)/pyinstaller-2.0/pyinstaller.py
|
||||
PYINSTALLER_CMD := python $(HOME)/pyinstaller-2.0/pyinstaller.py
|
||||
#PYINSTALLER_CMD := python $(HOME)/pyinstaller-2.0/pyinstaller.py
|
||||
PYINSTALLER_CMD := pyinstaller
|
||||
TAGGER_BASE ?= $(HOME)/Dropbox/tagger/comictagger
|
||||
TAGGER_SRC := $(TAGGER_BASE)/comictaggerlib
|
||||
|
||||
@ -16,7 +17,8 @@ DMG_FILE := $(VOLUME_NAME).dmg
|
||||
all: clean dist diskimage
|
||||
|
||||
dist:
|
||||
$(PYINSTALLER_CMD) $(TAGGER_BASE)/comictagger.py -o $(MAC_BASE) -w -n $(APP_NAME) -s
|
||||
#$(PYINSTALLER_CMD) $(TAGGER_BASE)/comictagger.py -o $(MAC_BASE) -w -n $(APP_NAME) -s
|
||||
$(PYINSTALLER_CMD) $(TAGGER_BASE)/comictagger.py -w -n $(APP_NAME) -s
|
||||
cp -a $(TAGGER_SRC)/ui $(APP_BUNDLE)/Contents/MacOS
|
||||
cp -a $(TAGGER_SRC)/graphics $(APP_BUNDLE)/Contents/MacOS
|
||||
cp $(MAC_BASE)/app.icns $(APP_BUNDLE)/Contents/Resources/icon-windowed.icns
|
||||
|
@ -1,191 +1,216 @@
|
||||
---------------------------------
|
||||
1.1.12-beta - 23-Mar-2014
|
||||
---------------------------------
|
||||
* Fixed noisy version update error
|
||||
|
||||
---------------------------------
|
||||
1.1.11-beta - 23-Mar-2014
|
||||
---------------------------------
|
||||
* Updated unrar library to hand Rar tools 5.0 and greater
|
||||
* Other misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.10-beta - 30-Jan-2014
|
||||
---------------------------------
|
||||
* Updated series query to match changes on Comic Vine side
|
||||
* Added a message when not able to open a file or folder
|
||||
* Fixed an issue where series names with periods would fail on search
|
||||
* Other misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.9-beta - 8-May-2013
|
||||
---------------------------------
|
||||
* Filename parser and identification enhancements
|
||||
* Misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.8-beta - 21-Apr-2013
|
||||
---------------------------------
|
||||
* Handle occasional error 500 from Comic Vine by retrying a few times
|
||||
* Nicer handling of colon (":") in file rename
|
||||
* Fixed command-line option parsing issue for add-on scripts
|
||||
* Misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.7-beta - 12-Apr-2013
|
||||
---------------------------------
|
||||
* Added description and cover date to issue selection dialogs
|
||||
* Added notification of new version
|
||||
* Added setting to attempt to parse scan info from file name
|
||||
* Last sorted column in the file list is now remembered
|
||||
* Added CLI option ('-1') to assume issue #1 if not found/parsed
|
||||
* Misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.6-beta - 3-Apr-2013
|
||||
---------------------------------
|
||||
* More ComicVine API-related fixes
|
||||
* More efficient automated search using new CV API issue filters
|
||||
---------------------------------
|
||||
1.1.15-beta - 13-Jun-2014
|
||||
---------------------------------
|
||||
* WebP support
|
||||
* Added user-configurable API key for Comic Vine access
|
||||
* Experimental option to wait and retry after exceeding Comic Vine rate limit
|
||||
|
||||
---------------------------------
|
||||
1.1.14-beta - 13-Apr-2014
|
||||
---------------------------------
|
||||
* Make sure app gets raised when enforcing single instance
|
||||
* Added warning dialog for when opening rar files, and no (un)rar tool
|
||||
* remove pil from python package requirements
|
||||
|
||||
---------------------------------
|
||||
1.1.13-beta - 9-Apr-2014
|
||||
---------------------------------
|
||||
* Handle non-ascii usernames properly
|
||||
* better parsing of html table in summary text, and optional removal
|
||||
* Python package should auto-install requirements
|
||||
* Specify default GUI tag style on command-line
|
||||
* enforce single GUI instance
|
||||
* new CBL tranform to copy story arcs to generic tags
|
||||
* Persist some auto-tag settings
|
||||
|
||||
---------------------------------
|
||||
1.1.12-beta - 23-Mar-2014
|
||||
---------------------------------
|
||||
* Fixed noisy version update error
|
||||
|
||||
---------------------------------
|
||||
1.1.11-beta - 23-Mar-2014
|
||||
---------------------------------
|
||||
* Updated unrar library to hand Rar tools 5.0 and greater
|
||||
* Other misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.10-beta - 30-Jan-2014
|
||||
---------------------------------
|
||||
* Updated series query to match changes on Comic Vine side
|
||||
* Added a message when not able to open a file or folder
|
||||
* Fixed an issue where series names with periods would fail on search
|
||||
* Other misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.9-beta - 8-May-2013
|
||||
---------------------------------
|
||||
* Filename parser and identification enhancements
|
||||
* Misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.8-beta - 21-Apr-2013
|
||||
---------------------------------
|
||||
* Handle occasional error 500 from Comic Vine by retrying a few times
|
||||
* Nicer handling of colon (":") in file rename
|
||||
* Fixed command-line option parsing issue for add-on scripts
|
||||
* Misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.7-beta - 12-Apr-2013
|
||||
---------------------------------
|
||||
* Added description and cover date to issue selection dialogs
|
||||
* Added notification of new version
|
||||
* Added setting to attempt to parse scan info from file name
|
||||
* Last sorted column in the file list is now remembered
|
||||
* Added CLI option ('-1') to assume issue #1 if not found/parsed
|
||||
* Misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.6-beta - 3-Apr-2013
|
||||
---------------------------------
|
||||
* More ComicVine API-related fixes
|
||||
* More efficient automated search using new CV API issue filters
|
||||
* Minor bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.5-beta - 30-Mar-2013
|
||||
---------------------------------
|
||||
* More updates for handling changes to ComicVine API and result sets
|
||||
* Even better handling of non-numeric issue "numbers" ("½", "X")
|
||||
|
||||
---------------------------------
|
||||
1.1.4-beta - 27-Mar-2013
|
||||
---------------------------------
|
||||
* Updated to match the changes to the ComicVine API and result sets
|
||||
* Better handling of weird issue numbers ("0.1", "6au")
|
||||
|
||||
---------------------------------
|
||||
1.1.3-beta - 25-Feb-2013
|
||||
---------------------------------
|
||||
Bug Fixes:
|
||||
* Fixed a bug when renaming on non-English systems
|
||||
* Fixed issue when saving settings on non-English systems
|
||||
* Fixed a bug when comic contains non-RGB images
|
||||
* Fixed a rare crash when comic image is not-RGB format
|
||||
* Fixed sequence order of ComicInfo.xml items
|
||||
|
||||
Note:
|
||||
New requirement for users of the python package: "configparser"
|
||||
|
||||
---------------------------------
|
||||
1.1.2-beta - 14-Feb-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
* Source is now packaged using Python distutils
|
||||
* Recursive mode for CLI
|
||||
* Run custom add-on scripts from CLI
|
||||
* Minor UI tweaks
|
||||
* Misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.0-beta - 06-Feb-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
* Enhanced identification process to use alternative covers from ComicVine
|
||||
* Post auto-tag manual matching now includes single low-confidence matches (CLI & GUI)
|
||||
* Page and cover view mini-browser available throughout app. Most images can be
|
||||
double-clicked for embiggened view
|
||||
* Export-to-zip in CLI (very handy in scripts!)
|
||||
* More rename template variables
|
||||
* Misc GUI & CLI Tweaks
|
||||
|
||||
---------------------------------
|
||||
1.0.3-beta - 31-Jan-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
Misc bug fixes and enhancements
|
||||
|
||||
---------------------------------
|
||||
1.0.2-beta - 25-Jan-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
More verbose logging during auto-tag
|
||||
Added %month% and %month_name% for renaming
|
||||
Better parsing of volume numbers in file name
|
||||
Bugs:
|
||||
Better exception handling with corrupted image data
|
||||
Fixed issues with RAR reading on OS X
|
||||
Other minor bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.0.1-beta - 24-Jan-2013
|
||||
---------------------------------
|
||||
Bug Fix:
|
||||
Fixed an issue where unicode strings can't be printed to OS X Console
|
||||
|
||||
---------------------------------
|
||||
1.0.0-beta - 23-Jan-2013
|
||||
---------------------------------
|
||||
Version 1! New multi-file processing in GUI!
|
||||
|
||||
GUI Changes:
|
||||
Open multiple files and/or folders via drag/drop or file dialog
|
||||
File management list for easy viewing and selection
|
||||
Batch tag remove
|
||||
Batch export as zip
|
||||
Batch rename
|
||||
Batch tag copy
|
||||
Batch auto-tag (automatic identification and save!)
|
||||
|
||||
---------------------------------
|
||||
0.9.5-beta - 16-Jan-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
Added CLI option to search by comicvine issue ID
|
||||
Some image loading optimizations
|
||||
Bug Fix: Some CBL fields that should have been ints were written as strings
|
||||
|
||||
---------------------------------
|
||||
0.9.4-beta - 7-Jan-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
Better handling of non-ascii characters in filenames and data
|
||||
Add CBL Transform to copy Web Link and Notes to comments
|
||||
Minor bug fixes
|
||||
|
||||
---------------------------------
|
||||
0.9.3-beta - 19-Dec-2012
|
||||
---------------------------------
|
||||
Changes:
|
||||
File rename in GUI
|
||||
Setting for file rename
|
||||
Option to use series start year as volume
|
||||
Added "CBL Transform" to handle primary credits copying data into the generic tags field
|
||||
Bug Fix: unicode characters in credits caused crash
|
||||
Bug Fix: bad or non-image data in file caused crash
|
||||
|
||||
Note:
|
||||
The user should clear the cache and delete the existing settings when first running this version.
|
||||
|
||||
---------------------------------
|
||||
0.9.2-beta - 13-Dec-2012
|
||||
---------------------------------
|
||||
Page List/Type editing in GUI
|
||||
File globbing for windows CLI (i.e. use of wildcards like '*.cbz')
|
||||
Fixed RAR writing bug on windows
|
||||
Minor bug and crash fixes
|
||||
|
||||
---------------------------------
|
||||
0.9.1-beta - 07-Dec-2012
|
||||
---------------------------------
|
||||
Export as ZIP Archive
|
||||
Added help menu option for websites
|
||||
Added Primary Credit Flag editing
|
||||
Menu enhancements
|
||||
CLI Enhancements:
|
||||
Interactive selection of matches
|
||||
Tag copy
|
||||
Better output
|
||||
CoMet support
|
||||
Minor bug and crash fixes
|
||||
|
||||
---------------------------------
|
||||
0.9.0-beta - 30-Nov-2012
|
||||
---------------------------------
|
||||
Initial beta release
|
||||
|
||||
---------------------------------
|
||||
1.1.5-beta - 30-Mar-2013
|
||||
---------------------------------
|
||||
* More updates for handling changes to ComicVine API and result sets
|
||||
* Even better handling of non-numeric issue "numbers" ("½", "X")
|
||||
|
||||
---------------------------------
|
||||
1.1.4-beta - 27-Mar-2013
|
||||
---------------------------------
|
||||
* Updated to match the changes to the ComicVine API and result sets
|
||||
* Better handling of weird issue numbers ("0.1", "6au")
|
||||
|
||||
---------------------------------
|
||||
1.1.3-beta - 25-Feb-2013
|
||||
---------------------------------
|
||||
Bug Fixes:
|
||||
* Fixed a bug when renaming on non-English systems
|
||||
* Fixed issue when saving settings on non-English systems
|
||||
* Fixed a bug when comic contains non-RGB images
|
||||
* Fixed a rare crash when comic image is not-RGB format
|
||||
* Fixed sequence order of ComicInfo.xml items
|
||||
|
||||
Note:
|
||||
New requirement for users of the python package: "configparser"
|
||||
|
||||
---------------------------------
|
||||
1.1.2-beta - 14-Feb-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
* Source is now packaged using Python distutils
|
||||
* Recursive mode for CLI
|
||||
* Run custom add-on scripts from CLI
|
||||
* Minor UI tweaks
|
||||
* Misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.0-beta - 06-Feb-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
* Enhanced identification process to use alternative covers from ComicVine
|
||||
* Post auto-tag manual matching now includes single low-confidence matches (CLI & GUI)
|
||||
* Page and cover view mini-browser available throughout app. Most images can be
|
||||
double-clicked for embiggened view
|
||||
* Export-to-zip in CLI (very handy in scripts!)
|
||||
* More rename template variables
|
||||
* Misc GUI & CLI Tweaks
|
||||
|
||||
---------------------------------
|
||||
1.0.3-beta - 31-Jan-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
Misc bug fixes and enhancements
|
||||
|
||||
---------------------------------
|
||||
1.0.2-beta - 25-Jan-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
More verbose logging during auto-tag
|
||||
Added %month% and %month_name% for renaming
|
||||
Better parsing of volume numbers in file name
|
||||
Bugs:
|
||||
Better exception handling with corrupted image data
|
||||
Fixed issues with RAR reading on OS X
|
||||
Other minor bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.0.1-beta - 24-Jan-2013
|
||||
---------------------------------
|
||||
Bug Fix:
|
||||
Fixed an issue where unicode strings can't be printed to OS X Console
|
||||
|
||||
---------------------------------
|
||||
1.0.0-beta - 23-Jan-2013
|
||||
---------------------------------
|
||||
Version 1! New multi-file processing in GUI!
|
||||
|
||||
GUI Changes:
|
||||
Open multiple files and/or folders via drag/drop or file dialog
|
||||
File management list for easy viewing and selection
|
||||
Batch tag remove
|
||||
Batch export as zip
|
||||
Batch rename
|
||||
Batch tag copy
|
||||
Batch auto-tag (automatic identification and save!)
|
||||
|
||||
---------------------------------
|
||||
0.9.5-beta - 16-Jan-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
Added CLI option to search by comicvine issue ID
|
||||
Some image loading optimizations
|
||||
Bug Fix: Some CBL fields that should have been ints were written as strings
|
||||
|
||||
---------------------------------
|
||||
0.9.4-beta - 7-Jan-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
Better handling of non-ascii characters in filenames and data
|
||||
Add CBL Transform to copy Web Link and Notes to comments
|
||||
Minor bug fixes
|
||||
|
||||
---------------------------------
|
||||
0.9.3-beta - 19-Dec-2012
|
||||
---------------------------------
|
||||
Changes:
|
||||
File rename in GUI
|
||||
Setting for file rename
|
||||
Option to use series start year as volume
|
||||
Added "CBL Transform" to handle primary credits copying data into the generic tags field
|
||||
Bug Fix: unicode characters in credits caused crash
|
||||
Bug Fix: bad or non-image data in file caused crash
|
||||
|
||||
Note:
|
||||
The user should clear the cache and delete the existing settings when first running this version.
|
||||
|
||||
---------------------------------
|
||||
0.9.2-beta - 13-Dec-2012
|
||||
---------------------------------
|
||||
Page List/Type editing in GUI
|
||||
File globbing for windows CLI (i.e. use of wildcards like '*.cbz')
|
||||
Fixed RAR writing bug on windows
|
||||
Minor bug and crash fixes
|
||||
|
||||
---------------------------------
|
||||
0.9.1-beta - 07-Dec-2012
|
||||
---------------------------------
|
||||
Export as ZIP Archive
|
||||
Added help menu option for websites
|
||||
Added Primary Credit Flag editing
|
||||
Menu enhancements
|
||||
CLI Enhancements:
|
||||
Interactive selection of matches
|
||||
Tag copy
|
||||
Better output
|
||||
CoMet support
|
||||
Minor bug and crash fixes
|
||||
|
||||
---------------------------------
|
||||
0.9.0-beta - 30-Nov-2012
|
||||
---------------------------------
|
||||
Initial beta release
|
||||
|
@ -1,3 +1,2 @@
|
||||
configparser
|
||||
beautifulsoup4 >= 4.1
|
||||
PIL >= 1.1.6
|
||||
|
@ -26,9 +26,10 @@ def main():
|
||||
#first find all comics with metadata
|
||||
print >> sys.stderr, "reading in all comics..."
|
||||
comic_list = []
|
||||
fmt_str = ""
|
||||
max_name_len = 2
|
||||
for filename in filelist:
|
||||
ca = ComicArchive(filename, settings )
|
||||
ca = ComicArchive(filename, settings.rar_exe_path )
|
||||
if ca.seemsToBeAComicArchive() and ca.hasMetadata( style ):
|
||||
max_name_len = max ( max_name_len, len(filename))
|
||||
fmt_str = u"{{0:{0}}}".format(max_name_len)
|
||||
@ -74,7 +75,7 @@ def main():
|
||||
print "Found {0} duplicate sets".format( len(dupe_set_list))
|
||||
|
||||
for dupe_set in dupe_set_list:
|
||||
ca = ComicArchive(dupe_set[0], settings )
|
||||
ca = ComicArchive(dupe_set[0], settings.rar_exe_path )
|
||||
md = ca.readMetadata( style )
|
||||
print "{0} #{1} ({2})".format( md.series, md.issue, md.year )
|
||||
for filename in dupe_set:
|
||||
|
@ -43,7 +43,7 @@ def main():
|
||||
metadata_list = []
|
||||
max_name_len = 2
|
||||
for filename in filelist:
|
||||
ca = ComicArchive(filename, settings )
|
||||
ca = ComicArchive(filename, settings.rar_exe_path )
|
||||
if ca.hasMetadata( style ):
|
||||
#make a list of paired filenames and metadata objects
|
||||
metadata_list.append((filename, ca.readMetadata( style )))
|
||||
|
@ -66,7 +66,7 @@ def main():
|
||||
comic_list = []
|
||||
max_name_len = 2
|
||||
for filename in filelist:
|
||||
ca = ComicArchive(filename, settings )
|
||||
ca = ComicArchive(filename, settings.rar_exe_path )
|
||||
if ca.seemsToBeAComicArchive() and ca.hasMetadata( style ):
|
||||
|
||||
comic_list.append((filename, ca.readMetadata( style )))
|
||||
|
@ -99,7 +99,7 @@ def main():
|
||||
max_name_len = 2
|
||||
fmt_str = ""
|
||||
for filename in filelist:
|
||||
ca = ComicArchive(filename, settings )
|
||||
ca = ComicArchive(filename, settings.rar_exe_path )
|
||||
# do we care if it already has metadata?
|
||||
if ca.seemsToBeAComicArchive() and not ca.hasMetadata( style ):
|
||||
|
||||
|
@ -51,7 +51,7 @@ def main():
|
||||
modify_list = []
|
||||
for filename in filelist:
|
||||
|
||||
ca = ComicArchive(filename, settings )
|
||||
ca = ComicArchive(filename, settings.rar_exe_path )
|
||||
if (ca.isZip or ca.isRar()) and ca.hasMetadata( style ):
|
||||
md = ca.readMetadata( style )
|
||||
if len(md.pages) != 0:
|
||||
@ -63,7 +63,7 @@ def main():
|
||||
|
||||
#now actually process those files
|
||||
for filename,md in modify_list:
|
||||
ca = ComicArchive(filename, settings )
|
||||
ca = ComicArchive(filename, settings.rar_exe_path )
|
||||
curr_folder = os.path.dirname( filename )
|
||||
curr_subfolder = os.path.join( curr_folder, subfolder_name )
|
||||
|
||||
@ -140,7 +140,7 @@ def main():
|
||||
print "Done!".format(filename)
|
||||
|
||||
# Create a new archive object for the new file, and write the old CIX data, with new page info
|
||||
ca = ComicArchive( filename, settings )
|
||||
ca = ComicArchive( filename, settings.rar_exe_path )
|
||||
md.pages = new_pages
|
||||
ca.writeMetadata( style, md )
|
||||
|
||||
|
@ -52,7 +52,7 @@ def main():
|
||||
fmt_str = u"{{0:{0}}}".format(max_name_len)
|
||||
for filename in filelist:
|
||||
|
||||
ca = ComicArchive(filename, settings )
|
||||
ca = ComicArchive(filename, settings.rar_exe_path )
|
||||
if (ca.seemsToBeAComicArchive()):
|
||||
# Check the images in the file, see if we need to reduce any
|
||||
|
||||
@ -180,7 +180,7 @@ def main():
|
||||
|
||||
# Create a new archive object for the new file, and write the old CIX data, w/o page info
|
||||
if cix_md is not None:
|
||||
ca = ComicArchive( filename, settings )
|
||||
ca = ComicArchive( filename, settings.rar_exe_path )
|
||||
cix_md.pages = []
|
||||
ca.writeCIX( cix_md )
|
||||
|
||||
|
@ -44,7 +44,7 @@ def main():
|
||||
print >> sys.stderr, filename + ": not found!"
|
||||
return
|
||||
|
||||
ca = ComicArchive(filename, settings )
|
||||
ca = ComicArchive(filename, settings.rar_exe_path )
|
||||
if not ca.seemsToBeAComicArchive():
|
||||
print >> sys.stderr, "Sorry, but "+ filename + " is not a comic archive!"
|
||||
return
|
||||
|
13
setup.py
13
setup.py
@ -1,16 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from distutils.core import setup
|
||||
from setuptools import setup
|
||||
import comictaggerlib.ctversion
|
||||
|
||||
with open('requirements.txt') as f:
|
||||
required = f.read().splitlines()
|
||||
|
||||
setup(name = "comictagger",
|
||||
install_requires=required,
|
||||
version = comictaggerlib.ctversion.version,
|
||||
description = "A cross-platform GUI/CLI app for writing metadata to comic archives",
|
||||
author = "Anthony Beville",
|
||||
author_email = "comictagger@gmail.com",
|
||||
url = "http://code.google.com/p/comictagger/",
|
||||
#download_url = "http://comictagger.googlecode.com/files/comictagger-{0}.zip".format(comictaggerlib.ctversion.version),
|
||||
download_url = "https://drive.google.com/uc?export=download&id=0Bw4IursaqWhhTjZ6UDB1aEx1am8",
|
||||
download_url = "https://pypi.python.org/packages/source/c/comictagger/comictagger-{0}.zip".format(comictaggerlib.ctversion.version),
|
||||
packages = [ "comictaggerlib", "comictaggerlib/UnRAR2" ] ,
|
||||
package_data = {
|
||||
'comictaggerlib': ['ui/*.ui', 'graphics/*'] ,
|
||||
@ -37,7 +40,7 @@ setup(name = "comictagger",
|
||||
license = "Apache License 2.0",
|
||||
|
||||
long_description = """
|
||||
ComicTagger is a multi-platform app for writing metadata to comic archives, written in Python and PyQt.
|
||||
ComicTagger is a multi-platform app for writing metadata to digital comics, written in Python and PyQt.
|
||||
|
||||
Features:
|
||||
|
||||
@ -52,7 +55,7 @@ Features:
|
||||
Requires:
|
||||
|
||||
* python 2.6 or 2.7
|
||||
* configparser
|
||||
* configparser
|
||||
* python imaging (PIL) >= 1.1.6
|
||||
* beautifulsoup > 4.1
|
||||
|
||||
|
11
todo.txt
11
todo.txt
@ -1,3 +1,7 @@
|
||||
TOP!:
|
||||
Does utils.get_actual_preferred_encoding() work on Mac python source version??
|
||||
(And does it matter?)
|
||||
|
||||
-----------------------------------------------------
|
||||
Features
|
||||
-----------------------------------------------------
|
||||
@ -11,12 +15,10 @@ Feature Requests:
|
||||
Move CBR to other folder after conversion to ZIP
|
||||
Pre-process series name before identification
|
||||
(using a list of regex transforms)
|
||||
(GC #28) Save auto-tag options
|
||||
(GC #24) Multiple options for -t i.e. "-t cr,cbl"
|
||||
(GC #18 ) Option for handling colon in rename
|
||||
(GC #31 ) Specify CV Series ID for auto-tag
|
||||
Re-org - move to new folder based on template
|
||||
repair incorrect extension
|
||||
|
||||
Denied Requests (for now):
|
||||
Auto-rename on auto-tag
|
||||
@ -89,9 +91,8 @@ Release Process
|
||||
Tag the repository
|
||||
Manually upload release packages to Google Drive
|
||||
Update the Downloads wiki page with direct links
|
||||
Edit setup.py to add direct link to zip
|
||||
"make upload"
|
||||
Announce on Forum and Main Page and Twitter
|
||||
"make upload" to the pypi site
|
||||
Announce on Forum and Main Page and Twitter and Facebook
|
||||
MacUpdate
|
||||
Update appspot value
|
||||
|
||||
|
Reference in New Issue
Block a user