diff --git a/src/libprs500/gui2/__init__.py b/src/libprs500/gui2/__init__.py index d4b036a1a2..27805dc194 100644 --- a/src/libprs500/gui2/__init__.py +++ b/src/libprs500/gui2/__init__.py @@ -14,13 +14,15 @@ ## 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, QFileInfo, QObject, SIGNAL +from PyQt4.QtCore import QVariant, QSettings, QFileInfo, QObject, SIGNAL, QBuffer, \ + QByteArray from PyQt4.QtGui import QFileDialog, QMessageBox, QPixmap, QFileIconProvider, QIcon from libprs500 import __appname__ as APP_TITLE from libprs500 import __author__ NONE = QVariant() #: Null value to return from the data function of item models -error_dialog = None +BOOK_EXTENSIONS = ['lrf', 'lrx', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', + 'html', 'xhtml', 'epub',] def extension(path): return os.path.splitext(path)[1][1:].lower() @@ -90,6 +92,25 @@ class FileIconProvider(QFileIconProvider): for i in ('dir', 'default'): self.icons[i] = QIcon(self.icons[i]) + def key_from_ext(self, ext): + key = ext if ext in self.icons.keys() else 'default' + if key == 'default' and ext.count('.') > 0: + ext = ext.rpartition('.')[2] + key = ext if ext in self.icons.keys() else 'default' + return key + + def cached_icon(self, key): + candidate = self.icons[key] + if isinstance(candidate, QIcon): + return candidate + icon = QIcon(candidate) + self.icons[key] = icon + return icon + + def icon_from_ext(self, ext): + key = self.key_from_ext(ext) + return self.cached_icon(key) + def load_icon(self, fileinfo): key = 'default' icons = self.icons @@ -101,18 +122,8 @@ class FileIconProvider(QFileIconProvider): key = 'dir' else: ext = qstring_to_unicode(fileinfo.completeSuffix()).lower() - key = ext if ext in self.icons.keys() else 'default' - if key == 'default' and ext.count('.') > 0: - ext = ext.rpartition('.')[2] - key = ext if ext in self.icons.keys() else 'default' - candidate = icons[key] - if isinstance(candidate, QIcon): - return candidate - icon = QIcon(candidate) - icons[key] = icon - if icon.isNull(): - print 'null icon: ', key - return icon + key = self.key_from_ext(ext) + return self.cached_icon(key) def icon(self, arg): if isinstance(arg, QFileInfo): @@ -123,7 +134,15 @@ class FileIconProvider(QFileIconProvider): return self.icons['default'] return QFileIconProvider.icon(self, arg) -file_icon_provider = None +_file_icon_provider = None +def initialize_file_icon_provider(): + global _file_icon_provider + if _file_icon_provider is None: + _file_icon_provider = FileIconProvider() + +def file_icon_provider(): + global _file_icon_provider + return _file_icon_provider class FileDialog(QFileDialog): def __init__(self, title='Choose Files', @@ -134,11 +153,9 @@ class FileDialog(QFileDialog): name = '', mode = QFileDialog.ExistingFiles, ): - global file_icon_provider - if file_icon_provider is None: - file_icon_provider = FileIconProvider() + initialize_file_icon_provider() QFileDialog.__init__(self, parent) - self.setIconProvider(file_icon_provider) + self.setIconProvider(_file_icon_provider) self.setModal(modal) settings = QSettings() state = settings.value(name, QVariant()).toByteArray() @@ -184,4 +201,25 @@ def choose_files(window, name, title, ) if fd.exec_() == QFileDialog.Accepted: return fd.get_files() - return None \ No newline at end of file + return None + +def choose_images(window, name, title, select_only_single_file=True): + mode = QFileDialog.ExistingFile if select_only_single_file else QFileDialog.ExistingFiles + fd = FileDialog(title=title, name=name, + filters=[('Images', ['png', 'gif', 'jpeg', 'jpg', 'svg'])], + parent=window, add_all_files_filter=False, mode=mode, + ) + if fd.exec_() == QFileDialog.Accepted: + return fd.get_files() + return None + +def pixmap_to_data(pixmap, format='JPEG'): + ''' + Return the QPixmap pixmap as a string saved in the specified format. + ''' + ba = QByteArray() + buf = QBuffer(ba) + buf.open(QBuffer.WriteOnly) + pixmap.save(buf, format) + return str(ba.data()) + \ 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 a9f7ee9ee2..2516fbb1e3 100644 --- a/src/libprs500/gui2/dialogs/metadata_single.py +++ b/src/libprs500/gui2/dialogs/metadata_single.py @@ -12,18 +12,19 @@ ## 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. -""" +''' The dialog used to edit meta information for a book as well as add/remove formats -""" +''' import os -from PyQt4.QtCore import Qt, SIGNAL +from PyQt4.QtCore import SIGNAL from PyQt4.Qt import QObject, QPixmap, QListWidgetItem, QErrorMessage, \ - QVariant, QSettings, QFileDialog + QVariant, QSettings, QFileDialog -from libprs500.gui2 import qstring_to_unicode, error_dialog +from libprs500.gui2 import qstring_to_unicode, error_dialog, file_icon_provider, \ + choose_files, pixmap_to_data, BOOK_EXTENSIONS, choose_images from libprs500.gui2.dialogs import ModalDialog from libprs500.gui2.dialogs.metadata_single_ui import Ui_MetadataSingleDialog @@ -31,22 +32,19 @@ class Format(QListWidgetItem): def __init__(self, parent, ext, path=None): self.path = path self.ext = ext - QListWidgetItem.__init__(self, ext.upper(), parent, \ - QListWidgetItem.UserType) + QListWidgetItem.__init__(self, file_icon_provider().icon_from_ext(ext), + ext.upper(), parent, QListWidgetItem.UserType) class MetadataSingleDialog(Ui_MetadataSingleDialog, ModalDialog): def select_cover(self, checked): - settings = QSettings() - _dir = settings.value("change cover dir", \ - QVariant(os.path.expanduser("~"))).toString() - _file = str(QFileDialog.getOpenFileName(self.parent, \ - "Choose cover for " + str(self.title.text()), _dir, \ - "Images (*.png *.gif *.jpeg *.jpg *.svg);;All files (*)")) - if len(_file): + files = choose_images(self.window, 'change cover dialog', + u'Choose cover for ' + qstring_to_unicode(self.title.text())) + if not files: + return + _file = files[0] + if _file: _file = os.path.abspath(_file) - settings.setValue("change cover dir", \ - QVariant(os.path.dirname(_file))) if not os.access(_file, os.R_OK): d = error_dialog(self.window, 'Cannot read', 'You do not have permission to read the file: ' + _file) @@ -63,43 +61,38 @@ class MetadataSingleDialog(Ui_MetadataSingleDialog, ModalDialog): if cover: pix = QPixmap() pix.loadFromData(cover) - if pix.isNull(): - QErrorMessage(self.parent).showMessage(_file + \ - " is not a valid picture") + if pix.isNull(): + d = error_dialog(self.window, _file + " is not a valid picture") + d.exec_() else: self.cover_path.setText(_file) - self.cover.setPixmap(pix) + self.cover.setPixmap(pix) + self.cover_changed = True + self.cpixmap = pix def add_format(self, x): - settings = QSettings() - _dir = settings.value("add formats dialog dir", \ - QVariant(os.path.expanduser("~"))).toString() - files = QFileDialog.getOpenFileNames(self.parent, \ - "Choose formats for " + str(self.title.text()), _dir, \ - "Books (*.lrf *.lrx *.rtf *.txt *.html *.xhtml *.htm *.rar);;"+\ - "All files (*)") - if not files.isEmpty(): - x = str(files[0]) - settings.setValue("add formats dialog dir", \ - QVariant(os.path.dirname(x))) - files = str(files.join("|||")).split("|||") - for _file in files: - _file = os.path.abspath(_file) - if not os.access(_file, os.R_OK): - QErrorMessage(self.window).showMessage("You do not have "+\ - "permission to read the file: " + _file) - continue - ext = os.path.splitext(_file)[1].lower() - if '.' in ext: - ext = ext.replace('.', '') - for row in range(self.formats.count()): - fmt = self.formats.item(row) - if fmt.ext == ext: - self.formats.takeItem(row) - break - Format(self.formats, ext, path=_file) - self.formats_changed = True + files = choose_files(self.window, 'add formats dialog', + "Choose formats for " + str(self.title.text()), + [('Books', BOOK_EXTENSIONS)]) + if not files: + return + for _file in files: + _file = os.path.abspath(_file) + if not os.access(_file, os.R_OK): + QErrorMessage(self.window).showMessage("You do not have "+\ + "permission to read the file: " + _file) + continue + ext = os.path.splitext(_file)[1].lower() + if '.' in ext: + ext = ext.replace('.', '') + for row in range(self.formats.count()): + fmt = self.formats.item(row) + if fmt.ext == ext: + self.formats.takeItem(row) + break + Format(self.formats, ext, path=_file) + self.formats_changed = True def remove_format(self, x): rows = self.formats.selectionModel().selectedRows(0) @@ -112,7 +105,7 @@ class MetadataSingleDialog(Ui_MetadataSingleDialog, ModalDialog): for row in range(self.formats.count()): fmt = self.formats.item(row) ext, path = fmt.ext, fmt.path - if "unknown" in ext.lower(): + if 'unknown' in ext.lower(): ext = None if path: new_extensions.add(ext) @@ -120,25 +113,26 @@ class MetadataSingleDialog(Ui_MetadataSingleDialog, ModalDialog): else: old_extensions.add(ext) for ext in new_extensions: - self.db.add_format(self.id, ext, file(paths[ext], "rb")) - db_extensions = self.db.get_extensions(self.id) + self.db.add_format(self.row, ext, open(paths[ext], "rb")) + db_extensions = set(self.db.formats(self.row).split(',')) extensions = new_extensions.union(old_extensions) for ext in db_extensions: if ext not in extensions: - self.db.remove_format(self.id, ext) - self.db.update_max_size(self.id) + self.db.remove_format(self.row, ext) - def __init__(self, window, row, db, slot): + def __init__(self, window, row, db): Ui_MetadataSingleDialog.__init__(self) ModalDialog.__init__(self, window) self.setupUi(self.dialog) self.splitter.setStretchFactor(100, 1) self.db = db self.id = db.id(row) + self.row = row self.cover_data = None self.formats_changed = False self.cover_changed = False - self.slot = slot + self.cpixmap = None + self.changed = False QObject.connect(self.cover_button, SIGNAL("clicked(bool)"), \ self.select_cover) QObject.connect(self.add_format_button, SIGNAL("clicked(bool)"), \ @@ -157,7 +151,7 @@ class MetadataSingleDialog(Ui_MetadataSingleDialog, ModalDialog): self.tags.setText(tags if tags else '') rating = self.db.rating(row) if rating > 0: - self.rating.setValue(rating) + self.rating.setValue(int(rating/2.)) comments = self.db.comments(row) self.comments.setPlainText(comments if comments else '') cover = self.db.cover(row) @@ -166,14 +160,40 @@ class MetadataSingleDialog(Ui_MetadataSingleDialog, ModalDialog): pm.loadFromData(cover) if not pm.isNull(): self.cover.setPixmap(pm) -# exts = self.db.get_extensions(self.id) -# for ext in exts: -# if not ext: -# ext = "Unknown" -# Format(self.formats, ext) + exts = self.db.formats(row) + if exts: + exts = exts.split(',') + for ext in exts: + if not ext: + ext = '' + Format(self.formats, ext) + + if qstring_to_unicode(self.series.currentText()): + self.enable_series_index() + + QObject.connect(self.series, SIGNAL('currentIndexChanged(int)'), self.enable_series_index) + QObject.connect(self.series, SIGNAL('editTextChanged(QString)'), self.enable_series_index) + + all_series = self.db.all_series() + series_id = self.db.series_id(row) + idx, c = None, 0 + for i in all_series: + id, name = i + if id == series_id: + idx = c + self.series.addItem(name) + c += 1 + if idx is not None: + self.series.setCurrentIndex(idx) + + self.series_index.setValue(self.db.series_index(row)) + self.dialog.exec_() + def enable_series_index(self, *args): + self.series_index.setEnabled(True) + def sync(self): if self.formats_changed: self.sync_formats() @@ -181,7 +201,15 @@ class MetadataSingleDialog(Ui_MetadataSingleDialog, ModalDialog): self.db.set_title(self.id, title) au = qstring_to_unicode(self.authors.text()).split(',') self.db.set_authors(self.id, au) - self.slot() + self.db.set_rating(self.id, 2*self.rating.value()) + self.db.set_publisher(self.id, qstring_to_unicode(self.publisher.text())) + self.db.set_tags(self.id, qstring_to_unicode(self.tags.text()).split(',')) + self.db.set_series(self.id, qstring_to_unicode(self.series.currentText())) + self.db.set_series_index(self.id, self.series_index.value()) + self.db.set_comment(self.id, qstring_to_unicode(self.comments.toPlainText())) + if self.cover_changed: + self.db.set_cover(self.id, pixmap_to_data(self.cover.pixmap())) + self.changed = True def reject(self): self.rejected = True diff --git a/src/libprs500/gui2/library.py b/src/libprs500/gui2/library.py index 441dc9db8e..d1730ba7a3 100644 --- a/src/libprs500/gui2/library.py +++ b/src/libprs500/gui2/library.py @@ -81,6 +81,8 @@ class LibraryDelegate(QItemDelegate): class BooksModel(QAbstractTableModel): + ROMAN = ['0', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX', 'X'] + def __init__(self, parent): QAbstractTableModel.__init__(self, parent) self.db = None @@ -172,6 +174,11 @@ class BooksModel(QAbstractTableModel): if not comments: comments = 'None' data['Comments'] = comments + series = self.db.series(idx) + if series: + sidx = self.db.series_index(idx) + sidx = self.__class__.ROMAN[sidx] if sidx < len(self.__class__.ROMAN) else str(sidx) + data['Series'] = 'Book %s of %s.'%(sidx, series) self.emit(SIGNAL('new_bookdisplay_data(PyQt_PyObject)'), data) def get_metadata(self, rows): diff --git a/src/libprs500/gui2/main.py b/src/libprs500/gui2/main.py index fa63acce1c..2efb32ddde 100644 --- a/src/libprs500/gui2/main.py +++ b/src/libprs500/gui2/main.py @@ -15,7 +15,7 @@ import os, tempfile, sys from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \ - QSettings, QVariant, QSize, QThread, QBuffer, QByteArray + QSettings, QVariant, QSize, QThread from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon from PyQt4.QtSvg import QSvgRenderer @@ -23,7 +23,9 @@ from libprs500 import __version__, __appname__ from libprs500.ebooks.metadata.meta import get_metadata from libprs500.devices.errors import FreeSpaceError from libprs500.devices.interface import Device -from libprs500.gui2 import APP_TITLE, warning_dialog, choose_files, error_dialog +from libprs500.gui2 import APP_TITLE, warning_dialog, choose_files, error_dialog, \ + initialize_file_icon_provider, BOOK_EXTENSIONS, \ + pixmap_to_data from libprs500.gui2.main_ui import Ui_MainWindow from libprs500.gui2.device import DeviceDetector, DeviceManager from libprs500.gui2.status import StatusBar @@ -39,11 +41,7 @@ class Main(QObject, Ui_MainWindow): p = QPainter(pixmap) r.render(p) p.end() - ba = QByteArray() - buf = QBuffer(ba) - buf.open(QBuffer.WriteOnly) - pixmap.save(buf, 'JPEG') - self.default_thumbnail = (pixmap.width(), pixmap.height(), ba.data()) + self.default_thumbnail = (pixmap.width(), pixmap.height(), pixmap_to_data(pixmap)) def __init__(self, window): QObject.__init__(self) @@ -196,8 +194,7 @@ 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', - filters=[('Books', ['lrf', 'lrx', 'rar', 'zip', - 'rtf', 'lit', 'txt', 'htm', 'html', 'xhtml', 'epub',])]) + filters=[('Books', BOOK_EXTENSIONS)]) if not books: return on_card = False if self.stack.currentIndex() != 2 else True @@ -317,10 +314,10 @@ class Main(QObject, Ui_MainWindow): 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 MetadataSingleDialog(self.window, row.row(), + self.library_view.model().db).changed: + changed = True if changed: self.library_view.model().resort() @@ -342,11 +339,7 @@ class Main(QObject, Ui_MainWindow): ht = self.device_manager.device_class.THUMBNAIL_HEIGHT if self.device_manager else \ Device.THUMBNAIL_HEIGHT p = p.scaledToHeight(ht, Qt.SmoothTransformation) - ba = QByteArray() - buf = QBuffer(ba) - buf.open(QBuffer.WriteOnly) - p.save(buf, 'JPEG') - return (p.width(), p.height(), ba.data()) + return (p.width(), p.height(), pixmap_to_data(p)) def sync_to_device(self, on_card): rows = self.library_view.selectionModel().selectedRows() @@ -438,6 +431,7 @@ def main(): window.setWindowTitle(APP_TITLE) QCoreApplication.setOrganizationName("KovidsBrain") QCoreApplication.setApplicationName(APP_TITLE) + initialize_file_icon_provider() main = Main(window) def unhandled_exception(type, value, tb): import traceback diff --git a/src/libprs500/library/database.py b/src/libprs500/library/database.py index ff7c4d4643..cc12d3788e 100644 --- a/src/libprs500/library/database.py +++ b/src/libprs500/library/database.py @@ -17,7 +17,7 @@ Backend that implements storage of ebooks in an sqlite database. """ import sqlite3 as sqlite import datetime, re -from zlib import compressobj, decompress +from zlib import compress, decompress class Concatenate(object): '''String concatenation aggregator for sqlite''' @@ -149,7 +149,7 @@ class LibraryDatabase(object): sort TEXT COLLATE NOCASE, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, uri TEXT, - series_index INTEGER + series_index INTEGER NOT NULL DEFAULT 1 ); CREATE INDEX books_idx ON books (sort COLLATE NOCASE); CREATE TRIGGER books_insert_trg @@ -476,7 +476,6 @@ class LibraryDatabase(object): /**** covers table ****/ CREATE TABLE covers ( id INTEGER PRIMARY KEY, book INTEGER NON NULL, - type TEXT NON NULL COLLATE NOCASE, uncompressed_size INTEGER NON NULL, data BLOB NON NULL, UNIQUE(book) @@ -644,13 +643,10 @@ class LibraryDatabase(object): 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: + data = self.conn.execute('SELECT data FROM covers WHERE book=?', (id,)).fetchone() + if not data: return None - raw = matches[0][0] - if raw: - return decompress(raw) - return None + return(decompress(data[0])) def tags(self, index): '''tags as a comman separated list or None''' @@ -660,6 +656,22 @@ class LibraryDatabase(object): return None return matches[0][0] + def series_id(self, index): + id = self.id(index) + ans= self.conn.execute('SELECT series from books_series_link WHERE book=?', (id,)).fetchone() + if ans: + return ans[0] + + def series(self, index): + id = self.series_id(index) + ans = self.conn.execute('SELECT name from series WHERE id=?', (id,)).fetchone() + if ans: + return ans[0] + + def series_index(self, index): + id = self.id(index) + return self.conn.execute('SELECT series_index FROM books WHERE id=?', (id,)).fetchone()[0] + def comments(self, index): '''Comments as string or None''' id = self.id(index) @@ -679,8 +691,38 @@ class LibraryDatabase(object): def format(self, index, format): id = self.id(index) return decompress(self.conn.execute('SELECT data FROM data WHERE book=? AND format=?', (id, format)).fetchone()[0]) - + def all_series(self): + return [ (i[0], i[1]) for i in \ + self.conn.execute('SELECT id, name FROM series').fetchall()] + + def add_format(self, index, ext, stream): + ''' + Add the format specified by ext. If it already exists it is replaced. + ''' + id = self.id(index) + stream.seek(0, 2) + usize = stream.tell() + stream.seek(0) + data = sqlite.Binary(compress(stream.read())) + exts = self.formats(index) + if not exts: + exts = [] + if ext in exts: + self.conn.execute('UPDATE data SET data=? WHERE format=? AND book=?', + (data, ext, id)) + self.conn.execute('UPDATE data SET uncompressed_size=? WHERE format=? AND book=?', + (usize, ext, id)) + else: + self.conn.execute('INSERT INTO data(book, format, uncompressed_size, data) VALUES (?, ?, ?, ?)', + (id, ext, usize, data)) + self.conn.commit() + + def remove_format(self, index, ext): + id = self.id(index) + self.conn.execute('DELETE FROM data WHERE book=? AND format=?', (id, ext.lower())) + self.conn.commit() + def set(self, row, column, val): ''' Convenience method for setting the title, authors, publisher or rating @@ -728,17 +770,38 @@ class LibraryDatabase(object): self.conn.commit() def set_publisher(self, id, publisher): - if not publisher: - return self.conn.execute('DELETE FROM books_publishers_link WHERE book=?',(id,)) - pub = self.conn.execute('SELECT id from publishers WHERE name=?', (publisher,)).fetchone() - if pub: - aid = pub[0] - else: - aid = self.conn.execute('INSERT INTO publishers(name) VALUES (?)', (publisher,)).lastrowid - self.conn.execute('INSERT INTO books_publishers_link(book, publisher) VALUES (?,?)', (id, aid)) + if publisher: + pub = self.conn.execute('SELECT id from publishers WHERE name=?', (publisher,)).fetchone() + if pub: + aid = pub[0] + else: + aid = self.conn.execute('INSERT INTO publishers(name) VALUES (?)', (publisher,)).lastrowid + self.conn.execute('INSERT INTO books_publishers_link(book, publisher) VALUES (?,?)', (id, aid)) self.conn.commit() + def set_comment(self, id, text): + self.conn.execute('DELETE FROM comments WHERE book=?', (id,)) + self.conn.execute('INSERT INTO comments(book,text) VALUES (?,?)', (id, text)) + self.conn.commit() + + def set_tags(self, id, tags): + ''' + @param tags: list of strings + ''' + self.conn.execute('DELETE FROM books_tags_link WHERE book=?', (id,)) + tag = set(tags) + for tag in tags: + t = self.conn.execute('SELECT id from tags WHERE name=?', (tag,)).fetchone() + if t: + tid = t[0] + else: + tid = self.conn.execute('INSERT INTO tags(name) VALUES(?)', (tag,)).lastrowid + self.conn.execute('INSERT INTO books_tags_link(book, tag) VALUES (?,?)', + (id, tid)) + self.conn.commit() + + def set_series(self, id, series): self.conn.execute('DELETE FROM books_series_link WHERE book=?',(id,)) if series: @@ -748,7 +811,12 @@ class LibraryDatabase(object): else: aid = self.conn.execute('INSERT INTO series(name) VALUES (?)', (series,)).lastrowid self.conn.execute('INSERT INTO books_series_link(book, series) VALUES (?,?)', (id, aid)) - self.conn.commit() + self.conn.commit() + + def set_series_index(self, id, idx): + print + self.conn.execute('UPDATE books SET series_index=? WHERE id=?', (idx, id)) + self.conn.commit() def set_rating(self, id, rating): rating = int(rating) @@ -757,7 +825,16 @@ class LibraryDatabase(object): rat = rat[0] if rat else self.conn.execute('INSERT INTO ratings(rating) VALUES (?)', (rating,)).lastrowid self.conn.execute('INSERT INTO books_ratings_link(book, rating) VALUES (?,?)', (id, rat)) self.conn.commit() - + + def set_cover(self, id, data): + self.conn.execute('DELETE FROM covers where book=?', (id,)) + if data: + usize = len(data) + data = compress(data) + self.conn.execute('INSERT INTO covers(book, uncompressed_size, data) VALUES (?,?,?)', + (id, usize, sqlite.Binary(data))) + self.conn.commit() + def add_books(self, paths, formats, metadata, uris=[]): ''' Add a book to the database. self.data and self.cache are not updated. @@ -792,7 +869,7 @@ class LibraryDatabase(object): stream.seek(0) format = formats.next() self.conn.execute('INSERT INTO data(book, format, uncompressed_size, data) VALUES (?,?,?,?)', - (id, format, usize, compressobj().compress(stream.read()))) + (id, format, usize, sqlite.Binary(compress(stream.read())))) stream.close() self.conn.commit()