diff --git a/icons/library.icns b/icons/library.icns index 39813eb8e6..1b796e2fe0 100644 Binary files a/icons/library.icns and b/icons/library.icns differ diff --git a/icons/library.ico b/icons/library.ico index 433b4f2d51..32ce8b5d0d 100644 Binary files a/icons/library.ico and b/icons/library.ico differ diff --git a/resources/images/library.png b/resources/images/library.png index bd3b90bfb1..cd2c9075b6 100644 Binary files a/resources/images/library.png and b/resources/images/library.png differ diff --git a/resources/tracer.epub b/resources/tracer.epub index 0c31dbcbbb..28f40c07d0 100644 Binary files a/resources/tracer.epub and b/resources/tracer.epub differ diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 831df4cdb8..55a19fd0db 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -12,15 +12,14 @@ from calibre import fit_image from calibre.constants import isosx, iswindows from calibre.devices.errors import UserFeedback from calibre.devices.interface import DevicePlugin -from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag +from calibre.ebooks.BeautifulSoup import BeautifulSoup from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata.epub import set_metadata from calibre.library.server.utils import strftime -from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.config import Config, config_dir -from calibre.utils.date import fromtimestamp, isoformat, now, parse_date, strptime +from calibre.utils.date import isoformat, now, parse_date from calibre.utils.logging import Log -from calibre.utils.zipfile import safe_replace, ZipFile +from calibre.utils.zipfile import ZipFile from PIL import Image as PILImage @@ -34,7 +33,6 @@ if isosx: if iswindows: import pythoncom, win32com.client - from calibre.ebooks.BeautifulSoup import BeautifulSoup class ITUNES(DevicePlugin): @@ -1665,8 +1663,6 @@ class ITUNES(DevicePlugin): ''' assumes pythoncom wrapper ''' -# if DEBUG: -# self.log.info(" ITUNES._get_device_books_playlist()") if iswindows: if 'iPod' in self.sources: pl = None @@ -1709,11 +1705,6 @@ class ITUNES(DevicePlugin): if update_md: self._update_epub_metadata(fpath, metadata) -# if DEBUG: -# self.log.info(" metadata before rewrite: '{0[0]}' '{0[1]}' '{0[2]}'".format(self._dump_epub_metadata(fpath))) -# self._update_epub_metadata(fpath, metadata) -# if DEBUG: -# self.log.info(" metadata after rewrite: '{0[0]}' '{0[1]}' '{0[2]}'".format(self._dump_epub_metadata(fpath))) return fpath def _get_library_books(self): @@ -2132,21 +2123,6 @@ class ITUNES(DevicePlugin): # Refresh epub metadata with open(fpath,'r+b') as zfo: - ''' - # Touch the timestamp to force a recache - if metadata.timestamp: - if DEBUG: - self.log.info(" old timestamp: %s" % metadata.timestamp) - old_ts = metadata.timestamp - metadata.timestamp = datetime.datetime(old_ts.year, old_ts.month, old_ts.day, old_ts.hour, - old_ts.minute, old_ts.second, old_ts.microsecond+1, old_ts.tzinfo) - if DEBUG: - self.log.info(" new timestamp: %s" % metadata.timestamp) - else: - metadata.timestamp = isoformat(now()) - if DEBUG: - self.log.info(" add timestamp: %s" % metadata.timestamp) - ''' # Touch the OPF timestamp zf_opf = ZipFile(fpath,'r') fnames = zf_opf.namelist() @@ -2186,7 +2162,7 @@ class ITUNES(DevicePlugin): if iswindows and metadata.series: metadata.tags = None - set_metadata(zfo,metadata) + set_metadata(zfo, metadata, update_timestamp=True) def _update_device(self, msg='', wait=True): ''' diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py index 38fac8b266..5860826778 100644 --- a/src/calibre/devices/prs505/driver.py +++ b/src/calibre/devices/prs505/driver.py @@ -55,6 +55,7 @@ class PRS505(USBMS): SUPPORTS_SUB_DIRS = True MUST_READ_METADATA = True + SUPPORTS_USE_AUTHOR_SORT = True EBOOK_DIR_MAIN = 'database/media/books' EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of metadata fields ' @@ -125,7 +126,7 @@ class PRS505(USBMS): d = os.path.dirname(paths[source_id]) if not os.path.exists(d): os.makedirs(d) - return XMLCache(paths, prefixes) + return XMLCache(paths, prefixes, self.settings().use_author_sort) def books(self, oncard=None, end_session=True): debug_print('PRS505: starting fetching books for card', oncard) diff --git a/src/calibre/devices/prs505/sony_cache.py b/src/calibre/devices/prs505/sony_cache.py index 727bdf68b2..e7d0e4686c 100644 --- a/src/calibre/devices/prs505/sony_cache.py +++ b/src/calibre/devices/prs505/sony_cache.py @@ -60,12 +60,13 @@ def uuid(): class XMLCache(object): - def __init__(self, paths, prefixes): + def __init__(self, paths, prefixes, use_author_sort): if DEBUG: debug_print('Building XMLCache...') pprint(paths) self.paths = paths self.prefixes = prefixes + self.use_author_sort = use_author_sort # Parse XML files {{{ parser = etree.XMLParser(recover=True) @@ -434,7 +435,10 @@ class XMLCache(object): if not ts: ts = title_sort(title) record.set('titleSorter', ts) - record.set('author', authors_to_string(book.authors)) + if self.use_author_sort and book.author_sort is not None: + record.set('author', book.author_sort) + else: + record.set('author', authors_to_string(book.authors)) ext = os.path.splitext(path)[1] if ext: ext = ext[1:].lower() diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index 2f01b8dd41..d899c8e995 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -80,6 +80,7 @@ class Device(DeviceConfig, DevicePlugin): SUPPORTS_SUB_DIRS = False MUST_READ_METADATA = False + SUPPORTS_USE_AUTHOR_SORT = False EBOOK_DIR_MAIN = '' EBOOK_DIR_CARD_A = '' diff --git a/src/calibre/devices/usbms/deviceconfig.py b/src/calibre/devices/usbms/deviceconfig.py index a8220261f3..5edefff743 100644 --- a/src/calibre/devices/usbms/deviceconfig.py +++ b/src/calibre/devices/usbms/deviceconfig.py @@ -32,6 +32,8 @@ class DeviceConfig(object): help=_('Place files in sub directories if the device supports them')) c.add_opt('read_metadata', default=True, help=_('Read metadata from files on device')) + c.add_opt('use_author_sort', default=False, + help=_('Use author sort instead of author')) c.add_opt('save_template', default=cls._default_save_template(), help=_('Template to control how books are saved')) c.add_opt('extra_customization', @@ -47,7 +49,8 @@ class DeviceConfig(object): def config_widget(cls): from calibre.gui2.device_drivers.configwidget import ConfigWidget cw = ConfigWidget(cls.settings(), cls.FORMATS, cls.SUPPORTS_SUB_DIRS, - cls.MUST_READ_METADATA, cls.EXTRA_CUSTOMIZATION_MESSAGE) + cls.MUST_READ_METADATA, cls.SUPPORTS_USE_AUTHOR_SORT, + cls.EXTRA_CUSTOMIZATION_MESSAGE) return cw @classmethod @@ -58,6 +61,8 @@ class DeviceConfig(object): proxy['use_subdirs'] = config_widget.use_subdirs() if not cls.MUST_READ_METADATA: proxy['read_metadata'] = config_widget.read_metadata() + if cls.SUPPORTS_USE_AUTHOR_SORT: + proxy['use_author_sort'] = config_widget.use_author_sort() if cls.EXTRA_CUSTOMIZATION_MESSAGE: ec = unicode(config_widget.opt_extra_customization.text()).strip() if not ec: diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index 6f558b9b34..2fc8b0d814 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -299,7 +299,7 @@ class USBMS(CLI, Device): def replfunc(match): if match.group(1) in ['title', 'series', 'series_index', 'isbn']: return '(?P<' + match.group(1) + '>.+?)' - elif match.group(1) == 'authors': + elif match.group(1) in ['authors', 'author_sort']: return '(?P.+?)' else: return '(.+?)' diff --git a/src/calibre/ebooks/metadata/epub.py b/src/calibre/ebooks/metadata/epub.py index d74ed37f66..b3980451bf 100644 --- a/src/calibre/ebooks/metadata/epub.py +++ b/src/calibre/ebooks/metadata/epub.py @@ -182,7 +182,7 @@ def get_metadata(stream, extract_cover=True): def get_quick_metadata(stream): return get_metadata(stream, False) -def set_metadata(stream, mi, apply_null=False): +def set_metadata(stream, mi, apply_null=False, update_timestamp=False): stream.seek(0) reader = OCFZipReader(stream, root=os.getcwdu()) mi = MetaInformation(mi) @@ -196,6 +196,8 @@ def set_metadata(stream, mi, apply_null=False): reader.opf.tags = [] if not getattr(mi, 'isbn', None): reader.opf.isbn = None + if update_timestamp and mi.timestamp is not None: + reader.opf.timestamp = mi.timestamp newopf = StringIO(reader.opf.render()) safe_replace(stream, reader.container[OPF.MIMETYPE], newopf) diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index 367212da1a..46924cad1f 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -920,7 +920,7 @@ class OPF(object): for attr in ('title', 'authors', 'author_sort', 'title_sort', 'publisher', 'series', 'series_index', 'rating', 'isbn', 'language', 'tags', 'category', 'comments', - 'pubdate','timestamp'): + 'pubdate'): val = getattr(mi, attr, None) if val is not None and val != [] and val != (None, None): setattr(self, attr, val) diff --git a/src/calibre/gui2/device_drivers/configwidget.py b/src/calibre/gui2/device_drivers/configwidget.py index 585eed30df..3d9c9ab2ee 100644 --- a/src/calibre/gui2/device_drivers/configwidget.py +++ b/src/calibre/gui2/device_drivers/configwidget.py @@ -11,7 +11,8 @@ from calibre.gui2.device_drivers.configwidget_ui import Ui_ConfigWidget class ConfigWidget(QWidget, Ui_ConfigWidget): def __init__(self, settings, all_formats, supports_subdirs, - must_read_metadata, extra_customization_message): + must_read_metadata, supports_use_author_sort, + extra_customization_message): QWidget.__init__(self) Ui_ConfigWidget.__init__(self) @@ -38,6 +39,10 @@ class ConfigWidget(QWidget, Ui_ConfigWidget): self.opt_read_metadata.setChecked(self.settings.read_metadata) else: self.opt_read_metadata.hide() + if supports_use_author_sort: + self.opt_use_author_sort.setChecked(self.settings.use_author_sort) + else: + self.opt_use_author_sort.hide() if extra_customization_message: self.extra_customization_label.setText(extra_customization_message) if settings.extra_customization: @@ -69,3 +74,6 @@ class ConfigWidget(QWidget, Ui_ConfigWidget): def read_metadata(self): return self.opt_read_metadata.isChecked() + + def use_author_sort(self): + return self.opt_use_author_sort.isChecked() diff --git a/src/calibre/gui2/device_drivers/configwidget.ui b/src/calibre/gui2/device_drivers/configwidget.ui index d007599424..497ba43259 100644 --- a/src/calibre/gui2/device_drivers/configwidget.ui +++ b/src/calibre/gui2/device_drivers/configwidget.ui @@ -90,7 +90,14 @@ - + + + + Use author sort for author + + + + Extra customization @@ -103,10 +110,10 @@ - + - + Save &template: @@ -116,7 +123,7 @@ - + diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index a3c51c287f..699978a92f 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -163,8 +163,7 @@ class ToolbarMixin(object): # {{{ self.convert_menu = cm pm = QMenu() - ap = self.action_preferences - pm.addAction(ap) + pm.addAction(QIcon(I('config.svg')), _('Preferences'), self.do_config) pm.addAction(QIcon(I('wizard.svg')), _('Run welcome wizard'), self.run_wizard) self.action_preferences.setMenu(pm) diff --git a/src/calibre/gui2/metadata.py b/src/calibre/gui2/metadata.py index daed69725c..cd4cc1be41 100644 --- a/src/calibre/gui2/metadata.py +++ b/src/calibre/gui2/metadata.py @@ -84,12 +84,12 @@ class DownloadMetadata(Thread): if mi.isbn: args['isbn'] = mi.isbn else: - if not mi.title: + if not mi.title or mi.title == _('Unknown'): self.failures[id] = \ (str(id), _('Book has neither title nor ISBN')) continue args['title'] = mi.title - if mi.authors: + if mi.authors and mi.authors[0] != _('Unknown'): args['author'] = mi.authors[0] if self.key: args['isbndb_key'] = self.key