diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py index b65e24e..11f8fd3 100644 --- a/comictaggerlib/autotagmatchwindow.py +++ b/comictaggerlib/autotagmatchwindow.py @@ -1,6 +1,7 @@ """ A PyQT4 dialog to select from automated issue matches """ +from comictaggerlib.ui.qtutils import reduceWidgetFontSize """ Copyright 2012-2014 Anthony Beville @@ -50,8 +51,8 @@ class AutoTagMatchWindow(QtGui.QDialog): gridlayout.addWidget( self.archiveCoverWidget ) gridlayout.setContentsMargins(0,0,0,0) - utils.reduceWidgetFontSize( self.twList ) - utils.reduceWidgetFontSize( self.teDescription, 1 ) + reduceWidgetFontSize( self.twList ) + reduceWidgetFontSize( self.teDescription, 1 ) self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowSystemMenuHint | diff --git a/comictaggerlib/autotagprogresswindow.py b/comictaggerlib/autotagprogresswindow.py index 1de5eb3..0bd0dcd 100644 --- a/comictaggerlib/autotagprogresswindow.py +++ b/comictaggerlib/autotagprogresswindow.py @@ -1,6 +1,7 @@ """ A PyQT4 dialog to show ID log and progress """ +from comictaggerlib.ui.qtutils import reduceWidgetFontSize """ Copyright 2012-2014 Anthony Beville @@ -49,7 +50,7 @@ class AutoTagProgressWindow(QtGui.QDialog): QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowMaximizeButtonHint) - utils.reduceWidgetFontSize( self.textEdit ) + reduceWidgetFontSize( self.textEdit ) def setArchiveImage( self, img_data): self.setCoverImage( img_data, self.archiveCoverWidget) diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py index 34b9a5d..d9462c1 100644 --- a/comictaggerlib/cli.py +++ b/comictaggerlib/cli.py @@ -117,7 +117,7 @@ def display_match_set_for_choice( label, match_set, opts, settings ): i = int(i) - 1 # save the data! # we know at this point, that the file is all good to go - ca = ComicArchive( match_set.filename, settings.rar_exe_path ) + ca = ComicArchive( match_set.filename, settings.rar_exe_path, ComicTaggerSettings.getGraphic('nocover.png') ) md = create_local_metadata( opts, ca, ca.hasMetadata(opts.data_style) ) cv_md = actual_issue_data_fetch(match_set.matches[int(i)], settings, opts) md.overlay( cv_md ) @@ -211,7 +211,7 @@ def process_file_cli( filename, opts, settings, match_results ): batch_mode = len( opts.file_list ) > 1 - ca = ComicArchive(filename, settings.rar_exe_path) + ca = ComicArchive(filename, settings.rar_exe_path, ComicTaggerSettings.getGraphic('nocover.png')) if not os.path.lexists( filename ): print >> sys.stderr,"Cannot find "+ filename diff --git a/comictaggerlib/comicarchive.py b/comictaggerlib/comicarchive.py index d5667ef..381dc68 100644 --- a/comictaggerlib/comicarchive.py +++ b/comictaggerlib/comicarchive.py @@ -26,6 +26,7 @@ import tempfile import subprocess import platform import locale +from natsort import natsorted if platform.system() == "Windows": import _subprocess @@ -42,12 +43,13 @@ sys.path.insert(0, os.path.abspath(".") ) import UnRAR2 from UnRAR2.rar_exceptions import * -from settings import ComicTaggerSettings +#from settings import ComicTaggerSettings from comicinfoxml import ComicInfoXml from comicbookinfo import ComicBookInfo from comet import CoMet from genericmetadata import GenericMetadata, PageType from filenameparser import FileNameParser +from PyPDF2 import PdfFileReader class MetaDataStyle: CBI = 0 @@ -507,39 +509,73 @@ class UnknownArchiver: def getArchiveFilenameList( self ): return [] +class PdfArchiver: + def __init__( self, path ): + self.path = path + + def getArchiveComment( self ): + return "" + def setArchiveComment( self, comment ): + return False + def readArchiveFile( self, page_num ): + return subprocess.check_output(['mudraw', '-o','-', self.path, str(int(os.path.basename(page_num)[:-4]))]) + def writeArchiveFile( self, archive_file, data ): + return False + def removeArchiveFile( self, archive_file ): + return False + def getArchiveFilenameList( self ): + out = [] + pdf = PdfFileReader(open(self.path, 'rb')) + for page in range(1, pdf.getNumPages() + 1): + out.append("/%04d.jpg" % (page)) + return out + #------------------------------------------------------------------ class ComicArchive: logo_data = None class ArchiveType: - Zip, Rar, Folder, Unknown = range(4) + Zip, Rar, Folder, Pdf, Unknown = range(5) - def __init__( self, path, rar_exe_path=None ): + def __init__( self, path, rar_exe_path=None, default_image_path=None ): self.path = path self.rar_exe_path = rar_exe_path self.ci_xml_filename = 'ComicInfo.xml' self.comet_default_filename = 'CoMet.xml' self.resetCache() + self.default_image_path = default_image_path - if self.rarTest(): - self.archive_type = self.ArchiveType.Rar - self.archiver = RarArchiver( self.path, rar_exe_path=self.rar_exe_path ) - - elif self.zipTest(): - self.archive_type = self.ArchiveType.Zip - self.archiver = ZipArchiver( self.path ) - - elif os.path.isdir( self.path ): - self.archive_type = self.ArchiveType.Folder - self.archiver = FolderArchiver( self.path ) + # Use file extension to decide which archive test we do first + ext = os.path.splitext(path)[1].lower() + + self.archive_type = self.ArchiveType.Unknown + self.archiver = UnknownArchiver( self.path ) + + if ext == ".cbr" or ext == ".rar": + if self.rarTest(): + self.archive_type = self.ArchiveType.Rar + self.archiver = RarArchiver( self.path, rar_exe_path=self.rar_exe_path ) + + elif self.zipTest(): + self.archive_type = self.ArchiveType.Zip + self.archiver = ZipArchiver( self.path ) else: - self.archive_type = self.ArchiveType.Unknown - self.archiver = UnknownArchiver( self.path ) + if self.zipTest(): + self.archive_type = self.ArchiveType.Zip + self.archiver = ZipArchiver( self.path ) + + elif self.rarTest(): + self.archive_type = self.ArchiveType.Rar + self.archiver = RarArchiver( self.path, rar_exe_path=self.rar_exe_path ) + elif os.path.basename(self.path)[-3:] == 'pdf': + self.archive_type = self.ArchiveType.Pdf + self.archiver = PdfArchiver(self.path) if ComicArchive.logo_data is None: - fname = ComicTaggerSettings.getGraphic('nocover.png') + #fname = ComicTaggerSettings.getGraphic('nocover.png') + fname = self.default_image_path with open(fname, 'rb') as fd: ComicArchive.logo_data = fd.read() @@ -580,7 +616,8 @@ class ComicArchive: def isRar( self ): return self.archive_type == self.ArchiveType.Rar - + def isPdf(self): + return self.archive_type == self.ArchiveType.Pdf def isFolder( self ): return self.archive_type == self.ArchiveType.Folder @@ -613,7 +650,7 @@ class ComicArchive: ext = os.path.splitext(self.path)[1].lower() if ( - ( self.isZip() or self.isRar() ) #or self.isFolder() ) + ( self.isZip() or self.isRar() or self.isPdf()) #or self.isFolder() ) and ( self.getNumberOfPages() > 0) @@ -754,12 +791,12 @@ class ComicArchive: if sort_list: def keyfunc(k): #hack to account for some weird scanner ID pages - basename=os.path.split(k)[1] - if basename < '0': - k = os.path.join(os.path.split(k)[0], "z" + basename) + #basename=os.path.split(k)[1] + #if basename < '0': + # k = os.path.join(os.path.split(k)[0], "z" + basename) return k.lower() - files.sort(key=keyfunc) + files = natsorted(files, key=keyfunc,signed=False) # make a sub-list of image files self.page_list = [] diff --git a/comictaggerlib/comicbookinfo.py b/comictaggerlib/comicbookinfo.py index d84a2e7..a0bbaf0 100644 --- a/comictaggerlib/comicbookinfo.py +++ b/comictaggerlib/comicbookinfo.py @@ -25,7 +25,7 @@ import zipfile from genericmetadata import GenericMetadata import utils -import ctversion +#import ctversion class ComicBookInfo: @@ -103,7 +103,7 @@ class ComicBookInfo: # Create the dictionary that we will convert to JSON text cbi = dict() - cbi_container = {'appID' : 'ComicTagger/' + ctversion.version, + cbi_container = {'appID' : 'ComicTagger/' + '1.0.0', #ctversion.version, 'lastModified' : str(datetime.now()), 'ComicBookInfo/1.0' : cbi } diff --git a/comictaggerlib/coverimagewidget.py b/comictaggerlib/coverimagewidget.py index c109696..36535ba 100644 --- a/comictaggerlib/coverimagewidget.py +++ b/comictaggerlib/coverimagewidget.py @@ -3,6 +3,7 @@ A PyQt4 widget display cover images from either local archive, or from ComicVine (TODO: This should be re-factored using subclasses!) """ +from comictaggerlib.ui.qtutils import reduceWidgetFontSize, getQImageFromData """ Copyright 2012-2014 Anthony Beville @@ -68,7 +69,7 @@ class CoverImageWidget(QWidget): uic.loadUi(ComicTaggerSettings.getUIFile('coverimagewidget.ui' ), self) - utils.reduceWidgetFontSize( self.label ) + reduceWidgetFontSize( self.label ) self.mode = mode self.comicVine = ComicVineTalker() @@ -237,7 +238,7 @@ class CoverImageWidget(QWidget): # called when the image is done loading from internet def coverRemoteFetchComplete( self, image_data, issue_id ): - img = utils.getQImageFromData( image_data) + img = getQImageFromData( image_data) self.current_pixmap = QPixmap(img) self.setDisplayPixmap( 0, 0) #print "ATB cover fetch complete!" diff --git a/comictaggerlib/fileselectionlist.py b/comictaggerlib/fileselectionlist.py index 2f6c9ba..d571f16 100644 --- a/comictaggerlib/fileselectionlist.py +++ b/comictaggerlib/fileselectionlist.py @@ -2,6 +2,7 @@ """ A PyQt4 widget for managing list of comic archive files """ +from comictaggerlib.ui.qtutils import reduceWidgetFontSize, centerWindowOnParent """ Copyright 2012-2014 Anthony Beville @@ -67,7 +68,7 @@ class FileSelectionList(QWidget): self.settings = settings - utils.reduceWidgetFontSize( self.twList ) + reduceWidgetFontSize( self.twList ) self.twList.setColumnCount(6) #self.twlist.setHorizontalHeaderLabels (["File", "Folder", "CR", "CBL", ""]) @@ -183,7 +184,7 @@ class FileSelectionList(QWidget): break progdialog.setValue(idx) progdialog.setLabelText(f) - utils.centerWindowOnParent( progdialog ) + centerWindowOnParent( progdialog ) QCoreApplication.processEvents() row = self.addPathItem( f ) if firstAdded is None and row is not None: @@ -258,7 +259,7 @@ class FileSelectionList(QWidget): if self.isListDupe(path): return self.getCurrentListRow(path) - ca = ComicArchive( path, self.settings.rar_exe_path ) + ca = ComicArchive( path, self.settings.rar_exe_path, ComicTaggerSettings.getGraphic('nocover.png') ) if ca.seemsToBeAComicArchive() : row = self.twList.rowCount() diff --git a/comictaggerlib/issueselectionwindow.py b/comictaggerlib/issueselectionwindow.py index 2272f61..d6cf4c6 100644 --- a/comictaggerlib/issueselectionwindow.py +++ b/comictaggerlib/issueselectionwindow.py @@ -1,6 +1,7 @@ """ A PyQT4 dialog to select specific issue from list """ +from comictaggerlib.ui.qtutils import reduceWidgetFontSize """ Copyright 2012-2014 Anthony Beville @@ -54,8 +55,8 @@ class IssueSelectionWindow(QtGui.QDialog): gridlayout.addWidget( self.coverWidget ) gridlayout.setContentsMargins(0,0,0,0) - utils.reduceWidgetFontSize( self.twList ) - utils.reduceWidgetFontSize( self.teDescription, 1 ) + reduceWidgetFontSize( self.twList ) + reduceWidgetFontSize( self.teDescription, 1 ) self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowSystemMenuHint | diff --git a/comictaggerlib/issuestring.py b/comictaggerlib/issuestring.py index 658baa4..751aa8c 100644 --- a/comictaggerlib/issuestring.py +++ b/comictaggerlib/issuestring.py @@ -1,3 +1,4 @@ +# coding=utf-8 """ Class for handling the odd permutations of an 'issue number' that the comics industry throws at us @@ -123,6 +124,11 @@ class IssueString: def asFloat( self ): #return the float, with no suffix + if self.suffix == u"½": + if self.num is not None: + return self.num + .5 + else: + return .5 return self.num def asInt( self ): diff --git a/comictaggerlib/matchselectionwindow.py b/comictaggerlib/matchselectionwindow.py index e4711cd..2758f64 100644 --- a/comictaggerlib/matchselectionwindow.py +++ b/comictaggerlib/matchselectionwindow.py @@ -1,6 +1,7 @@ """ A PyQT4 dialog to select from automated issue matches """ +from comictaggerlib.ui.qtutils import reduceWidgetFontSize """ Copyright 2012-2014 Anthony Beville @@ -50,8 +51,8 @@ class MatchSelectionWindow(QtGui.QDialog): gridlayout.addWidget( self.archiveCoverWidget ) gridlayout.setContentsMargins(0,0,0,0) - utils.reduceWidgetFontSize( self.twList ) - utils.reduceWidgetFontSize( self.teDescription, 1 ) + reduceWidgetFontSize( self.twList ) + reduceWidgetFontSize( self.teDescription, 1 ) self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowSystemMenuHint | diff --git a/comictaggerlib/pageloader.py b/comictaggerlib/pageloader.py index 182911a..66ee986 100644 --- a/comictaggerlib/pageloader.py +++ b/comictaggerlib/pageloader.py @@ -1,6 +1,7 @@ """ A PyQT4 class to load a page image from a ComicArchive in a background thread """ +from comictaggerlib.ui.qtutils import getQImageFromData """ Copyright 2012-2014 Anthony Beville @@ -67,7 +68,7 @@ class PageLoader( QtCore.QThread ): return if image_data is not None: - img = utils.getQImageFromData( image_data) + img = getQImageFromData( image_data) if self.abandoned: return diff --git a/comictaggerlib/progresswindow.py b/comictaggerlib/progresswindow.py index 67628ad..7d29812 100644 --- a/comictaggerlib/progresswindow.py +++ b/comictaggerlib/progresswindow.py @@ -1,6 +1,7 @@ """ A PyQT4 dialog to show ID log and progress """ +from comictaggerlib.ui.qtutils import reduceWidgetFontSize """ Copyright 2012-2014 Anthony Beville @@ -36,7 +37,7 @@ class IDProgressWindow(QtGui.QDialog): QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowMaximizeButtonHint) - utils.reduceWidgetFontSize( self.textEdit ) + reduceWidgetFontSize( self.textEdit ) \ No newline at end of file diff --git a/comictaggerlib/taggerwindow.py b/comictaggerlib/taggerwindow.py index 84c761f..80cbf2e 100644 --- a/comictaggerlib/taggerwindow.py +++ b/comictaggerlib/taggerwindow.py @@ -2,6 +2,7 @@ """ The main window of the ComicTagger app """ +from comictaggerlib.ui.qtutils import reduceWidgetFontSize, centerWindowOnParent """ Copyright 2012-2014 Anthony Beville @@ -187,10 +188,10 @@ class TaggerWindow( QtGui.QMainWindow): # for all sorts of wacky things # tweak some control fonts - utils.reduceWidgetFontSize( self.lblFilename, 1 ) - utils.reduceWidgetFontSize( self.lblArchiveType ) - utils.reduceWidgetFontSize( self.lblTagList ) - utils.reduceWidgetFontSize( self.lblPageCount ) + reduceWidgetFontSize( self.lblFilename, 1 ) + reduceWidgetFontSize( self.lblArchiveType ) + reduceWidgetFontSize( self.lblTagList ) + reduceWidgetFontSize( self.lblPageCount ) #make sure some editable comboboxes don't take drop actions self.cbFormat.lineEdit().setAcceptDrops(False) @@ -450,7 +451,7 @@ class TaggerWindow( QtGui.QMainWindow): progdialog.setValue(prog_idx) prog_idx += 1 progdialog.setLabelText( ca.path ) - utils.centerWindowOnParent( progdialog ) + centerWindowOnParent( progdialog ) QtCore.QCoreApplication.processEvents() original_path = os.path.abspath( ca.path ) @@ -1441,7 +1442,7 @@ class TaggerWindow( QtGui.QMainWindow): progdialog.setValue(prog_idx) prog_idx += 1 progdialog.setLabelText( ca.path ) - utils.centerWindowOnParent( progdialog ) + centerWindowOnParent( progdialog ) QtCore.QCoreApplication.processEvents() if ca.hasMetadata( style ) and ca.isWritable(): @@ -1518,7 +1519,7 @@ class TaggerWindow( QtGui.QMainWindow): progdialog.setValue(prog_idx) prog_idx += 1 progdialog.setLabelText( ca.path ) - utils.centerWindowOnParent( progdialog ) + centerWindowOnParent( progdialog ) QtCore.QCoreApplication.processEvents() if ca.hasMetadata( src_style ) and ca.isWritable(): @@ -1722,7 +1723,7 @@ class TaggerWindow( QtGui.QMainWindow): self.atprogdialog.progressBar.setValue( prog_idx ) prog_idx += 1 self.atprogdialog.label.setText( ca.path ) - utils.centerWindowOnParent( self.atprogdialog ) + centerWindowOnParent( self.atprogdialog ) QtCore.QCoreApplication.processEvents() if ca.isWritable(): diff --git a/comictaggerlib/ui/__init__.py b/comictaggerlib/ui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/comictaggerlib/ui/qtutils.py b/comictaggerlib/ui/qtutils.py new file mode 100644 index 0000000..9ab05ea --- /dev/null +++ b/comictaggerlib/ui/qtutils.py @@ -0,0 +1,82 @@ +import StringIO +from PIL import Image +from comictaggerlib.settings import ComicTaggerSettings + + +try: + from PyQt4 import QtGui + qt_available = True +except ImportError: + qt_available = False + +if qt_available: + + def reduceWidgetFontSize( widget , delta = 2): + f = widget.font() + if f.pointSize() > 10: + f.setPointSize( f.pointSize() - delta ) + widget.setFont( f ) + + + def centerWindowOnScreen( window ): + """ + Center the window on screen. This implemention will handle the window + being resized or the screen resolution changing. + """ + # Get the current screens' dimensions... + screen = QtGui.QDesktopWidget().screenGeometry() + # ... and get this windows' dimensions + mysize = window.geometry() + # The horizontal position is calulated as screenwidth - windowwidth /2 + hpos = ( screen.width() - window.width() ) / 2 + # And vertical position the same, but with the height dimensions + vpos = ( screen.height() - window.height() ) / 2 + # And the move call repositions the window + window.move(hpos, vpos) + + + def centerWindowOnParent( window ): + + top_level = window + while top_level.parent() is not None: + top_level = top_level.parent() + + # Get the current screens' dimensions... + main_window_size = top_level.geometry() + # ... and get this windows' dimensions + mysize = window.geometry() + # The horizontal position is calulated as screenwidth - windowwidth /2 + hpos = ( main_window_size.width() - window.width() ) / 2 + # And vertical position the same, but with the height dimensions + vpos = ( main_window_size.height() - window.height() ) / 2 + # And the move call repositions the window + window.move(hpos + main_window_size.left(), vpos + main_window_size.top()) + + try: + from PIL import Image + from PIL import WebPImagePlugin + import StringIO + pil_available = True + except ImportError: + pil_available = False + + def getQImageFromData(image_data): + img = QtGui.QImage() + success = img.loadFromData( image_data ) + if not success: + try: + if pil_available: + # Qt doesn't understand the format, but maybe PIL does + # so try to convert the image data to uncompressed tiff format + im = Image.open(StringIO.StringIO(image_data)) + output = StringIO.StringIO() + im.save(output, format="TIFF") + img.loadFromData( output.getvalue() ) + success = True + except Exception as e: + pass + # if still nothing, go with default image + if not success: + img.load(ComicTaggerSettings.getGraphic('nocover.png')) + return img + diff --git a/comictaggerlib/utils.py b/comictaggerlib/utils.py index 4d9b529..e315cd7 100644 --- a/comictaggerlib/utils.py +++ b/comictaggerlib/utils.py @@ -4,6 +4,7 @@ Some generic utilities """ + """ Copyright 2012-2014 Anthony Beville @@ -25,8 +26,8 @@ import re import platform import locale import codecs -from settings import ComicTaggerSettings - + + class UtilsVars: already_fixed_encoding = False @@ -586,78 +587,11 @@ def getLanguageFromISO( iso ): return lang_dict[ iso ] -try: - from PyQt4 import QtGui - qt_available = True -except ImportError: - qt_available = False - -if qt_available: - def reduceWidgetFontSize( widget , delta = 2): - f = widget.font() - if f.pointSize() > 10: - f.setPointSize( f.pointSize() - delta ) - widget.setFont( f ) - - def centerWindowOnScreen( window ): - """ - Center the window on screen. This implemention will handle the window - being resized or the screen resolution changing. - """ - # Get the current screens' dimensions... - screen = QtGui.QDesktopWidget().screenGeometry() - # ... and get this windows' dimensions - mysize = window.geometry() - # The horizontal position is calulated as screenwidth - windowwidth /2 - hpos = ( screen.width() - window.width() ) / 2 - # And vertical position the same, but with the height dimensions - vpos = ( screen.height() - window.height() ) / 2 - # And the move call repositions the window - window.move(hpos, vpos) - def centerWindowOnParent( window ): - top_level = window - while top_level.parent() is not None: - top_level = top_level.parent() - - # Get the current screens' dimensions... - main_window_size = top_level.geometry() - # ... and get this windows' dimensions - mysize = window.geometry() - # The horizontal position is calulated as screenwidth - windowwidth /2 - hpos = ( main_window_size.width() - window.width() ) / 2 - # And vertical position the same, but with the height dimensions - vpos = ( main_window_size.height() - window.height() ) / 2 - # And the move call repositions the window - window.move(hpos + main_window_size.left(), vpos + main_window_size.top()) - try: - from PIL import Image - from PIL import WebPImagePlugin - import StringIO - pil_available = True - except ImportError: - pil_available = False - def getQImageFromData(image_data): - img = QtGui.QImage() - success = img.loadFromData( image_data ) - if not success: - try: - if pil_available: - # Qt doesn't understand the format, but maybe PIL does - # so try to convert the image data to uncompressed tiff format - im = Image.open(StringIO.StringIO(image_data)) - output = StringIO.StringIO() - im.save(output, format="TIFF") - img.loadFromData( output.getvalue() ) - success = True - except Exception as e: - pass - # if still nothing, go with default image - if not success: - img.load(ComicTaggerSettings.getGraphic('nocover.png')) - return img - + + + diff --git a/comictaggerlib/volumeselectionwindow.py b/comictaggerlib/volumeselectionwindow.py index c421029..320ad7b 100644 --- a/comictaggerlib/volumeselectionwindow.py +++ b/comictaggerlib/volumeselectionwindow.py @@ -1,6 +1,7 @@ """ A PyQT4 dialog to select specific series/volume from list """ +from comictaggerlib.ui.qtutils import reduceWidgetFontSize """ Copyright 2012-2014 Anthony Beville @@ -100,8 +101,8 @@ class VolumeSelectionWindow(QtGui.QDialog): gridlayout.addWidget( self.imageWidget ) gridlayout.setContentsMargins(0,0,0,0) - utils.reduceWidgetFontSize( self.teDetails, 1 ) - utils.reduceWidgetFontSize( self.twList ) + reduceWidgetFontSize( self.teDetails, 1 ) + reduceWidgetFontSize( self.twList ) self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowSystemMenuHint |