Compare commits

..

8 Commits

Author SHA1 Message Date
bf9ab71fd9 release notes update
git-svn-id: http://comictagger.googlecode.com/svn/trunk@737 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-06-14 03:56:46 +00:00
33b00ad323 Text tweaks
git-svn-id: http://comictagger.googlecode.com/svn/trunk@736 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-06-14 03:56:32 +00:00
301ff084f1 fixes for webp, api key handling, and CV rate limit
git-svn-id: http://comictagger.googlecode.com/svn/trunk@734 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-06-13 06:26:44 +00:00
0c146bb245 minor fix
git-svn-id: http://comictagger.googlecode.com/svn/trunk@733 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-06-13 06:26:13 +00:00
08cc4a1acb Use pip-installed pyinstaller
git-svn-id: http://comictagger.googlecode.com/svn/trunk@732 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-06-13 06:25:35 +00:00
f97a1653d9 dos-ified release_notes file
git-svn-id: http://comictagger.googlecode.com/svn/trunk@728 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-04-18 15:44:38 +00:00
d9dbab301a prep for release
git-svn-id: http://comictagger.googlecode.com/svn/trunk@727 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-04-18 15:42:05 +00:00
3d93197101 Added warning when rar is tried be loaded, and unrar tool isn't known
Fixed a bug when erroneous message is show when file is attempted to be reloaded

git-svn-id: http://comictagger.googlecode.com/svn/trunk@726 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-04-12 06:08:07 +00:00
26 changed files with 698 additions and 370 deletions

View File

@ -52,7 +52,7 @@ upload:
#$(UPLOAD_TOOL) -p comictagger -s "ComicTagger $(VERSION_STR) Source" -l Featured,Type-Source -u beville -w $(PASSWORD) "release/comictagger-$(VERSION_STR).zip"
#$(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 register
python setup.py sdist --formats=zip upload
svn_tag:

View File

@ -58,7 +58,9 @@ class AutoTagStartWindow(QtGui.QDialog):
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)
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
@ -90,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
@ -107,6 +110,7 @@ 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
@ -114,6 +118,7 @@ class AutoTagStartWindow(QtGui.QDialog):
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())

View File

@ -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
@ -117,7 +119,7 @@ def display_match_set_for_choice( label, match_set, opts, settings ):
# we know at this point, that the file is all good to go
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 )
@ -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

View File

@ -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

View File

@ -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']
@ -577,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']

View File

@ -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!"

View File

@ -1,3 +1,3 @@
# This file should contan only these comments, and the line below.
# Used by packaging makefiles and app
version="1.1.13-beta"
version="1.1.15-beta"

View File

@ -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,7 +264,7 @@ class FileSelectionList(QWidget):
#print "processing", path
if self.isListDupe(path):
return None
return self.getCurrentListRow(path)
ca = ComicArchive( path, self.settings.rar_exe_path )

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -98,6 +98,7 @@ 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
@ -106,6 +107,7 @@ class ComicTaggerSettings:
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
@ -131,6 +133,7 @@ class ComicTaggerSettings:
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):
@ -242,6 +245,8 @@ 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' )
@ -249,7 +254,8 @@ class ComicTaggerSettings:
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' )
@ -289,6 +295,8 @@ class ComicTaggerSettings:
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 ):
@ -329,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' )
@ -341,6 +350,7 @@ class ComicTaggerSettings:
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' )
@ -370,6 +380,7 @@ class ComicTaggerSettings:
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)

View File

@ -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 ):
@ -131,6 +133,7 @@ class SettingsWindow(QtGui.QDialog):
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)
@ -185,7 +188,8 @@ class SettingsWindow(QtGui.QDialog):
self.settings.use_series_start_as_volume = self.cbxUseSeriesStartAsVolume.isChecked()
self.settings.clear_form_before_populating_from_cv = self.cbxClearFormBeforePopulating.isChecked()
self.settings.remove_html_tables = self.cbxRemoveHtmlTables.isChecked()
self.settings.cv_api_key = unicode(self.leKey.text())
ComicVineTalker.api_key = self.settings.cv_api_key
self.settings.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()
@ -215,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()

View File

@ -992,11 +992,14 @@ 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:
@ -1554,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"
@ -1601,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 )
@ -1682,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_():
@ -1923,12 +1930,47 @@ class TaggerWindow( QtGui.QMainWindow):
# read in the file list if they're giving it,
# and add to our own list
localSocket = self.socketServer.nextPendingConnection()
if not localSocket.waitForReadyRead(3000):
print localSocket.errorString().toLatin1()
return
byteArray = localSocket.readAll()
if len(byteArray) > 0:
obj = pickle.loads(byteArray)
localSocket.disconnectFromServer()
if type(obj) is list:
self.fileSelectionList.addPathList( obj )
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()

View File

@ -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 &quot;1&quot;</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 &quot;1&quot;</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/>

View File

@ -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,45 +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>251</width>
<height>25</height>
</rect>
</property>
<property name="text">
<string>Use Series Start Date as Volume</string>
</property>
</widget>
<widget class="QCheckBox" name="cbxClearFormBeforePopulating">
<property name="geometry">
<rect>
<x>30</x>
<y>50</y>
<width>341</width>
<height>25</height>
</rect>
</property>
<property name="text">
<string>Clear Form Before Importing Comic Vine data</string>
</property>
</widget>
<widget class="QCheckBox" name="cbxRemoveHtmlTables">
<property name="geometry">
<rect>
<x>30</x>
<y>70</y>
<width>351</width>
<height>25</height>
</rect>
</property>
<property name="text">
<string>Remove HTML tables from CV summary field</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;A personal API key from &lt;a href=&quot;http://www.comicvine.com/api/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Comic Vine&lt;/span&gt;&lt;/a&gt; is recommended in order to search for tag data. Login (or create a new account) there to get your key, and enter it below.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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">

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,17 @@
---------------------------------
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
---------------------------------
@ -5,9 +19,9 @@
* 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
* 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

View File

@ -1,3 +1,2 @@
configparser
beautifulsoup4 >= 4.1
PIL >= 1.1.6

View File

@ -26,6 +26,7 @@ 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.rar_exe_path )