mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
metadata editing for single book implemented.
This commit is contained in:
parent
93a1a15533
commit
7cd1973a85
@ -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()
|
||||
@ -185,3 +202,24 @@ def choose_files(window, name, title,
|
||||
if fd.exec_() == QFileDialog.Accepted:
|
||||
return fd.get_files()
|
||||
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())
|
||||
|
@ -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
|
||||
|
||||
|
||||
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)
|
||||
@ -64,26 +62,21 @@ class MetadataSingleDialog(Ui_MetadataSingleDialog, ModalDialog):
|
||||
pix = QPixmap()
|
||||
pix.loadFromData(cover)
|
||||
if pix.isNull():
|
||||
QErrorMessage(self.parent).showMessage(_file + \
|
||||
" is not a valid picture")
|
||||
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_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("|||")
|
||||
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):
|
||||
@ -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
|
||||
|
@ -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 <font face="serif">%s</font> of %s.'%(sidx, series)
|
||||
self.emit(SIGNAL('new_bookdisplay_data(PyQt_PyObject)'), data)
|
||||
|
||||
def get_metadata(self, rows):
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
return None
|
||||
raw = matches[0][0]
|
||||
if raw:
|
||||
return decompress(raw)
|
||||
data = self.conn.execute('SELECT data FROM covers WHERE book=?', (id,)).fetchone()
|
||||
if not data:
|
||||
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)
|
||||
@ -680,6 +692,36 @@ class LibraryDatabase(object):
|
||||
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):
|
||||
'''
|
||||
@ -728,9 +770,8 @@ 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,))
|
||||
if publisher:
|
||||
pub = self.conn.execute('SELECT id from publishers WHERE name=?', (publisher,)).fetchone()
|
||||
if pub:
|
||||
aid = pub[0]
|
||||
@ -739,6 +780,28 @@ class LibraryDatabase(object):
|
||||
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:
|
||||
@ -750,6 +813,11 @@ class LibraryDatabase(object):
|
||||
self.conn.execute('INSERT INTO books_series_link(book, series) VALUES (?,?)', (id, aid))
|
||||
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)
|
||||
self.conn.execute('DELETE FROM books_ratings_link WHERE book=?',(id,))
|
||||
@ -758,6 +826,15 @@ class LibraryDatabase(object):
|
||||
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()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user