diff --git a/comicarchive.py b/comicarchive.py
index 9752b5e..80281a7 100644
--- a/comicarchive.py
+++ b/comicarchive.py
@@ -453,36 +453,45 @@ class ComicArchive:
def getPage( self, index ):
- num_pages = self.getNumberOfPages()
+ image_data = None
+
+ filename = self.getPageName( index )
+
+ if filename is not None:
+ image_data = self.archiver.readArchiveFile( filename )
+
+ return image_data
+
+ def getPageName( self, index ):
+
+ page_list = self.getPageNameList()
+
+ num_pages = len( page_list )
if num_pages == 0 or index >= num_pages:
return None
-
+
+ return page_list[index]
+
+ def getPageNameList( self , sort_list=True):
+
# get the list file names in the archive, and sort
files = self.archiver.getArchiveFilenameList()
# seems like some archive creators are on Windows, and don't know about case-sensitivity!
- files.sort(key=lambda x: x.lower())
+ if sort_list:
+ files.sort(key=lambda x: x.lower())
# make a sub-list of image files
page_list = []
for name in files:
if ( name[-4:].lower() in [ ".jpg", "jpeg", ".png" ] ):
page_list.append(name)
+
+ return page_list
- image_data = self.archiver.readArchiveFile( page_list[index] )
-
- return image_data
-
-
- def getNumberOfPages(self):
-
- count = 0
+ def getNumberOfPages( self ):
- for item in self.archiver.getArchiveFilenameList():
- if ( item[-4:].lower() in [ ".jpg", "jpeg", ".png" ] ):
- count += 1
-
- return count
+ return len( self.getPageNameList( sort_list=False ) )
def readCBI( self ):
diff --git a/comicinfoxml.py b/comicinfoxml.py
index efa56b9..e421c72 100644
--- a/comicinfoxml.py
+++ b/comicinfoxml.py
@@ -26,7 +26,27 @@ from genericmetadata import GenericMetadata
import utils
class ComicInfoXml:
+
+ 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 ))
@@ -110,25 +130,25 @@ class ComicInfoXml:
# first, loop thru credits, and build a list for each role that CIX supports
for credit in metadata.credits:
- if credit['role'].lower() in set( ['writer', 'plotter', 'scripter'] ):
+ if credit['role'].lower() in set( self.writer_synonyms ):
credit_writer_list.append(credit['person'])
- if credit['role'].lower() in set( [ 'artist', 'penciller', 'penciler', 'breakdowns' ] ):
+ if credit['role'].lower() in set( self.penciller_synonyms ):
credit_penciller_list.append(credit['person'])
- if credit['role'].lower() in set( [ 'inker', 'artist', 'finishes' ] ):
+ if credit['role'].lower() in set( self.inker_synonyms ):
credit_inker_list.append(credit['person'])
- if credit['role'].lower() in set( [ 'colorist', 'colourist', 'colorer', 'colourer' ]):
+ if credit['role'].lower() in set( self.colorist_synonyms ):
credit_colorist_list.append(credit['person'])
- if credit['role'].lower() in set( [ 'letterer'] ):
+ if credit['role'].lower() in set( self.letterer_synonyms ):
credit_letterer_list.append(credit['person'])
- if credit['role'].lower() in set( [ 'cover', 'covers', 'coverartist', 'cover artist' ] ):
+ if credit['role'].lower() in set( self.cover_synonyms ):
credit_cover_list.append(credit['person'])
- if credit['role'].lower() in set( [ 'editor'] ):
+ if credit['role'].lower() in set( self.editor_synonyms ):
credit_editor_list.append(credit['person'])
# second, convert each list to string, and add to XML struct
diff --git a/comicvinetalker.py b/comicvinetalker.py
index 527107a..1a790bb 100644
--- a/comicvinetalker.py
+++ b/comicvinetalker.py
@@ -39,7 +39,8 @@ class ComicVineTalker(QObject):
def __init__(self, api_key):
QObject.__init__(self)
- self.api_key = api_key
+ # key that is registered to comictagger
+ self.api_key = '27431e6787042105bd3e47e169a624521f89f3a4'
def testKey( self ):
diff --git a/issueidentifier.py b/issueidentifier.py
index bdf71dd..eb73582 100644
--- a/issueidentifier.py
+++ b/issueidentifier.py
@@ -32,6 +32,13 @@ from imagefetcher import ImageFetcher
import utils
class IssueIdentifier:
+
+ ResultNoMatches = 0
+ ResultFoundMatchButBadCoverScore = 1
+ ResultFoundMatchButNotFirstPage = 2
+ ResultMultipleMatchesWithBadImageScores = 3
+ ResultOneGoodMatch = 4
+ ResultMultipleGoodMatches = 5
def __init__(self, comic_archive, cv_api_key ):
self.comic_archive = comic_archive
@@ -39,6 +46,7 @@ class IssueIdentifier:
self.additional_metadata = None
self.min_score_thresh = 22
self.min_score_distance = 2
+ self.strong_score_thresh = 8
self.additional_metadata = GenericMetadata()
self.cv_api_key = cv_api_key
self.output_function = IssueIdentifier.defaultWriteOutput
@@ -263,7 +271,7 @@ class IssueIdentifier:
if len(self.match_list) == 0:
self.log_msg( ":-( no matches!" )
return self.match_list
-
+
# sort list by image match scores
self.match_list.sort(key=lambda k: k['distance'])
@@ -286,13 +294,33 @@ class IssueIdentifier:
if len(self.match_list) == 1:
if best_score > self.min_score_thresh:
self.log_msg( "!!!! Very weak score for the cover. Maybe it's not the cover?" )
+
+
+ self.log_msg( "Comparing other pages now..." )
+ found = False
+ for i in range(ca.getNumberOfPages()):
+ image_data = ca.getPage(i)
+ page_hash = self.calculateHash( image_data )
+ distance = ImageHasher.hamming_distance(page_hash, self.match_list[0]['url_image_hash'])
+ if distance <= self.strong_score_thresh:
+ print "Found a great match d={0} on page {1}!".format(distance, i+1)
+ found = True
+ break
+ elif distance < self.min_score_thresh:
+ print "Found a good match d={0} on page {1}".format(distance, i)
+ found = True
+ self.log_msg( ".", newline=False )
+ self.log_msg( "" )
+ if not found:
+ self.log_msg( "No matching pages in the issue. Bummer" )
+
print_match(self.match_list[0])
return self.match_list
elif best_score > self.min_score_thresh and len(self.match_list) > 1:
self.log_msg( "No good image matches! Need to use other info..." )
return self.match_list
-
+
#now pare down list, remove any item more than specified distant from the top scores
for item in reversed(self.match_list):
if item['distance'] > best_score + self.min_score_distance:
@@ -307,7 +335,7 @@ class IssueIdentifier:
self.log_msg( "More than one likley candiate. Maybe a lexical comparison??" )
for item in self.match_list:
print_match(item)
-
+
return self.match_list
\ No newline at end of file
diff --git a/pagebrowser.py b/pagebrowser.py
index c7f53c3..67b8411 100644
--- a/pagebrowser.py
+++ b/pagebrowser.py
@@ -109,6 +109,6 @@ class PageBrowserWindow(QtGui.QDialog):
new_w = 0;
scaled_pixmap = self.current_pixmap.scaled(new_w, new_h, QtCore.Qt.KeepAspectRatio)
self.lblPage.setPixmap( scaled_pixmap )
-
-
+
+
\ No newline at end of file
diff --git a/tagger.py b/tagger.py
index 41d6e3d..d116bfc 100755
--- a/tagger.py
+++ b/tagger.py
@@ -89,7 +89,20 @@ def main():
splash.show()
splash.raise_()
app.processEvents()
- 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 )
diff --git a/taggerwindow.py b/taggerwindow.py
index 5efed9c..ea11404 100644
--- a/taggerwindow.py
+++ b/taggerwindow.py
@@ -28,6 +28,7 @@ import os
from volumeselectionwindow import VolumeSelectionWindow
from options import Options, MetaDataStyle
+from comicinfoxml import ComicInfoXml
from genericmetadata import GenericMetadata
from comicvinetalker import ComicVineTalker
from comicarchive import ComicArchive
@@ -63,6 +64,163 @@ 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):
@@ -312,7 +470,8 @@ class TaggerWindow( QtGui.QMainWindow):
self.metadataToForm()
self.clearDirtyFlag() # also updates the app title
- self.updateInfoBox()
+ self.updateInfoBox()
+ #self.updatePagesInfo()
else:
QtGui.QMessageBox.information(self, self.tr("Whoops!"), self.tr("That file doesn't appear to be a comic archive!"))
@@ -349,7 +508,39 @@ class TaggerWindow( QtGui.QMainWindow):
self.lblTagList.setText( tag_info )
+ def updatePagesInfo( self ):
+
+ #tablemodel = PageTableModel( self.comic_archive, self )
+ #self.tableView.setModel(tablemodel)
+ listmodel = PageListModel( self.comic_archive, self )
+ self.listView.setModel(listmodel)
+
+ #self.listView.setDragDropMode(self.InternalMove)
+ #listmodel.installEventFilter(self)
+
+ self.listView.setDragEnabled(True)
+ self.listView.setAcceptDrops(True)
+ self.listView.setDropIndicatorShown(True)
+
+ #listmodel.rowsMoved.connect( self.rowsMoved )
+ #listmodel.rowsRemoved.connect( self.rowsRemoved )
+ #listmodel.beginMoveRows.connect( self.beginMoveRows )
+
+ #def rowsMoved( self, b, c, d):
+ # print "rowsMoved"
+ #def rowsRemoved( self,b, c, d):
+ # print "rowsRemoved"
+
+
+ """
+ def eventFilter(self, sender, event):
+ if (event.type() == QtCore.QEvent.ChildRemoved):
+ print "QEvent::ChildRemoved"
+ return False # don't actually interrupt anything
+ """
+
+
def setDirtyFlag( self, param1=None, param2=None, param3=None ):
if not self.dirtyFlag:
self.dirtyFlag = True
@@ -495,6 +686,7 @@ class TaggerWindow( QtGui.QMainWindow):
row += 1
self.twCredits.setSortingEnabled( True )
+ self.updateCreditColors()
def addNewCreditEntry( self, row, role, name ):
self.twCredits.insertRow(row)
@@ -520,7 +712,6 @@ class TaggerWindow( QtGui.QMainWindow):
return False
-
def formToMetadata( self ):
#helper func
@@ -681,6 +872,32 @@ class TaggerWindow( QtGui.QMainWindow):
def setDataStyle(self, s):
self.data_style, b = self.cbDataStyle.itemData(s).toInt()
self.updateStyleTweaks()
+
+ def updateCreditColors( self ):
+ inactive_color = QtGui.QColor(255, 170, 150)
+ active_palette = self.leSeries.palette()
+ active_color = active_palette.color( QtGui.QPalette.Base )
+
+ cix_credits = ComicInfoXml().getParseableCredits()
+
+ if self.data_style == MetaDataStyle.CIX:
+ #loop over credit table, mark selected rows
+ r = 0
+ while r < self.twCredits.rowCount():
+ if str(self.twCredits.item(r, 0).text()).lower() not in cix_credits:
+ print "Bad credit for CIX:", self.twCredits.item(r, 0).text()
+ self.twCredits.item(r, 0).setBackgroundColor( inactive_color )
+ else:
+ self.twCredits.item(r, 0).setBackgroundColor( active_color )
+ r = r + 1
+
+ if self.data_style == MetaDataStyle.CBI:
+ #loop over credit table, make all active color
+ r = 0
+ while r < self.twCredits.rowCount():
+ self.twCredits.item(r, 0).setBackgroundColor( active_color )
+ r = r + 1
+
def updateStyleTweaks( self ):
@@ -707,6 +924,7 @@ class TaggerWindow( QtGui.QMainWindow):
if enable:
item.setPalette(active_palette)
+ item.setAutoFillBackground( False )
if type(item) == QtGui.QCheckBox:
item.setEnabled( True )
elif type(item) == QtGui.QComboBox:
@@ -714,6 +932,7 @@ class TaggerWindow( QtGui.QMainWindow):
else:
item.setReadOnly( False )
else:
+ item.setAutoFillBackground( True )
if type(item) == QtGui.QCheckBox:
item.setPalette(inactive_palette2)
item.setEnabled( False )
@@ -733,7 +952,7 @@ class TaggerWindow( QtGui.QMainWindow):
self.leWebLink, self.teCharacters, self.teTeams,
self.teLocations, self.cbMaturityRating, self.cbFormat
]
-
+
if self.data_style == MetaDataStyle.CIX:
for item in cix_only:
enableWidget( item, True )
@@ -746,6 +965,8 @@ class TaggerWindow( QtGui.QMainWindow):
for item in cix_only:
enableWidget(item, False )
+ self.updateCreditColors()
+
def cellDoubleClicked( self, r, c ):
self.editCredit()
@@ -804,7 +1025,8 @@ class TaggerWindow( QtGui.QMainWindow):
# add new entry
row = self.twCredits.rowCount()
self.addNewCreditEntry( row, new_role, new_name)
-
+
+ self.updateCreditColors()
self.setDirtyFlag()
def removeCredit( self ):
diff --git a/taggerwindow.ui b/taggerwindow.ui
index 738e31e..6b6bc7c 100644
--- a/taggerwindow.ui
+++ b/taggerwindow.ui
@@ -96,7 +96,7 @@
- filename
+
true
@@ -131,7 +131,7 @@
QFrame::Sunken
- archive type
+
false
@@ -152,7 +152,7 @@
- page count
+
@@ -167,7 +167,7 @@
- tag list
+
@@ -386,12 +386,12 @@
-
- # Issues
+ Volume
-
-
+
@@ -403,12 +403,12 @@
-
- Volume
+ # Issues
-
-
+
-
@@ -458,6 +458,9 @@
-
+
+ QFormLayout::AllNonFixedFieldsGrow
+
-
@@ -497,7 +500,7 @@
-
- true
+ false
true
@@ -557,7 +560,7 @@
-
- true
+ false
Black && White
@@ -574,7 +577,7 @@
-
- true
+ false
@@ -962,6 +965,39 @@
Pages
+
+ -
+
+
+ true
+
+
+ QAbstractItemView::InternalMove
+
+
+ Qt::MoveAction
+
+
+ QListView::Snap
+
+
+ QListView::Adjust
+
+
+
+ 100
+ 170
+
+
+
+ QListView::IconMode
+
+
+ true
+
+
+
+
@@ -977,7 +1013,7 @@
0
0
975
- 28
+ 25