diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py index d25787fc89..eab625f7be 100644 --- a/src/calibre/devices/usbms/books.py +++ b/src/calibre/devices/usbms/books.py @@ -94,12 +94,12 @@ class CollectionsBookList(BookList): def supports_collections(self): return True - def compute_category_name(self, attr, category, cust_field_meta): + def compute_category_name(self, attr, category, field_meta): renames = tweaks['sony_collection_renaming_rules'] attr_name = renames.get(attr, None) if attr_name is None: - if attr in cust_field_meta: - attr_name = '(%s)'%cust_field_meta[attr]['name'] + if field_meta['is_custom']: + attr_name = '(%s)'%field_meta['name'] else: attr_name = '' elif attr_name != '': @@ -138,23 +138,23 @@ class CollectionsBookList(BookList): # specified 'on_connect' attrs = collection_attributes meta_vals = book.get_all_non_none_attributes() - cust_field_meta = book.get_all_user_metadata(make_copy=False) for attr in attrs: attr = attr.strip() - ign, val = book.format_field(attr, - ignore_series_index=True, - return_multiples_as_list=True) + ign, val, orig_val, fm = book.format_field_extended(attr) if not val: continue if isbytestring(val): val = val.decode(preferred_encoding, 'replace') if isinstance(val, (list, tuple)): val = list(val) + elif fm['datatype'] == 'series': + val = [orig_val] + elif fm['datatype'] == 'text' and fm['is_multiple']: + val = orig_val else: val = [val] for category in val: is_series = False - if attr in cust_field_meta: # is a custom field - fm = cust_field_meta[attr] + if fm['is_custom']: # is a custom field if fm['datatype'] == 'text' and len(category) > 1 and \ category[0] == '[' and category[-1] == ']': continue @@ -168,8 +168,7 @@ class CollectionsBookList(BookList): ('series' in collection_attributes and meta_vals.get('series', None) == category): is_series = True - cat_name = self.compute_category_name(attr, category, - cust_field_meta) + cat_name = self.compute_category_name(attr, category, fm) if cat_name not in collections: collections[cat_name] = [] collections_lpaths[cat_name] = set() diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index b954911242..928d00ad4a 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -829,12 +829,14 @@ class Device(DeviceConfig, DevicePlugin): ext = os.path.splitext(fname)[1] from calibre.library.save_to_disk import get_components + from calibre.library.save_to_disk import config + opts = config().parse() if not isinstance(template, unicode): template = template.decode('utf-8') app_id = str(getattr(mdata, 'application_id', '')) # The db id will be in the created filename extra_components = get_components(template, mdata, fname, - length=250-len(app_id)-1) + timefmt=opts.send_timefmt, length=250-len(app_id)-1) if not extra_components: extra_components.append(sanitize(self.filename_callback(fname, mdata))) diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index 7405f20a7c..b252f518da 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -343,8 +343,11 @@ class Metadata(object): def format_rating(self): return unicode(self.rating) - def format_field(self, key, ignore_series_index=False, - return_multiples_as_list=False): + def format_field(self, key): + name, val, ign, ign = self.format_field_extended(key) + return (name, val) + + def format_field_extended(self, key): from calibre.ebooks.metadata import authors_to_string ''' returns the tuple (field_name, formatted_value) @@ -352,43 +355,41 @@ class Metadata(object): if key in self.user_metadata_keys: res = self.get(key, None) if res is None or res == '': - return (None, None) + return (None, None, None, None) + orig_res = res cmeta = self.get_user_metadata(key, make_copy=False) name = unicode(cmeta['name']) datatype = cmeta['datatype'] if datatype == 'text' and cmeta['is_multiple']: - if not return_multiples_as_list: - res = u', '.join(res) + res = u', '.join(res) elif datatype == 'series': - if not ignore_series_index: - res = res + \ - ' [%s]'%self.format_series_index(val=self.get_extra(key)) + res = res + \ + ' [%s]'%self.format_series_index(val=self.get_extra(key)) elif datatype == 'datetime': res = format_date(res, cmeta['display'].get('date_format','dd MMM yyyy')) elif datatype == 'bool': res = _('Yes') if res else _('No') - return (name, res) + return (name, res, orig_res, cmeta) if key in field_metadata and field_metadata[key]['kind'] == 'field': res = self.get(key, None) if res is None or res == '': - return (None, None) + return (None, None, None, None) + orig_res = res fmeta = field_metadata[key] name = unicode(fmeta['name']) datatype = fmeta['datatype'] if key == 'authors': res = authors_to_string(res) elif datatype == 'text' and fmeta['is_multiple']: - if not return_multiples_as_list: - res = u', '.join(res) + res = u', '.join(res) elif datatype == 'series': - if not ignore_series_index: - res = res + ' [%s]'%self.format_series_index() + res = res + ' [%s]'%self.format_series_index() elif datatype == 'datetime': res = format_date(res, fmeta['display'].get('date_format','dd MMM yyyy')) - return (name, res) + return (name, res, orig_res, fmeta) - return (None, None) + return (None, None, None, None) def __unicode__(self): from calibre.ebooks.metadata import authors_to_string diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index ecbef3194d..8a4ff6a5bd 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -1415,9 +1415,11 @@ def test_user_metadata(): mi = Metadata('Test title', ['test author1', 'test author2']) um = { '#myseries': { '#value#': u'test series\xe4', 'datatype':'text', - 'is_multiple': False, 'name': u'My Series'}, + 'is_multiple': None, 'name': u'My Series'}, '#myseries_index': { '#value#': 2.45, 'datatype': 'float', - 'is_multiple': False} + 'is_multiple': None}, + '#mytags': {'#value#':['t1','t2','t3'], 'datatype':'text', + 'is_multiple': '|', 'name': u'My Tags'} } mi.set_all_user_metadata(um) raw = metadata_to_opf(mi) diff --git a/src/calibre/gui2/actions/add.py b/src/calibre/gui2/actions/add.py index add7bf1d5b..aa20b8bc16 100644 --- a/src/calibre/gui2/actions/add.py +++ b/src/calibre/gui2/actions/add.py @@ -230,7 +230,7 @@ class AddAction(InterfaceAction): self._files_added(paths, names, infos, on_card=on_card) # set the in-library flags, and as a consequence send the library's # metadata for this book to the device. This sets the uuid to the - # correct value. + # correct value. Note that set_books_in_library might sync_booklists self.gui.set_books_in_library(booklists=[model.db], reset=True) model.reset() diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 36f6a7d6eb..b20cd7594f 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -745,6 +745,7 @@ class DeviceMixin(object): # {{{ if job.failed: self.device_job_exception(job) return + # set_books_in_library might schedule a sync_booklists job self.set_books_in_library(job.result, reset=True) mainlist, cardalist, cardblist = job.result self.memory_view.set_database(mainlist) @@ -789,11 +790,12 @@ class DeviceMixin(object): # {{{ self.device_manager.remove_books_from_metadata(paths, self.booklists()) model.paths_deleted(paths) - self.upload_booklists() # Force recomputation the library's ondevice info. We need to call # set_books_in_library even though books were not added because - # the deleted book might have been an exact match. - self.set_books_in_library(self.booklists(), reset=True) + # the deleted book might have been an exact match. Upload the booklists + # if set_books_in_library did not. + if not self.set_books_in_library(self.booklists(), reset=True): + self.upload_booklists() self.book_on_device(None, None, reset=True) # We need to reset the ondevice flags in the library. Use a big hammer, # so we don't need to worry about whether some succeeded or not. @@ -1280,8 +1282,6 @@ class DeviceMixin(object): # {{{ self.device_manager.add_books_to_metadata(job.result, metadata, self.booklists()) - self.upload_booklists() - books_to_be_deleted = [] if memory and memory[1]: books_to_be_deleted = memory[1] @@ -1291,12 +1291,15 @@ class DeviceMixin(object): # {{{ # book already there with a different book. This happens frequently in # news. When this happens, the book match indication will be wrong # because the UUID changed. Force both the device and the library view - # to refresh the flags. - self.set_books_in_library(self.booklists(), reset=True) + # to refresh the flags. Set_books_in_library could upload the booklists. + # If it does not, then do it here. + if not self.set_books_in_library(self.booklists(), reset=True): + self.upload_booklists() self.book_on_device(None, reset=True) self.refresh_ondevice_info(device_connected = True) - view = self.card_a_view if on_card == 'carda' else self.card_b_view if on_card == 'cardb' else self.memory_view + view = self.card_a_view if on_card == 'carda' else \ + self.card_b_view if on_card == 'cardb' else self.memory_view view.model().resort(reset=False) view.model().research() for f in files: @@ -1371,7 +1374,7 @@ class DeviceMixin(object): # {{{ try: db = self.library_view.model().db except: - return + return False # Build a cache (map) of the library, so the search isn't On**2 self.db_book_title_cache = {} self.db_book_uuid_cache = {} @@ -1466,11 +1469,13 @@ class DeviceMixin(object): # {{{ # Set author_sort if it isn't already asort = getattr(book, 'author_sort', None) 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( Dispatcher(self.metadata_synced), booklists) + return update_metadata # }}} diff --git a/src/calibre/gui2/preferences/save_template.py b/src/calibre/gui2/preferences/save_template.py index 26dc02f259..0dbee5bf21 100644 --- a/src/calibre/gui2/preferences/save_template.py +++ b/src/calibre/gui2/preferences/save_template.py @@ -8,8 +8,10 @@ __docformat__ = 'restructuredtext en' from PyQt4.Qt import QWidget, pyqtSignal +from calibre.gui2 import error_dialog from calibre.gui2.preferences.save_template_ui import Ui_Form -from calibre.library.save_to_disk import FORMAT_ARG_DESCS +from calibre.library.save_to_disk import FORMAT_ARG_DESCS, preprocess_template,\ + safe_format class SaveTemplate(QWidget, Ui_Form): @@ -24,8 +26,11 @@ class SaveTemplate(QWidget, Ui_Form): variables = sorted(FORMAT_ARG_DESCS.keys()) rows = [] for var in variables: - rows.append(u'
'+_('The template %s is invalid:')%tmpl + \
+ '
'+str(err), show=True)
+ return False
return True
-# tmpl = preprocess_template(self.opt_template.text())
-# fa = {}
-# for x in FORMAT_ARG_DESCS.keys():
-# fa[x]='random long string'
-# try:
-# tmpl.format(**fa)
-# except Exception, err:
-# error_dialog(self, _('Invalid template'),
-# '
'+_('The template %s is invalid:')%tmpl + \
-# '
'+str(err), show=True)
-# return False
-# return True
def set_value(self, val):
self.opt_template.set_value(val)
diff --git a/src/calibre/gui2/preferences/sending.py b/src/calibre/gui2/preferences/sending.py
index 748c6b2a2d..ac4abbcf41 100644
--- a/src/calibre/gui2/preferences/sending.py
+++ b/src/calibre/gui2/preferences/sending.py
@@ -22,6 +22,9 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r = self.register
+ for x in ('send_timefmt',):
+ r(x, self.proxy)
+
choices = [(_('Manual management'), 'manual'),
(_('Only on send'), 'on_send'),
(_('Automatic management'), 'on_connect')]
diff --git a/src/calibre/gui2/preferences/sending.ui b/src/calibre/gui2/preferences/sending.ui
index e064646afd..75b1899a3a 100644
--- a/src/calibre/gui2/preferences/sending.ui
+++ b/src/calibre/gui2/preferences/sending.ui
@@ -80,7 +80,20 @@
-