Merge lp:~john-peterson/calibre/meta into lp:calibre
- meta
- Merge into trunk
Proposed by
John Peterson
Status: | Needs review |
---|---|
Proposed branch: | lp:~john-peterson/calibre/meta |
Merge into: | lp:calibre |
Diff against target: |
415 lines (+157/-40) 7 files modified
src/calibre/ebooks/metadata/sources/base.py (+4/-0) src/calibre/gui2/actions/edit_metadata.py (+22/-13) src/calibre/gui2/add.py (+37/-3) src/calibre/gui2/metadata/bulk_download.py (+45/-15) src/calibre/gui2/preferences/metadata_sources.py (+4/-0) src/calibre/gui2/preferences/metadata_sources.ui (+37/-9) src/calibre/library/database2.py (+8/-0) |
To merge this branch: | bzr merge lp:~john-peterson/calibre/meta |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Kovid Goyal | Disapprove | ||
Review via email: mp+146039@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Kovid Goyal (kovid) wrote : | # |
Since you still insist on using github for comments and from your general tone, I conclude that dealing with you is more trouble than it is worth. I will not be accepting any patches from you in the future, so dont bother submitting any.
review:
Disapprove
Revision history for this message
John Peterson (john-peterson) wrote : | # |
A reply is posted at https:/
Unmerged revisions
- 14248. By John Peterson
-
Adding option to auto add metadata on import
Coordinating add actions so that convert waits for metadata adding
Narrowing the metadata download modification check to avoid checking for changes in custom metadata (which is not downloaded or overwritten)
Adding option to clear IDs before metadata download (to obtain the default edition)
Adding option to not confirm before updating metadata in library
- 14247. By John Peterson
-
Adding postimport plugin call to manual add
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/calibre/ebooks/metadata/sources/base.py' |
2 | --- src/calibre/ebooks/metadata/sources/base.py 2013-01-31 15:49:28 +0000 |
3 | +++ src/calibre/ebooks/metadata/sources/base.py 2013-02-01 04:56:24 +0000 |
4 | @@ -18,6 +18,10 @@ |
5 | from calibre.ebooks.metadata import check_isbn |
6 | |
7 | msprefs = JSONConfig('metadata_sources/global.json') |
8 | +msprefs.defaults['confirm'] = True |
9 | +msprefs.defaults['auto_add'] = False |
10 | +msprefs.defaults['auto_overwrite'] = False |
11 | +msprefs.defaults['auto_clear'] = False |
12 | msprefs.defaults['txt_comments'] = False |
13 | msprefs.defaults['ignore_fields'] = [] |
14 | msprefs.defaults['user_default_ignore_fields'] = [] |
15 | |
16 | === modified file 'src/calibre/gui2/actions/edit_metadata.py' |
17 | --- src/calibre/gui2/actions/edit_metadata.py 2012-10-05 06:00:02 +0000 |
18 | +++ src/calibre/gui2/actions/edit_metadata.py 2013-02-01 04:56:24 +0000 |
19 | @@ -67,18 +67,18 @@ |
20 | self.action_merge.setEnabled(enabled) |
21 | |
22 | # Download metadata {{{ |
23 | - def download_metadata(self, ids=None, ensure_fields=None): |
24 | + def download_metadata(self, ids=None, ensure_fields=None, confirm=True, clear=False, identify=True, covers=True, callback_done=None): |
25 | + db = self.gui.library_view.model().db |
26 | if ids is None: |
27 | rows = self.gui.library_view.selectionModel().selectedRows() |
28 | if not rows or len(rows) == 0: |
29 | return error_dialog(self.gui, _('Cannot download metadata'), |
30 | _('No books selected'), show=True) |
31 | - db = self.gui.library_view.model().db |
32 | ids = [db.id(row.row()) for row in rows] |
33 | from calibre.gui2.metadata.bulk_download import start_download |
34 | start_download(self.gui, ids, |
35 | Dispatcher(self.metadata_downloaded), |
36 | - ensure_fields=ensure_fields) |
37 | + ensure_fields=ensure_fields, confirm=confirm, clear=clear, identify=identify, covers=covers, callback_done=callback_done) |
38 | |
39 | def cleanup_bulk_download(self, tdir, *args): |
40 | try: |
41 | @@ -87,6 +87,11 @@ |
42 | pass |
43 | |
44 | def metadata_downloaded(self, job): |
45 | + self._metadata_downloaded(job) |
46 | + if job.callback_done: |
47 | + job.callback_done() |
48 | + |
49 | + def _metadata_downloaded(self, job): |
50 | if job.failed: |
51 | self.gui.job_exception(job, dialog_title=_('Failed to download metadata')) |
52 | return |
53 | @@ -118,16 +123,21 @@ |
54 | 'after updating metadata') |
55 | |
56 | payload = (id_map, tdir, log_file, lm_map, |
57 | - failed_ids.union(failed_covers)) |
58 | - self.gui.proceed_question(self.apply_downloaded_metadata, payload, |
59 | - log_file, _('Download log'), _('Download complete'), msg, |
60 | - det_msg=det_msg, show_copy_button=show_copy_button, |
61 | - cancel_callback=partial(self.cleanup_bulk_download, tdir), |
62 | - log_is_file=True, checkbox_msg=checkbox_msg, |
63 | - checkbox_checked=False) |
64 | + failed_ids.union(failed_covers), job.old_metadata) |
65 | + |
66 | + from calibre.ebooks.metadata.sources.base import msprefs |
67 | + if msprefs['confirm']: |
68 | + self.gui.proceed_question(self.apply_downloaded_metadata, payload, |
69 | + log_file, _('Download log'), _('Download complete'), msg, |
70 | + det_msg=det_msg, show_copy_button=show_copy_button, |
71 | + cancel_callback=partial(self.cleanup_bulk_download, tdir), |
72 | + log_is_file=True, checkbox_msg=checkbox_msg, |
73 | + checkbox_checked=False) |
74 | + else: |
75 | + self.apply_downloaded_metadata(payload) |
76 | |
77 | def apply_downloaded_metadata(self, payload, *args): |
78 | - good_ids, tdir, log_file, lm_map, failed_ids = payload |
79 | + good_ids, tdir, log_file, lm_map, failed_ids, old_metadata = payload |
80 | if not good_ids: |
81 | return |
82 | |
83 | @@ -135,8 +145,7 @@ |
84 | db = self.gui.current_db |
85 | |
86 | for i in good_ids: |
87 | - lm = db.metadata_last_modified(i, index_is_id=True) |
88 | - if lm is not None and lm_map[i] is not None and lm > lm_map[i]: |
89 | + if db.check_if_standard_metadata_modified(i, index_is_id=True, other=old_metadata[i]): |
90 | title = db.title(i, index_is_id=True) |
91 | authors = db.authors(i, index_is_id=True) |
92 | if authors: |
93 | |
94 | === modified file 'src/calibre/gui2/add.py' |
95 | --- src/calibre/gui2/add.py 2013-01-06 08:22:38 +0000 |
96 | +++ src/calibre/gui2/add.py 2013-02-01 04:56:24 +0000 |
97 | @@ -9,7 +9,7 @@ |
98 | |
99 | from calibre.gui2.dialogs.progress import ProgressDialog |
100 | from calibre.gui2 import (error_dialog, info_dialog, gprefs, |
101 | - warning_dialog, available_width) |
102 | + warning_dialog, available_width, Dispatcher) |
103 | from calibre.ebooks.metadata.opf2 import OPF |
104 | from calibre.ebooks.metadata import MetaInformation |
105 | from calibre.constants import preferred_encoding, filesystem_encoding, DEBUG |
106 | @@ -111,15 +111,49 @@ |
107 | self.auto_convert_books = set() |
108 | |
109 | def end(self): |
110 | + from calibre.ebooks.metadata.sources.base import msprefs |
111 | + callback_done = None |
112 | + ids_add_metadata = set() |
113 | + |
114 | + # check if metadata should be added |
115 | + if msprefs['auto_overwrite'] or msprefs['auto_clear']: |
116 | + ids_add_metadata = self.auto_convert_books |
117 | + dl_identify = True |
118 | + dl_covers = True |
119 | + |
120 | + if msprefs['auto_add'] and not (msprefs['auto_overwrite'] or msprefs['auto_clear']): |
121 | + dl_identify = False |
122 | + dl_covers = False |
123 | + for id in self.auto_convert_books: |
124 | + mi = self.db.get_metadata(id, index_is_id=True, get_cover=True) |
125 | + missing_identify = bool(not (mi and mi.isbn and mi.pubdate and mi.comments)) |
126 | + missing_cover = bool(not (mi and mi.cover)) |
127 | + if missing_identify: dl_identify = True |
128 | + if missing_cover: dl_covers = True |
129 | + if missing_identify or missing_cover: |
130 | + ids_add_metadata.add(id) |
131 | + |
132 | + # auto convert now or after metadata is added |
133 | if (gprefs['manual_add_auto_convert'] and |
134 | self.auto_convert_books): |
135 | + if ids_add_metadata: |
136 | + callback_done = Dispatcher(self.auto_convert) |
137 | + else: |
138 | + self.auto_convert() |
139 | + |
140 | + if ids_add_metadata: |
141 | from calibre.gui2.ui import get_gui |
142 | gui = get_gui() |
143 | - gui.iactions['Convert Books'].auto_convert_auto_add( |
144 | - self.auto_convert_books) |
145 | + gui.iactions['Edit Metadata'].download_metadata(ids_add_metadata, confirm=False, clear=msprefs['auto_clear'], identify=dl_identify, covers=dl_covers, callback_done=callback_done) |
146 | |
147 | self.input_queue.put((None, None, None)) |
148 | |
149 | + def auto_convert(self): |
150 | + from calibre.gui2.ui import get_gui |
151 | + gui = get_gui() |
152 | + gui.iactions['Convert Books'].auto_convert_auto_add( |
153 | + self.auto_convert_books) |
154 | + |
155 | def start(self): |
156 | try: |
157 | id, opf, cover = self.input_queue.get_nowait() |
158 | |
159 | === modified file 'src/calibre/gui2/metadata/bulk_download.py' |
160 | --- src/calibre/gui2/metadata/bulk_download.py 2012-04-12 09:50:24 +0000 |
161 | +++ src/calibre/gui2/metadata/bulk_download.py 2013-02-01 04:56:24 +0000 |
162 | @@ -77,8 +77,8 @@ |
163 | l.setColumnStretch(1, 100) |
164 | |
165 | self.identify = self.covers = True |
166 | - self.bb = QDialogButtonBox(QDialogButtonBox.Cancel) |
167 | - self.bb.rejected.connect(self.reject) |
168 | + self.clear = False |
169 | + self.bb = QDialogButtonBox() |
170 | b = self.bb.addButton(_('Download only &metadata'), |
171 | self.bb.AcceptRole) |
172 | b.clicked.connect(self.only_metadata) |
173 | @@ -87,20 +87,36 @@ |
174 | self.bb.AcceptRole) |
175 | b.clicked.connect(self.only_covers) |
176 | b.setIcon(QIcon(I('default_cover.png'))) |
177 | - b = self.b = self.bb.addButton(_('&Configure download'), self.bb.ActionRole) |
178 | - b.setIcon(QIcon(I('config.png'))) |
179 | - b.clicked.connect(partial(show_config, parent, self)) |
180 | - l.addWidget(self.bb, 1, 0, 1, 2) |
181 | - b = self.bb.addButton(_('Download &both'), |
182 | - self.bb.AcceptRole) |
183 | + |
184 | + self.bb2 = QDialogButtonBox() |
185 | + b = self.bb2.addButton(_('Download &both'), |
186 | + self.bb2.AcceptRole) |
187 | b.clicked.connect(self.accept) |
188 | b.setDefault(True) |
189 | b.setAutoDefault(True) |
190 | b.setIcon(QIcon(I('ok.png'))) |
191 | + b = self.bb2.addButton(_('Clear &IDs and download both'), |
192 | + self.bb2.AcceptRole) |
193 | + b.clicked.connect(self.clear_ids) |
194 | + b.setIcon(QIcon(I('trash.png'))) |
195 | |
196 | + self.bb3 = QDialogButtonBox(QDialogButtonBox.Cancel) |
197 | + self.bb3.rejected.connect(self.reject) |
198 | + b = self.b = self.bb3.addButton(_('&Configure download'), self.bb3.ActionRole) |
199 | + b.setIcon(QIcon(I('config.png'))) |
200 | + b.clicked.connect(partial(show_config, parent, self)) |
201 | + |
202 | + l.addWidget(self.bb, 1, 0, 1, 2, Qt.AlignHCenter) |
203 | + l.addWidget(self.bb2, 2, 0, 1, 2, Qt.AlignHCenter) |
204 | + l.addWidget(self.bb3, 3, 0, 1, 2, Qt.AlignHCenter) |
205 | + |
206 | self.resize(self.sizeHint()) |
207 | b.setFocus(Qt.OtherFocusReason) |
208 | |
209 | + def clear_ids(self): |
210 | + self.clear = True |
211 | + self.accept() |
212 | + |
213 | def only_metadata(self): |
214 | self.covers = False |
215 | self.accept() |
216 | @@ -118,20 +134,34 @@ |
217 | ids = ids[batch_size:] |
218 | return ans |
219 | |
220 | -def start_download(gui, ids, callback, ensure_fields=None): |
221 | - d = ConfirmDialog(ids, gui) |
222 | - ret = d.exec_() |
223 | - d.b.clicked.disconnect() |
224 | - if ret != d.Accepted: |
225 | - return |
226 | +def start_download(gui, ids, callback, ensure_fields=None, confirm=True, clear=False, identify=True, covers=True, callback_done=None): |
227 | + if confirm: |
228 | + d = ConfirmDialog(ids, gui) |
229 | + ret = d.exec_() |
230 | + d.b.clicked.disconnect() |
231 | + if ret != d.Accepted: |
232 | + return |
233 | + identify = d.identify |
234 | + covers = d.covers |
235 | + clear = d.clear |
236 | tf = PersistentTemporaryFile('_metadata_bulk.log') |
237 | tf.close() |
238 | |
239 | + # clear ids |
240 | + if clear: |
241 | + for id in ids: |
242 | + gui.current_db.clear_identifiers(id) |
243 | + |
244 | job = Job('metadata bulk download', |
245 | _('Download metadata for %d books')%len(ids), |
246 | - download, (ids, tf.name, gui.current_db, d.identify, d.covers, |
247 | + download, (ids, tf.name, gui.current_db, identify, covers, |
248 | ensure_fields), {}, callback) |
249 | job.download_debug_log = tf.name |
250 | + job.callback_done = callback_done |
251 | + # save current metadata for modification check |
252 | + job.old_metadata = {} |
253 | + for id in ids: |
254 | + job.old_metadata[id] = gui.current_db.get_metadata(id, index_is_id=True, get_cover=False) |
255 | gui.job_manager.run_threaded_job(job) |
256 | gui.status_bar.show_message(_('Metadata download started'), 3000) |
257 | |
258 | |
259 | === modified file 'src/calibre/gui2/preferences/metadata_sources.py' |
260 | --- src/calibre/gui2/preferences/metadata_sources.py 2012-10-06 07:32:50 +0000 |
261 | +++ src/calibre/gui2/preferences/metadata_sources.py 2013-02-01 04:56:24 +0000 |
262 | @@ -290,6 +290,10 @@ |
263 | |
264 | def genesis(self, gui): |
265 | r = self.register |
266 | + r('confirm', msprefs) |
267 | + r('auto_add', msprefs) |
268 | + r('auto_overwrite', msprefs) |
269 | + r('auto_clear', msprefs) |
270 | r('txt_comments', msprefs) |
271 | r('max_tags', msprefs) |
272 | r('wait_after_first_identify_result', msprefs) |
273 | |
274 | === modified file 'src/calibre/gui2/preferences/metadata_sources.ui' |
275 | --- src/calibre/gui2/preferences/metadata_sources.ui 2012-10-06 07:45:50 +0000 |
276 | +++ src/calibre/gui2/preferences/metadata_sources.ui 2013-02-01 04:56:24 +0000 |
277 | @@ -126,20 +126,48 @@ |
278 | </widget> |
279 | </item> |
280 | <item row="1" column="1" colspan="2"> |
281 | + <widget class="QCheckBox" name="opt_confirm"> |
282 | + <property name="text"> |
283 | + <string>Confirm before updating metadata in library</string> |
284 | + </property> |
285 | + </widget> |
286 | + </item> |
287 | + <item row="2" column="1" colspan="2"> |
288 | + <widget class="QCheckBox" name="opt_auto_add"> |
289 | + <property name="text"> |
290 | + <string>Automatically add missing metadata on import</string> |
291 | + </property> |
292 | + </widget> |
293 | + </item> |
294 | + <item row="3" column="1" colspan="2"> |
295 | + <widget class="QCheckBox" name="opt_auto_overwrite"> |
296 | + <property name="text"> |
297 | + <string>Automatically overwrite metadata on import</string> |
298 | + </property> |
299 | + </widget> |
300 | + </item> |
301 | + <item row="4" column="1" colspan="2"> |
302 | + <widget class="QCheckBox" name="opt_auto_clear"> |
303 | + <property name="text"> |
304 | + <string>Automatically clear IDs and overwrite metadata on import</string> |
305 | + </property> |
306 | + </widget> |
307 | + </item> |
308 | + <item row="5" column="1" colspan="2"> |
309 | <widget class="QCheckBox" name="opt_txt_comments"> |
310 | <property name="text"> |
311 | <string>Convert all downloaded comments to plain &text</string> |
312 | </property> |
313 | </widget> |
314 | </item> |
315 | - <item row="2" column="1" colspan="2"> |
316 | + <item row="6" column="1" colspan="2"> |
317 | <widget class="QCheckBox" name="opt_swap_author_names"> |
318 | <property name="text"> |
319 | <string>Swap author names from FN LN to LN, FN</string> |
320 | </property> |
321 | </widget> |
322 | </item> |
323 | - <item row="5" column="1"> |
324 | + <item row="9" column="1"> |
325 | <widget class="QLabel" name="label_2"> |
326 | <property name="text"> |
327 | <string>Max. number of &tags to download:</string> |
328 | @@ -149,10 +177,10 @@ |
329 | </property> |
330 | </widget> |
331 | </item> |
332 | - <item row="5" column="2"> |
333 | + <item row="9" column="2"> |
334 | <widget class="QSpinBox" name="opt_max_tags"/> |
335 | </item> |
336 | - <item row="6" column="1"> |
337 | + <item row="10" column="1"> |
338 | <widget class="QLabel" name="label_3"> |
339 | <property name="text"> |
340 | <string>Max. &time to wait after first match is found:</string> |
341 | @@ -162,14 +190,14 @@ |
342 | </property> |
343 | </widget> |
344 | </item> |
345 | - <item row="6" column="2"> |
346 | + <item row="10" column="2"> |
347 | <widget class="QSpinBox" name="opt_wait_after_first_identify_result"> |
348 | <property name="suffix"> |
349 | <string> secs</string> |
350 | </property> |
351 | </widget> |
352 | </item> |
353 | - <item row="7" column="1"> |
354 | + <item row="11" column="1"> |
355 | <widget class="QLabel" name="label_4"> |
356 | <property name="text"> |
357 | <string>Max. time to wait after first &cover is found:</string> |
358 | @@ -179,14 +207,14 @@ |
359 | </property> |
360 | </widget> |
361 | </item> |
362 | - <item row="7" column="2"> |
363 | + <item row="11" column="2"> |
364 | <widget class="QSpinBox" name="opt_wait_after_first_cover_result"> |
365 | <property name="suffix"> |
366 | <string> secs</string> |
367 | </property> |
368 | </widget> |
369 | </item> |
370 | - <item row="4" column="1" colspan="2"> |
371 | + <item row="8" column="1" colspan="2"> |
372 | <widget class="QCheckBox" name="opt_fewer_tags"> |
373 | <property name="toolTip"> |
374 | <string><p>Different metadata sources have different sets of tags for the same book. If this option is checked, then calibre will use the smaller tag sets. These tend to be more like genres, while the larger tag sets tend to describe the books content. |
375 | @@ -197,7 +225,7 @@ |
376 | </property> |
377 | </widget> |
378 | </item> |
379 | - <item row="3" column="1" colspan="2"> |
380 | + <item row="7" column="1" colspan="2"> |
381 | <widget class="QCheckBox" name="opt_find_first_edition_date"> |
382 | <property name="text"> |
383 | <string>Use published date of "first edition" (from worldcat.org)</string> |
384 | |
385 | === modified file 'src/calibre/library/database2.py' |
386 | --- src/calibre/library/database2.py 2013-01-29 06:26:38 +0000 |
387 | +++ src/calibre/library/database2.py 2013-02-01 04:56:24 +0000 |
388 | @@ -532,6 +532,10 @@ |
389 | self.refresh() |
390 | self.refresh_format_cache() |
391 | self.last_update_check = utcnow() |
392 | + |
393 | + def check_if_standard_metadata_modified(self, index, index_is_id, other): |
394 | + mi = self.get_metadata(index, index_is_id=index_is_id, get_cover=False) |
395 | + return mi.get_all_standard_metadata(True) != other.get_all_standard_metadata(True) |
396 | |
397 | def path(self, index, index_is_id=False): |
398 | 'Return the relative path to the directory containing this books files as a unicode string.' |
399 | @@ -1504,6 +1508,7 @@ |
400 | self.refresh_ids([id]) |
401 | if notify: |
402 | self.notify('metadata', [id]) |
403 | + run_plugins_on_postimport(self, id, format) |
404 | return True |
405 | |
406 | def save_original_format(self, book_id, fmt, notify=True): |
407 | @@ -3296,6 +3301,9 @@ |
408 | if notify: |
409 | self.notify('metadata', [id_]) |
410 | |
411 | + def clear_identifiers(self, id_): |
412 | + self.set_identifiers(id_, {}) |
413 | + |
414 | def set_isbn(self, id_, isbn, notify=True, commit=True): |
415 | self.set_identifier(id_, 'isbn', isbn, notify=notify, commit=commit) |
416 |
Comments exist at https:/ /github. com/mirror/ calibre/ pull/3