Setting metadata via CLI basics working

git-svn-id: http://comictagger.googlecode.com/svn/trunk@61 6c5673fe-1810-88d6-992b-cd32ca31540c
This commit is contained in:
beville@gmail.com 2012-11-19 19:57:16 +00:00
parent f97a76a018
commit e19714bf98
13 changed files with 192 additions and 129 deletions

View File

@ -552,13 +552,13 @@ class ComicArchive:
fnp.parseFilename( self.path )
if fnp.issue != "":
metadata.issueNumber = fnp.issue
metadata.issue = fnp.issue
if fnp.series != "":
metadata.series = fnp.series
if fnp.volume != "":
metadata.volumeNumber = fnp.volume
metadata.volume = fnp.volume
if fnp.year != "":
metadata.publicationYear = fnp.year
metadata.year = fnp.year
if fnp.issue_count != "":
metadata.issueCount = fnp.issue_count

View File

@ -47,15 +47,15 @@ class ComicBookInfo:
metadata.series = xlate( 'series' )
metadata.title = xlate( 'title' )
metadata.issueNumber = xlate( 'issue' )
metadata.issue = xlate( 'issue' )
metadata.publisher = xlate( 'publisher' )
metadata.publicationMonth = xlate( 'publicationMonth' )
metadata.publicationYear = xlate( 'publicationYear' )
metadata.month = xlate( 'publicationMonth' )
metadata.year = xlate( 'publicationYear' )
metadata.issueCount = xlate( 'numberOfIssues' )
metadata.comments = xlate( 'comments' )
metadata.credits = xlate( 'credits' )
metadata.genre = xlate( 'genre' )
metadata.volumeNumber = xlate( 'volume' )
metadata.volume = xlate( 'volume' )
metadata.volumeCount = xlate( 'numberOfVolumes' )
metadata.language = xlate( 'language' )
metadata.country = xlate( 'country' )
@ -107,14 +107,14 @@ class ComicBookInfo:
assign( 'series', metadata.series )
assign( 'title', metadata.title )
assign( 'issue', metadata.issueNumber )
assign( 'issue', metadata.issue )
assign( 'publisher', metadata.publisher )
assign( 'publicationMonth', metadata.publicationMonth )
assign( 'publicationYear', metadata.publicationYear )
assign( 'publicationMonth', metadata.month )
assign( 'publicationYear', metadata.year )
assign( 'numberOfIssues', metadata.issueCount )
assign( 'comments', metadata.comments )
assign( 'genre', metadata.genre )
assign( 'volume', metadata.volumeNumber )
assign( 'volume', metadata.volume )
assign( 'numberOfVolumes', metadata.volumeCount )
assign( 'language', utils.getLanguageFromISO(metadata.language) )
assign( 'country', metadata.country )

View File

@ -54,8 +54,10 @@ class ComicInfoXml:
def stringFromMetadata( self, metadata ):
header = '<?xml version="1.0"?>\n'
tree = self.convertMetadataToXML( self, metadata )
return ET.tostring(tree.getroot())
return header + ET.tostring(tree.getroot())
def indent( self, elem, level=0 ):
# for making the XML output readable
@ -80,25 +82,25 @@ class ComicInfoXml:
# build a tree structure
root = ET.Element("ComicInfo")
root.attrib['xmlns:xsi']="http://www.w3.org/2001/XMLSchema-instance"
root.attrib['xmlns:xsd']="http://www.w3.org/2001/XMLSchema"
#helper func
def assign( cix_entry, md_entry):
if md_entry is not None:
ET.SubElement(root, cix_entry).text = u"{0}".format(md_entry)
assign( 'Series', md.series )
assign( 'Number', md.issueNumber )
assign( 'Number', md.issue )
assign( 'Title', md.title )
assign( 'Count', md.issueCount )
assign( 'Volume', md.volumeNumber )
assign( 'Volume', md.volume )
assign( 'AlternateSeries', md.alternateSeries )
assign( 'AlternateNumber', md.alternateNumber )
assign( 'AlternateCount', md.alternateCount )
assign( 'Summary', md.comments )
assign( 'Notes', md.notes )
assign( 'Year', md.publicationYear )
assign( 'Month', md.publicationMonth )
assign( 'Year', md.year )
assign( 'Month', md.month )
assign( 'Publisher', md.publisher )
assign( 'Imprint', md.imprint )
assign( 'Genre', md.genre )
@ -213,16 +215,16 @@ class ComicInfoXml:
md.series = xlate( 'Series' )
md.title = xlate( 'Title' )
md.issueNumber = xlate( 'Number' )
md.issue = xlate( 'Number' )
md.issueCount = xlate( 'Count' )
md.volumeNumber = xlate( 'Volume' )
md.volume = xlate( 'Volume' )
md.alternateSeries = xlate( 'AlternateSeries' )
md.alternateNumber = xlate( 'AlternateNumber' )
md.alternateCount = xlate( 'AlternateCount' )
md.comments = xlate( 'Summary' )
md.notes = xlate( 'Notes' )
md.publicationYear = xlate( 'Year' )
md.publicationMonth = xlate( 'Month' )
md.year = xlate( 'Year' )
md.month = xlate( 'Month' )
md.publisher = xlate( 'Publisher' )
md.imprint = xlate( 'Imprint' )
md.genre = xlate( 'Genre' )

