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