diff --git a/src/calibre/gui2/actions/copy_to_library.py b/src/calibre/gui2/actions/copy_to_library.py index 6d5f6efe5c..9ab6504e37 100644 --- a/src/calibre/gui2/actions/copy_to_library.py +++ b/src/calibre/gui2/actions/copy_to_library.py @@ -5,9 +5,57 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' +from functools import partial +from threading import Thread + from PyQt4.Qt import QMenu, QToolButton from calibre.gui2.actions import InterfaceAction +from calibre.gui2 import error_dialog, Dispatcher +from calibre.gui2.dialogs.progress import ProgressDialog + +class Worker(Thread): + + def __init__(self, ids, db, loc, progress, done): + Thread.__init__(self) + self.ids = ids + self.db = db + self.loc = loc + self.error = None + self.progress = progress + self.done = done + + def run(self): + try: + self.doit() + except Exception, err: + import traceback + try: + err = unicode(err) + except: + err = repr(err) + self.error = (err, traceback.format_exc()) + + self.done() + + def doit(self): + from calibre.library.database2 import LibraryDatabase2 + newdb = LibraryDatabase2(self.loc) + for i, x in enumerate(self.ids): + mi = self.db.get_metadata(x, index_is_id=True, get_cover=True) + self.progress(i, mi.title) + fmts = self.db.formats(x, index_is_id=True) + if not fmts: fmts = [] + else: fmts = fmts.split(',') + paths = [self.db.format_abspath(x, fmt, index_is_id=True) for fmt in + fmts] + newdb.import_book(mi, paths, notify=False, import_hooks=False) + co = self.db.conversion_options(x, 'PIPE') + if co is not None: + newdb.set_conversion_options(x, 'PIPE', co) + + + class CopyToLibraryAction(InterfaceAction): @@ -18,5 +66,63 @@ class CopyToLibraryAction(InterfaceAction): def genesis(self): self.menu = QMenu(self.gui) + self.qaction.setMenu(self.menu) + + @property + def stats(self): + return self.gui.iactions['Choose Library'].stats + + def library_changed(self, db): + self.build_menus() + + def initialization_complete(self): + self.library_changed(self.gui.library_view.model().db) + + def location_selected(self, loc): + enabled = loc == 'library' + self.qaction.setEnabled(enabled) + + def build_menus(self): + self.menu.clear() + db = self.gui.library_view.model().db + locations = list(self.stats.locations(db)) + for name, loc in locations: + self.menu.addAction(name, partial(self.copy_to_library, + loc)) + self.qaction.setVisible(bool(locations)) + + def copy_to_library(self, loc): + rows = self.gui.library_view.selectionModel().selectedRows() + if not rows or len(rows) == 0: + return error_dialog(self.gui, _('Cannot copy'), + _('No books selected'), show=True) + ids = list(map(self.gui.library_view.model().id, rows)) + db = self.gui.library_view.model().db + if not db.exists_at(loc): + return error_dialog(self.gui, _('No library'), + _('No library found at %s')%loc, show=True) + + + self.pd = ProgressDialog(_('Copying'), min=0, max=len(ids)-1, + parent=self.gui, cancelable=False) + + def progress(idx, title): + self.pd.set_msg(_('Copying') + ' ' + title) + self.pd.set_value(idx) + + self.worker = Worker(ids, db, loc, Dispatcher(progress), Dispatcher(self.pd.accept)) + self.worker.start() + + self.pd.exec_() + + if self.worker.error is not None: + e, tb = self.worker.error + error_dialog(self.gui, _('Failed'), _('Could not copy books: ') + e, + det_msg=tb, show=True) + else: + self.gui.status_bar.show_message(_('Copied %d books to %s') % + (len(ids), loc), 2000) + + diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 73de77b13b..5d34159076 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -28,9 +28,10 @@ def partial(*args, **kwargs): return ans LIBRARY_CONTEXT_MENU = ( - 'Edit Metadata', 'Send To Device', 'Save To Disk', 'Connect Share', None, - 'Convert Books', 'View', 'Open Folder', 'Show Book Details', None, - 'Remove Books', + 'Edit Metadata', 'Send To Device', 'Save To Disk', + 'Connect Share', 'Copy To Library', None, + 'Convert Books', 'View', 'Open Folder', 'Show Book Details', + 'Similar Books', None, 'Remove Books', ) DEVICE_CONTEXT_MENU = ('View', 'Save To Disk', None, 'Remove Books', None, diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 4e8611c0ef..ef74188bdf 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1754,7 +1754,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): return (paths, formats, metadata), len(ids) return None, len(ids) - def import_book(self, mi, formats, notify=True): + def import_book(self, mi, formats, notify=True, import_hooks=True): series_index = 1.0 if mi.series_index is None else mi.series_index if not mi.title: mi.title = _('Unknown') @@ -1779,7 +1779,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): ext = os.path.splitext(path)[1][1:].lower() if ext == 'opf': continue - self.add_format_with_hooks(id, ext, path, index_is_id=True) + if import_hooks: + self.add_format_with_hooks(id, ext, path, index_is_id=True) + else: + with open(path, 'rb') as f: + self.add_format(id, ext, f, index_is_id=True) self.conn.commit() self.data.refresh_ids(self, [id]) # Needed to update format list and size if notify: