mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
First iteration of automatic metadata updating
This commit is contained in:
parent
40d302fb22
commit
66d498e7b3
@ -132,6 +132,8 @@ class CollectionsBookList(BookList):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def get_collections(self, collection_attributes):
|
def get_collections(self, collection_attributes):
|
||||||
|
from calibre.devices.usbms.driver import debug_print
|
||||||
|
debug_print('Starting get_collections:', prefs['manage_device_metadata'])
|
||||||
collections = {}
|
collections = {}
|
||||||
series_categories = set([])
|
series_categories = set([])
|
||||||
# This map of sets is used to avoid linear searches when testing for
|
# This map of sets is used to avoid linear searches when testing for
|
||||||
@ -146,13 +148,18 @@ class CollectionsBookList(BookList):
|
|||||||
# book in all existing collections. Do not add any new ones.
|
# book in all existing collections. Do not add any new ones.
|
||||||
attrs = ['device_collections']
|
attrs = ['device_collections']
|
||||||
if getattr(book, '_new_book', False):
|
if getattr(book, '_new_book', False):
|
||||||
if prefs['preserve_user_collections']:
|
if prefs['manage_device_metadata'] == 'manual':
|
||||||
# Ensure that the book is in all the book's existing
|
# Ensure that the book is in all the book's existing
|
||||||
# collections plus all metadata collections
|
# collections plus all metadata collections
|
||||||
attrs += collection_attributes
|
attrs += collection_attributes
|
||||||
else:
|
else:
|
||||||
# The book's existing collections are ignored. Put the book
|
# For new books, both 'on_send' and 'on_connect' do the same
|
||||||
# in collections defined by its metadata.
|
# thing. The book's existing collections are ignored. Put
|
||||||
|
# the book in collections defined by its metadata.
|
||||||
|
attrs = collection_attributes
|
||||||
|
elif prefs['manage_device_metadata'] == 'on_connect':
|
||||||
|
# For existing books, modify the collections only if the user
|
||||||
|
# specified 'on_connect'
|
||||||
attrs = collection_attributes
|
attrs = collection_attributes
|
||||||
for attr in attrs:
|
for attr in attrs:
|
||||||
attr = attr.strip()
|
attr = attr.strip()
|
||||||
|
@ -58,7 +58,7 @@ class USBMS(CLI, Device):
|
|||||||
|
|
||||||
debug_print ('USBMS: Fetching list of books from device. oncard=', oncard)
|
debug_print ('USBMS: Fetching list of books from device. oncard=', oncard)
|
||||||
|
|
||||||
dummy_bl = BookList(None, None, None)
|
dummy_bl = booklist_class(None, None, None)
|
||||||
|
|
||||||
if oncard == 'carda' and not self._card_a_prefix:
|
if oncard == 'carda' and not self._card_a_prefix:
|
||||||
self.report_progress(1.0, _('Getting list of books on device...'))
|
self.report_progress(1.0, _('Getting list of books on device...'))
|
||||||
|
@ -33,6 +33,7 @@ from calibre.devices.apple.driver import ITUNES_ASYNC
|
|||||||
from calibre.devices.folder_device.driver import FOLDER_DEVICE
|
from calibre.devices.folder_device.driver import FOLDER_DEVICE
|
||||||
from calibre.ebooks.metadata.meta import set_metadata
|
from calibre.ebooks.metadata.meta import set_metadata
|
||||||
from calibre.constants import DEBUG
|
from calibre.constants import DEBUG
|
||||||
|
from calibre.utils.config import prefs
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -1425,19 +1426,24 @@ class DeviceMixin(object): # {{{
|
|||||||
aus = re.sub('(?u)\W|[_]', '', aus)
|
aus = re.sub('(?u)\W|[_]', '', aus)
|
||||||
self.db_book_title_cache[title]['author_sort'][aus] = mi
|
self.db_book_title_cache[title]['author_sort'][aus] = mi
|
||||||
self.db_book_title_cache[title]['db_ids'][mi.application_id] = mi
|
self.db_book_title_cache[title]['db_ids'][mi.application_id] = mi
|
||||||
self.db_book_uuid_cache[mi.uuid] = mi.application_id
|
self.db_book_uuid_cache[mi.uuid] = mi
|
||||||
|
|
||||||
# Now iterate through all the books on the device, setting the
|
# Now iterate through all the books on the device, setting the
|
||||||
# in_library field Fastest and most accurate key is the uuid. Second is
|
# in_library field Fastest and most accurate key is the uuid. Second is
|
||||||
# the application_id, which is really the db key, but as this can
|
# the application_id, which is really the db key, but as this can
|
||||||
# accidentally match across libraries we also verify the title. The
|
# accidentally match across libraries we also verify the title. The
|
||||||
# db_id exists on Sony devices. Fallback is title and author match
|
# db_id exists on Sony devices. Fallback is title and author match
|
||||||
|
|
||||||
|
update_metadata = prefs['manage_device_metadata'] == 'on_connect'
|
||||||
for booklist in booklists:
|
for booklist in booklists:
|
||||||
for book in booklist:
|
for book in booklist:
|
||||||
if getattr(book, 'uuid', None) in self.db_book_uuid_cache:
|
if getattr(book, 'uuid', None) in self.db_book_uuid_cache:
|
||||||
|
if update_metadata:
|
||||||
|
book.smart_update(self.db_book_uuid_cache[book.uuid])
|
||||||
book.in_library = True
|
book.in_library = True
|
||||||
# ensure that the correct application_id is set
|
# ensure that the correct application_id is set
|
||||||
book.application_id = self.db_book_uuid_cache[book.uuid]
|
book.application_id = \
|
||||||
|
self.db_book_uuid_cache[book.uuid].application_id
|
||||||
continue
|
continue
|
||||||
|
|
||||||
book_title = book.title.lower() if book.title else ''
|
book_title = book.title.lower() if book.title else ''
|
||||||
@ -1447,10 +1453,12 @@ class DeviceMixin(object): # {{{
|
|||||||
if d is not None:
|
if d is not None:
|
||||||
if getattr(book, 'application_id', None) in d['db_ids']:
|
if getattr(book, 'application_id', None) in d['db_ids']:
|
||||||
book.in_library = True
|
book.in_library = True
|
||||||
|
if update_metadata:
|
||||||
book.smart_update(d['db_ids'][book.application_id])
|
book.smart_update(d['db_ids'][book.application_id])
|
||||||
continue
|
continue
|
||||||
if book.db_id in d['db_ids']:
|
if book.db_id in d['db_ids']:
|
||||||
book.in_library = True
|
book.in_library = True
|
||||||
|
if update_metadata:
|
||||||
book.smart_update(d['db_ids'][book.db_id])
|
book.smart_update(d['db_ids'][book.db_id])
|
||||||
continue
|
continue
|
||||||
if book.authors:
|
if book.authors:
|
||||||
@ -1460,14 +1468,19 @@ class DeviceMixin(object): # {{{
|
|||||||
book_authors = re.sub('(?u)\W|[_]', '', book_authors)
|
book_authors = re.sub('(?u)\W|[_]', '', book_authors)
|
||||||
if book_authors in d['authors']:
|
if book_authors in d['authors']:
|
||||||
book.in_library = True
|
book.in_library = True
|
||||||
|
if update_metadata:
|
||||||
book.smart_update(d['authors'][book_authors])
|
book.smart_update(d['authors'][book_authors])
|
||||||
elif book_authors in d['author_sort']:
|
elif book_authors in d['author_sort']:
|
||||||
book.in_library = True
|
book.in_library = True
|
||||||
|
if update_metadata:
|
||||||
book.smart_update(d['author_sort'][book_authors])
|
book.smart_update(d['author_sort'][book_authors])
|
||||||
# Set author_sort if it isn't already
|
# Set author_sort if it isn't already
|
||||||
asort = getattr(book, 'author_sort', None)
|
asort = getattr(book, 'author_sort', None)
|
||||||
if not asort and book.authors:
|
if not asort and book.authors:
|
||||||
book.author_sort = self.library_view.model().db.author_sort_from_authors(book.authors)
|
book.author_sort = self.library_view.model().db.author_sort_from_authors(book.authors)
|
||||||
|
|
||||||
|
if update_metadata:
|
||||||
|
if self.device_manager.is_device_connected:
|
||||||
|
self.device_manager.sync_booklists(None, booklists)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -45,7 +45,12 @@ class AddSave(QTabWidget, Ui_TabWidget):
|
|||||||
self.metadata_box.layout().insertWidget(0, self.filename_pattern)
|
self.metadata_box.layout().insertWidget(0, self.filename_pattern)
|
||||||
self.opt_swap_author_names.setChecked(prefs['swap_author_names'])
|
self.opt_swap_author_names.setChecked(prefs['swap_author_names'])
|
||||||
self.opt_add_formats_to_existing.setChecked(prefs['add_formats_to_existing'])
|
self.opt_add_formats_to_existing.setChecked(prefs['add_formats_to_existing'])
|
||||||
self.preserve_user_collections.setChecked(prefs['preserve_user_collections'])
|
if prefs['manage_device_metadata'] == 'manual':
|
||||||
|
self.manage_device_metadata.setCurrentIndex(0)
|
||||||
|
elif prefs['manage_device_metadata'] == 'on_send':
|
||||||
|
self.manage_device_metadata.setCurrentIndex(1)
|
||||||
|
else:
|
||||||
|
self.manage_device_metadata.setCurrentIndex(2)
|
||||||
help = '\n'.join(textwrap.wrap(c.get_option('template').help, 75))
|
help = '\n'.join(textwrap.wrap(c.get_option('template').help, 75))
|
||||||
self.save_template.initialize('save_to_disk', opts.template, help)
|
self.save_template.initialize('save_to_disk', opts.template, help)
|
||||||
self.send_template.initialize('send_to_device', opts.send_template, help)
|
self.send_template.initialize('send_to_device', opts.send_template, help)
|
||||||
@ -72,12 +77,14 @@ class AddSave(QTabWidget, Ui_TabWidget):
|
|||||||
prefs['filename_pattern'] = pattern
|
prefs['filename_pattern'] = pattern
|
||||||
prefs['swap_author_names'] = bool(self.opt_swap_author_names.isChecked())
|
prefs['swap_author_names'] = bool(self.opt_swap_author_names.isChecked())
|
||||||
prefs['add_formats_to_existing'] = bool(self.opt_add_formats_to_existing.isChecked())
|
prefs['add_formats_to_existing'] = bool(self.opt_add_formats_to_existing.isChecked())
|
||||||
prefs['preserve_user_collections'] = bool(self.preserve_user_collections.isChecked())
|
if self.manage_device_metadata.currentIndex() == 0:
|
||||||
|
prefs['manage_device_metadata'] = 'manual'
|
||||||
|
elif self.manage_device_metadata.currentIndex() == 1:
|
||||||
|
prefs['manage_device_metadata'] = 'on_send'
|
||||||
|
else:
|
||||||
|
prefs['manage_device_metadata'] = 'on_connect'
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from PyQt4.Qt import QApplication
|
from PyQt4.Qt import QApplication
|
||||||
app=QApplication([])
|
app=QApplication([])
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<string>TabWidget</string>
|
<string>TabWidget</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>2</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="tab">
|
<widget class="QWidget" name="tab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
@ -179,16 +179,34 @@ Title match ignores leading indefinite articles ("the", "a",
|
|||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="preserve_user_collections">
|
<widget class="QComboBox" name="manage_device_metadata">
|
||||||
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Preserve device collections.</string>
|
<string extracomment="foobar">Manual management. Calibre updates cached metadata and adds collections, but never removes them</string>
|
||||||
</property>
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Calibre manages metadata when sending books. Calibre updates cached metadata, and adds/remove collections</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Calibre manages metadata at device connection. Calibre updates cached metadata, and adds/removes collections</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_41">
|
<widget class="QLabel" name="label_41">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>If checked, collections will not be deleted even if a book with changed metadata is resent and the collection is not in the book's metadata. In addition, editing collections in the device view will be enabled. If unchecked, collections will be always reflect only the metadata in the calibre library.</string>
|
<string>Choose 'Manual Management', to have Calibre update the metadata cache (not the book) and add collections when a book is sent. With this option, calibre will never remove a collection. Choose 'Calibre manages metadata when sending books' to have Calibre update the metadata cache and add/remove collections when you send a book to the device. Choose 'Calibre manages metadata when device is connected' to have Calibre update the metadata cache and add/remove collections when you connect the device. </string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@ -198,7 +216,7 @@ Title match ignores leading indefinite articles ("the", "a",
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_42">
|
<widget class="QLabel" name="label_42">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string> </string>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -939,7 +939,7 @@ class DeviceBooksModel(BooksModel): # {{{
|
|||||||
(cname == 'collections' and \
|
(cname == 'collections' and \
|
||||||
callable(getattr(self.db, 'supports_collections', None)) and \
|
callable(getattr(self.db, 'supports_collections', None)) and \
|
||||||
self.db.supports_collections() and \
|
self.db.supports_collections() and \
|
||||||
prefs['preserve_user_collections']):
|
prefs['manage_device_metadata']=='manual'):
|
||||||
flags |= Qt.ItemIsEditable
|
flags |= Qt.ItemIsEditable
|
||||||
return flags
|
return flags
|
||||||
|
|
||||||
|
@ -503,7 +503,7 @@ class DeviceBooksView(BooksView): # {{{
|
|||||||
self.edit_collections_menu.setVisible(
|
self.edit_collections_menu.setVisible(
|
||||||
callable(getattr(self._model.db, 'supports_collections', None)) and \
|
callable(getattr(self._model.db, 'supports_collections', None)) and \
|
||||||
self._model.db.supports_collections() and \
|
self._model.db.supports_collections() and \
|
||||||
prefs['preserve_user_collections'])
|
prefs['manage_device_metadata'] == 'manual')
|
||||||
self.context_menu.popup(event.globalPos())
|
self.context_menu.popup(event.globalPos())
|
||||||
event.accept()
|
event.accept()
|
||||||
|
|
||||||
|
@ -698,8 +698,8 @@ def _prefs():
|
|||||||
# calibre server can execute searches
|
# calibre server can execute searches
|
||||||
c.add_opt('saved_searches', default={}, help=_('List of named saved searches'))
|
c.add_opt('saved_searches', default={}, help=_('List of named saved searches'))
|
||||||
c.add_opt('user_categories', default={}, help=_('User-created tag browser categories'))
|
c.add_opt('user_categories', default={}, help=_('User-created tag browser categories'))
|
||||||
c.add_opt('preserve_user_collections', default=True,
|
c.add_opt('manage_device_metadata', default='manual',
|
||||||
help=_('Preserve all collections even if not in library metadata.'))
|
help=_('How and when calibre updates metadata on the device.'))
|
||||||
|
|
||||||
c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.')
|
c.add_opt('migrated', default=False, help='For Internal use. Don\'t modify.')
|
||||||
return c
|
return c
|
||||||
|
Loading…
x
Reference in New Issue
Block a user