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:
parent
121889ed1b
commit
deeeef90a6
226
comet.py
Normal file
226
comet.py
Normal 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 )
|
||||
|
@ -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 ):
|
||||
|
||||
|
@ -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 = ""
|
||||
|
@ -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 )
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user