From 263598ef4a8accef2df9d3c649936991c7d2329b Mon Sep 17 00:00:00 2001 From: "beville@gmail.com" Date: Sun, 18 Nov 2012 00:32:01 +0000 Subject: [PATCH] A slew of enhancements git-svn-id: http://comictagger.googlecode.com/svn/trunk@56 6c5673fe-1810-88d6-992b-cd32ca31540c --- comicarchive.py | 13 ++-- comicvinecacher.py | 26 ++++--- filenameparser.py | 15 ++++- imagefetcher.py | 2 +- issueidentifier.py | 48 ++++++++++--- matchselectionwindow.py | 118 ++++++++++++++++++++++++++++++++ matchselectionwindow.ui | 142 +++++++++++++++++++++++++++++++++++++++ taggerwindow.py | 76 +++++++++++++++------ taggerwindow.ui | 6 +- todo.txt | 90 ++++++++++--------------- utils.py | 20 +++++- volumeselectionwindow.py | 41 +++++++++-- volumeselectionwindow.ui | 6 +- 13 files changed, 484 insertions(+), 119 deletions(-) create mode 100644 matchselectionwindow.py create mode 100644 matchselectionwindow.ui diff --git a/comicarchive.py b/comicarchive.py index 80281a7..445b5c2 100644 --- a/comicarchive.py +++ b/comicarchive.py @@ -397,16 +397,11 @@ class ComicArchive: def seemsToBeAComicArchive( self ): - ext = os.path.splitext(self.path)[1].lower() - if ( - ( ( ( self.isZip() ) and - ( ext.lower() in [ '.zip', '.cbz' ] )) - or - (( self.isRar() ) and - ( ext.lower() in [ '.rar', '.cbr' ] )) - or - ( self.isFolder() ) ) + # Do we even care about extensions?? + ext = os.path.splitext(self.path)[1].lower() + if ( + ( self.isZip() or self.isRar() or self.isFolder() ) and ( self.getNumberOfPages() > 3) diff --git a/comicvinecacher.py b/comicvinecacher.py index 1284e2d..daa0401 100644 --- a/comicvinecacher.py +++ b/comicvinecacher.py @@ -55,7 +55,7 @@ class ComicVineCacher: "count_of_issues INT," + "image_url TEXT," + "description TEXT," + - "timestamp TEXT)" + "timestamp DATE DEFAULT (datetime('now','localtime')) ) " ) cur.execute("CREATE TABLE Volumes(" + @@ -63,7 +63,7 @@ class ComicVineCacher: "name TEXT," + "publisher TEXT," + "count_of_issues INT," + - "timestamp TEXT," + + "timestamp DATE DEFAULT (datetime('now','localtime')), " + "PRIMARY KEY (id) )" ) @@ -78,7 +78,7 @@ class ComicVineCacher: "thumb_image_hash TEXT," + "publish_month TEXT," + "publish_year TEXT," + - "timestamp TEXT," + + "timestamp DATE DEFAULT (datetime('now','localtime')), " + "PRIMARY KEY (id ) )" ) @@ -107,7 +107,9 @@ class ComicVineCacher: else: url = record['image']['super_url'] - cur.execute("INSERT INTO VolumeSearchCache VALUES( ?, ?, ?, ?, ?, ?, ?, ?, ? )" , + cur.execute("INSERT INTO VolumeSearchCache " + + "(search_term, id, name, start_year, publisher, count_of_issues, image_url, description ) " + + "VALUES( ?, ?, ?, ?, ?, ?, ?, ? )" , ( search_term.lower(), record['id'], record['name'], @@ -115,8 +117,7 @@ class ComicVineCacher: pub_name, record['count_of_issues'], url, - record['description'], - timestamp ) + record['description']) ) def get_search_results( self, search_term ): @@ -126,7 +127,10 @@ class ComicVineCacher: with con: cur = con.cursor() - # TODO purge stale search results ( older than a day, maybe??) + + # purge stale search results + a_day_ago = datetime.datetime.today()-datetime.timedelta(days=1) + cur.execute( "DELETE FROM VolumeSearchCache WHERE timestamp < ?", [ str(a_day_ago) ] ) # fetch cur.execute("SELECT * FROM VolumeSearchCache WHERE search_term=?", [ search_term.lower() ] ) @@ -194,7 +198,13 @@ class ComicVineCacher: with con: cur = con.cursor() - # TODO purge stale volume records ( older than a week, maybe??) + # purge stale volume info + a_week_ago = datetime.datetime.today()-datetime.timedelta(days=7) + cur.execute( "DELETE FROM Volumes WHERE timestamp < ?", [ str(a_week_ago) ] ) + + # purge stale issue info - probably issue data won't change much.... + a_month_ago = datetime.datetime.today()-datetime.timedelta(days=30) + cur.execute( "DELETE FROM Issues WHERE timestamp < ?", [ str(a_month_ago) ] ) # fetch cur.execute("SELECT id,name,publisher,count_of_issues FROM Volumes WHERE id = ?", [ volume_id ] ) diff --git a/filenameparser.py b/filenameparser.py index 355ade1..3f4d261 100644 --- a/filenameparser.py +++ b/filenameparser.py @@ -83,12 +83,21 @@ class FileNameParser: # use the issue number string to split the filename string # assume first element of list is the series name, plus cruft - #!!! this could fail in the case of small numerics in the series name!!! + + # TODO: we really should pass in the *INDEX* of the issue, that makes + # finding it easier + tmpstr = self.fixSpaces(filename) - if issue != "": - series = tmpstr.split(issue)[0] + + #remove pound signs. this might mess up the series name if there is a# in it. + tmpstr = tmpstr.replace("#", " ") + + if issue != "": + # assume that issue substr has at least on space before it + issue_str = " " + str(issue) + series = tmpstr.split(issue_str)[0] else: # no issue to work off of #!!! TODO we should look for the year, and split from that diff --git a/imagefetcher.py b/imagefetcher.py index ca57b90..dd5ac36 100644 --- a/imagefetcher.py +++ b/imagefetcher.py @@ -103,7 +103,7 @@ class ImageFetcher(QObject): # wipe any existing image cache folder too if os.path.isdir( self.cache_folder ): - shutil.rmtree(path) + shutil.rmtree( self.cache_folder ) os.makedirs( self.cache_folder ) con = lite.connect( self.db_file ) diff --git a/issueidentifier.py b/issueidentifier.py index 6adf147..d01e90a 100644 --- a/issueidentifier.py +++ b/issueidentifier.py @@ -52,13 +52,16 @@ class IssueIdentifier: self.min_score_thresh = 20 # the min distance a hamming score must be to separate itself from closest neighbor - self.min_score_distance = 2 + self.min_score_distance = 4 # a very strong hamming score, almost certainly the same image self.strong_score_thresh = 8 # used to eliminate series names that are too long based on our search string - self.length_delta_thresh = 3 + self.length_delta_thresh = 5 + + # used to eliminate unlikely publishers + self.publisher_blacklist = [ 'panini comics', 'abril', 'scholastic book services' ] self.additional_metadata = GenericMetadata() self.cv_api_key = cv_api_key @@ -75,6 +78,12 @@ class IssueIdentifier: def setAdditionalMetadata( self, md ): self.additional_metadata = md + def setNameLengthDeltaThreshold( self, delta ): + self.length_delta_thresh = md + + def setPublisherBlackList( self, blacklist ): + self.publisher_blacklist = blacklist + def setHasherAlgorithm( self, algo ): self.image_hasher = algo pass @@ -244,12 +253,31 @@ class IssueIdentifier: series_shortlist = [] - #self.log_msg( "Removing results with too long names" ) + #self.log_msg( "Removing results with too long names, banned publishers, or future start dates" ) for item in cv_search_results: + length_approved = False + publisher_approved = True + date_approved = True + + # 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 + #assume that our search name is close to the actual name, say within ,e.g. 5 chars shortened_key = utils.removearticles(keys['series']) shortened_item_name = utils.removearticles(item['name']) if len( shortened_item_name ) < ( len( shortened_key ) + self.length_delta_thresh) : + length_approved = True + + # remove any series from publishers on the blacklist + if item['publisher'] is not None: + publisher = item['publisher']['name'] + if publisher is not None and publisher.lower() in self.publisher_blacklist: + publisher_approved = False + + if length_approved and publisher_approved and date_approved: series_shortlist.append(item) # if we don't think it's an issue number 1, remove any series' that are one-shots @@ -325,11 +353,15 @@ class IssueIdentifier: match['issue_number'] = num_s match['url_image_hash'] = url_image_hash match['issue_title'] = issue['name'] - match['img_url'] = thumb_url + match['img_url'] = img_url match['issue_id'] = issue['id'] match['volume_id'] = series['id'] match['month'] = month match['year'] = year + match['publisher'] = None + if series['publisher'] is not None: + match['publisher'] = series['publisher']['name'] + self.match_list.append(match) self.log_msg( " --> {0}".format(match['distance']), newline=False ) @@ -370,18 +402,18 @@ class IssueIdentifier: 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 archive pages now..." ) + self.log_msg( "Comparing to some other archive pages now..." ) found = False - for i in range(ca.getNumberOfPages()): + for i in range( min(5, 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) + self.log_msg( "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) + self.log_msg( "Found a good match d={0} on page {1}".format(distance, i) ) found = True self.log_msg( ".", newline=False ) self.log_msg( "" ) diff --git a/matchselectionwindow.py b/matchselectionwindow.py new file mode 100644 index 0000000..b271eb5 --- /dev/null +++ b/matchselectionwindow.py @@ -0,0 +1,118 @@ +""" +A PyQT4 dialog to select from automated issue matches +""" + +""" +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. +""" + +import sys +import os +from PyQt4 import QtCore, QtGui, uic + +from PyQt4.QtCore import QUrl, pyqtSignal, QByteArray + +from comicvinetalker import ComicVineTalker +from imagefetcher import ImageFetcher +from settings import ComicTaggerSettings + +class MatchSelectionWindow(QtGui.QDialog): + + volume_id = 0 + + def __init__(self, parent, matches): + super(MatchSelectionWindow, self).__init__(parent) + + uic.loadUi(os.path.join(ComicTaggerSettings.baseDir(), 'matchselectionwindow.ui' ), self) + + self.matches = matches + self.populateTable( ) + self.twList.resizeColumnsToContents() + self.twList.currentItemChanged.connect(self.currentItemChanged) + self.twList.cellDoubleClicked.connect(self.cellDoubleClicked) + + self.current_row = 0 + self.twList.selectRow( 0 ) + + + def populateTable( self ): + + while self.twList.rowCount() > 0: + self.twList.removeRow(0) + + self.twList.setSortingEnabled(False) + + row = 0 + for match in self.matches: + self.twList.insertRow(row) + + item_text = match['series'] + item = QtGui.QTableWidgetItem(item_text) + item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled) + self.twList.setItem(row, 0, item) + + """ + item_text = u"{0}".format(match['issue_number']) + item = QtGui.QTableWidgetItem(item_text) + item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled) + self.twList.setItem(row, 1, item) + """ + if match['publisher'] is not None: + item_text = u"{0}".format(match['publisher']) + else: + item_text = u"Unknown" + item = QtGui.QTableWidgetItem(item_text) + item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled) + self.twList.setItem(row, 1, item) + + item_text = "" + if match['month'] is not None: + item_text = u"{0}/".format(match['month']) + if match['year'] is not None: + item_text += u"{0}".format(match['year']) + else: + item_text += u"????" + item = QtGui.QTableWidgetItem(item_text) + item.setFlags(QtCore.Qt.ItemIsSelectable| QtCore.Qt.ItemIsEnabled) + self.twList.setItem(row, 2, item) + + row += 1 + + + def cellDoubleClicked( self, r, c ): + self.accept() + + def currentItemChanged( self, curr, prev ): + + if curr is None: + return + if prev is not None and prev.row() == curr.row(): + return + + self.current_row = curr.row() + + # list selection was changed, update the the issue cover + self.labelThumbnail.setPixmap(QtGui.QPixmap(os.path.join(ComicTaggerSettings.baseDir(), 'graphics/nocover.png' ))) + + self.cover_fetcher = ImageFetcher( ) + self.cover_fetcher.fetchComplete.connect(self.coverFetchComplete) + self.cover_fetcher.fetch( self.matches[self.current_row]['img_url'] ) + + # called when the image is done loading + def coverFetchComplete( self, image_data, issue_id ): + img = QtGui.QImage() + img.loadFromData( image_data ) + self.labelThumbnail.setPixmap(QtGui.QPixmap(img)) + diff --git a/matchselectionwindow.ui b/matchselectionwindow.ui new file mode 100644 index 0000000..b08b1df --- /dev/null +++ b/matchselectionwindow.ui @@ -0,0 +1,142 @@ + + + dialogMatchSelect + + + + 0 + 0 + 831 + 506 + + + + Select Match + + + + + + + + + + + 9 + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + 0 + + + 3 + + + true + + + false + + + + Series + + + + + Publisher + + + + + Date + + + + + + + + + 300 + 0 + + + + + 300 + 450 + + + + QFrame::Panel + + + QFrame::Sunken + + + + + + true + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + dialogMatchSelect + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + dialogMatchSelect + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/taggerwindow.py b/taggerwindow.py index 02aa270..5baed15 100644 --- a/taggerwindow.py +++ b/taggerwindow.py @@ -252,6 +252,7 @@ class TaggerWindow( QtGui.QMainWindow): self.statusBar() self.updateAppTitle() self.setAcceptDrops(True) + self.updateSaveMenu() self.droppedFile=None self.page_browser = None @@ -292,7 +293,7 @@ class TaggerWindow( QtGui.QMainWindow): mod_str = " [modified]" if not self.comic_archive.isWritable(): - ro_str = " [read only ]" + ro_str = " [read only]" self.setWindowTitle( self.appName + " - " + self.comic_archive.path + mod_str + ro_str) @@ -484,12 +485,24 @@ class TaggerWindow( QtGui.QMainWindow): self.metadataToForm() self.clearDirtyFlag() # also updates the app title - self.updateInfoBox() + self.updateInfoBox() + self.updateSaveMenu() #self.updatePagesInfo() else: QtGui.QMessageBox.information(self, self.tr("Whoops!"), self.tr("That file doesn't appear to be a comic archive!")) + def updateSaveMenu( self ): + + if ( self.comic_archive is not None and + self.comic_archive.isWritable() and + not ( self.data_style == MetaDataStyle.CBI and self.comic_archive.isRar() ) + ): + self.actionWrite_Tags.setEnabled( True ) + else: + self.actionWrite_Tags.setEnabled( False ) + + def updateInfoBox( self ): ca = self.comic_archive @@ -800,17 +813,23 @@ class TaggerWindow( QtGui.QMainWindow): def useFilename( self ): - self.metadata = self.comic_archive.metadataFromFilename( ) - self.metadataToForm() - + if self.comic_archive is not None: + self.metadata = self.comic_archive.metadataFromFilename( ) + self.metadataToForm() def selectFile( self ): dialog = QtGui.QFileDialog(self) dialog.setFileMode(QtGui.QFileDialog.ExistingFile) #dialog.setFileMode(QtGui.QFileDialog.Directory ) + + if platform.system() != "Windows" and utils.which("unrar") is None: + archive_filter = "Comic archive files (*.cbz *.zip)" + else: + archive_filter = "Comic archive files (*.cbz *.zip *.cbr *.rar)" + filters = [ - "Comic archive files (*.cbz *.zip *.cbr *.rar)", + archive_filter, "Any files (*)" ] @@ -826,7 +845,7 @@ class TaggerWindow( QtGui.QMainWindow): def autoSelectSearch(self): if self.comic_archive is None: - QtGui.QMessageBox.warning(self, self.tr("Automatic Search"), + QtGui.QMessageBox.warning(self, self.tr("Automatic Online Search"), self.tr("You need to load a comic first!")) return @@ -834,20 +853,25 @@ class TaggerWindow( QtGui.QMainWindow): def queryOnline(self, autoselect=False): - if self.settings.cv_api_key == "": - QtGui.QMessageBox.warning(self, self.tr("Online Search"), - self.tr("You need an API key from ComicVine to search online. " + - "Go to settings and enter it.")) - return + #if self.settings.cv_api_key == "": + # QtGui.QMessageBox.warning(self, self.tr("Online Search"), + # self.tr("You need an API key from ComicVine to search online. " + + # "Go to settings and enter it.")) + # return + + issue_number = str(self.leIssueNum.text()).strip() + + if autoselect and issue_number == "": + QtGui.QMessageBox.information(self,"Automatic Online Search", "Can't auto-select without an issue number (yet!)") + return if str(self.leSeries.text()).strip() != "": series_name = str(self.leSeries.text()).strip() else: - QtGui.QMessageBox.information(self, self.tr("Whoops"), self.tr("Need to enter a series name to query.")) + QtGui.QMessageBox.information(self, self.tr("Online Search"), self.tr("Need to enter a series name to search.")) return - issue_number = str(self.leIssueNum.text()).strip() year = str(self.lePubYear.text()).strip() if year == "": @@ -877,14 +901,21 @@ class TaggerWindow( QtGui.QMainWindow): def commitMetadata(self): if ( self.metadata is not None and self.comic_archive is not None): - QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor)) - self.formToMetadata() - self.comic_archive.writeMetadata( self.metadata, self.data_style ) - self.clearDirtyFlag() - self.updateInfoBox() - QtGui.QApplication.restoreOverrideCursor() - - QtGui.QMessageBox.information(self, self.tr("Yeah!"), self.tr("File written.")) + + reply = QtGui.QMessageBox.question(self, + self.tr("Save Tags"), + self.tr("Are you sure you wish to save " + MetaDataStyle.name[self.data_style] + " tags to this archive?"), + QtGui.QMessageBox.Yes, QtGui.QMessageBox.No ) + + if reply == QtGui.QMessageBox.Yes: + QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor)) + self.formToMetadata() + self.comic_archive.writeMetadata( self.metadata, self.data_style ) + self.clearDirtyFlag() + self.updateInfoBox() + QtGui.QApplication.restoreOverrideCursor() + + QtGui.QMessageBox.information(self, self.tr("Yeah!"), self.tr("File written.")) else: @@ -894,6 +925,7 @@ class TaggerWindow( QtGui.QMainWindow): def setDataStyle(self, s): self.data_style, b = self.cbDataStyle.itemData(s).toInt() self.updateStyleTweaks() + self.updateSaveMenu() def updateCreditColors( self ): inactive_color = QtGui.QColor(255, 170, 150) diff --git a/taggerwindow.ui b/taggerwindow.ui index 6b6bc7c..002eac5 100644 --- a/taggerwindow.ui +++ b/taggerwindow.ui @@ -260,7 +260,7 @@ 0 0 - 680 + 685 380 @@ -1013,7 +1013,7 @@ 0 0 975 - 25 + 28 @@ -1080,7 +1080,7 @@ Qt::NoToolBarArea - Qt::ToolButtonFollowStyle + Qt::ToolButtonTextBesideIcon false diff --git a/todo.txt b/todo.txt index b258648..ee8efad 100644 --- a/todo.txt +++ b/todo.txt @@ -1,53 +1,52 @@ - ----------------- Features ---------------- - -Auto-select: - Multi-match dialog - Check aspect ratio, and maybe break cover into two parts for hashing? - Stand-alone CLI Info dump optionless args remove tags copy tags +Settings/Preferences Dialog + Remove API Key + Add clear cache + Add reset settings + Tab w/Identifier Settings + Add publisher blacklist + +Add class for warning/info messages with "Don't show again" checkbox. + Add list of these flags to settings TaggerWindow entry fields Special tabbed Dialog needed for: Pages Info - maybe a custom painted widget At minimum, preserve the page data - -Verify/warn on save (maybe just on over-write?) - -Style sheets for windows/mac/linux - -Add class for warning/info messages with "Don't show again" checkbox. - Add list of these flag to settings -Version 2 - GUI to handle mutliple files or folders ------------ - +Style sheets for windows/mac/linux ----------------- Bugs ---------------- -Disable CBL for RAR +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 + http://stackoverflow.com/questions/11578443/trigger-io-errno-18-cross-device-link OSX: toolbar weird unrar complaints Page browser sizing + Override curson is not beachball -Disable save when read-only - -Be more tolerant of mis-labled extensions i.e. cbr when it's a cbz +Other settings possibilities: + Last tag style + Last "Open" folder (include dragged) + Clear caches + +Filename parsing: + Rework how series name is separated from issue Form type validation Ints vs strings for month, year. etc @@ -55,45 +54,26 @@ Check all HTTP responses for errors Lots of error checking -Other settings possibilities: - Last tag style - Last "Open" folder (include dragged) - Clear caches - - -Image Hashes: - Failures of image hash: - Thor 600 Wrap-around w/ different aspect ratio - Bone 3 - Variant Cover, - Avengers #1, #13, #81 - - -Filename parsing: - Concatenation of Name and Issue?? - "1602" - -App option to covert RAR to ZIP +------------- +Future +------------ +Add warning message to allow writing CBI to RAR, and ask them to contact CBL ! :-) -If no unrar in path, then filter out CBR/RAR from open dialog +Scrape alternate Covers from ComicVine issue pages + +GCD scraper or DB reader + +GUI to handle mutliple files or folders + +Auto search: + Searching w/o issue #? + +Content Hashes!! Wizard for converting between tag styles -Remove stale data from cache DB - -SQLite chokes on "Batman\ Li'l Gotham 001.cbr" name - -Auto search: - Choosing with pub year - Lexical analysis - Searching w/o issue #? - -Determine alternate covers from CV somehow - -------------- -Other ------------- -Content Hashes!! +App option to covert RAR to ZIP Archive function to detect tag blocks out of sync diff --git a/utils.py b/utils.py index 1ef17ba..f8cdf07 100644 --- a/utils.py +++ b/utils.py @@ -53,9 +53,27 @@ def addtopath( dir ): if dir is not None and dir != "": os.environ['PATH'] = dir + os.pathsep + os.environ['PATH'] +# returns executable path, if it exists +def which(program): + import os + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + + return None + def removearticles( text ): text = text.lower() - articles = ['and', 'the', 'a', '&' ] + articles = ['and', 'the', 'a', '&', 'issue' ] newText = '' for word in text.split(' '): if word not in articles: diff --git a/volumeselectionwindow.py b/volumeselectionwindow.py index bf9e8dd..c66d781 100644 --- a/volumeselectionwindow.py +++ b/volumeselectionwindow.py @@ -33,7 +33,7 @@ from genericmetadata import GenericMetadata from imagefetcher import ImageFetcher from progresswindow import IDProgressWindow from settings import ComicTaggerSettings - +from matchselectionwindow import MatchSelectionWindow class SearchThread( QtCore.QThread): @@ -111,6 +111,14 @@ class VolumeSelectionWindow(QtGui.QDialog): def autoSelect( self ): + if self.comic_archive is None: + QtGui.QMessageBox.information(self,"Auto-Select", "You need to load a comic first!") + return + + if self.issue_number is None or self.issue_number == "": + QtGui.QMessageBox.information(self,"Auto-Select", "Can't auto-select without an issue number (yet!)") + return + self.iddialog = IDProgressWindow( self) self.iddialog.setModal(True) self.iddialog.rejected.connect( self.identifyCancel ) @@ -118,6 +126,7 @@ class VolumeSelectionWindow(QtGui.QDialog): self.ii = IssueIdentifier( self.comic_archive, self.cv_api_key ) + md = GenericMetadata() md.series = self.series_name md.issueNumber = self.issue_number @@ -151,29 +160,49 @@ class VolumeSelectionWindow(QtGui.QDialog): matches = self.ii.match_list result = self.ii.search_result + match_index = 0 found_match = False + choices = False if result == self.ii.ResultNoMatches: QtGui.QMessageBox.information(self,"Auto-Select Result", " No matches found :-(") elif result == self.ii.ResultFoundMatchButBadCoverScore: - QtGui.QMessageBox.information(self,"Auto-Select Result", " Found a match, but cover doesn't seem to match. Verify before commiting!") + QtGui.QMessageBox.information(self,"Auto-Select Result", " Found a match, but cover doesn't seem the same. Verify before commiting!") found_match = True elif result == self.ii.ResultFoundMatchButNotFirstPage : QtGui.QMessageBox.information(self,"Auto-Select Result", " Found a match, but not with the first page of the archive.") found_match = True elif result == self.ii.ResultMultipleMatchesWithBadImageScores: QtGui.QMessageBox.information(self,"Auto-Select Result", " Found some possibilities, but no confidence. Proceed manually.") + choices = True elif result == self.ii.ResultOneGoodMatch: found_match = True elif result == self.ii.ResultMultipleGoodMatches: - QtGui.QMessageBox.information(self,"Auto-Select Result", " Found multiple likely matches! Selection DIALOG TBD.") + QtGui.QMessageBox.information(self,"Auto-Select Result", " Found multiple likely matches. Please select.") + choices = True + + if choices: + selector = MatchSelectionWindow( self, matches ) + selector.setModal(True) + title = self.series_name + title += " #" + self.issue_number + if self.year is not None: + title += " (" + str(self.year) + ")" + title += " - " + + selector.setWindowTitle( title + "Select Match") + selector.exec_() + if selector.result(): + #we should now have a list index + found_match = True + match_index = selector.current_row + if found_match: self.iddialog.accept() - print "VolumeSelectionWindow found a match!!", matches[0]['volume_id'], matches[0]['issue_number'] - self.volume_id = matches[0]['volume_id'] - self.issue_number = matches[0]['issue_number'] + self.volume_id = matches[match_index]['volume_id'] + self.issue_number = matches[match_index]['issue_number'] self.selectByID() self.showIssues() diff --git a/volumeselectionwindow.ui b/volumeselectionwindow.ui index 0ddeb73..b9d8764 100644 --- a/volumeselectionwindow.ui +++ b/volumeselectionwindow.ui @@ -6,8 +6,8 @@ 0 0 - 796 - 454 + 801 + 470 @@ -156,7 +156,7 @@ - Re-Query + Re-Search