diff --git a/osx_installer.py b/osx_installer.py index 3b024de911..baaf6d5898 100644 --- a/osx_installer.py +++ b/osx_installer.py @@ -196,6 +196,9 @@ _check_symlinks_prescript() print print 'Adding unrtf' os.link(os.path.expanduser('~/unrtf'), os.path.join(frameworks_dir, 'unrtf')) + print + print 'Adding pdftohtml' + os.link(os.path.expanduser('~/pdftohtml'), os.path.join(frameworks_dir, 'pdftohtml')) print print 'Installing prescipt' sf = [os.path.basename(s) for s in all_names] diff --git a/setup.py b/setup.py index 9c92f8e035..cb988b5146 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,7 @@ entry_points = { 'rtf2lrf = libprs500.ebooks.lrf.rtf.convert_from:main',\ 'web2disk = libprs500.web.fetch.simple:main',\ 'web2lrf = libprs500.ebooks.lrf.web.convert_from:main',\ + 'pdf2lrf = libprs500.ebooks.lrf.pdf.convert_from:main',\ ], 'gui_scripts' : [ APPNAME+' = libprs500.gui.main:main'] } diff --git a/src/libprs500/__init__.py b/src/libprs500/__init__.py index 7bd1535092..bf4e54e657 100644 --- a/src/libprs500/__init__.py +++ b/src/libprs500/__init__.py @@ -13,7 +13,7 @@ ## with this program; if not, write to the Free Software Foundation, Inc., ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ''' E-book management software''' -__version__ = "0.3.79" +__version__ = "0.3.81" __docformat__ = "epytext" __author__ = "Kovid Goyal " __appname__ = 'libprs500' diff --git a/src/libprs500/ebooks/lrf/html/convert_from.py b/src/libprs500/ebooks/lrf/html/convert_from.py index 1436ef6121..981cbc71bd 100644 --- a/src/libprs500/ebooks/lrf/html/convert_from.py +++ b/src/libprs500/ebooks/lrf/html/convert_from.py @@ -255,11 +255,17 @@ class HTMLConverter(object): # Fix pdftohtml markup PDFTOHTML = [ # Remove
tags - (re.compile(r'', re.IGNORECASE), lambda match: ''), + (re.compile(r'', re.IGNORECASE), lambda match: ' '), + # Remove page numbers + (re.compile(r'\d+
', re.IGNORECASE), lambda match: ''), # Remove
and replace

with

