elements do not have ids')
- ids = [x for x in ids if x is not None]
+ id = rec.get('id', None)
+ ids.add(id)
+ # ids cannot contain None, so no reason to check
+
playlist = self.get_or_create_playlist(bl_index, category)
- playlist_ids = []
+ # Reduce ids to books not already in the playlist
for item in playlist:
id_ = item.get('id', None)
if id_ is not None:
- playlist_ids.append(id_)
- for item in list(playlist):
- playlist.remove(item)
-
- extra_ids = [x for x in playlist_ids if x not in ids]
- for id_ in ids + extra_ids:
+ ids.discard(id_)
+ # Add the books in ids that were not already in the playlist
+ for id_ in ids:
item = playlist.makeelement(
'{%s}item'%self.namespaces[bl_index],
nsmap=playlist.nsmap, attrib={'id':id_})
@@ -416,11 +443,23 @@ class XMLCache(object):
root.append(ans)
return ans
- def update_text_record(self, record, book, path, bl_index):
+ def check_timestamp(self, record, book, path):
+ '''
+ Checks the timestamp in the Sony DB against the file. If different,
+ return the file timestamp. Otherwise return None.
+ '''
timestamp = os.path.getmtime(path)
date = strftime(timestamp)
if date != record.get('date', None):
- record.set('date', date)
+ return date
+ return None
+
+ def update_text_record(self, record, book, date, path, bl_index):
+ '''
+ Update the Sony database from the book. This is done if the timestamp in
+ the db differs from the timestamp on the file.
+ '''
+ record.set('date', date)
record.set('size', str(os.stat(path).st_size))
title = book.title if book.title else _('Unknown')
record.set('title', title)
diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py
index 0f5c848c1a..6204ee3ccb 100644
--- a/src/calibre/devices/usbms/books.py
+++ b/src/calibre/devices/usbms/books.py
@@ -134,9 +134,16 @@ class CollectionsBookList(BookList):
def get_collections(self, collection_attributes):
collections = {}
series_categories = set([])
+ # This map of sets is used to avoid linear searches when testing for
+ # book equality
+ collections_lpaths = {}
for book in self:
- # The default: leave the book in all existing collections. Do not
- # add any new ones.
+ # Make sure we can identify this book via the lpath
+ lpath = getattr(book, 'lpath', None)
+ if lpath is None:
+ continue
+ # Decide how we will build the collections. The default: leave the
+ # book in all existing collections. Do not add any new ones.
attrs = ['device_collections']
if getattr(book, '_new_book', False):
if prefs['preserve_user_collections']:
@@ -163,11 +170,12 @@ class CollectionsBookList(BookList):
continue
if category not in collections:
collections[category] = []
- if book not in collections[category]:
+ collections_lpaths[category] = set()
+ if lpath not in collections_lpaths[category]:
+ collections_lpaths[category].add(lpath)
collections[category].append(book)
if attr == 'series':
series_categories.add(category)
-
# Sort collections
for category, books in collections.items():
def tgetter(x):
diff --git a/src/calibre/ebooks/chm/input.py b/src/calibre/ebooks/chm/input.py
index a3a49296d2..820178408c 100644
--- a/src/calibre/ebooks/chm/input.py
+++ b/src/calibre/ebooks/chm/input.py
@@ -92,7 +92,7 @@ class CHMInput(InputFormatPlugin):
metadata.add('identifier', mi.isbn, attrib={'scheme':'ISBN'})
if not metadata.language:
oeb.logger.warn(u'Language not specified')
- metadata.add('language', get_lang())
+ metadata.add('language', get_lang().replace('_', '-'))
if not metadata.creator:
oeb.logger.warn('Creator not specified')
metadata.add('creator', _('Unknown'))
diff --git a/src/calibre/ebooks/compression/palmdoc.c b/src/calibre/ebooks/compression/palmdoc.c
index b85a404eb6..4d913dfd2b 100644
--- a/src/calibre/ebooks/compression/palmdoc.c
+++ b/src/calibre/ebooks/compression/palmdoc.c
@@ -151,6 +151,7 @@ cpalmdoc_do_compress(buffer *b, char *output) {
for (j=0; j < temp.len; j++) *(output++) = (char)temp.data[j];
}
}
+ PyMem_Free(temp.data);
return output - head;
}
@@ -168,7 +169,9 @@ cpalmdoc_compress(PyObject *self, PyObject *args) {
for (j = 0; j < input_len; j++)
b.data[j] = (_input[j] < 0) ? _input[j]+256 : _input[j];
b.len = input_len;
- output = (char *)PyMem_Malloc(sizeof(char) * b.len);
+ // Make the output buffer larger than the input as sometimes
+ // compression results in a larger block
+ output = (char *)PyMem_Malloc(sizeof(char) * (int)(1.25*b.len));
if (output == NULL) return PyErr_NoMemory();
j = cpalmdoc_do_compress(&b, output);
if ( j == 0) return PyErr_NoMemory();
diff --git a/src/calibre/ebooks/html/input.py b/src/calibre/ebooks/html/input.py
index 73fd020d7b..73bc22be66 100644
--- a/src/calibre/ebooks/html/input.py
+++ b/src/calibre/ebooks/html/input.py
@@ -329,7 +329,7 @@ class HTMLInput(InputFormatPlugin):
metadata.add('identifier', mi.isbn, attrib={'scheme':'ISBN'})
if not metadata.language:
oeb.logger.warn(u'Language not specified')
- metadata.add('language', get_lang())
+ metadata.add('language', get_lang().replace('_', '-'))
if not metadata.creator:
oeb.logger.warn('Creator not specified')
metadata.add('creator', self.oeb.translate(__('Unknown')))
diff --git a/src/calibre/ebooks/metadata/fetch.py b/src/calibre/ebooks/metadata/fetch.py
index 8b82d3c972..cb75d93f59 100644
--- a/src/calibre/ebooks/metadata/fetch.py
+++ b/src/calibre/ebooks/metadata/fetch.py
@@ -313,6 +313,8 @@ def search(title=None, author=None, publisher=None, isbn=None, isbndb_key=None,
def sort_func(x, y):
def cleanup_title(s):
+ if s is None:
+ s = _('Unknown')
s = s.strip().lower()
s = prefix_pat.sub(' ', s)
s = trailing_paren_pat.sub('', s)
diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py
index 579398d3b0..0bb0c570ed 100644
--- a/src/calibre/ebooks/metadata/opf2.py
+++ b/src/calibre/ebooks/metadata/opf2.py
@@ -1069,7 +1069,8 @@ class OPFCreator(MetaInformation):
dc_attrs={'id':__appname__+'_id'}))
if getattr(self, 'pubdate', None) is not None:
a(DC_ELEM('date', self.pubdate.isoformat()))
- a(DC_ELEM('language', self.language if self.language else get_lang()))
+ a(DC_ELEM('language', self.language if self.language else
+ get_lang().replace('_', '-')))
if self.comments:
a(DC_ELEM('description', self.comments))
if self.publisher:
@@ -1194,7 +1195,8 @@ def metadata_to_opf(mi, as_string=True):
factory(DC('identifier'), mi.isbn, scheme='ISBN')
if mi.rights:
factory(DC('rights'), mi.rights)
- factory(DC('language'), mi.language if mi.language and mi.language.lower() != 'und' else get_lang())
+ factory(DC('language'), mi.language if mi.language and mi.language.lower()
+ != 'und' else get_lang().replace('_', '-'))
if mi.tags:
for tag in mi.tags:
factory(DC('subject'), tag)
diff --git a/src/calibre/ebooks/oeb/reader.py b/src/calibre/ebooks/oeb/reader.py
index ebe6e78d08..d7d7bbf725 100644
--- a/src/calibre/ebooks/oeb/reader.py
+++ b/src/calibre/ebooks/oeb/reader.py
@@ -131,7 +131,7 @@ class OEBReader(object):
stream = cStringIO.StringIO(etree.tostring(opf))
mi = MetaInformation(OPF(stream))
if not mi.language:
- mi.language = get_lang()
+ mi.language = get_lang().replace('_', '-')
self.oeb.metadata.add('language', mi.language)
if not mi.title:
mi.title = self.oeb.translate(__('Unknown'))
diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py
index 6d33f95184..584796231a 100644
--- a/src/calibre/gui2/device.py
+++ b/src/calibre/gui2/device.py
@@ -1416,7 +1416,6 @@ class DeviceMixin(object): # {{{
# the application_id, which is really the db key, but as this can
# accidentally match across libraries we also verify the title. The
# db_id exists on Sony devices. Fallback is title and author match
- resend_metadata = False
for booklist in booklists:
for book in booklist:
if getattr(book, 'uuid', None) in self.db_book_uuid_cache:
@@ -1433,12 +1432,10 @@ class DeviceMixin(object): # {{{
if getattr(book, 'application_id', None) in d['db_ids']:
book.in_library = True
book.smart_update(d['db_ids'][book.application_id])
- resend_metadata = True
continue
if book.db_id in d['db_ids']:
book.in_library = True
book.smart_update(d['db_ids'][book.db_id])
- resend_metadata = True
continue
if book.authors:
# Compare against both author and author sort, because
@@ -1448,21 +1445,13 @@ class DeviceMixin(object): # {{{
if book_authors in d['authors']:
book.in_library = True
book.smart_update(d['authors'][book_authors])
- resend_metadata = True
elif book_authors in d['author_sort']:
book.in_library = True
book.smart_update(d['author_sort'][book_authors])
- resend_metadata = True
# 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)
- resend_metadata = True
-
- if resend_metadata:
- # Correct the metadata cache on device.
- if self.device_manager.is_device_connected:
- self.device_manager.sync_booklists(None, booklists)
# }}}
diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py
index 8be3774203..5a4eaec9d7 100644
--- a/src/calibre/gui2/dialogs/metadata_single.py
+++ b/src/calibre/gui2/dialogs/metadata_single.py
@@ -103,7 +103,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
if _file:
_file = os.path.abspath(_file)
if not os.access(_file, os.R_OK):
- d = error_dialog(self.window, _('Cannot read'),
+ d = error_dialog(self, _('Cannot read'),
_('You do not have permission to read the file: ') + _file)
d.exec_()
return
@@ -112,14 +112,14 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
cf = open(_file, "rb")
cover = cf.read()
except IOError, e:
- d = error_dialog(self.window, _('Error reading file'),
+ d = error_dialog(self, _('Error reading file'),
_("There was an error reading from file:
") + _file + "
"+str(e))
d.exec_()
if cover:
pix = QPixmap()
pix.loadFromData(cover)
if pix.isNull():
- d = error_dialog(self.window,
+ d = error_dialog(self,
_("Not a valid picture"),
_file + _(" is not a valid picture"))
d.exec_()
@@ -162,7 +162,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.formats_changed = True
added = True
if bad_perms:
- error_dialog(self.window, _('No permission'),
+ error_dialog(self, _('No permission'),
_('You do not have '
'permission to read the following files:'),
det_msg='\n'.join(bad_perms), show=True)