First cut at real CLI feature

git-svn-id: http://comictagger.googlecode.com/svn/trunk@58 6c5673fe-1810-88d6-992b-cd32ca31540c
This commit is contained in:
beville@gmail.com 2012-11-19 01:01:17 +00:00
parent ea6b2d1e9e
commit f45e7b887f
7 changed files with 274 additions and 252 deletions

View File

@ -23,6 +23,8 @@ See the License for the specific language governing permissions and
limitations under the License.
"""
import utils
# These page info classes are exactly the same as the CIX scheme, since it's unique
class PageType:
FrontCover = "FrontCover"
@ -68,7 +70,7 @@ class GenericMetadata:
self.comments = None # use same way as Summary in CIX
self.volumeCount = None
self.criticalRating = None
self.criticalRating = None
self.country = None
self.alternateSeries = None
@ -94,8 +96,7 @@ class GenericMetadata:
self.credits = list()
self.tags = list()
self.pages = list()
def addCredit( self, person, role, primary = False ):
credit = dict()
@ -105,5 +106,61 @@ class GenericMetadata:
credit['primary'] = primary
self.credits.append(credit)
def __str__( self ):
vals = []
if self.isEmpty:
return "No metadata"
def add( tag, val ):
if val is not None and str(val) != "":
vals.append( (tag, val) )
add( "series", self.series )
add( "issue number", self.issueNumber )
add( "issue count", self.issueCount )
add( "title", self.title )
add( "publisher", self.publisher )
add( "month", self.publicationMonth )
add( "year", self.publicationYear )
add( "volume number", self.volumeNumber )
add( "volume count", self.volumeCount )
add( "genre", self.genre )
add( "language", self.language )
add( "country", self.country )
add( "user rating", self.criticalRating )
add( "alt. series", self.alternateSeries )
add( "alt. number", self.alternateNumber )
add( "alt. count", self.alternateCount )
add( "imprint", self.imprint )
add( "web", self.webLink )
add( "format", self.format )
add( "manga", self.manga )
add( "B&W", self.blackAndWhite )
add( "age rating", self.maturityRating )
add( "story arc", self.storyArc )
add( "series group", self.seriesGroup )
add( "scan info", self.scanInfo )
add( "characters", self.characters )
add( "teams", self.teams )
add( "locations", self.locations )
add( "comments", self.comments )
add( "notes", self.notes )
add( "tags", utils.listToString( self.tags ) )
for c in self.credits:
add( "credit", c['role']+": "+c['person'] )
# find the longest field name
flen = 0
for i in vals:
flen = max( flen, len(i[0]) )
flen += 1
#format the data nicely
outstr = ""
for i in vals:
outstr += ("{0: <" + str(flen) + "}: {1}\n").format( i[0], i[1] )
return outstr

View File

@ -261,7 +261,6 @@ class IssueIdentifier:
# remove any series that starts after the issue year
if keys['year'] is not None and keys['year'].isdigit():
print "ATB", keys['year'] , item['start_year']
if int(keys['year']) < item['start_year']:
date_approved = False

View File

@ -21,6 +21,7 @@ limitations under the License.
import sys
import getopt
import platform
import os
class Enum(set):
def __getattr__(self, name):
@ -35,57 +36,128 @@ class MetaDataStyle:
class Options:
help_text = """
Usage: {0} [OPTION]... [FILE]
A utility for read 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
-d, --delete Deletes the tag block of specified type (via -t)
-s, --save Save out tags as specified type (via -t)
Must specify also at least -o, -p, or -m
-n, --dryrun Don't actually modify file (only relevent for -d, -s, or -r)
-t, --type=TYPE Specify TYPE as either "CR" or "CBL", (as either
ComicRack or ComicBookLover style tags, respectivly)
-f, --parsefilename Parse the filename to get some info, specifically
series name, issue number, volume, and publication
year
-o, --online Search online and attempt to identify file using
existing metadata and images in archive. May be used
in conjuntion with -p and -m
-m, --metadata=LIST Explicity define some tags to be used as a list
....TBD........
....TBD........
-r, --rename Rename the file based on metadata as indicated. TBD!
-a, --abort Abort save operation when online match is of low confidence TBD!
-v, --verbose Be noisy when doing what it does
-h, --help Display this message
"""
def __init__(self):
self.data_style = MetaDataStyle.CIX
self.data_style = None
self.no_gui = False
self.filename = None
self.verbose = False
self.md_settings = None
self.print_tags = False
self.delete_tags = False
self.search_online = False
self.dryrun = True # keep this true for now!
self.save_tags = False
self.parse_filename = False
self.rename_file = False
def display_help_and_quit( self, msg, code ):
appname = os.path.basename(sys.argv[0])
if msg is not None:
print( msg )
print self.help_text.format(appname)
sys.exit(code)
self.series_name = ''
self.issue_number = ''
self.filename = ''
self.image_hasher = 1
def parseCmdLineArgs(self):
# mac no likey this from .app bundle
if platform.system() == "Darwin" and getattr(sys, 'frozen', None):
return
# parse command line options
try:
opts, args = getopt.getopt(sys.argv[1:], "cht:s:i:vf:m:", ["cli", "help", "type=", "series=", "issue=", "verbose", "file", "imagehasher=" ])
except (getopt.error, msg):
print( msg )
print( "for help use --help" )
sys.exit(2)
opts, args = getopt.getopt(sys.argv[1:],
"hpdt:fm:vonsr",
[ "help", "print", "delete", "type=", "parsefilename", "metadata=", "verbose", "online", "dryrun", "save", "rename" ])
except getopt.GetoptError as err:
self.display_help_and_quit( str(err), 2 )
# process options
for o, a in opts:
if o in ("-h", "--help"):
print( __doc__ )
sys.exit(0)
self.display_help_and_quit( None, 0 )
if o in ("-v", "--verbose"):
print( "Verbose output!" )
if o in ("-c", "--cli"):
self.no_gui = True
if o in ("-m", "--imagehasher"):
self.image_hasher = a
if o in ("-s", "--series"):
self.series_name = a
if o in ("-i", "--issue"):
self.issue_number = a
if o in ("-f", "--file"):
self.filename = a
self.verbose = True
if o in ("-p", "--print"):
self.print_tags = True
if o in ("-d", "--delete"):
self.delete_tags = True
if o in ("-o", "--online"):
self.search_online = True
if o in ("-n", "--dryrun"):
self.dryrun = True
if o in ("-m", "--metadata"):
self.md_settings = a
if o in ("-s", "--save"):
self.save_tags = True
if o in ("-r", "--rename"):
self.rename_file = True
if o in ("-f", "--parsefilename"):
self.parse_filename = True
if o in ("-t", "--type"):
if a == "cr":
if a.lower() == "cr":
self.data_style = MetaDataStyle.CIX
elif a == "cbl":
elif a.lower() == "cbl":
self.data_style = MetaDataStyle.CBI
else:
print( __doc__ )
sys.exit(0)
if self.filename == "" and len(args) > 0:
self.filename = args[0]
self.display_help_and_quit( "Invalid tag type", 1 )
return opts
if self.print_tags or self.delete_tags or self.save_tags or self.rename_file:
self.no_gui = True
count = 0
if self.print_tags: count += 1
if self.delete_tags: count += 1
if self.save_tags: count += 1
if self.rename_file: count += 1
if count > 1:
self.display_help_and_quit( "Must choose only one action of print, delete, save, or rename", 1 )
if len(args) > 0:
self.filename = args[0]
if self.no_gui and self.filename is None:
self.display_help_and_quit( "Command requires a filename!", 1 )
if self.delete_tags and self.data_style is None:
self.display_help_and_quit( "Please specify the type to delete with -t", 1 )
if self.save_tags and self.data_style is None:
self.display_help_and_quit( "Please specify the type to save with -t", 1 )

View File

@ -26,7 +26,6 @@ from settings import ComicTaggerSettings
class PageBrowserWindow(QtGui.QDialog):
def __init__(self, parent):
super(PageBrowserWindow, self).__init__(parent)

126
tagger.py
View File

@ -40,30 +40,102 @@ import utils
#-----------------------------
def cli_mode( opts, settings ):
if opts.filename is None:
return
ca = ComicArchive(opts.filename)
if settings.rar_exe_path != "":
ca.setExternalRarProgram( settings.rar_exe_path )
if not ca.seemsToBeAComicArchive():
print "Sorry, but "+ opts.filename + " is not a comic archive!"
return
ii = IssueIdentifier( ca, settings.cv_api_key )
matches = ii.search()
"""
if len(matches) == 1:
# now get the particular issue data
metadata = comicVine.fetchIssueData( match[0]['series'], match[0]['issue_number'] )
# write out the new data
ca.writeMetadata( metadata, opts.data_style )
elif len(matches) == 0:
pass
elif len(matches) == 0:
# print match options, with CV issue ID's
pass
"""
cix = False
cbi = False
if ca.hasCIX(): cix = True
if ca.hasCBI(): cbi = True
if opts.print_tags:
if opts.data_style is None:
page_count = ca.getNumberOfPages()
brief = ""
if ca.isZip(): brief = "ZIP archive "
elif ca.isRar(): brief = "RAR archive "
elif ca.isFolder(): brief = "Folder archive "
brief += "({0: >3} pages)".format(page_count)
brief += " tags:[ "
if not (cbi or cix):
brief += "none"
else:
if cbi: brief += "CBL "
if cix: brief += "CR "
brief += "]"
print brief
print
if opts.data_style is None or opts.data_style == MetaDataStyle.CIX:
if cix:
print "------ComicRack tags--------"
print ca.readCIX()
if opts.data_style is None or opts.data_style == MetaDataStyle.CBI:
if cbi:
print "------ComicBookLover tags--------"
print ca.readCBI()
elif opts.delete_tags:
if not ca.isWritable():
print "This archive is not writable."
return
if opts.data_style == MetaDataStyle.CIX:
if cix:
ca.removeCIX()
print "Removed ComicRack tags."
else:
print "This archive doesn't have ComicRack tags."
if opts.data_style == MetaDataStyle.CBI:
if cbi:
ca.removeCBI()
print "Removed ComicBookLover tags."
else:
print "This archive doesn't have ComicBookLover tags."
#elif opt.rename:
# print "Gonna rename file"
elif opts.save_tags:
if opts.data_style == MetaDataStyle.CIX:
print "Gonna save ComicRack tags"
if opts.data_style == MetaDataStyle.CBI:
print "Gonna save ComicBookLover tags"
"""
ii = IssueIdentifier( ca, settings.cv_api_key )
matches = ii.search()
if len(matches) == 1:
# now get the particular issue data
metadata = comicVine.fetchIssueData( match[0]['series'], match[0]['issue_number'] )
# write out the new data
ca.writeMetadata( metadata, opts.data_style )
elif len(matches) == 0:
pass
elif len(matches) == 0:
# print match options, with CV issue ID's
pass
"""
#-----------------------------
def main():
@ -90,22 +162,8 @@ def main():
splash.raise_()
app.processEvents()
"""
lw = QtGui.QListWidget()
icon = QtGui.QIcon('app.png')
for i in range(10):
lw.addItem( QtGui.QListWidgetItem( icon, "Item {0}".format(i) ) )
lw.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
#lw.setViewMode(QtGui.QListView.IconMode)
lw.setMovement(QtGui.QListView.Snap)
lw.setGridSize(QtCore.QSize(100,100))
lw.show()
sys.exit(app.exec_())
"""
try:
tagger_window = TaggerWindow( opts, settings )
tagger_window = TaggerWindow( opts.filename, settings )
tagger_window.show()
splash.finish( tagger_window )
sys.exit(app.exec_())

View File

@ -27,7 +27,7 @@ import platform
import os
from volumeselectionwindow import VolumeSelectionWindow
from options import Options, MetaDataStyle
from options import MetaDataStyle
from comicinfoxml import ComicInfoXml
from genericmetadata import GenericMetadata
from comicvinetalker import ComicVineTalker
@ -64,162 +64,6 @@ def clickable(widget):
widget.installEventFilter(filter)
return filter.dblclicked
"""
class PageTableModel(QtCore.QAbstractTableModel):
def __init__(self, comic_archive, parent=None, *args):
QtCore.QAbstractTableModel.__init__(self, parent, *args)
self.comic_archive = comic_archive
page_list = comic_archive.getPageNameList()
self.page_model = []
i = 0
for page in page_list:
item = dict()
item['number'] = i
item['filename'] = page
item['thumb'] = None
self.page_model.append( item )
i +=1
def rowCount(self, parent):
return len(self.page_model)
def columnCount(self, parent):
return 3
def data(self, index, role):
if not index.isValid():
return QtCore.QVariant()
elif role == QtCore.Qt.DisplayRole:
# page num
if index.column() == 0:
return QtCore.QVariant(self.page_model[index.row()]['number'])
# page filename
if index.column() == 1:
return QtCore.QVariant(self.page_model[index.row()]['filename'])
elif role == QtCore.Qt.DecorationRole:
if index.column() == 2:
if self.page_model[index.row()]['thumb'] is None:
image_data = self.comic_archive.getPage( self.page_model[index.row()]['number'] )
img = QtGui.QImage()
img.loadFromData( image_data )
pixmap = QtGui.QPixmap(QtGui.QPixmap(img))
#scaled_pixmap = pixmap.scaled(100, 150, QtCore.Qt.KeepAspectRatio)
self.page_model[index.row()]['thumb'] = pixmap #scaled_pixmap
return QtCore.QVariant(self.page_model[index.row()]['thumb'])
else:
return QtCore.QVariant()
"""
class PageListModel(QtCore.QAbstractListModel):
def __init__(self, comic_archive, parent=None, *args):
QtCore.QAbstractTableModel.__init__(self, parent, *args)
self.comic_archive = comic_archive
page_list = comic_archive.getPageNameList()
self.page_model = []
i = 0
for page in page_list:
item = dict()
item['number'] = i
item['filename'] = page
item['thumb'] = None
self.page_model.append( item )
i +=1
def rowCount(self, parent):
return len(self.page_model)
def data(self, index, role):
if not index.isValid():
return QtCore.QVariant()
elif role == QtCore.Qt.DisplayRole:
# page num
return QtCore.QVariant(self.page_model[index.row()]['number'])
elif role == QtCore.Qt.DecorationRole:
if self.page_model[index.row()]['thumb'] is None:
#timestamp = datetime.datetime.now()
image_data = self.comic_archive.getPage( self.page_model[index.row()]['number'] )
img = QtGui.QImage()
img.loadFromData( image_data )
pixmap = QtGui.QPixmap(QtGui.QPixmap(img))
scaled_pixmap = pixmap.scaled(100, 150, QtCore.Qt.KeepAspectRatio)
self.page_model[index.row()]['thumb'] = scaled_pixmap
return QtCore.QVariant(self.page_model[index.row()]['thumb'])
else:
return QtCore.QVariant()
def flags( self, index):
defaultFlags = QtCore.QAbstractTableModel.flags(self, index)
if index.isValid():
return QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled | defaultFlags
else:
return QtCore.Qt.ItemIsDropEnabled | defaultFlags
def removeRows(self, row, count, parent=QtCore.QModelIndex()):
print "removeRows", row, count
return True
def insertRows(self, row, count, parent=QtCore.QModelIndex()):
print "insertRows", row, count
return False
def beginRemoveRows(self, sourceParent, start, end, destinationParent, dest):
print "beginRemoveRows"
def dropMimeData(self,data, action, row, col, parent):
print "dropMimeData", action, row, col
if (row != -1):
beginRow = row
elif (parent.isValid()):
beginRow = parent.row()
print beginRow
return True
if (action == QtCore.Qt.IgnoreAction):
return True
#if ( not data.hasFormat("application/vnd.text.list"))
# return False
if (column > 0):
return False
#def beginMoveRows(self, sourceParent, start, end, destinationParent, dest):
# print "rowsMoved"
def supportedDropActions(self):
#print "supportedDropActions"
return QtCore.Qt.CopyAction | QtCore.Qt.MoveAction
class TaggerWindow( QtGui.QMainWindow):
@ -227,7 +71,7 @@ class TaggerWindow( QtGui.QMainWindow):
appName = "ComicTagger"
version = ctversion.version
def __init__(self, opts, settings, parent = None):
def __init__(self, filename, settings, parent = None):
super(TaggerWindow, self).__init__(parent)
uic.loadUi(os.path.join(ComicTaggerSettings.baseDir(), 'taggerwindow.ui' ), self)
@ -240,9 +84,8 @@ class TaggerWindow( QtGui.QMainWindow):
#print platform.system(), platform.release()
self.dirtyFlag = False
self.opts = opts
self.settings = settings
self.data_style = opts.data_style
self.data_style = MetaDataStyle.CIX
#set up a default metadata object
self.metadata = GenericMetadata()
@ -270,16 +113,8 @@ class TaggerWindow( QtGui.QMainWindow):
self.updateStyleTweaks()
self.openArchive( opts.filename )
# fill in some explicit metadata stuff from our options
# this overrides what we just read in
if self.metadata.series is None:
self.metadata.series = opts.series_name
if self.metadata.issueNumber is None:
self.metadata.issueNumber = opts.issue_number
if filename is not None:
self.openArchive( filename )
def updateAppTitle( self ):

View File

@ -1,11 +1,9 @@
-----------------
Features
----------------
Stand-alone CLI
Info dump
optionless args
remove tags
copy tags
CLI
rename
save
Settings/Preferences Dialog
Remove API Key
@ -13,6 +11,7 @@ Settings/Preferences Dialog
Add reset settings
Tab w/Identifier Settings
Add publisher blacklist
other Identifier tunings
Add class for warning/info messages with "Don't show again" checkbox.
Add list of these flags to settings
@ -22,18 +21,21 @@ TaggerWindow entry fields
Pages Info - maybe a custom painted widget
At minimum, preserve the page data
File rename
-Dialog??
Style sheets for windows/mac/linux
Better stripping of html from CV text
-----------------
Bugs
----------------
SQLite chokes on "Batman\ Li'l Gotham 001.cbr" name -- Doesn't like single quote '
SERIOUS BUG: rebuilding zips!
http://stackoverflow.com/questions/11578443/trigger-io-errno-18-cross-device-link
Test ComicRack android
OSX:
toolbar
weird unrar complaints