First cut at CoMet support

git-svn-id: http://comictagger.googlecode.com/svn/trunk@192 6c5673fe-1810-88d6-992b-cd32ca31540c
This commit is contained in:
beville@gmail.com 2012-12-02 20:17:39 +00:00
parent 121889ed1b
commit deeeef90a6
4 changed files with 315 additions and 9 deletions

226
comet.py Normal file
View File

@ -0,0 +1,226 @@
"""
A python class to encapsulate CoMets's data and file handling
"""
"""
Copyright 2012 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from datetime import datetime
import zipfile
from pprint import pprint
import xml.etree.ElementTree as ET
from genericmetadata import GenericMetadata
import utils
class CoMet:
writer_synonyms = ['writer', 'plotter', 'scripter']
penciller_synonyms = [ 'artist', 'penciller', 'penciler', 'breakdowns' ]
inker_synonyms = [ 'inker', 'artist', 'finishes' ]
colorist_synonyms = [ 'colorist', 'colourist', 'colorer', 'colourer' ]
letterer_synonyms = [ 'letterer']
cover_synonyms = [ 'cover', 'covers', 'coverartist', 'cover artist' ]
editor_synonyms = [ 'editor']
def getParseableCredits( self ):
parsable_credits = []
parsable_credits.extend( self.writer_synonyms )
parsable_credits.extend( self.penciller_synonyms )
parsable_credits.extend( self.inker_synonyms )
parsable_credits.extend( self.colorist_synonyms )
parsable_credits.extend( self.letterer_synonyms )
parsable_credits.extend( self.cover_synonyms )
parsable_credits.extend( self.editor_synonyms )
return parsable_credits
def metadataFromString( self, string ):
tree = ET.ElementTree(ET.fromstring( string ))
return self.convertXMLToMetadata( tree )
def stringFromMetadata( self, metadata ):
header = '<?xml version="1.0" encoding="UTF-8"?>\n'
tree = self.convertMetadataToXML( self, metadata )
return header + ET.tostring(tree.getroot())
def indent( self, elem, level=0 ):
# for making the XML output readable
i = "\n" + level*" "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
self.indent( elem, level+1 )
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def convertMetadataToXML( self, filename, metadata ):
#shorthand for the metadata
md = metadata
# build a tree structure
root = ET.Element("comet")
root.attrib['xmlns:comet'] = "http://www.denvog.com/comet/"
root.attrib['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
root.attrib['xsi:schemaLocation'] = "http://www.denvog.com http://www.denvog.com/comet/comet.xsd"
#helper func
def assign( comet_entry, md_entry):
if md_entry is not None:
ET.SubElement(root, comet_entry).text = u"{0}".format(md_entry)
# title is manditory
if md.title is None:
md.title = ""
assign( 'title', md.title )
assign( 'series', md.series )
assign( 'issue', md.issue ) #must be int...
assign( 'volume', md.volume )
assign( 'description', md.comments )
#assign( 'date', md.year ) #need to make a date from month, year
assign( 'publisher', md.publisher )
assign( 'genre', md.genre )
assign( 'pages', md.pageCount )
assign( 'format', md.format )
assign( 'language', md.language )
#assign( 'readingDirection', md.??? )
assign( 'character', md.characters )
assign( 'rating', md.maturityRating )
#assign( 'price', md.??? )
#assign( 'isVersionOf', md.??? )
#assign( 'rights', md.??? )
#assign( 'identifier', md.??? )
#assign( 'coverImage', md.??? )
#assign( 'lastMark', md.??? )
# need to specially process the credits, since they are structured differently than CIX
credit_writer_list = list()
credit_penciller_list = list()
credit_inker_list = list()
credit_colorist_list = list()
credit_letterer_list = list()
credit_cover_list = list()
credit_editor_list = list()
# loop thru credits, and build a list for each role that CoMet supports
for credit in metadata.credits:
if credit['role'].lower() in set( self.writer_synonyms ):
ET.SubElement(root, 'writer').text = u"{0}".format(credit['person'])
if credit['role'].lower() in set( self.penciller_synonyms ):
ET.SubElement(root, 'penciller').text = u"{0}".format(credit['person'])
if credit['role'].lower() in set( self.inker_synonyms ):
ET.SubElement(root, 'inker').text = u"{0}".format(credit['person'])
if credit['role'].lower() in set( self.colorist_synonyms ):
ET.SubElement(root, 'inker').text = u"{0}".format(credit['person'])
credit_colorist_list.append(credit['person'].replace(",",""))
if credit['role'].lower() in set( self.letterer_synonyms ):
ET.SubElement(root, 'letterer').text = u"{0}".format(credit['person'])
if credit['role'].lower() in set( self.cover_synonyms ):
ET.SubElement(root, 'coverDesigner').text = u"{0}".format(credit['person'])
if credit['role'].lower() in set( self.editor_synonyms ):
ET.SubElement(root, 'editor').text = u"{0}".format(credit['person'])
# self pretty-print
self.indent(root)
# wrap it in an ElementTree instance, and save as XML
tree = ET.ElementTree(root)
return tree
def convertXMLToMetadata( self, tree ):
root = tree.getroot()
if root.tag != 'comet':
raise 1
return None
metadata = GenericMetadata()
md = metadata
# Helper function
def xlate( tag ):
node = root.find( tag )
if node is not None:
return node.text
else:
return None
md.series = xlate( 'series' )
md.title = xlate( 'title' )
md.issue = xlate( 'issue' )
md.volume = xlate( 'volume' )
md.comments = xlate( 'description' )
#md.year = xlate( 'Year' )
#md.month = xlate( 'Month' )
md.publisher = xlate( 'publisher' )
md.genre = xlate( 'genre' )
md.language = xlate( 'language' )
md.format = xlate( 'format' )
#md.manga = xlate( 'readingDirection' )
#md.characters = xlate( 'character' )
md.pageCount = xlate( 'pages' )
md.maturityRating = xlate( 'rating' )
# Now extract the credit info
for n in root:
if ( n.tag == 'writer' or
n.tag == 'penciller' or
n.tag == 'inker' or
n.tag == 'colorist' or
n.tag == 'letterer' or
n.tag == 'editor'
):
metadata.addCredit( n.text.strip(), n.tag.title() )
if n.tag == 'coverDesigner':
metadata.addCredit( n.text.strip(), "Cover" )
metadata.isEmpty = False
return metadata
def writeToExternalFile( self, filename, metadata ):
tree = self.convertMetadataToXML( self, metadata )
#ET.dump(tree)
tree.write(filename, encoding='utf-8')
def readFromExternalFile( self, filename ):
tree = ET.parse( filename )
return self.convertXMLToMetadata( tree )

View File

@ -36,6 +36,7 @@ from UnRAR2.rar_exceptions import *
from options import Options, MetaDataStyle
from comicinfoxml import ComicInfoXml
from comicbookinfo import ComicBookInfo
from comet import CoMet
from genericmetadata import GenericMetadata
from filenameparser import FileNameParser
@ -385,6 +386,7 @@ class ComicArchive:
def __init__( self, path ):
self.path = path
self.ci_xml_filename = 'ComicInfo.xml'
self.comet_filename = 'CoMet.xml'
if self.zipTest():
self.archive_type = self.ArchiveType.Zip
@ -470,6 +472,8 @@ class ComicArchive:
return self.readCIX()
elif style == MetaDataStyle.CBI:
return self.readCBI()
elif style == MetaDataStyle.COMET:
return self.readCoMet()
else:
return GenericMetadata()
@ -479,6 +483,8 @@ class ComicArchive:
return self.writeCIX( metadata )
elif style == MetaDataStyle.CBI:
return self.writeCBI( metadata )
elif style == MetaDataStyle.COMET:
return self.writeCoMet( metadata )
def hasMetadata( self, style ):
@ -486,6 +492,8 @@ class ComicArchive:
return self.hasCIX()
elif style == MetaDataStyle.CBI:
return self.hasCBI()
elif style == MetaDataStyle.COMET:
return self.hasCoMet()
else:
return False
@ -494,6 +502,8 @@ class ComicArchive:
return self.removeCIX()
elif style == MetaDataStyle.CBI:
return self.removeCBI()
elif style == MetaDataStyle.COMET:
return self.removeCoMet()
def getCoverPage(self):
@ -555,7 +565,14 @@ class ComicArchive:
return self.archiver.getArchiveComment()
def hasCBI(self):
#if ( not ( self.isZip() or self.isRar()) or not self.seemsToBeAComicArchive() ):
if not self.seemsToBeAComicArchive():
return False
comment = self.archiver.getArchiveComment()
return ComicBookInfo().validateString( comment )
def writeCBI( self, metadata ):
cbi_string = ComicBookInfo().stringFromMetadata( metadata )
return self.archiver.setArchiveComment( cbi_string )
@ -597,14 +614,41 @@ class ComicArchive:
else:
return False
def hasCBI(self):
#if ( not ( self.isZip() or self.isRar()) or not self.seemsToBeAComicArchive() ):
if not self.seemsToBeAComicArchive():
def readCoMet( self ):
raw_comet = self.readRawCoMet()
if raw_comet is None:
return GenericMetadata()
return CoMet().metadataFromString( raw_comet )
def readRawCoMet( self ):
if not self.hasCoMet():
print self.path, "doesn't has CoMet data!"
return None
return self.archiver.readArchiveFile( self.comet_filename )
def writeCoMet(self, metadata):
if metadata is not None:
comet_string = CoMet().stringFromMetadata( metadata )
return self.archiver.writeArchiveFile( self.comet_filename, comet_string )
else:
return False
def removeCoMet( self ):
return self.archiver.removeArchiveFile( self.comet_filename )
def hasCoMet(self):
if not self.seemsToBeAComicArchive():
return False
elif self.comet_filename in self.archiver.getArchiveFilenameList():
return True
else:
return False
comment = self.archiver.getArchiveComment()
return ComicBookInfo().validateString( comment )
def metadataFromFilename( self ):

View File

@ -77,8 +77,10 @@ def process_file_cli( filename, opts, settings ):
cix = False
cbi = False
comet = False
if ca.hasCIX(): cix = True
if ca.hasCBI(): cbi = True
if ca.hasCoMet(): comet = True
if opts.print_tags:
@ -93,11 +95,12 @@ def process_file_cli( filename, opts, settings ):
brief += "({0: >3} pages)".format(page_count)
brief += " tags:[ "
if not (cbi or cix):
if not ( cbi or cix or comet ):
brief += "none "
else:
if cbi: brief += "CBL "
if cix: brief += "CR "
if comet: brief += "CoMet "
brief += "]"
print brief
@ -118,6 +121,14 @@ def process_file_cli( filename, opts, settings ):
pprint(json.loads(ca.readRawCBI()))
else:
print u"{0}".format(ca.readCBI())
if opts.data_style is None or opts.data_style == MetaDataStyle.COMET:
if comet:
print "------CoMet tags--------"
if opts.raw:
print u"{0}".format(ca.readRawCoMet())
else:
print u"{0}".format(ca.readCoMet())
elif opts.delete_tags:
@ -144,6 +155,18 @@ def process_file_cli( filename, opts, settings ):
print "dry-run. ComicBookLover tags not removed"
else:
print "This archive doesn't have ComicBookLover tags."
if opts.data_style == MetaDataStyle.COMET:
if comet:
if not opts.dryrun:
if not ca.removeCoMet():
print "Tag removal seemed to fail!"
else:
print "Removed CoMet tags."
else:
print "dry-run. CoMet tags not removed"
else:
print "This archive doesn't have CoMet tags."
elif opts.save_tags:
@ -155,6 +178,8 @@ def process_file_cli( filename, opts, settings ):
md = ca.readCIX()
elif opts.data_style == MetaDataStyle.CBI and cbi:
md = ca.readCBI()
elif opts.data_style == MetaDataStyle.COMET and comet:
md = ca.readCoMet()
# now, overlay the new data onto the old, in order
@ -249,6 +274,8 @@ def process_file_cli( filename, opts, settings ):
md = ca.readCIX()
elif opts.data_style == MetaDataStyle.CBI and cbi:
md = ca.readCBI()
elif opts.data_style == MetaDataStyle.COMET and comet:
md = ca.readCoMet()
if md.isEmpty:
print "Comic archive contains no tags!"
@ -264,6 +291,12 @@ def process_file_cli( filename, opts, settings ):
md = ca.readCBI()
else:
print "Comic archive contains no ComicBookLover tags!"
if opts.data_style == MetaDataStyle.COMET:
if comet:
md = ca.readCoMet()
else:
print "Comic archive contains no CoMet tags!"
# TODO move this to ComicArchive, or maybe another class???
new_name = ""

View File

@ -34,7 +34,8 @@ class Enum(set):
class MetaDataStyle:
CBI = 0
CIX = 1
name = [ 'ComicBookLover', 'ComicRack' ]
COMET = 2
name = [ 'ComicBookLover', 'ComicRack', 'CoMet' ]
class Options:
@ -53,8 +54,8 @@ If no options are given, {0} will run in windowed mode
-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)
-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
@ -197,6 +198,8 @@ If no options are given, {0} will run in windowed mode
self.data_style = MetaDataStyle.CIX
elif a.lower() == "cbl":
self.data_style = MetaDataStyle.CBI
elif a.lower() == "comet":
self.data_style = MetaDataStyle.COMET
else:
self.display_help_and_quit( "Invalid tag type", 1 )