From 1ec485de2acbe6fbcf5460fd97dd8518faddbf88 Mon Sep 17 00:00:00 2001 From: "beville@gmail.com" Date: Tue, 6 Nov 2012 20:29:18 +0000 Subject: [PATCH] Added settings dialog for setting rar paths and CV api key git-svn-id: http://comictagger.googlecode.com/svn/trunk@12 6c5673fe-1810-88d6-992b-cd32ca31540c --- comicarchive.py | 40 +++++++++++++++++++++---- comicvinetalker.py | 16 +++++++++- issueselectionwindow.py | 9 +++--- tagger.py | 26 +++++++--------- taggerwindow.py | 64 ++++++++++++++++++++++++++++++++++------ taggerwindow.ui | 19 ++++++++++-- todo.txt | 42 +++++++------------------- utils.py | 27 +++++++++++++++-- volumeselectionwindow.py | 7 +++-- 9 files changed, 176 insertions(+), 74 deletions(-) diff --git a/comicarchive.py b/comicarchive.py index b47b43c..b7bece9 100644 --- a/comicarchive.py +++ b/comicarchive.py @@ -7,7 +7,9 @@ import os import struct import sys import tempfile -from subprocess import call +import subprocess +import platform +import time sys.path.insert(0, os.path.abspath(".") ) import UnRAR2 @@ -151,7 +153,18 @@ class RarArchiver: def __init__( self, path ): self.path = path self.rar_exe_path = None + self.devnull = open(os.devnull, "w") + # windows only, keeps the cmd.exe from popping up + if platform.system() == "Windows": + self.startupinfo = subprocess.STARTUPINFO() + self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + else: + self.startupinfo = None + + def __del__(self): + self.devnull.close() + def getArchiveComment( self ): rarc = UnRAR2.RarFile( self.path ) @@ -167,8 +180,13 @@ class RarArchiver: f.close() # use external program to write comment to Rar archive - call([self.rar_exe_path, 'c', '-z' + tmp_name, self.path]) - + subprocess.call([self.rar_exe_path, 'c', '-c-', '-z' + tmp_name, self.path], + startupinfo=self.startupinfo, + stdout=self.devnull) + + if platform.system() == "Darwin": + time.sleep(1) + os.remove( tmp_name) def readArchiveFile( self, archive_file ): @@ -194,8 +212,12 @@ class RarArchiver: f.close() # use external program to write file to Rar archive - call([self.rar_exe_path, 'a', '-ep', self.path, tmp_file]) - + subprocess.call([self.rar_exe_path, 'a', '-c-', '-ep', self.path, tmp_file], + startupinfo=self.startupinfo, + stdout=self.devnull) + + if platform.system() == "Darwin": + time.sleep(1) os.remove( tmp_file) os.rmdir( tmp_folder) @@ -203,7 +225,12 @@ class RarArchiver: if self.rar_exe_path is not None: # use external program to remove file from Rar archive - call([self.rar_exe_path, 'd', self.path, archive_file]) + subprocess.call([self.rar_exe_path, 'd','-c-', self.path, archive_file], + startupinfo=self.startupinfo, + stdout=self.devnull) + + if platform.system() == "Darwin": + time.sleep(1) def getArchiveFilenameList( self ): @@ -227,6 +254,7 @@ class FolderArchiver: def readArchiveFile( self, archive_file ): + data = "" fname = os.path.join( self.path, archive_file ) try: with open( fname, 'rb' ) as f: diff --git a/comicvinetalker.py b/comicvinetalker.py index 9852330..f3e26ca 100644 --- a/comicvinetalker.py +++ b/comicvinetalker.py @@ -10,7 +10,21 @@ from genericmetadata import GenericMetadata class ComicVineTalker: - api_key = '67ac5baeba16a2f56acf7ff136a1d591324c2253' + def __init__(self, api_key): + self.api_key = api_key + + + def testKey( self ): + + test_url = "http://api.comicvine.com/issue/1/?api_key=" + self.api_key + "&format=json&field_list=name" + resp = urllib2.urlopen( test_url ) + content = resp.read() + + cv_response = json.loads( content ) + + # Bogus request, but if the key is wrong, you get error 100: "Invalid API Key" + return cv_response[ 'status_code' ] != 100 + def searchForSeries( self, series_name ): diff --git a/issueselectionwindow.py b/issueselectionwindow.py index 9451cda..22c6e25 100644 --- a/issueselectionwindow.py +++ b/issueselectionwindow.py @@ -4,18 +4,19 @@ from PyQt4 import QtCore, QtGui, uic from PyQt4.QtCore import QUrl from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest -from comicvinetalker import * +from comicvinetalker import ComicVineTalker class IssueSelectionWindow(QtGui.QDialog): volume_id = 0 - def __init__(self, parent, series_id, issue_number): + def __init__(self, parent, cv_api_key, series_id, issue_number): super(IssueSelectionWindow, self).__init__(parent) uic.loadUi('issueselectionwindow.ui', self) self.series_id = series_id + self.cv_api_key = cv_api_key if issue_number is None or issue_number == "": self.issue_number = 1 @@ -45,7 +46,7 @@ class IssueSelectionWindow(QtGui.QDialog): while self.twList.rowCount() > 0: self.twList.removeRow(0) - comicVine = ComicVineTalker() + comicVine = ComicVineTalker( self.cv_api_key ) volume_data = comicVine.fetchVolumeData( self.series_id ) self.issue_list = volume_data['issues'] @@ -101,7 +102,7 @@ class IssueSelectionWindow(QtGui.QDialog): # We don't yet have an image URL for this issue. Make a request for URL, and hold onto it # TODO: this should be reworked... too much UI latency, maybe chain the NAMs?? if record['url'] == None: - record['url'] = ComicVineTalker().fetchIssueCoverURL( self.issue_id ) + record['url'] = ComicVineTalker( self.cv_api_key ).fetchIssueCoverURL( self.issue_id ) self.labelThumbnail.setText("loading...") self.nam = QNetworkAccessManager() diff --git a/tagger.py b/tagger.py index 033ee2f..b0eb7bf 100755 --- a/tagger.py +++ b/tagger.py @@ -11,6 +11,10 @@ import xml from pprint import pprint from PyQt4 import QtCore, QtGui import signal +import os + + +from settings import ComicTaggerSettings from taggerwindow import TaggerWindow from options import Options, MetaDataStyle @@ -19,6 +23,7 @@ from comicarchive import ComicArchive from comicvinetalker import ComicVineTalker from comicinfoxml import ComicInfoXml from comicbookinfo import ComicBookInfo +import utils #----------------------------- def cliProcedure( opts ): @@ -52,22 +57,13 @@ def cliProcedure( opts ): def main(): opts = Options() opts.parseCmdLineArgs() - + settings = ComicTaggerSettings() + + # make sure unrar program is in the path for the UnRAR class + utils.addtopath(os.path.dirname(settings.unrar_exe_path)) + signal.signal(signal.SIGINT, signal.SIG_DFL) - #ca = ComicArchive( opts.filename ) - - #metadata = ca.readMetadata( MetaDataStyle.CBI ) - #ca.writeMetadata( metadata, MetaDataStyle.CIX ) - - #ComicInfoXml().writeToExternalFile( "test.xml", metadata ) - #ComicBookInfo().writeToExternalFile("test.json", metadata) - - #quit() - - - - if opts.no_gui: cliProcedure( opts ) @@ -75,7 +71,7 @@ def main(): else: app = QtGui.QApplication(sys.argv) - tagger_window = TaggerWindow( opts ) + tagger_window = TaggerWindow( opts, settings ) tagger_window.show() sys.exit(app.exec_()) diff --git a/taggerwindow.py b/taggerwindow.py index e927dd0..21f2178 100644 --- a/taggerwindow.py +++ b/taggerwindow.py @@ -1,6 +1,7 @@ from PyQt4 import QtCore, QtGui, uic import locale +import platform from volumeselectionwindow import VolumeSelectionWindow from options import Options, MetaDataStyle @@ -8,6 +9,8 @@ from genericmetadata import GenericMetadata from comicvinetalker import ComicVineTalker from comicarchive import ComicArchive from crediteditorwindow import CreditEditorWindow +from settingswindow import SettingsWindow +from settings import ComicTaggerSettings import utils # this reads the environment and inits the right locale @@ -19,7 +22,7 @@ class TaggerWindow( QtGui.QMainWindow): appName = "ComicTagger" - def __init__(self, opts , parent = None): + def __init__(self, opts, settings, parent = None): super(TaggerWindow, self).__init__(parent) uic.loadUi('taggerwindow.ui', self) @@ -27,8 +30,10 @@ class TaggerWindow( QtGui.QMainWindow): self.center() self.raise_() + print platform.system(), platform.release() self.dirtyFlag = False self.opts = opts + self.settings = settings self.data_style = opts.data_style #set up a default metadata object @@ -116,6 +121,10 @@ class TaggerWindow( QtGui.QMainWindow): self.actionRepackage.setStatusTip( 'Re-create archive as CBZ' ) self.actionRepackage.triggered.connect( self.repackageArchive ) + #self.actionRepackage.setShortcut( ) + self.actionSettings.setStatusTip( 'Configure ComicTagger' ) + self.actionSettings.triggered.connect( self.showSettings ) + # Tag Menu self.actionParse_Filename.setShortcut( 'Ctrl+F' ) self.actionParse_Filename.setStatusTip( 'Try to extract tags from filename' ) @@ -171,8 +180,9 @@ class TaggerWindow( QtGui.QMainWindow): return ca = ComicArchive( path ) - ca.setExternalRarProgram( "/usr/bin/rar" ) - + if self.settings.rar_exe_path != "": + ca.setExternalRarProgram( self.settings.rar_exe_path ) + if ca is not None and ca.seemsToBeAComicArchive(): # clear form and current metadata, we're all in! @@ -329,7 +339,6 @@ class TaggerWindow( QtGui.QMainWindow): assignText( self.teComments, md.comments ) assignText( self.teNotes, md.notes ) assignText( self.leCriticalRating, md.criticalRating ) - assignText( self.leMaturityRating, md.maturityRating ) assignText( self.leStoryArc, md.storyArc ) assignText( self.leScanInfo, md.scanInfo ) assignText( self.leSeriesGroup, md.seriesGroup ) @@ -341,7 +350,14 @@ class TaggerWindow( QtGui.QMainWindow): assignText( self.teTeams, md.teams ) assignText( self.teLocations, md.locations ) assignText( self.leFormat, md.format ) - + + if md.maturityRating is not None and md.maturityRating != "": + i = self.cbMaturityRating.findText( md.maturityRating ) + if i == -1: + self.cbMaturityRating.setEditText( md.maturityRating ) + else: + self.cbMaturityRating.setCurrentIndex( i ) + if md.language is not None: i = self.cbLanguage.findData( md.language ) self.cbLanguage.setCurrentIndex( i ) @@ -432,7 +448,7 @@ class TaggerWindow( QtGui.QMainWindow): md.comments = xlate( self.teComments.toPlainText(), "str" ) md.notes = xlate( self.teNotes.toPlainText(), "str" ) md.criticalRating = xlate( self.leCriticalRating.text(), "int" ) - md.maturityRating = xlate( self.leMaturityRating.text(), "str" ) + md.maturityRating = xlate( self.cbMaturityRating.currentText(), "str" ) md.storyArc = xlate( self.leStoryArc.text(), "str" ) md.scanInfo = xlate( self.leScanInfo.text(), "str" ) @@ -504,6 +520,13 @@ class TaggerWindow( QtGui.QMainWindow): def queryOnline(self): + + if self.settings.cv_api_key == "": + QtGui.QMessageBox.warning(self, self.tr("Online Query"), + self.tr("You need an API key from ComicVine to search online. " + + "Go to settings and enter it.")) + return + if str(self.leSeries.text()).strip() != "": series_name = str(self.leSeries.text()).strip() @@ -513,14 +536,14 @@ class TaggerWindow( QtGui.QMainWindow): issue_number = str(self.leIssueNum.text()).strip() - selector = VolumeSelectionWindow( self, series_name, issue_number ) + selector = VolumeSelectionWindow( self, self.settings.cv_api_key, series_name, issue_number ) selector.setModal(True) selector.exec_() if selector.result(): #we should now have a volume ID - comicVine = ComicVineTalker() + comicVine = ComicVineTalker( self.settings.cv_api_key ) self.metadata = comicVine.fetchIssueData( selector.volume_id, selector.issue_number ) # Now push the right data into the edit controls @@ -595,7 +618,7 @@ class TaggerWindow( QtGui.QMainWindow): self.leStoryArc, self.leScanInfo, self.leSeriesGroup, self.leAltSeries, self.leAltIssueNum, self.leAltIssueCount, self.leWebLink, self.teCharacters, self.teTeams, - self.teLocations, self.leMaturityRating, self.leFormat + self.teLocations, self.cbMaturityRating, self.leFormat ] if self.data_style == MetaDataStyle.CIX: @@ -677,6 +700,13 @@ class TaggerWindow( QtGui.QMainWindow): self.twCredits.removeRow( row ) self.setDirtyFlag() + def showSettings( self ): + + settingswin = SettingsWindow( self, self.settings ) + settingswin.setModal(True) + settingswin.exec_() + if settingswin.result(): + pass def center(self): screen = QtGui.QDesktopWidget().screenGeometry() @@ -716,6 +746,22 @@ class TaggerWindow( QtGui.QMainWindow): self.cbManga.addItem( "Yes (Right to Left)", "YesAndRightToLeft" ) self.cbManga.addItem( "No", "No" ) + # Add the entries to the maturity combobox + self.cbMaturityRating.addItem( "", "" ) + self.cbMaturityRating.addItem( "Everyone", "" ) + self.cbMaturityRating.addItem( "G", "" ) + self.cbMaturityRating.addItem( "Early Childhood", "" ) + self.cbMaturityRating.addItem( "Everyone 10+", "" ) + self.cbMaturityRating.addItem( "PG", "" ) + self.cbMaturityRating.addItem( "Kids to Adults", "" ) + self.cbMaturityRating.addItem( "Teen", "" ) + self.cbMaturityRating.addItem( "MA15+", "" ) + self.cbMaturityRating.addItem( "Mature 17+", "" ) + self.cbMaturityRating.addItem( "R18+", "" ) + self.cbMaturityRating.addItem( "X18+", "" ) + self.cbMaturityRating.addItem( "Adults Only 18+", "" ) + self.cbMaturityRating.addItem( "Rating Pending", "" ) + def removeCBLTags( self ): self.removeTags( MetaDataStyle.CBI ) diff --git a/taggerwindow.ui b/taggerwindow.ui index b084f04..594b431 100644 --- a/taggerwindow.ui +++ b/taggerwindow.ui @@ -323,9 +323,6 @@ - - - @@ -336,6 +333,16 @@ + + + + true + + + true + + + @@ -702,6 +709,7 @@ + @@ -805,6 +813,11 @@ Reload Selected Tag Style + + + Settings... + + diff --git a/todo.txt b/todo.txt index 97d1fd7..957fc17 100644 --- a/todo.txt +++ b/todo.txt @@ -1,15 +1,6 @@ - - -Verification prompt for removing tags - Add License/Copyright headers -ComicArchive support for folders -ComicArchive support for reading RARs - -API Key config - Toolbar icons Consolidate Credit Roles for english variants? : Penciler vs Penciller @@ -20,7 +11,8 @@ TaggerWindow entry fields General layout Special Dialogs needed for: Pages Info - + Color changing stuff need more work + - Indicate credits for CR style CR has editable dropdowns/comboboxes for Format, Publisher, Imprint ----------- @@ -33,12 +25,9 @@ Lots of error checking Archive function to detect tag blocks out of sync -Use external RAR tools to generate rar with CIX! - Hourglass popup, or whatever, for when busy Idea: Support only CBI or CIX for any given file, and not both - Determine style when opening file, and set that as current save style If user selects different one, warn about potential loss/re-arranging of data Longer term: @@ -46,17 +35,23 @@ Longer term: Maybe: keep a history of tagged volumes IDs from CV, and present those first -Remember preferred style (and other stuff) (prolly kept in ~/.comictagger.conf, or some such) +Other settings possibilities: + Last tag style + Last "Open" folder (include dragged) + Keep a history of queries somewhere?? + +Content Hashes!! App option to covert RAR to ZIP - +If no unrar in path, then filter out CBR/RAR from open dialog "Select Issues" dialog request cover URLs in background "Select Issues" dialog cache cover images - +---------------------------------------------- +COMIC RACK Questions Missing from XML as enterable in ComicRack: Main Character or Team @@ -69,18 +64,3 @@ Some that seem library only: Proposed Values Community Rating - -Age Rating: - Adults Only 18+ - Early Childhood - Everyone - Everyone 10+ - G - Kids to Adults - MA15+ - Mature 17+ - PG - r18+ - Rating Pending - Teen - X18+ diff --git a/utils.py b/utils.py index c89b01d..ac23ce4 100644 --- a/utils.py +++ b/utils.py @@ -1,5 +1,7 @@ # coding=utf-8 - + +import os + def listToString( l ): string = "" if l is not None: @@ -9,7 +11,28 @@ def listToString( l ): string += item return string - +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 addtopath( dir ): + # TODO only add if not there already + if dir is not None and dir != "": + os.environ['PATH'] = dir + os.pathsep + os.environ['PATH'] + # -o- coding: utf-8 -o- # ISO639 python dict diff --git a/volumeselectionwindow.py b/volumeselectionwindow.py index ad737e1..ca258e0 100644 --- a/volumeselectionwindow.py +++ b/volumeselectionwindow.py @@ -11,13 +11,14 @@ class VolumeSelectionWindow(QtGui.QDialog): volume_id = 0 - def __init__(self, parent, series_name, issue_number): + def __init__(self, parent, cv_api_key, series_name, issue_number): super(VolumeSelectionWindow, self).__init__(parent) uic.loadUi('volumeselectionwindow.ui', self) self.series_name = series_name self.issue_number = issue_number + self.cv_api_key = cv_api_key self.performQuery() @@ -34,7 +35,7 @@ class VolumeSelectionWindow(QtGui.QDialog): self.twList.selectRow(0) def showIssues( self ): - selector = IssueSelectionWindow( self, self.volume_id, self.issue_number ) + selector = IssueSelectionWindow( self, self.cv_api_key, self.volume_id, self.issue_number ) selector.setModal(True) selector.exec_() if selector.result(): @@ -48,7 +49,7 @@ class VolumeSelectionWindow(QtGui.QDialog): while self.twList.rowCount() > 0: self.twList.removeRow(0) - comicVine = ComicVineTalker() + comicVine = ComicVineTalker( self.cv_api_key ) self.cv_search_results = comicVine.searchForSeries( self.series_name ) self.twList.setSortingEnabled(False)