Merge lp:~john-peterson/calibre/meta into lp:calibre

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
Reviewer Review Type Date Requested Status
Kovid Goyal Disapprove
Review via email: mp+146039@code.launchpad.net
To post a comment you must log in.
Revision history for this message
John Peterson (john-peterson) wrote :
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 :

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
=== modified file 'src/calibre/ebooks/metadata/sources/base.py'
--- src/calibre/ebooks/metadata/sources/base.py 2013-01-31 15:49:28 +0000
+++ src/calibre/ebooks/metadata/sources/base.py 2013-02-01 04:56:24 +0000
@@ -18,6 +18,10 @@
18from calibre.ebooks.metadata import check_isbn18from calibre.ebooks.metadata import check_isbn
1919
20msprefs = JSONConfig('metadata_sources/global.json')20msprefs = JSONConfig('metadata_sources/global.json')
21msprefs.defaults['confirm'] = True
22msprefs.defaults['auto_add'] = False
23msprefs.defaults['auto_overwrite'] = False
24msprefs.defaults['auto_clear'] = False
21msprefs.defaults['txt_comments'] = False25msprefs.defaults['txt_comments'] = False
22msprefs.defaults['ignore_fields'] = []26msprefs.defaults['ignore_fields'] = []
23msprefs.defaults['user_default_ignore_fields'] = []27msprefs.defaults['user_default_ignore_fields'] = []
2428
=== modified file 'src/calibre/gui2/actions/edit_metadata.py'
--- src/calibre/gui2/actions/edit_metadata.py 2012-10-05 06:00:02 +0000
+++ src/calibre/gui2/actions/edit_metadata.py 2013-02-01 04:56:24 +0000
@@ -67,18 +67,18 @@
67 self.action_merge.setEnabled(enabled)67 self.action_merge.setEnabled(enabled)
6868
69 # Download metadata {{{69 # Download metadata {{{
70 def download_metadata(self, ids=None, ensure_fields=None):70 def download_metadata(self, ids=None, ensure_fields=None, confirm=True, clear=False, identify=True, covers=True, callback_done=None):
71 db = self.gui.library_view.model().db
71 if ids is None:72 if ids is None:
72 rows = self.gui.library_view.selectionModel().selectedRows()73 rows = self.gui.library_view.selectionModel().selectedRows()
73 if not rows or len(rows) == 0:74 if not rows or len(rows) == 0:
74 return error_dialog(self.gui, _('Cannot download metadata'),75 return error_dialog(self.gui, _('Cannot download metadata'),
75 _('No books selected'), show=True)76 _('No books selected'), show=True)
76 db = self.gui.library_view.model().db
77 ids = [db.id(row.row()) for row in rows]77 ids = [db.id(row.row()) for row in rows]
78 from calibre.gui2.metadata.bulk_download import start_download78 from calibre.gui2.metadata.bulk_download import start_download
79 start_download(self.gui, ids,79 start_download(self.gui, ids,
80 Dispatcher(self.metadata_downloaded),80 Dispatcher(self.metadata_downloaded),
81 ensure_fields=ensure_fields)81 ensure_fields=ensure_fields, confirm=confirm, clear=clear, identify=identify, covers=covers, callback_done=callback_done)
8282
83 def cleanup_bulk_download(self, tdir, *args):83 def cleanup_bulk_download(self, tdir, *args):
84 try:84 try:
@@ -87,6 +87,11 @@
87 pass87 pass
8888
89 def metadata_downloaded(self, job):89 def metadata_downloaded(self, job):
90 self._metadata_downloaded(job)
91 if job.callback_done:
92 job.callback_done()
93
94 def _metadata_downloaded(self, job):
90 if job.failed:95 if job.failed:
91 self.gui.job_exception(job, dialog_title=_('Failed to download metadata'))96 self.gui.job_exception(job, dialog_title=_('Failed to download metadata'))
92 return97 return
@@ -118,16 +123,21 @@
118 'after updating metadata')123 'after updating metadata')
119124
120 payload = (id_map, tdir, log_file, lm_map,125 payload = (id_map, tdir, log_file, lm_map,
121 failed_ids.union(failed_covers))126 failed_ids.union(failed_covers), job.old_metadata)
122 self.gui.proceed_question(self.apply_downloaded_metadata, payload,127
123 log_file, _('Download log'), _('Download complete'), msg,128 from calibre.ebooks.metadata.sources.base import msprefs
124 det_msg=det_msg, show_copy_button=show_copy_button,129 if msprefs['confirm']:
125 cancel_callback=partial(self.cleanup_bulk_download, tdir),130 self.gui.proceed_question(self.apply_downloaded_metadata, payload,
126 log_is_file=True, checkbox_msg=checkbox_msg,131 log_file, _('Download log'), _('Download complete'), msg,
127 checkbox_checked=False)132 det_msg=det_msg, show_copy_button=show_copy_button,
133 cancel_callback=partial(self.cleanup_bulk_download, tdir),
134 log_is_file=True, checkbox_msg=checkbox_msg,
135 checkbox_checked=False)
136 else:
137 self.apply_downloaded_metadata(payload)
128138
129 def apply_downloaded_metadata(self, payload, *args):139 def apply_downloaded_metadata(self, payload, *args):
130 good_ids, tdir, log_file, lm_map, failed_ids = payload140 good_ids, tdir, log_file, lm_map, failed_ids, old_metadata = payload
131 if not good_ids:141 if not good_ids:
132 return142 return
133143
@@ -135,8 +145,7 @@
135 db = self.gui.current_db145 db = self.gui.current_db
136146
137 for i in good_ids:147 for i in good_ids:
138 lm = db.metadata_last_modified(i, index_is_id=True)148 if db.check_if_standard_metadata_modified(i, index_is_id=True, other=old_metadata[i]):
139 if lm is not None and lm_map[i] is not None and lm > lm_map[i]:
140 title = db.title(i, index_is_id=True)149 title = db.title(i, index_is_id=True)
141 authors = db.authors(i, index_is_id=True)150 authors = db.authors(i, index_is_id=True)
142 if authors:151 if authors:
143152
=== modified file 'src/calibre/gui2/add.py'
--- src/calibre/gui2/add.py 2013-01-06 08:22:38 +0000
+++ src/calibre/gui2/add.py 2013-02-01 04:56:24 +0000
@@ -9,7 +9,7 @@
99
10from calibre.gui2.dialogs.progress import ProgressDialog10from calibre.gui2.dialogs.progress import ProgressDialog
11from calibre.gui2 import (error_dialog, info_dialog, gprefs,11from calibre.gui2 import (error_dialog, info_dialog, gprefs,
12 warning_dialog, available_width)12 warning_dialog, available_width, Dispatcher)
13from calibre.ebooks.metadata.opf2 import OPF13from calibre.ebooks.metadata.opf2 import OPF
14from calibre.ebooks.metadata import MetaInformation14from calibre.ebooks.metadata import MetaInformation
15from calibre.constants import preferred_encoding, filesystem_encoding, DEBUG15from calibre.constants import preferred_encoding, filesystem_encoding, DEBUG
@@ -111,15 +111,49 @@
111 self.auto_convert_books = set()111 self.auto_convert_books = set()
112112
113 def end(self):113 def end(self):
114 from calibre.ebooks.metadata.sources.base import msprefs
115 callback_done = None
116 ids_add_metadata = set()
117
118 # check if metadata should be added
119 if msprefs['auto_overwrite'] or msprefs['auto_clear']:
120 ids_add_metadata = self.auto_convert_books
121 dl_identify = True
122 dl_covers = True
123
124 if msprefs['auto_add'] and not (msprefs['auto_overwrite'] or msprefs['auto_clear']):
125 dl_identify = False
126 dl_covers = False
127 for id in self.auto_convert_books:
128 mi = self.db.get_metadata(id, index_is_id=True, get_cover=True)
129 missing_identify = bool(not (mi and mi.isbn and mi.pubdate and mi.comments))
130 missing_cover = bool(not (mi and mi.cover))
131 if missing_identify: dl_identify = True
132 if missing_cover: dl_covers = True
133 if missing_identify or missing_cover:
134 ids_add_metadata.add(id)
135
136 # auto convert now or after metadata is added
114 if (gprefs['manual_add_auto_convert'] and137 if (gprefs['manual_add_auto_convert'] and
115 self.auto_convert_books):138 self.auto_convert_books):
139 if ids_add_metadata:
140 callback_done = Dispatcher(self.auto_convert)
141 else:
142 self.auto_convert()
143
144 if ids_add_metadata:
116 from calibre.gui2.ui import get_gui145 from calibre.gui2.ui import get_gui
117 gui = get_gui()146 gui = get_gui()
118 gui.iactions['Convert Books'].auto_convert_auto_add(147 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)
119 self.auto_convert_books)
120148
121 self.input_queue.put((None, None, None))149 self.input_queue.put((None, None, None))
122150
151 def auto_convert(self):
152 from calibre.gui2.ui import get_gui
153 gui = get_gui()
154 gui.iactions['Convert Books'].auto_convert_auto_add(
155 self.auto_convert_books)
156
123 def start(self):157 def start(self):
124 try:158 try:
125 id, opf, cover = self.input_queue.get_nowait()159 id, opf, cover = self.input_queue.get_nowait()
126160
=== modified file 'src/calibre/gui2/metadata/bulk_download.py'
--- src/calibre/gui2/metadata/bulk_download.py 2012-04-12 09:50:24 +0000
+++ src/calibre/gui2/metadata/bulk_download.py 2013-02-01 04:56:24 +0000
@@ -77,8 +77,8 @@
77 l.setColumnStretch(1, 100)77 l.setColumnStretch(1, 100)
7878
79 self.identify = self.covers = True79 self.identify = self.covers = True
80 self.bb = QDialogButtonBox(QDialogButtonBox.Cancel)80 self.clear = False
81 self.bb.rejected.connect(self.reject)81 self.bb = QDialogButtonBox()
82 b = self.bb.addButton(_('Download only &metadata'),82 b = self.bb.addButton(_('Download only &metadata'),
83 self.bb.AcceptRole)83 self.bb.AcceptRole)
84 b.clicked.connect(self.only_metadata)84 b.clicked.connect(self.only_metadata)
@@ -87,20 +87,36 @@
87 self.bb.AcceptRole)87 self.bb.AcceptRole)
88 b.clicked.connect(self.only_covers)88 b.clicked.connect(self.only_covers)
89 b.setIcon(QIcon(I('default_cover.png')))89 b.setIcon(QIcon(I('default_cover.png')))
90 b = self.b = self.bb.addButton(_('&Configure download'), self.bb.ActionRole)90
91 b.setIcon(QIcon(I('config.png')))91 self.bb2 = QDialogButtonBox()
92 b.clicked.connect(partial(show_config, parent, self))92 b = self.bb2.addButton(_('Download &both'),
93 l.addWidget(self.bb, 1, 0, 1, 2)93 self.bb2.AcceptRole)
94 b = self.bb.addButton(_('Download &both'),
95 self.bb.AcceptRole)
96 b.clicked.connect(self.accept)94 b.clicked.connect(self.accept)
97 b.setDefault(True)95 b.setDefault(True)
98 b.setAutoDefault(True)96 b.setAutoDefault(True)
99 b.setIcon(QIcon(I('ok.png')))97 b.setIcon(QIcon(I('ok.png')))
98 b = self.bb2.addButton(_('Clear &IDs and download both'),
99 self.bb2.AcceptRole)
100 b.clicked.connect(self.clear_ids)
101 b.setIcon(QIcon(I('trash.png')))
100102
103 self.bb3 = QDialogButtonBox(QDialogButtonBox.Cancel)
104 self.bb3.rejected.connect(self.reject)
105 b = self.b = self.bb3.addButton(_('&Configure download'), self.bb3.ActionRole)
106 b.setIcon(QIcon(I('config.png')))
107 b.clicked.connect(partial(show_config, parent, self))
108
109 l.addWidget(self.bb, 1, 0, 1, 2, Qt.AlignHCenter)
110 l.addWidget(self.bb2, 2, 0, 1, 2, Qt.AlignHCenter)
111 l.addWidget(self.bb3, 3, 0, 1, 2, Qt.AlignHCenter)
112
101 self.resize(self.sizeHint())113 self.resize(self.sizeHint())
102 b.setFocus(Qt.OtherFocusReason)114 b.setFocus(Qt.OtherFocusReason)
103115
116 def clear_ids(self):
117 self.clear = True
118 self.accept()
119
104 def only_metadata(self):120 def only_metadata(self):
105 self.covers = False121 self.covers = False
106 self.accept()122 self.accept()
@@ -118,20 +134,34 @@
118 ids = ids[batch_size:]134 ids = ids[batch_size:]
119 return ans135 return ans
120136
121def start_download(gui, ids, callback, ensure_fields=None):137def start_download(gui, ids, callback, ensure_fields=None, confirm=True, clear=False, identify=True, covers=True, callback_done=None):
122 d = ConfirmDialog(ids, gui)138 if confirm:
123 ret = d.exec_()139 d = ConfirmDialog(ids, gui)
124 d.b.clicked.disconnect()140 ret = d.exec_()
125 if ret != d.Accepted:141 d.b.clicked.disconnect()
126 return142 if ret != d.Accepted:
143 return
144 identify = d.identify
145 covers = d.covers
146 clear = d.clear
127 tf = PersistentTemporaryFile('_metadata_bulk.log')147 tf = PersistentTemporaryFile('_metadata_bulk.log')
128 tf.close()148 tf.close()
129149
150 # clear ids
151 if clear:
152 for id in ids:
153 gui.current_db.clear_identifiers(id)
154
130 job = Job('metadata bulk download',155 job = Job('metadata bulk download',
131 _('Download metadata for %d books')%len(ids),156 _('Download metadata for %d books')%len(ids),
132 download, (ids, tf.name, gui.current_db, d.identify, d.covers,157 download, (ids, tf.name, gui.current_db, identify, covers,
133 ensure_fields), {}, callback)158 ensure_fields), {}, callback)
134 job.download_debug_log = tf.name159 job.download_debug_log = tf.name
160 job.callback_done = callback_done
161 # save current metadata for modification check
162 job.old_metadata = {}
163 for id in ids:
164 job.old_metadata[id] = gui.current_db.get_metadata(id, index_is_id=True, get_cover=False)
135 gui.job_manager.run_threaded_job(job)165 gui.job_manager.run_threaded_job(job)
136 gui.status_bar.show_message(_('Metadata download started'), 3000)166 gui.status_bar.show_message(_('Metadata download started'), 3000)
137167
138168
=== modified file 'src/calibre/gui2/preferences/metadata_sources.py'
--- src/calibre/gui2/preferences/metadata_sources.py 2012-10-06 07:32:50 +0000
+++ src/calibre/gui2/preferences/metadata_sources.py 2013-02-01 04:56:24 +0000
@@ -290,6 +290,10 @@
290290
291 def genesis(self, gui):291 def genesis(self, gui):
292 r = self.register292 r = self.register
293 r('confirm', msprefs)
294 r('auto_add', msprefs)
295 r('auto_overwrite', msprefs)
296 r('auto_clear', msprefs)
293 r('txt_comments', msprefs)297 r('txt_comments', msprefs)
294 r('max_tags', msprefs)298 r('max_tags', msprefs)
295 r('wait_after_first_identify_result', msprefs)299 r('wait_after_first_identify_result', msprefs)
296300
=== modified file 'src/calibre/gui2/preferences/metadata_sources.ui'
--- src/calibre/gui2/preferences/metadata_sources.ui 2012-10-06 07:45:50 +0000
+++ src/calibre/gui2/preferences/metadata_sources.ui 2013-02-01 04:56:24 +0000
@@ -126,20 +126,48 @@
126 </widget>126 </widget>
127 </item>127 </item>
128 <item row="1" column="1" colspan="2">128 <item row="1" column="1" colspan="2">
129 <widget class="QCheckBox" name="opt_confirm">
130 <property name="text">
131 <string>Confirm before updating metadata in library</string>
132 </property>
133 </widget>
134 </item>
135 <item row="2" column="1" colspan="2">
136 <widget class="QCheckBox" name="opt_auto_add">
137 <property name="text">
138 <string>Automatically add missing metadata on import</string>
139 </property>
140 </widget>
141 </item>
142 <item row="3" column="1" colspan="2">
143 <widget class="QCheckBox" name="opt_auto_overwrite">
144 <property name="text">
145 <string>Automatically overwrite metadata on import</string>
146 </property>
147 </widget>
148 </item>
149 <item row="4" column="1" colspan="2">
150 <widget class="QCheckBox" name="opt_auto_clear">
151 <property name="text">
152 <string>Automatically clear IDs and overwrite metadata on import</string>
153 </property>
154 </widget>
155 </item>
156 <item row="5" column="1" colspan="2">
129 <widget class="QCheckBox" name="opt_txt_comments">157 <widget class="QCheckBox" name="opt_txt_comments">
130 <property name="text">158 <property name="text">
131 <string>Convert all downloaded comments to plain &amp;text</string>159 <string>Convert all downloaded comments to plain &amp;text</string>
132 </property>160 </property>
133 </widget>161 </widget>
134 </item>162 </item>
135 <item row="2" column="1" colspan="2">163 <item row="6" column="1" colspan="2">
136 <widget class="QCheckBox" name="opt_swap_author_names">164 <widget class="QCheckBox" name="opt_swap_author_names">
137 <property name="text">165 <property name="text">
138 <string>Swap author names from FN LN to LN, FN</string>166 <string>Swap author names from FN LN to LN, FN</string>
139 </property>167 </property>
140 </widget>168 </widget>
141 </item>169 </item>
142 <item row="5" column="1">170 <item row="9" column="1">
143 <widget class="QLabel" name="label_2">171 <widget class="QLabel" name="label_2">
144 <property name="text">172 <property name="text">
145 <string>Max. number of &amp;tags to download:</string>173 <string>Max. number of &amp;tags to download:</string>
@@ -149,10 +177,10 @@
149 </property>177 </property>
150 </widget>178 </widget>
151 </item>179 </item>
152 <item row="5" column="2">180 <item row="9" column="2">
153 <widget class="QSpinBox" name="opt_max_tags"/>181 <widget class="QSpinBox" name="opt_max_tags"/>
154 </item>182 </item>
155 <item row="6" column="1">183 <item row="10" column="1">
156 <widget class="QLabel" name="label_3">184 <widget class="QLabel" name="label_3">
157 <property name="text">185 <property name="text">
158 <string>Max. &amp;time to wait after first match is found:</string>186 <string>Max. &amp;time to wait after first match is found:</string>
@@ -162,14 +190,14 @@
162 </property>190 </property>
163 </widget>191 </widget>
164 </item>192 </item>
165 <item row="6" column="2">193 <item row="10" column="2">
166 <widget class="QSpinBox" name="opt_wait_after_first_identify_result">194 <widget class="QSpinBox" name="opt_wait_after_first_identify_result">
167 <property name="suffix">195 <property name="suffix">
168 <string> secs</string>196 <string> secs</string>
169 </property>197 </property>
170 </widget>198 </widget>
171 </item>199 </item>
172 <item row="7" column="1">200 <item row="11" column="1">
173 <widget class="QLabel" name="label_4">201 <widget class="QLabel" name="label_4">
174 <property name="text">202 <property name="text">
175 <string>Max. time to wait after first &amp;cover is found:</string>203 <string>Max. time to wait after first &amp;cover is found:</string>
@@ -179,14 +207,14 @@
179 </property>207 </property>
180 </widget>208 </widget>
181 </item>209 </item>
182 <item row="7" column="2">210 <item row="11" column="2">
183 <widget class="QSpinBox" name="opt_wait_after_first_cover_result">211 <widget class="QSpinBox" name="opt_wait_after_first_cover_result">
184 <property name="suffix">212 <property name="suffix">
185 <string> secs</string>213 <string> secs</string>
186 </property>214 </property>
187 </widget>215 </widget>
188 </item>216 </item>
189 <item row="4" column="1" colspan="2">217 <item row="8" column="1" colspan="2">
190 <widget class="QCheckBox" name="opt_fewer_tags">218 <widget class="QCheckBox" name="opt_fewer_tags">
191 <property name="toolTip">219 <property name="toolTip">
192 <string>&lt;p&gt;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.220 <string>&lt;p&gt;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.
@@ -197,7 +225,7 @@
197 </property>225 </property>
198 </widget>226 </widget>
199 </item>227 </item>
200 <item row="3" column="1" colspan="2">228 <item row="7" column="1" colspan="2">
201 <widget class="QCheckBox" name="opt_find_first_edition_date">229 <widget class="QCheckBox" name="opt_find_first_edition_date">
202 <property name="text">230 <property name="text">
203 <string>Use published date of &quot;first edition&quot; (from worldcat.org)</string>231 <string>Use published date of &quot;first edition&quot; (from worldcat.org)</string>
204232
=== modified file 'src/calibre/library/database2.py'
--- src/calibre/library/database2.py 2013-01-29 06:26:38 +0000
+++ src/calibre/library/database2.py 2013-02-01 04:56:24 +0000
@@ -532,6 +532,10 @@
532 self.refresh()532 self.refresh()
533 self.refresh_format_cache()533 self.refresh_format_cache()
534 self.last_update_check = utcnow()534 self.last_update_check = utcnow()
535
536 def check_if_standard_metadata_modified(self, index, index_is_id, other):
537 mi = self.get_metadata(index, index_is_id=index_is_id, get_cover=False)
538 return mi.get_all_standard_metadata(True) != other.get_all_standard_metadata(True)
535539
536 def path(self, index, index_is_id=False):540 def path(self, index, index_is_id=False):
537 'Return the relative path to the directory containing this books files as a unicode string.'541 'Return the relative path to the directory containing this books files as a unicode string.'
@@ -1504,6 +1508,7 @@
1504 self.refresh_ids([id])1508 self.refresh_ids([id])
1505 if notify:1509 if notify:
1506 self.notify('metadata', [id])1510 self.notify('metadata', [id])
1511 run_plugins_on_postimport(self, id, format)
1507 return True1512 return True
15081513
1509 def save_original_format(self, book_id, fmt, notify=True):1514 def save_original_format(self, book_id, fmt, notify=True):
@@ -3296,6 +3301,9 @@
3296 if notify:3301 if notify:
3297 self.notify('metadata', [id_])3302 self.notify('metadata', [id_])
32983303
3304 def clear_identifiers(self, id_):
3305 self.set_identifiers(id_, {})
3306
3299 def set_isbn(self, id_, isbn, notify=True, commit=True):3307 def set_isbn(self, id_, isbn, notify=True, commit=True):
3300 self.set_identifier(id_, 'isbn', isbn, notify=notify, commit=commit)3308 self.set_identifier(id_, 'isbn', isbn, notify=notify, commit=commit)
33013309

Subscribers

People subscribed via source and target branches