Compare commits

..

27 Commits

Author SHA1 Message Date
3eca25db34 changed build checklist
git-svn-id: http://comictagger.googlecode.com/svn/trunk@688 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-23 21:39:16 +00:00
c8a5a89369 changed download URL to point at google drive site
git-svn-id: http://comictagger.googlecode.com/svn/trunk@687 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-23 21:38:55 +00:00
ff578ea819 bumped version to 1.1.12
git-svn-id: http://comictagger.googlecode.com/svn/trunk@686 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-23 21:38:22 +00:00
1c730c25d5 removed auto-upload to google code site
git-svn-id: http://comictagger.googlecode.com/svn/trunk@685 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-23 21:38:02 +00:00
35b7b39b86 Don't choke when the version string server fails.
git-svn-id: http://comictagger.googlecode.com/svn/trunk@683 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-23 20:59:35 +00:00
719c711484 Language tweak
git-svn-id: http://comictagger.googlecode.com/svn/trunk@668 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-23 18:03:08 +00:00
afbbc9d00c git-svn-id: http://comictagger.googlecode.com/svn/trunk@667 6c5673fe-1810-88d6-992b-cd32ca31540c 2014-03-23 17:48:59 +00:00
b8e0a45fc8 bumped version and release notes
git-svn-id: http://comictagger.googlecode.com/svn/trunk@665 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-23 17:31:00 +00:00
b7360dd33e Updated copyright dates
git-svn-id: http://comictagger.googlecode.com/svn/trunk@664 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-23 17:30:23 +00:00
d9f1956426 handle a crash bug when file starts with --
git-svn-id: http://comictagger.googlecode.com/svn/trunk@663 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-23 16:56:04 +00:00
b5c7f36410 New pyunrar version to handle rar tools 5.x
git-svn-id: http://comictagger.googlecode.com/svn/trunk@662 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-22 21:43:03 +00:00
0b0663d935 Update copyright date
git-svn-id: http://comictagger.googlecode.com/svn/trunk@661 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-22 21:42:07 +00:00
eee1f65436 handle corner case of non-numeric issue ending in "."
git-svn-id: http://comictagger.googlecode.com/svn/trunk@660 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-22 21:41:38 +00:00
9a8d4149f2 fixed spelling error
git-svn-id: http://comictagger.googlecode.com/svn/trunk@659 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-22 21:40:01 +00:00
b02a205668 Make sure all error print outs are unicode
Catch error when zipfile list fails

git-svn-id: http://comictagger.googlecode.com/svn/trunk@658 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-22 21:38:36 +00:00
57284dfbed fixed typo in makefile
git-svn-id: http://comictagger.googlecode.com/svn/trunk@657 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-03-22 21:37:19 +00:00
afcbde7fc6 update todo
git-svn-id: http://comictagger.googlecode.com/svn/trunk@651 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-01-31 04:45:15 +00:00
151fac5bf1 updated release notes
git-svn-id: http://comictagger.googlecode.com/svn/trunk@650 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-01-31 04:45:06 +00:00
57c1efdab9 makefile TAGGER_BASE can be set in the environment
git-svn-id: http://comictagger.googlecode.com/svn/trunk@649 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-01-31 04:43:22 +00:00
6b272cef87 When searching for a title, convert the string to a list of words separated by "ANDS", and then back to a string
git-svn-id: http://comictagger.googlecode.com/svn/trunk@648 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-01-31 04:40:58 +00:00
1cdc732739 Added a message when not able to open selected folder or file
git-svn-id: http://comictagger.googlecode.com/svn/trunk@647 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-01-31 04:39:47 +00:00
d1b00d162d Allow any size archive to be considered a comic
git-svn-id: http://comictagger.googlecode.com/svn/trunk@646 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-01-31 04:37:13 +00:00
3dd3980bc1 update todo file
git-svn-id: http://comictagger.googlecode.com/svn/trunk@645 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-08-18 18:01:01 +00:00
cbf475eb26 removed filtering out of period (".")
git-svn-id: http://comictagger.googlecode.com/svn/trunk@644 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-08-18 18:00:04 +00:00
ac8b575659 bumped version number
git-svn-id: http://comictagger.googlecode.com/svn/trunk@643 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-08-18 17:56:38 +00:00
ac8ef286a4 Perform the rar test first, since some rars can be falsly identified as zips, somehow...
git-svn-id: http://comictagger.googlecode.com/svn/trunk@641 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-07-23 17:06:35 +00:00
f567dc37be Handle case of None value credit tags in XML
git-svn-id: http://comictagger.googlecode.com/svn/trunk@640 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-07-08 23:32:24 +00:00
50 changed files with 273 additions and 156 deletions