View File

@ -193,11 +193,11 @@ class ComicVineTalker(QObject):
if math.floor(num_f) != num_f:
num_s = str( num_f )
metadata.issueNumber = num_s
metadata.issue = num_s
metadata.title = issue_results['name']
metadata.publisher = volume_results['publisher']['name']
metadata.publicationMonth = issue_results['publish_month']
metadata.publicationYear = issue_results['publish_year']
metadata.month = issue_results['publish_month']
metadata.year = issue_results['publish_year']
#metadata.issueCount = volume_results['count_of_issues']
metadata.comments = self.cleanup_html(issue_results['description'])

View File

@ -73,7 +73,7 @@ class FileNameParser:
return count
def getIssueNumber( self,filename ):
def getIssueNumber( self, filename ):
found = False
issue = ''

View File

@ -58,13 +58,13 @@ class GenericMetadata:
self.tagOrigin = None
self.series = None
self.issueNumber = None
self.issue = None
self.title = None
self.publisher = None
self.publicationMonth = None
self.publicationYear = None
self.month = None
self.year = None
self.issueCount = None
self.volumeNumber = None
self.volume = None
self.genre = None
self.language = None # 2 letter iso code
self.comments = None # use same way as Summary in CIX
@ -104,25 +104,28 @@ class GenericMetadata:
def assign( cur, new ):
if new is not None:
setattr(self, cur, new)
if type(new) == str and len(new) == 0:
setattr(self, cur, None)
else:
setattr(self, cur, new)
if not new_md.isEmpty:
self.isEmpty = False
assign( 'series', new_md.series )
assign( "issueNumber", new_md.issueNumber )
assign( "issue", new_md.issue )
assign( "issueCount", new_md.issueCount )
assign( "title", new_md.title )
assign( "publisher", new_md.publisher )
assign( "publicationMonth", new_md.publicationMonth )
assign( "publicationYear", new_md.publicationYear )
assign( "volumeNumber", new_md.volumeNumber )
assign( "month", new_md.month )
assign( "year", new_md.year )
assign( "volume", new_md.volume )
assign( "volumeCount", new_md.volumeCount )
assign( "genre", new_md.genre )
assign( "language", new_md.language )
assign( "country", new_md.country )
assign( "alternateSeries", new_md.criticalRating )
assign( "alt. series", new_md.alternateSeries )
assign( "alternateSeries", new_md.alternateSeries )
assign( "alternateNumber", new_md.alternateNumber )
assign( "alternateCount", new_md.alternateCount )
assign( "imprint", new_md.imprint )
@ -144,10 +147,12 @@ class GenericMetadata:
# not sure if the tags, credits, and pages should broken down, or treated
# as whole lists.... For now, go the easy route, where any overlay
# value wipes out the whole list
assign( "tags", new_md.tags )
assign( "credits", new_md.credits )
assign( "pages", new_md.pages )
if len(new_md.tags) > 0:
assign( "tags", new_md.tags )
if len(new_md.credits) > 0:
assign( "credits", new_md.credits )
if len(new_md.pages) > 0:
assign( "pages", new_md.pages )
def addCredit( self, person, role, primary = False ):
@ -166,44 +171,49 @@ class GenericMetadata:
if self.isEmpty:
return "No metadata"
def add( tag, val ):
def add_string( tag, val ):
if val is not None and u"{0}".format(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 ) )
def add_attr_string( tag ):
val = getattr(self,tag)
add_string( tag, getattr(self,tag) )
add_attr_string( "series" )
add_attr_string( "issue" )
add_attr_string( "issueCount" )
add_attr_string( "title" )
add_attr_string( "publisher" )
add_attr_string( "month" )
add_attr_string( "year" )
add_attr_string( "volume" )
add_attr_string( "volumeCount" )
add_attr_string( "genre" )
add_attr_string( "language" )
add_attr_string( "country" )
add_attr_string( "criticalRating" )
add_attr_string( "alternateSeries" )
add_attr_string( "alternateNumber" )
add_attr_string( "alternateCount" )
add_attr_string( "imprint" )
add_attr_string( "webLink" )
add_attr_string( "format" )
add_attr_string( "manga" )
add_attr_string( "blackAndWhite" )
add_attr_string( "maturityRating" )
add_attr_string( "storyArc" )
add_attr_string( "seriesGroup" )
add_attr_string( "scanInfo" )
add_attr_string( "characters" )
add_attr_string( "teams" )
add_attr_string( "locations" )
add_attr_string( "comments" )
add_attr_string( "notes" )
add_string( "tags", utils.listToString( self.tags ) )
for c in self.credits:
add( "credit", c['role']+": "+c['person'] )
add_string( "credit", c['role']+": "+c['person'] )
# find the longest field name
flen = 0
@ -213,8 +223,8 @@ class GenericMetadata:
#format the data nicely
outstr = ""
fmt_str = u"{0: <" + str(flen) + "}: {1}\n"
fmt_str = u"{0: <" + str(flen) + "} {1}\n"
for i in vals:
outstr += fmt_str.format( i[0], i[1] )
outstr += fmt_str.format( i[0]+":", i[1] )
return outstr