(re.compile(r'\s*', re.IGNORECASE), lambda match: '

'), - (re.compile(r'(.{75,}?)', re.IGNORECASE), - lambda match: match.group(1)), + (re.compile(r'(.*)', re.IGNORECASE), + lambda match: match.group() if re.match('<', match.group(1).lstrip()) or len(match.group(1)) < 40 + else match.group(1)), + # Remove hyphenation + (re.compile(r'-\n|-\n\r'), lambda match: ''), + ] class Link(object): diff --git a/src/libprs500/ebooks/lrf/lit/convert_from.py b/src/libprs500/ebooks/lrf/lit/convert_from.py index 84a829826d..994abde005 100644 --- a/src/libprs500/ebooks/lrf/lit/convert_from.py +++ b/src/libprs500/ebooks/lrf/lit/convert_from.py @@ -18,7 +18,7 @@ from subprocess import Popen, PIPE from libprs500.ebooks.lrf import option_parser as lrf_option_parser from libprs500.ebooks import ConversionError from libprs500.ebooks.lrf.html.convert_from import process_file -from libprs500 import isosx +from libprs500 import isosx, __appname__ CLIT = 'clit' if isosx and hasattr(sys, 'frameworks_dir'): CLIT = os.path.join(sys.frameworks_dir, CLIT) @@ -32,7 +32,7 @@ def option_parser(): def generate_html(pathtolit): if not os.access(pathtolit, os.R_OK): raise ConversionError, 'Cannot read from ' + pathtolit - tdir = mkdtemp(prefix='libprs500_lit2lrf_') + tdir = mkdtemp(prefix=__appname__+'_') cmd = ' '.join([CLIT, '"'+pathtolit+'"', tdir]) p = Popen(cmd, shell=True, stderr=PIPE) ret = p.wait() diff --git a/src/libprs500/ebooks/lrf/pdf/__init__.py b/src/libprs500/ebooks/lrf/pdf/__init__.py new file mode 100644 index 0000000000..aaf49de99e --- /dev/null +++ b/src/libprs500/ebooks/lrf/pdf/__init__.py @@ -0,0 +1,14 @@ +## Copyright (C) 2007 Kovid Goyal kovid@kovidgoyal.net +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License along +## with this program; if not, write to the Free Software Foundation, Inc., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. diff --git a/src/libprs500/ebooks/lrf/pdf/convert_from.py b/src/libprs500/ebooks/lrf/pdf/convert_from.py new file mode 100644 index 0000000000..29b528d19d --- /dev/null +++ b/src/libprs500/ebooks/lrf/pdf/convert_from.py @@ -0,0 +1,73 @@ +## Copyright (C) 2007 Kovid Goyal kovid@kovidgoyal.net +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License along +## with this program; if not, write to the Free Software Foundation, Inc., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +'''''' + +import sys, os, subprocess +from libprs500 import isosx +from libprs500.ebooks import ConversionError +from libprs500.ptempfile import PersistentTemporaryFile +from libprs500.ebooks.lrf import option_parser as lrf_option_parser +from libprs500.ebooks.lrf.html.convert_from import process_file + +PDFTOHTML = 'pdftohtml' +if isosx and hasattr(sys, 'frameworks_dir'): + PDFTOHTML = os.path.join(sys.frameworks_dir, PDFTOHTML) + + +def generate_html(pathtopdf): + ''' + Convert the pdf into html. + @return: A closed PersistentTemporaryFile. + ''' + if not os.access(pathtopdf, os.R_OK): + raise ConversionError, 'Cannot read from ' + pathtopdf + pf = PersistentTemporaryFile('.html') + pf.close() + cmd = PDFTOHTML + ' -noframes -p -nomerge "%s" "%s"'%(pathtopdf, pf.name) + p = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE) + ret = p.wait() + if ret != 0: + err = p.stderr.read() + raise ConversionError, err + return pf + +def option_parser(): + return lrf_option_parser( + '''Usage: %prog [options] mybook.pdf\n\n''' + '''%prog converts mybook.pdf to mybook.lrf\n\n''' + ) + + +def main(args=sys.argv): + parser = option_parser() + options, args = parser.parse_args(args) + if len(args) != 2: + parser.print_help() + print + print 'No pdf file specified' + return 1 + pdf = os.path.abspath(os.path.expanduser(args[1])) + htmlfile = generate_html(pdf) + if not options.output: + ext = '.lrs' if options.lrs else '.lrf' + options.output = os.path.abspath(os.path.basename(os.path.splitext(args[1])[0]) + ext) + else: + options.output = os.path.abspath(options.output) + options.pdftohtml = True + process_file(htmlfile.name, options) + return 0 + +if __name__ == '__main__': + sys.exit(main()) \ No newline at end of file diff --git a/src/libprs500/gui2/Makefile b/src/libprs500/gui2/Makefile index 712126e1aa..25241d654d 100644 --- a/src/libprs500/gui2/Makefile +++ b/src/libprs500/gui2/Makefile @@ -7,10 +7,6 @@ RC = images_rc.pyc %_rc.pyc : %.qrc % pyrcc4 $< > $*_rc.py python -c "import compiler; compiler.compileFile('$*_rc.py')" - echo > $*_rc.py - touch $*_rc.pyc - - all : $(UI) $(RC) diff --git a/src/libprs500/gui2/__init__.py b/src/libprs500/gui2/__init__.py index 4f041b10a7..42e4c98c15 100644 --- a/src/libprs500/gui2/__init__.py +++ b/src/libprs500/gui2/__init__.py @@ -14,8 +14,8 @@ ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ The GUI for libprs500. """ import sys, os, re, StringIO, traceback -from PyQt4.QtCore import QVariant, QSettings -from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap +from PyQt4.QtCore import QVariant, QSettings, QFileInfo +from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap, QFileIconProvider from libprs500 import __appname__ as APP_TITLE from libprs500 import __author__ NONE = QVariant() #: Null value to return from the data function of item models @@ -37,6 +37,8 @@ def error_dialog(parent, title, msg): d.setIconPixmap(QPixmap(':/images/dialog_error.svg')) return d +def qstring_to_unicode(q): + return unicode(q.toUtf8(), 'utf8') def human_readable(size): """ Convert a size in bytes into a human readable form """ @@ -53,31 +55,111 @@ def human_readable(size): size = size[:size.find(".")+2] return size + " " + suffix -def choose_files(window, dialog, title, filetype='', - extensions=[], all_files=True): + +class FileIconProvider(QFileIconProvider): + + ICONS = { + 'default' : 'unknown', + 'dir' : 'dir', + 'zero' : 'zero', + 'jpeg' : 'jpeg', + 'jpg' : 'jpeg', + 'gif' : 'gif', + 'png' : 'png', + 'bmp' : 'bmp', + 'svg' : 'svg', + 'html' : 'html', + 'lit' : 'lit', + 'lrf' : 'lrf', + 'lrx' : 'lrx', + 'pdf' : 'pdf', + 'rar' : 'rar', + 'zip' : 'zip', + 'txt' : 'txt', + } + + def __init__(self): + QFileIconProvider.__init__(self) + self.icons = {} + for key in self.__class__.ICONS.keys(): + self.icons[key] = ':/images/mimetypes/'+self.__class__.ICONS[key]+'.svg' + + + def pixmap(self, fileinfo): + key = 'default' + icons = self.icons + if fileinfo.isSymlink(): + if not fileinfo.exists(): + return icons['zero'] + fileinfo = QFileInfo(fileinfo.readLink()) + if fileinfo.isDir(): + key = 'dir' + else: + ext = qstring_to_unicode(fileinfo.extension(True)).lower() + key = [i for i in icons.keys() if i in ext] + key = key[0] if key else 'default' + if key: + key = key[0] + candidate = icons[key] + if isinstance(candidate, QPixmap): + return candidate + pixmap = QPixmap(candidate) + icons[key] = pixmap + return pixmap + +file_icon_provider = None + +class FileDialog(QFileDialog): + def __init__(self, title='Choose Files', + filters=[], + add_all_files_filter=True, + parent=None, + modal = True, + name = '', + mode = QFileDialog.ExistingFiles, + ): + QFileDialog.__init__(self, parent) + self.setModal(modal) + global file_icon_provider + if not file_icon_provider: + file_icon_provider = FileIconProvider() + if not self.iconProvider() is file_icon_provider: + self.setIconProvider(file_icon_provider) + + settings = QSettings() + _dir = settings.value(name, QVariant(os.path.expanduser("~"))).toString() + self.setDirectory(_dir) + ftext = '' + if filters: + for filter in filters: + text, extensions = filter + extensions = ['.'+i if not i.startswith('.') else i for i in extensions] + ftext += '%s (%s);;'%(text, ' '.join(extensions)) + if add_all_files_filter or not ftext: + ftext += 'All files (*)' + self.setFilters(ftext) + self.setWindowTitle(title) + + def get_files(self): + self.exec_() + return tuple(os.path.abspath(qstring_to_unicode(i)) for i in self.selectedFiles()) + + +def choose_files(window, name, title, + filters=[], all_files=True, select_only_single_file=False): ''' Ask user to choose a bunch of files. - @param dialog: Unique gialog name used to store the opened directory + @param name: Unique dialog name used to store the opened directory @param title: Title to show in dialogs titlebar - @param filetype: What types of files is this dialog choosing - @params extensions: list of allowable extension - @params all_files: If True show all files + @param filters: list of allowable extensions. Each element of the list + must be a 2-tuple with first element a string describing + the type of files to be filtered and second element a list + of extensions. + @param all_files: If True add All files to filters. + @param select_only_single_file: If True only one file can be selected ''' - settings = QSettings() - _dir = settings.value(dialog, QVariant(os.path.expanduser("~"))).toString() - books = [] - extensions = ['*.'+i for i in extensions] - if extensions: - filter = filetype + ' (' + ' '.join(extensions) + ')' - if all_files: - filter += ';;All files (*)' - else: - filter = 'All files (*)' - files = QFileDialog.getOpenFileNames(window, title, _dir, filter) - for file in files: - file = unicode(file.toUtf8(), 'utf8') - books.append(os.path.abspath(file)) - if books: - settings.setValue(dialog, QVariant(os.path.dirname(books[0]))) - return books - + mode = QFileDialog.ExistingFile if select_only_single_file else QFileDialog.ExistingFiles + fd = FileDialog(title=title, name=name, filters=filters, + parent=window, add_all_files_filter=all_files, mode=mode, + ) + return fd.get_files() \ No newline at end of file diff --git a/src/libprs500/gui2/dialogs/__init__.py b/src/libprs500/gui2/dialogs/__init__.py index 5ba33494de..ab1d9f6e26 100644 --- a/src/libprs500/gui2/dialogs/__init__.py +++ b/src/libprs500/gui2/dialogs/__init__.py @@ -14,5 +14,13 @@ ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. '''Various dialogs used in the GUI''' +from PyQt4.QtCore import QObject +from PyQt4.QtGui import QDialog - +class ModalDialog(QObject): + def __init__(self, window): + QObject.__init__(self, window) + self.dialog = QDialog(window) + self.accept = self.dialog.accept + self.reject = self.dialog.reject + self.window = window \ No newline at end of file diff --git a/src/libprs500/gui2/dialogs/metadata_single.py b/src/libprs500/gui2/dialogs/metadata_single.py index e18498ef3d..a9f7ee9ee2 100644 --- a/src/libprs500/gui2/dialogs/metadata_single.py +++ b/src/libprs500/gui2/dialogs/metadata_single.py @@ -20,9 +20,11 @@ import os from PyQt4.QtCore import Qt, SIGNAL from PyQt4.Qt import QObject, QPixmap, QListWidgetItem, QErrorMessage, \ - QVariant, QSettings, QFileDialog, QDialog + QVariant, QSettings, QFileDialog +from libprs500.gui2 import qstring_to_unicode, error_dialog +from libprs500.gui2.dialogs import ModalDialog from libprs500.gui2.dialogs.metadata_single_ui import Ui_MetadataSingleDialog class Format(QListWidgetItem): @@ -32,7 +34,7 @@ class Format(QListWidgetItem): QListWidgetItem.__init__(self, ext.upper(), parent, \ QListWidgetItem.UserType) -class EditBookDialog(Ui_MetadataSingleDialog, QDialog): +class MetadataSingleDialog(Ui_MetadataSingleDialog, ModalDialog): def select_cover(self, checked): settings = QSettings() @@ -40,25 +42,27 @@ class EditBookDialog(Ui_MetadataSingleDialog, QDialog): QVariant(os.path.expanduser("~"))).toString() _file = str(QFileDialog.getOpenFileName(self.parent, \ "Choose cover for " + str(self.title.text()), _dir, \ - "Images (*.png *.gif *.jpeg *.jpg);;All files (*)")) + "Images (*.png *.gif *.jpeg *.jpg *.svg);;All files (*)")) if len(_file): _file = os.path.abspath(_file) settings.setValue("change cover dir", \ QVariant(os.path.dirname(_file))) if not os.access(_file, os.R_OK): - QErrorMessage(self.parent).showMessage("You do not have "+\ - "permission to read the file: " + _file) + d = error_dialog(self.window, 'Cannot read', + 'You do not have permission to read the file: ' + _file) + d.exec_() return cf, cover = None, None try: cf = open(_file, "rb") cover = cf.read() except IOError, e: - QErrorMessage(self.parent).showMessage("There was an error"+\ - " reading from file: " + _file + "\n"+str(e)) + d = error_dialog(self.window, 'Error reading file', + "

There was an error reading from file:
" + _file + "


"+str(e)) + d.exec_() if cover: pix = QPixmap() - pix.loadFromData(cover, "", Qt.AutoColor) + pix.loadFromData(cover) if pix.isNull(): QErrorMessage(self.parent).showMessage(_file + \ " is not a valid picture") @@ -83,7 +87,7 @@ class EditBookDialog(Ui_MetadataSingleDialog, QDialog): for _file in files: _file = os.path.abspath(_file) if not os.access(_file, os.R_OK): - QErrorMessage(self.parent).showMessage("You do not have "+\ + QErrorMessage(self.window).showMessage("You do not have "+\ "permission to read the file: " + _file) continue ext = os.path.splitext(_file)[1].lower() @@ -124,15 +128,17 @@ class EditBookDialog(Ui_MetadataSingleDialog, QDialog): self.db.remove_format(self.id, ext) self.db.update_max_size(self.id) - def __init__(self, parent, row, db): + def __init__(self, window, row, db, slot): Ui_MetadataSingleDialog.__init__(self) - QDialog.__init__(parent) - self.setupUi(parent) + ModalDialog.__init__(self, window) + self.setupUi(self.dialog) self.splitter.setStretchFactor(100, 1) self.db = db self.id = db.id(row) self.cover_data = None self.formats_changed = False + self.cover_changed = False + self.slot = slot QObject.connect(self.cover_button, SIGNAL("clicked(bool)"), \ self.select_cover) QObject.connect(self.add_format_button, SIGNAL("clicked(bool)"), \ @@ -140,10 +146,8 @@ class EditBookDialog(Ui_MetadataSingleDialog, QDialog): QObject.connect(self.remove_format_button, SIGNAL("clicked(bool)"), \ self.remove_format) QObject.connect(self.button_box, SIGNAL("accepted()"), \ - self.sync_formats) + self.sync) - data = self.db.get_row_by_id(self.id, \ - ["title","authors","rating","publisher","tags","comments"]) self.title.setText(db.title(row)) au = self.db.authors(row) self.authors.setText(au if au else '') @@ -154,7 +158,8 @@ class EditBookDialog(Ui_MetadataSingleDialog, QDialog): rating = self.db.rating(row) if rating > 0: self.rating.setValue(rating) - self.comments.setPlainText(data["comments"] if data["comments"] else "") + comments = self.db.comments(row) + self.comments.setPlainText(comments if comments else '') cover = self.db.cover(row) if cover: pm = QPixmap() @@ -166,3 +171,17 @@ class EditBookDialog(Ui_MetadataSingleDialog, QDialog): # if not ext: # ext = "Unknown" # Format(self.formats, ext) + + self.dialog.exec_() + + def sync(self): + if self.formats_changed: + self.sync_formats() + title = qstring_to_unicode(self.title.text()) + self.db.set_title(self.id, title) + au = qstring_to_unicode(self.authors.text()).split(',') + self.db.set_authors(self.id, au) + self.slot() + + def reject(self): + self.rejected = True diff --git a/src/libprs500/gui2/dialogs/metadata_single.ui b/src/libprs500/gui2/dialogs/metadata_single.ui index 40d19c2d18..f3f0cee0c5 100644 --- a/src/libprs500/gui2/dialogs/metadata_single.ui +++ b/src/libprs500/gui2/dialogs/metadata_single.ui @@ -494,22 +494,6 @@ - - button_box - rejected() - MetadataSingleDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - button_box accepted() @@ -526,5 +510,21 @@ + + button_box + rejected() + MetadataSingleDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + diff --git a/src/libprs500/gui2/images.qrc b/src/libprs500/gui2/images.qrc index 55df0dfbaf..2205db8bd1 100644 --- a/src/libprs500/gui2/images.qrc +++ b/src/libprs500/gui2/images.qrc @@ -10,22 +10,27 @@ images/jobs.svg images/library.png images/list_remove.svg + images/mimetypes/bmp.svg + images/mimetypes/dir.svg + images/mimetypes/gif.svg + images/mimetypes/html.svg + images/mimetypes/jpeg.svg + images/mimetypes/lit.svg + images/mimetypes/lrf.svg + images/mimetypes/lrx.svg + images/mimetypes/pdf.svg + images/mimetypes/png.svg + images/mimetypes/rar.svg + images/mimetypes/rtf.svg + images/mimetypes/svg.svg + images/mimetypes/txt.svg + images/mimetypes/unknown.svg + images/mimetypes/zero.svg + images/mimetypes/zip.svg images/plus.svg images/reader.svg images/sd.svg images/sync.svg images/trash.svg - - images/mimetypes/format_html.svg - images/mimetypes/format_lit.svg - images/mimetypes/format_lrf.svg - images/mimetypes/format_lrx.svg - images/mimetypes/format_pdf.svg - images/mimetypes/format_rar.svg - images/mimetypes/format_rtf.svg - images/mimetypes/format_txt.svg - images/mimetypes/format_unknown.svg - images/mimetypes/format_zip.svg - diff --git a/src/libprs500/gui2/main.py b/src/libprs500/gui2/main.py index edb9d48877..fa63acce1c 100644 --- a/src/libprs500/gui2/main.py +++ b/src/libprs500/gui2/main.py @@ -28,6 +28,7 @@ from libprs500.gui2.main_ui import Ui_MainWindow from libprs500.gui2.device import DeviceDetector, DeviceManager from libprs500.gui2.status import StatusBar from libprs500.gui2.jobs import JobManager, JobException +from libprs500.gui2.dialogs.metadata_single import MetadataSingleDialog class Main(QObject, Ui_MainWindow): @@ -194,8 +195,9 @@ class Main(QObject, Ui_MainWindow): ''' Add books from the local filesystem to either the library or the device. ''' - books = choose_files(self.window, 'add books dialog dir', 'Select books', 'Books', - extensions=['lrf', 'lrx', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'html', 'xhtml', 'epub']) + books = choose_files(self.window, 'add books dialog dir', 'Select books', + filters=[('Books', ['lrf', 'lrx', 'rar', 'zip', + 'rtf', 'lit', 'txt', 'htm', 'html', 'xhtml', 'epub',])]) if not books: return on_card = False if self.stack.currentIndex() != 2 else True @@ -309,9 +311,21 @@ class Main(QObject, Ui_MainWindow): ############################### Edit metadata ############################## def edit_metadata(self, checked): ''' - Edit metadata of selected books in library or on device. + Edit metadata of selected books in library individually. ''' - pass + rows = self.library_view.selectionModel().selectedRows() + if not rows or len(rows) == 0: + return + changed = False + def cs(): + changed = True + for row in rows: + MetadataSingleDialog(self.window, row.row(), self.library_view.model().db, cs) + + if changed: + self.library_view.model().resort() + self.library_view.model().research() + ############################################################################ ############################# Syncing to device############################# @@ -413,7 +427,7 @@ def main(): "manually delete the file", lock sys.exit(1) from PyQt4.Qt import QApplication, QMainWindow - app = QApplication(sys.argv) + app = QApplication(sys.argv) #from IPython.Shell import IPShellEmbed #ipshell = IPShellEmbed([], # banner = 'Dropping into IPython', diff --git a/src/libprs500/library/database.py b/src/libprs500/library/database.py index c4b1756ae3..ff7c4d4643 100644 --- a/src/libprs500/library/database.py +++ b/src/libprs500/library/database.py @@ -626,6 +626,7 @@ class LibraryDatabase(object): return self.data[index][1] def authors(self, index): + ''' Authors as a comman separated list or None''' return self.data[index][2] def publisher(self, index): @@ -641,6 +642,7 @@ class LibraryDatabase(object): return self.data[index][6] def cover(self, index): + '''Cover as a data string or None''' id = self.id(index) matches = self.conn.execute('SELECT data from covers where id=?', (id,)).fetchall() if not matches: @@ -651,6 +653,7 @@ class LibraryDatabase(object): return None def tags(self, index): + '''tags as a comman separated list or None''' id = self.id(index) matches = self.conn.execute('SELECT concat(name) FROM tags WHERE tags.id IN (SELECT tag from books_tags_link WHERE book=?)', (id,)).fetchall() if not matches: @@ -658,6 +661,7 @@ class LibraryDatabase(object): return matches[0][0] def comments(self, index): + '''Comments as string or None''' id = self.id(index) matches = self.conn.execute('SELECT text FROM comments WHERE book=?', (id,)).fetchall() if not matches: @@ -678,7 +682,9 @@ class LibraryDatabase(object): def set(self, row, column, val): - ''' Convenience method for setting the title, authors, publisher or rating ''' + ''' + Convenience method for setting the title, authors, publisher or rating + ''' id = self.data[row][0] cols = {'title' : 1, 'authors': 2, 'publisher': 3, 'rating':4} col = cols[column] @@ -699,10 +705,14 @@ class LibraryDatabase(object): self.set_rating(id, val) def set_authors(self, id, authors): + ''' + @param authors: A list of authors. + ''' self.conn.execute('DELETE FROM books_authors_link WHERE book=?',(id,)) for a in authors: if not a: continue + a = a.strip() author = self.conn.execute('SELECT id from authors WHERE name=?', (a,)).fetchone() if author: aid = author[0] diff --git a/src/libprs500/linux.py b/src/libprs500/linux.py index a4c961b435..f0ee2145a1 100644 --- a/src/libprs500/linux.py +++ b/src/libprs500/linux.py @@ -80,6 +80,7 @@ def setup_completion(): f.write(opts_and_exts('txt2lrf', txtop, ['txt'])) f.write(opts_and_exts('lit2lrf', htmlop, ['lit'])) f.write(opts_and_exts('rtf2lrf', htmlop, ['rtf'])) + f.write(opts_and_exts('pdf2lrf', htmlop, ['pdf'])) f.write(opts_and_exts('lrf-meta', metaop, ['lrf'])) f.write(''' _prs500_ls() diff --git a/windows_installer.py b/windows_installer.py index eff29da240..01b446b6d0 100644 --- a/windows_installer.py +++ b/windows_installer.py @@ -57,6 +57,7 @@ Var MUI_TEMP !define LIBUNRAR_DIR "C:\Program Files\UnrarDLL" !define CLIT "C:\clit\clit.exe" !define UNRTF "C:\unrtf\unrtf.exe" +!define PDFTOHTML "C:\pdftohtml\pdftohtml.exe" ;------------------------------------------------------------------------------------------------------ ;General @@ -126,6 +127,7 @@ Section "libprs500" Seclibprs500 File /r "${PY2EXE_DIR}\*" File "${CLIT}" File "${UNRTF}" + File "${PDFTOHTML}" SetOutPath "$INSTDIR\driver" File "${LIBUSB_DIR}\*.dll"