View File

@ -1,4 +1,4 @@
TAGGER_BASE := $(HOME)/Dropbox/tagger/comictagger
TAGGER_BASE ?= $(HOME)/Dropbox/tagger/comictagger
TAGGER_SRC := $(TAGGER_BASE)/comictaggerlib
VERSION_STR := $(shell grep version $(TAGGER_SRC)/ctversion.py| cut -d= -f2 | sed 's/\"//g')
PASSWORD := $(shell cat $(TAGGER_BASE)/project_password.txt)
@ -49,9 +49,9 @@ remove_test_install:
# #-d 'python-qt4 >= 4.8'
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"
#$(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
svn_tag:

View File

@ -1,4 +1,4 @@
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:

View File

@ -33,7 +33,7 @@ similar to the C interface provided by UnRAR. There is also a
higher level interface which makes some common operations easier.
"""
__version__ = '0.99.2'
__version__ = '0.99.3'
try:
WindowsError

View File

@ -12,13 +12,11 @@ def cleanup(dir='test'):
os.removedirs(os.path.join(path, dir))
# reuse RarArchive object, en
# basic test
cleanup()
rarc = UnRAR2.RarFile('test.rar')
rarc.infolist()
assert rarc.comment == "This is a test."
for info in rarc.infoiter():
saveinfo = info
assert (str(info)=="""<RarInfo "test" in "test.rar">""")
@ -98,7 +96,8 @@ list(UnRAR2.RarFile('test_nulls.rar').infoiter())
# extract files from an archive with protected files
cleanup()
UnRAR2.RarFile('test_protected_files.rar', password="protected").extract()
rarc = UnRAR2.RarFile('test_protected_files.rar', password="protected")
rarc.extract()
assert os.path.exists('test'+os.sep+'top_secret_xxx_file.txt')
cleanup()
errored = False

View File

@ -33,6 +33,7 @@ from rar_exceptions import *
class UnpackerNotInstalled(Exception): pass
rar_executable_cached = None
rar_executable_version = None
def call_unrar(params):
"Calls rar/unrar command line executable, returns stdout pipe"
@ -59,10 +60,10 @@ def call_unrar(params):
class RarFileImplementation(object):
def init(self, password=None):
global rar_executable_version
self.password = password
stdoutdata, stderrdata = self.call('v', []).communicate()
for line in stderrdata.splitlines():
@ -73,18 +74,42 @@ class RarFileImplementation(object):
accum = []
source = iter(stdoutdata.splitlines())
line = ''
while not (line.startswith('Comment:') or line.startswith('Pathname/Comment')):
if line.strip().endswith('is not RAR archive'):
raise InvalidRARArchive
while not (line.startswith('UNRAR')):
line = source.next()
while not line.startswith('Pathname/Comment'):
accum.append(line.rstrip('\n'))
signature = line
# The code below is mighty flaky
# and will probably crash on localized versions of RAR
# but I see no safe way to rewrite it using a CLI tool
if signature.startswith("UNRAR 4"):
rar_executable_version = 4
while not (line.startswith('Comment:') or line.startswith('Pathname/Comment')):
if line.strip().endswith('is not RAR archive'):
raise InvalidRARArchive
line = source.next()
while not line.startswith('Pathname/Comment'):
accum.append(line.rstrip('\n'))
line = source.next()
if len(accum):
accum[0] = accum[0][9:] # strip out "Comment:" part
self.comment = '\n'.join(accum[:-1])
else:
self.comment = None
elif signature.startswith("UNRAR 5"):
rar_executable_version = 5
line = source.next()
if len(accum):
accum[0] = accum[0][9:]
self.comment = '\n'.join(accum[:-1])
while not line.startswith('Archive:'):
if line.strip().endswith('is not RAR archive'):
raise InvalidRARArchive
accum.append(line.rstrip('\n'))
line = source.next()
if len(accum):
self.comment = '\n'.join(accum[:-1]).strip()
else:
self.comment = None
else:
self.comment = None
raise UnpackerNotInstalled("Unsupported RAR version, expected 4.x or 5.x, found: "
+ signature.split(" ")[1])
def escaped_password(self):
return '-' if self.password == None else self.password
@ -97,7 +122,8 @@ class RarFileImplementation(object):
def infoiter(self):
stdoutdata, stderrdata = self.call('v', ['c-']).communicate()
command = "v" if rar_executable_version == 4 else "l"
stdoutdata, stderrdata = self.call(command, ['c-']).communicate()
for line in stderrdata.splitlines():
if line.strip().startswith("Cannot open"):
@ -106,33 +132,48 @@ class RarFileImplementation(object):
accum = []
source = iter(stdoutdata.splitlines())
line = ''
while not line.startswith('--------------'):
while not line.startswith('-----------'):
if line.strip().endswith('is not RAR archive'):
raise InvalidRARArchive
if line.find("CRC failed")>=0:
if line.startswith("CRC failed") or line.startswith("Checksum error"):
raise IncorrectRARPassword
line = source.next()
line = source.next()
i = 0
re_spaces = re.compile(r"\s+")
while not line.startswith('--------------'):
accum.append(line)
if len(accum)==2:
if rar_executable_version == 4:
while not line.startswith('-----------'):
accum.append(line)
if len(accum)==2:
data = {}
data['index'] = i
# asterisks mark password-encrypted files
data['filename'] = accum[0].strip().lstrip("*") # asterisks marks password-encrypted files
fields = re_spaces.split(accum[1].strip())
data['size'] = int(fields[0])
attr = fields[5]
data['isdir'] = 'd' in attr.lower()
data['datetime'] = time.strptime(fields[3]+" "+fields[4], '%d-%m-%y %H:%M')
data['comment'] = None
yield data
accum = []
i += 1
line = source.next()
elif rar_executable_version == 5:
while not line.startswith('-----------'):
fields = line.strip().lstrip("*").split()
data = {}
data['index'] = i
#!!!ATB - changed this because it was choking when a folder or file started with a space.
#!!! now, just strip off the first char in the string
data['filename'] = accum[0].rstrip()[1:]
info = re_spaces.split(accum[1].strip())
data['size'] = int(info[0])
attr = info[5]
data['filename'] = " ".join(fields[4:])
data['size'] = int(fields[1])
attr = fields[0]
data['isdir'] = 'd' in attr.lower()
data['datetime'] = time.strptime(info[3]+" "+info[4], '%d-%m-%y %H:%M')
data['datetime'] = time.strptime(fields[2]+" "+fields[3], '%d-%m-%y %H:%M')
data['comment'] = None
yield data
accum = []
i += 1
line = source.next()
line = source.next()
def read_files(self, checker):
res = []
@ -153,7 +194,7 @@ class RarFileImplementation(object):
if overwrite:
options.append('o+')
else:
options.append('o-')
options.append('o-')
if not path.endswith(os.sep):
path += os.sep
names = []
@ -167,7 +208,7 @@ class RarFileImplementation(object):
names.append(path)
proc = self.call(command, options, names)
stdoutdata, stderrdata = proc.communicate()
if stderrdata.find("CRC failed")>=0:
if stderrdata.find("CRC failed")>=0 or stderrdata.find("Checksum error")>=0:
raise IncorrectRARPassword
return res

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to select from automated issue matches
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to show ID log and progress
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to confirm and set options for auto-tag
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ Class to manage modifying metadata specifically for CBL/CBI
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A python class to encapsulate CoMet data
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A python class to represent a single comic, be it file or folder of images
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -72,15 +72,16 @@ class ZipArchiver:
def readArchiveFile( self, archive_file ):
data = ""
zf = zipfile.ZipFile( self.path, 'r' )
try:
data = zf.read( archive_file )
except zipfile.BadZipfile as e:
print >> sys.stderr, "bad zipfile [{0}]: {1} :: {2}".format(e, self.path, archive_file)
print >> sys.stderr, u"bad zipfile [{0}]: {1} :: {2}".format(e, self.path, archive_file)
zf.close()
raise IOError
except Exception as e:
zf.close()
print >> sys.stderr, "bad zipfile [{0}]: {1} :: {2}".format(e, self.path, archive_file)
print >> sys.stderr, u"bad zipfile [{0}]: {1} :: {2}".format(e, self.path, archive_file)
raise IOError
finally:
zf.close()
@ -109,12 +110,16 @@ class ZipArchiver:
except:
return False
def getArchiveFilenameList( self ):
zf = zipfile.ZipFile( self.path, 'r' )
namelist = zf.namelist()
zf.close()
return namelist
def getArchiveFilenameList( self ):
try:
zf = zipfile.ZipFile( self.path, 'r' )
namelist = zf.namelist()
zf.close()
return namelist
except Exception as e:
print >> sys.stderr, u"Unable to get zipfile list [{0}]: {1}".format(e, self.path)
return []
# zip helper func
def rebuildZipFile( self, exclude_list ):
@ -222,7 +227,7 @@ class ZipArchiver:
if not self.writeZipComment( self.path, comment ):
return False
except Exception as e:
print >> sys.stderr, "Error while copying to {0}: {1}".format(self.path, e)
print >> sys.stderr, u"Error while copying to {0}: {1}".format(self.path, e)
return False
else:
return True
@ -301,22 +306,22 @@ class RarArchiver:
entries = rarc.read_files( archive_file )
if entries[0][0].size != len(entries[0][1]):
print >> sys.stderr, "readArchiveFile(): [file is not expected size: {0} vs {1}] {2}:{3} [attempt # {4}]".format(
print >> sys.stderr, u"readArchiveFile(): [file is not expected size: {0} vs {1}] {2}:{3} [attempt # {4}]".format(
entries[0][0].size,len(entries[0][1]), self.path, archive_file, tries)
continue
except (OSError, IOError) as e:
print >> sys.stderr, "readArchiveFile(): [{0}] {1}:{2} attempt#{3}".format(str(e), self.path, archive_file, tries)
print >> sys.stderr, u"readArchiveFile(): [{0}] {1}:{2} attempt#{3}".format(str(e), self.path, archive_file, tries)
time.sleep(1)
except Exception as e:
print >> sys.stderr, "Unexpected exception in readArchiveFile(): [{0}] for {1}:{2} attempt#{3}".format(str(e), self.path, archive_file, tries)
print >> sys.stderr, u"Unexpected exception in readArchiveFile(): [{0}] for {1}:{2} attempt#{3}".format(str(e), self.path, archive_file, tries)
break
else:
#Success"
#entries is a list of of tuples: ( rarinfo, filedata)
if tries > 1:
print >> sys.stderr, "Attempted read_files() {0} times".format(tries)
print >> sys.stderr, u"Attempted read_files() {0} times".format(tries)
if (len(entries) == 1):
return entries[0][1]
else:
@ -392,7 +397,7 @@ class RarArchiver:
namelist.append( item.filename )
except (OSError, IOError) as e:
print >> sys.stderr, "getArchiveFilenameList(): [{0}] {1} attempt#{2}".format(str(e), self.path, tries)
print >> sys.stderr, u"getArchiveFilenameList(): [{0}] {1} attempt#{2}".format(str(e), self.path, tries)
time.sleep(1)
else:
@ -410,7 +415,7 @@ class RarArchiver:
rarc = UnRAR2.RarFile( self.path )
except (OSError, IOError) as e:
print >> sys.stderr, "getRARObj(): [{0}] {1} attempt#{2}".format(str(e), self.path, tries)
print >> sys.stderr, u"getRARObj(): [{0}] {1} attempt#{2}".format(str(e), self.path, tries)
time.sleep(1)
else:
@ -512,19 +517,20 @@ class ComicArchive:
def __init__( self, path, settings ):
self.path = path
self.ci_xml_filename = 'ComicInfo.xml'
self.comet_default_filename = 'CoMet.xml'
self.resetCache()
self.settings = settings
if self.zipTest():
self.archive_type = self.ArchiveType.Zip
self.archiver = ZipArchiver( self.path )
elif self.rarTest():
if self.rarTest():
self.archive_type = self.ArchiveType.Rar
self.archiver = RarArchiver( self.path, settings )
elif self.zipTest():
self.archive_type = self.ArchiveType.Zip
self.archiver = ZipArchiver( self.path )
elif os.path.isdir( self.path ):
self.archive_type = self.ArchiveType.Folder
self.archiver = FolderArchiver( self.path )
@ -609,7 +615,7 @@ class ComicArchive:
if (
( self.isZip() or self.isRar() ) #or self.isFolder() )
and
( self.getNumberOfPages() > 2)
( self.getNumberOfPages() > 0)
):
return True
@ -670,7 +676,7 @@ class ComicArchive:
try:
image_data = self.archiver.readArchiveFile( filename )
except IOError:
print >> sys.stderr, "Error reading in page. Substituting logo page."
print >> sys.stderr, u"Error reading in page. Substituting logo page."
image_data = ComicArchive.logo_data
return image_data
@ -921,7 +927,7 @@ class ComicArchive:
try:
raw_comet = self.archiver.readArchiveFile( self.comet_filename )
except IOError:
print >> sys.stderr, "Error reading in raw CoMet!"
print >> sys.stderr, u"Error reading in raw CoMet!"
raw_comet = ""
return raw_comet
@ -972,7 +978,7 @@ class ComicArchive:
data = self.archiver.readArchiveFile( n )
except:
data = ""
print >> sys.stderr, "Error reading in Comet XML for validation!"
print >> sys.stderr, u"Error reading in Comet XML for validation!"
if CoMet().validateString( data ):
# since we found it, save it!
self.comet_filename = n

View File

@ -3,7 +3,7 @@ A python class to encapsulate the ComicBookInfo data
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A python class to encapsulate ComicRack's ComicInfo.xml data
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -260,12 +260,14 @@ class ComicInfoXml:
n.tag == 'Letterer' or
n.tag == 'Editor'
):
for name in n.text.split(','):
metadata.addCredit( name.strip(), n.tag )
if n.text is not None:
for name in n.text.split(','):
metadata.addCredit( name.strip(), n.tag )
if n.tag == 'CoverArtist':
for name in n.text.split(','):
metadata.addCredit( name.strip(), "Cover" )
if n.text is not None:
for name in n.text.split(','):
metadata.addCredit( name.strip(), "Cover" )
# parse page data now
pages_node = root.find( "Pages" )

View File

@ -3,7 +3,7 @@ A python class to manage caching of data from Comic Vine
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A python class to manage communication with Comic Vine's REST API
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -145,10 +145,22 @@ class ComicVineTalker(QObject):
return cached_search_results
original_series_name = series_name
series_name = urllib.quote_plus(series_name.encode("utf-8"))
#series_name = urllib.quote_plus(unicode(series_name))
search_url = self.api_base_url + "/search/?api_key=" + self.api_key + "&format=json&resources=volume&query=" + series_name + "&field_list=name,id,start_year,publisher,image,description,count_of_issues"
# We need to make the series name into an "AND"ed query list
query_word_list = series_name.split()
and_list = ['AND'] * (len(query_word_list)-1)
and_list.append('')
# zipper up the two lists
query_list = zip(query_word_list, and_list)
# flatten the list
query_list = [ item for sublist in query_list for item in sublist]
# convert back to a string
query_string = " ".join( query_list ).strip()
#print "Query string = ", query_string
query_string = 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)

View File

@ -5,7 +5,7 @@ A PyQt4 widget display cover images from either local archive, or from ComicVine
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to edit credits
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

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.9-beta"
version="1.1.12-beta"

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to confirm and set options for export to zip
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -6,7 +6,7 @@ This should probably be re-written, but, well, it mostly works!
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -170,8 +170,11 @@ class FileNameParser:
series = tmpstr
volume = ""
#save the last word
last_word = series.split()[-1]
#save the last word
try:
last_word = series.split()[-1]
except:
last_word = ""
# remove any parenthetical phrases
series = re.sub( "\(.*?\)", "", series)
@ -197,10 +200,13 @@ class FileNameParser:
# be removed to help search online
if issue_start == 0:
one_shot_words = [ "tpb", "os", "one-shot", "ogn", "gn" ]
last_word = series.split()[-1]
if last_word.lower() in one_shot_words:
series = series.rsplit(' ', 1)[0]
try:
last_word = series.split()[-1]
if last_word.lower() in one_shot_words:
series = series.rsplit(' ', 1)[0]
except:
pass
return series, volume.strip()
def getYear( self,filename, issue_end):
@ -269,4 +275,3 @@ class FileNameParser:
self.issue = "0"
if self.issue[0] == ".":
self.issue = "0" + self.issue

View File

@ -3,7 +3,7 @@ Functions for renaming files based on metadata
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -4,7 +4,7 @@ A PyQt4 widget for managing list of comic archive files
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -198,7 +198,13 @@ class FileSelectionList(QWidget):
progdialog.close()
if firstAdded is not None:
self.twList.selectRow(firstAdded)
else:
if len(pathlist) == 1 and os.path.isfile(pathlist[0]):
QMessageBox.information(self, self.tr("File Open"), self.tr("Selected file doesn't seem to be a comic archive."))
else:
QMessageBox.information(self, self.tr("File/Folder Open"), self.tr("No comic archives were found."))
self.twList.setSortingEnabled(True)
# Adjust column size

View File

@ -8,7 +8,7 @@
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -31,7 +31,7 @@ class PageType:
InnerCover = "InnerCover"
Roundup = "Roundup"
Story = "Story"
Advertisment = "Advertisment"
Advertisment = "Advertisement"
Editorial = "Editorial"
Letters = "Letters"
Preview = "Preview"

View File

@ -3,7 +3,7 @@ A python class to manage fetching and caching of images by URL
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A PyQT4 widget to display a popup image
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A python class to automatically identify a comic archive
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to select specific issue from list
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -13,7 +13,7 @@ e.g.:
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -52,35 +52,39 @@ class IssueString:
else:
start = 0
# walk through the string, look for split point (the first non-numeric)
decimal_count = 0
for idx in range( start, len(text) ):
if text[idx] not in "0123456789.":
break
# special case: also split on second "."
if text[idx] == ".":
decimal_count += 1
if decimal_count > 1:
# if it's still not numeric at start skip it
if text[start].isdigit() or text[start] == ".":
# walk through the string, look for split point (the first non-numeric)
decimal_count = 0
for idx in range( start, len(text) ):
if text[idx] not in "0123456789.":
break
# special case: also split on second "."
if text[idx] == ".":
decimal_count += 1
if decimal_count > 1:
break
else:
idx = len(text)
# move trailing numeric decimal to suffix
# (only if there is other junk after )
if text[idx-1] == "." and len(text) != idx:
idx = idx -1
# if there is no numeric after the minus, make the minus part of the suffix
if idx == 1 and start == 1:
idx = 0
part1 = text[0:idx]
part2 = text[idx:len(text)]
if part1 != "":
self.num = float( part1 )
self.suffix = part2
else:
idx = len(text)
# move trailing numeric decimal to suffix
# (only if there is other junk after )
if text[idx-1] == "." and len(text) != idx:
idx = idx -1
# if there is no numeric after the minus, make the minus part of the suffix
if idx == 1 and start == 1:
idx = 0
part1 = text[0:idx]
part2 = text[idx:len(text)]
if part1 != "":
self.num = float( part1 )
self.suffix = part2
self.suffix = text
#print "num: {0} suf: {1}".format(self.num, self.suffix)
def asString( self, pad = 0 ):

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to a text file or log
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A python app to (automatically) tag comic archives
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to select from automated issue matches
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A PyQt4 dialog to show a message and let the user check a box
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ CLI options class for comictagger app
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -304,7 +304,7 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
if o == "--nooverwrite":
self.no_overwrite = True
if o == "--version":
print "ComicTagger {0}: Copyright (c) 2012-2013 Anthony Beville".format(ctversion.version)
print "ComicTagger {0}: Copyright (c) 2012-2014 Anthony Beville".format(ctversion.version)
print "Distributed under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)"
new_version = VersionChecker().getLatestVersion("", False)
if new_version is not None and new_version != ctversion.version:

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to show pages of a comic archive
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A PyQt4 widget for editing the page list info
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A PyQT4 class to load a page image from a ComicArchive in a background thread
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to show ID log and progress
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to confirm rename
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ Settings class for comictagger app
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to enter app settings
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -4,7 +4,7 @@ The main window of the ComicTagger app
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -473,7 +473,7 @@ class TaggerWindow( QtGui.QMainWindow):
msgBox.setIconPixmap( QtGui.QPixmap(ComicTaggerSettings.getGraphic('about.png')) )
msgBox.setText( "<br><br><br>"
+ self.appName + " v" + self.version + "<br>"
+ "(c)2012 Anthony Beville<br><br>"
+ "(c)2014 Anthony Beville<br><br>"
+ "<a href='{0}'>{0}</a><br><br>".format(website)
+ "<a href='mailto:{0}'>{0}</a><br><br>".format(email)
+ "License: <a href='{0}'>{1}</a>".format(license_link, license_name) )

View File

@ -5,7 +5,7 @@ Some generic utilities
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -126,9 +126,13 @@ def removearticles( text ):
# now get rid of some other junk
newText = newText.replace(":", "")
newText = newText.replace(".", "")
newText = newText.replace(",", "")
newText = newText.replace("-", " ")
# since the CV api changed, searches for series names with periods
# now explicity require the period to be in the search key,
# so the line below is removed (for now)
#newText = newText.replace(".", "")
return newText

View File

@ -24,7 +24,7 @@ import urllib,urllib2
import ctversion
try:
from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest
from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from PyQt4.QtCore import QUrl, pyqtSignal, QObject, QByteArray
except ImportError:
# No Qt, so define a few dummy QObjects to help us compile
@ -82,6 +82,9 @@ class VersionChecker(QObject):
self.nam.get(QNetworkRequest(QUrl(str(url))))
def asyncGetLatestVersionComplete( self, reply ):
if (reply.error() != QNetworkReply.NoError):
return
# read in the response
new_version = str(reply.readAll())

View File

@ -3,7 +3,7 @@ A PyQT4 dialog to select specific series/volume from list
"""
"""
Copyright 2012 Anthony Beville
Copyright 2012-2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -1,6 +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
TAGGER_BASE := $(HOME)/Dropbox/tagger/comictagger
TAGGER_BASE ?= $(HOME)/Dropbox/tagger/comictagger
TAGGER_SRC := $(TAGGER_BASE)/comictaggerlib
APP_NAME := ComicTagger

View File

@ -1,3 +1,22 @@
---------------------------------
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
---------------------------------

View File

@ -9,7 +9,8 @@ setup(name = "comictagger",
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 = "http://comictagger.googlecode.com/files/comictagger-{0}.zip".format(comictaggerlib.ctversion.version),
download_url = "https://drive.google.com/uc?export=download&id=0Bw4IursaqWhhTjZ6UDB1aEx1am8",
packages = [ "comictaggerlib", "comictaggerlib/UnRAR2" ] ,
package_data = {
'comictaggerlib': ['ui/*.ui', 'graphics/*'] ,

View File

@ -4,13 +4,23 @@ Features
Rename dialog:
check-box for rows?
manual edit the preview?
Maybe replace configparser -- seems to be causing all sorts of problems
Feature Requests:
Move CBR to other folder after conversion to ZIP
Pre-process series name before identification
(using a list of regex transforms)
NO - Auto-rename on auto-tag
NO - Re-zip (to remove compression)
(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
Re-zip (to remove compression)
Selective fields on CLI print (use -m option. Maybe internally remove all but specified fields in MD object before print )
@ -25,6 +35,8 @@ Bugs
Zip flakes out when filename differs from index (or whatever) i.e "\" vs "/". Python issue
-----------------------------------------------------
Big Future Features
-----------------------------------------------------
@ -32,7 +44,7 @@ Support for ACBF metatdata in CBZ
GCD scraper or DB reader
Batch Edit
(GC #29) Batch Edit
Form Mode: Single vs Batch
-----------------------------------------------------
@ -75,7 +87,10 @@ Release Process
Make dmg on Mac
Make zip on Mac or Linux
Tag the repository
Upload packages
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
MacUpdate
Update appspot value

View File

@ -2,7 +2,7 @@
# rm, cp, grep, cut, cat
HOMEPATH ?= $(HOME)
TAGGER_BASE:= $(HOMEPATH)/Dropbox/tagger/comictagger
TAGGER_BASE?= $(HOMEPATH)/Dropbox/tagger/comictagger
TAGGER_SRC := $(TAGGER_BASE)/comictaggerlib
DIST_DIR := $(TAGGER_BASE)\windows\dist
NSIS_CMD := "C:\Program Files (x86)\NSIS\makensis.exe"

View File

@ -1,4 +1,4 @@
ComicTagger - Copyright 2012 Anthony Beville
ComicTagger - Copyright 2014 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.