View File

@ -137,9 +137,9 @@ class IssueIdentifier:
if self.onlyUseAdditionalMetaData:
search_keys['series'] = self.additional_metadata.series
search_keys['issue_number'] = self.additional_metadata.issueNumber
search_keys['year'] = self.additional_metadata.publicationYear
search_keys['month'] = self.additional_metadata.publicationMonth
search_keys['issue_number'] = self.additional_metadata.issue
search_keys['year'] = self.additional_metadata.year
search_keys['month'] = self.additional_metadata.month
return search_keys
# see if the archive has any useful meta data for searching with
@ -165,26 +165,26 @@ class IssueIdentifier:
else:
search_keys['series'] = md_from_filename.series
if self.additional_metadata.issueNumber is not None:
search_keys['issue_number'] = self.additional_metadata.issueNumber
elif internal_metadata.issueNumber is not None:
search_keys['issue_number'] = internal_metadata.issueNumber
if self.additional_metadata.issue is not None:
search_keys['issue_number'] = self.additional_metadata.issue
elif internal_metadata.issue is not None:
search_keys['issue_number'] = internal_metadata.issue
else:
search_keys['issue_number'] = md_from_filename.issueNumber
search_keys['issue_number'] = md_from_filename.issue
if self.additional_metadata.publicationYear is not None:
search_keys['year'] = self.additional_metadata.publicationYear
elif internal_metadata.publicationYear is not None:
search_keys['year'] = internal_metadata.publicationYear
if self.additional_metadata.year is not None:
search_keys['year'] = self.additional_metadata.year
elif internal_metadata.year is not None:
search_keys['year'] = internal_metadata.year
else:
search_keys['year'] = md_from_filename.publicationYear
search_keys['year'] = md_from_filename.year
if self.additional_metadata.publicationMonth is not None:
search_keys['month'] = self.additional_metadata.publicationMonth
elif internal_metadata.publicationMonth is not None:
search_keys['month'] = internal_metadata.publicationMonth
if self.additional_metadata.month is not None:
search_keys['month'] = self.additional_metadata.month
elif internal_metadata.month is not None:
search_keys['month'] = internal_metadata.month
else:
search_keys['month'] = md_from_filename.publicationMonth
search_keys['month'] = md_from_filename.month
return search_keys
@ -230,15 +230,15 @@ class IssueIdentifier:
self.log_msg("Not enough info for a search!")
return []
"""
self.log_msg( "Going to search for:" )
self.log_msg( "Series: " + keys['series'] )
self.log_msg( "Issue : " + keys['issue_number'] )
self.log_msg( "\tSeries: " + keys['series'] )
self.log_msg( "\tIssue : " + keys['issue_number'] )
if keys['year'] is not None:
self.log_msg( "Year : " + keys['year'] )
self.log_msg( "\tYear : " + keys['year'] )
if keys['month'] is not None:
self.log_msg( "Month : " + keys['month'] )
"""
self.log_msg( "\tMonth : " + keys['month'] )
comicVine = ComicVineTalker( self.cv_api_key )

