1#!/usr/local/bin/python3.8 2# vim:fileencoding=utf-8 3 4 5__license__ = 'GPL v3' 6__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>' 7 8from functools import partial 9 10from qt.core import QTimer, QProgressDialog, Qt 11 12from calibre import force_unicode 13from calibre.gui2 import gprefs 14from calibre.gui2.actions import InterfaceAction 15 16 17class EmbedAction(InterfaceAction): 18 19 name = 'Embed Metadata' 20 action_spec = (_('Embed metadata'), 'modified.png', _('Embed metadata into book files'), None) 21 action_type = 'current' 22 action_add_menu = True 23 action_menu_clone_qaction = _('Embed metadata into book files') 24 25 accepts_drops = True 26 27 def accept_enter_event(self, event, mime_data): 28 if mime_data.hasFormat("application/calibre+from_library"): 29 return True 30 return False 31 32 def accept_drag_move_event(self, event, mime_data): 33 if mime_data.hasFormat("application/calibre+from_library"): 34 return True 35 return False 36 37 def drop_event(self, event, mime_data): 38 mime = 'application/calibre+from_library' 39 if mime_data.hasFormat(mime): 40 self.dropped_ids = tuple(map(int, mime_data.data(mime).data().split())) 41 QTimer.singleShot(1, self.do_drop) 42 return True 43 return False 44 45 def do_drop(self): 46 book_ids = self.dropped_ids 47 del self.dropped_ids 48 if book_ids: 49 self.do_embed(book_ids) 50 51 def genesis(self): 52 self.qaction.triggered.connect(self.embed) 53 self.embed_menu = self.qaction.menu() 54 m = partial(self.create_menu_action, self.embed_menu) 55 m('embed-specific', 56 _('Embed metadata into files of a specific format from selected books...'), 57 triggered=self.embed_selected_formats) 58 self.qaction.setMenu(self.embed_menu) 59 self.pd_timer = t = QTimer() 60 t.timeout.connect(self.do_one) 61 62 def embed(self): 63 rb = self.gui.iactions['Remove Books'] 64 ids = rb._get_selected_ids(err_title=_('Cannot embed')) 65 if not ids: 66 return 67 self.do_embed(ids) 68 69 def embed_selected_formats(self): 70 rb = self.gui.iactions['Remove Books'] 71 ids = rb._get_selected_ids(err_title=_('Cannot embed')) 72 if not ids: 73 return 74 fmts = rb._get_selected_formats( 75 _('Choose formats to be updated'), ids) 76 if not fmts: 77 return 78 self.do_embed(ids, fmts) 79 80 def do_embed(self, book_ids, only_fmts=None): 81 pd = QProgressDialog(_('Embedding updated metadata into book files...'), _('&Stop'), 0, len(book_ids), self.gui) 82 pd.setWindowTitle(_('Embedding metadata...')) 83 pd.setWindowModality(Qt.WindowModality.WindowModal) 84 errors = [] 85 self.job_data = (0, tuple(book_ids), pd, only_fmts, errors) 86 self.pd_timer.start() 87 88 def do_one(self): 89 try: 90 i, book_ids, pd, only_fmts, errors = self.job_data 91 except (TypeError, AttributeError): 92 return 93 if i >= len(book_ids) or pd.wasCanceled(): 94 pd.setValue(pd.maximum()) 95 pd.hide() 96 self.pd_timer.stop() 97 self.job_data = None 98 self.gui.library_view.model().refresh_ids(book_ids) 99 if i > 0: 100 self.gui.status_bar.show_message(ngettext( 101 'Embedded metadata in one book', 'Embedded metadata in {} books', i).format(i), 5000) 102 if errors: 103 det_msg = '\n\n'.join([_('The {0} format of {1}:\n\n{2}\n').format( 104 (fmt or '').upper(), force_unicode(mi.title), force_unicode(tb)) for mi, fmt, tb in errors]) 105 from calibre.gui2.dialogs.message_box import MessageBox 106 title, msg = _('Failed for some files'), _( 107 'Failed to embed metadata into some book files. Click "Show details" for details.') 108 d = MessageBox(MessageBox.WARNING, _('WARNING:')+ ' ' + title, msg, det_msg, parent=self.gui, show_copy_button=True) 109 tc = d.toggle_checkbox 110 tc.setVisible(True), tc.setText(_('Show the &failed books in the main book list')) 111 tc.setChecked(gprefs.get('show-embed-failed-books', False)) 112 d.resize_needed.emit() 113 d.exec() 114 gprefs['show-embed-failed-books'] = tc.isChecked() 115 if tc.isChecked(): 116 failed_ids = {mi.book_id for mi, fmt, tb in errors} 117 db = self.gui.current_db 118 db.data.set_marked_ids(failed_ids) 119 self.gui.search.set_search_string('marked:true') 120 return 121 pd.setValue(i) 122 db = self.gui.current_db.new_api 123 book_id = book_ids[i] 124 125 def report_error(mi, fmt, tb): 126 mi.book_id = book_id 127 errors.append((mi, fmt, tb)) 128 db.embed_metadata((book_id,), only_fmts=only_fmts, report_error=report_error) 129 self.job_data = (i + 1, book_ids, pd, only_fmts, errors) 130