View File

@ -23,6 +23,8 @@ import getopt
import platform
import os
from genericmetadata import GenericMetadata
class Enum(set):
def __getattr__(self, name):
if name in self:
@ -57,9 +59,13 @@ If no options are given, {0} will run in windowed mode
-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
-m, --metadata=LIST Explicity define some tags to be used as a list
....TBD........
....TBD........
-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 ",", ash show in the example above
Some names that can be used:
series, issue, issueCount, year, publisher, title
-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
@ -90,8 +96,46 @@ If no options are given, {0} will run in windowed mode
sys.exit(code)
def parseMetadataFromString( self, mdstr ):
print "TBD!!!!!!!!!!!!!!!!!!!!!"
return None
# The metadata string is a comma separated list of name-value pairs
# The names match the attributes of the internal metadata struct (for now)
# The caret is the special "escape character", since it's not common in
# natural language text
# example = "series=Kickers^, Inc. ,issue=1, year=1986"
escaped_comma = "^,"
escaped_equals = "^="
replacement_token = "<_~_>"
md = GenericMetadata()
# First, replace escaped commas with with a unique token (to be changed back later)
mdstr = mdstr.replace( escaped_comma, replacement_token)
tmp_list = mdstr.split(",")
md_list = []
for item in tmp_list:
item = item.replace( replacement_token, "," )
md_list.append(item)
# Now build a nice dict from the list
md_dict = dict()
for item in md_list:
# Make sure to fix any escaped equal signs
i = item.replace( escaped_equals, replacement_token)
key,value = i.split("=")
value = value.replace( replacement_token, "=" ).strip()
key = key.strip()
md_dict[key] = value
# Map the dict to the metadata object
for key in md_dict:
if not hasattr(md, key):
print "Warning: '{0}' is not a valid tag name".format(key)
else:
md.isEmpty = False
setattr( md, key, md_dict[key] )
#print md
return md
def parseCmdLineArgs(self):

View File

@ -42,7 +42,8 @@ import utils
#-----------------------------
def cli_mode( opts, settings ):
for f in opts.file_list:
print "Processing: ", f
if len( opts.file_list ) > 1:
print "Processing: ", f
process_file_cli( f, opts, settings )
def process_file_cli( filename, opts, settings ):
@ -94,11 +95,12 @@ def process_file_cli( filename, opts, settings ):
if opts.data_style is None or opts.data_style == MetaDataStyle.CIX:
if cix:
print "------ComicRack tags--------"
print u"{0}".format(ca.readCIX())
print u"{0}".format(ca.readCIX()).encode("utf-8")
if opts.data_style is None or opts.data_style == MetaDataStyle.CBI:
if cbi:
print "------ComicBookLover tags--------"
print u"{0}".format(ca.readCBI())
print u"{0}".format(ca.readCBI()).encode("utf-8")
elif opts.delete_tags:

View File

@ -474,14 +474,14 @@ class TaggerWindow( QtGui.QMainWindow):
md = self.metadata
assignText( self.leSeries, md.series )
assignText( self.leIssueNum, md.issueNumber )
assignText( self.leIssueNum, md.issue )
assignText( self.leIssueCount, md.issueCount )
assignText( self.leVolumeNum, md.volumeNumber )
assignText( self.leVolumeNum, md.volume )
assignText( self.leVolumeCount, md.volumeCount )
assignText( self.leTitle, md.title )
assignText( self.lePublisher, md.publisher )
assignText( self.lePubMonth, md.publicationMonth )
assignText( self.lePubYear, md.publicationYear )
assignText( self.lePubMonth, md.month )
assignText( self.lePubYear, md.year )
assignText( self.leGenre, md.genre )
assignText( self.leImprint, md.imprint )
assignText( self.teComments, md.comments )
@ -589,14 +589,14 @@ class TaggerWindow( QtGui.QMainWindow):
# copy the data from the form into the metadata
md = self.metadata
md.series = xlate( self.leSeries.text(), "str" )
md.issueNumber = xlate( self.leIssueNum.text(), "str" )
md.issue = xlate( self.leIssueNum.text(), "str" )
md.issueCount = xlate( self.leIssueCount.text(), "int" )
md.volumeNumber = xlate( self.leVolumeNum.text(), "int" )
md.volume = xlate( self.leVolumeNum.text(), "int" )
md.volumeCount = xlate( self.leVolumeCount.text(), "int" )
md.title = xlate( self.leTitle.text(), "str" )
md.publisher = xlate( self.lePublisher.text(), "str" )
md.publicationMonth = xlate( self.lePubMonth.text(), "int" )
md.publicationYear = xlate( self.lePubYear.text(), "int" )
md.month = xlate( self.lePubMonth.text(), "int" )
md.year = xlate( self.lePubYear.text(), "int" )
md.genre = xlate( self.leGenre.text(), "str" )
md.imprint = xlate( self.leImprint.text(), "str" )
md.comments = xlate( self.teComments.toPlainText(), "str" )

View File

@ -261,7 +261,7 @@
<x>0</x>
<y>0</y>
<width>685</width>
<height>380</height>
<height>384</height>
</rect>
</property>
<widget class="QWidget" name="layoutWidget">

View File

@ -1,15 +1,6 @@
-----------------
Features
----------------
CLI
multiple files?
save log
abort flag
explicit metadata settings option format
just series, issue, year??
verbose settings for identifier
interactive for choices option?
--- or defer choices to end, by keeping special log of those files
Settings/Preferences Dialog
Remove API Key
@ -40,6 +31,10 @@ Better stripping of html from CV text
-----------------
Bugs
----------------
Unicode errors when piping or redirecting stdout
Windows zip and rar: set proper path for writing to archive
SERIOUS BUG: rebuilding zips!
http://stackoverflow.com/questions/11578443/trigger-io-errno-18-cross-device-link
@ -52,8 +47,6 @@ OSX:
Filename parsing:
Rework how series name is separated from issue
Test ComicRack android
Form type validation Ints vs strings for month, year. etc
Check all HTTP responses for errors
@ -64,6 +57,16 @@ Lots of error checking
-------------
Future
------------
CLI
write a log for multiple file processing
override abort on low confidence flag
explicit metadata settings option format
-- figure out how to add credits,tags
interactive for choices option?
--- or defer choices to end, by keeping special log of those files
Add warning message to allow writing CBI to RAR, and ask them to contact CBL ! :-)
Scrape alternate Covers from ComicVine issue pages
@ -91,6 +94,8 @@ Idea: Support only CBI or CIX for any given file, and not both
Longer term:
Think about mass tagging and (semi) automatic volume selection
<?xml version="1.0"?>
-<ComicInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
----------------------------------------------
COMIC RACK Questions

View File

@ -129,8 +129,8 @@ class VolumeSelectionWindow(QtGui.QDialog):
md = GenericMetadata()
md.series = self.series_name
md.issueNumber = self.issue_number
md.publicationYear = self.year
md.issue = self.issue_number
md.year = self.year
self.ii.setAdditionalMetadata( md )
self.ii.onlyUseAdditionalMetaData = True