diff --git a/installer/linux/freeze.py b/installer/linux/freeze.py index c381041675..a6151c4931 100644 --- a/installer/linux/freeze.py +++ b/installer/linux/freeze.py @@ -81,7 +81,8 @@ def freeze(): 'PyQt4.QtScript.so', 'PyQt4.QtSql.so', 'PyQt4.QtTest.so', 'qt', 'glib', 'gobject'] - packages = ['calibre', 'encodings', 'cherrypy', 'cssutils', 'xdg'] + packages = ['calibre', 'encodings', 'cherrypy', 'cssutils', 'xdg', + 'dateutil'] includes += ['calibre.web.feeds.recipes.'+r for r in recipe_modules] diff --git a/installer/osx/freeze.py b/installer/osx/freeze.py index 3ec24d3aba..dbaad72748 100644 --- a/installer/osx/freeze.py +++ b/installer/osx/freeze.py @@ -342,6 +342,7 @@ def main(): 'calibre.ebooks.lrf.any.*', 'calibre.ebooks.lrf.feeds.*', 'keyword', 'codeop', 'pydoc', 'readline', 'BeautifulSoup', 'calibre.ebooks.lrf.fonts.prs500.*', + 'dateutil', ], 'packages' : ['PIL', 'Authorization', 'lxml'], 'excludes' : ['IPython'], diff --git a/installer/windows/freeze.py b/installer/windows/freeze.py index ab58fb669d..56486f6bd5 100644 --- a/installer/windows/freeze.py +++ b/installer/windows/freeze.py @@ -179,7 +179,8 @@ def main(args=sys.argv): 'calibre.ebooks.lrf.fonts.prs500.*', 'PyQt4.QtWebKit', 'PyQt4.QtNetwork', ], - 'packages' : ['PIL', 'lxml', 'cherrypy'], + 'packages' : ['PIL', 'lxml', 'cherrypy', + 'dateutil'], 'excludes' : ["Tkconstants", "Tkinter", "tcl", "_imagingtk", "ImageTk", "FixTk" ], diff --git a/src/calibre/ebooks/metadata/__init__.py b/src/calibre/ebooks/metadata/__init__.py index eabd082142..d9b0514362 100644 --- a/src/calibre/ebooks/metadata/__init__.py +++ b/src/calibre/ebooks/metadata/__init__.py @@ -192,7 +192,8 @@ class MetaInformation(object): for attr in ('author_sort', 'title_sort', 'comments', 'category', 'publisher', 'series', 'series_index', 'rating', 'isbn', 'tags', 'cover_data', 'application_id', 'guide', - 'manifest', 'spine', 'toc', 'cover', 'language', 'book_producer'): + 'manifest', 'spine', 'toc', 'cover', 'language', + 'book_producer', 'timestamp'): if hasattr(mi, attr): setattr(ans, attr, getattr(mi, attr)) @@ -217,7 +218,7 @@ class MetaInformation(object): for x in ('author_sort', 'title_sort', 'comments', 'category', 'publisher', 'series', 'series_index', 'rating', 'isbn', 'language', 'application_id', 'manifest', 'toc', 'spine', 'guide', 'cover', - 'book_producer', + 'book_producer', 'timestamp' ): setattr(self, x, getattr(mi, x, None)) @@ -235,7 +236,8 @@ class MetaInformation(object): for attr in ('author_sort', 'title_sort', 'comments', 'category', 'publisher', 'series', 'series_index', 'rating', 'isbn', 'application_id', 'manifest', 'spine', 'toc', - 'cover', 'language', 'guide', 'book_producer'): + 'cover', 'language', 'guide', 'book_producer', + 'timestamp'): if hasattr(mi, attr): val = getattr(mi, attr) if val is not None: @@ -276,6 +278,8 @@ class MetaInformation(object): ans += u'Series : '+unicode(self.series) + ' #%s\n'%self.format_series_index() if self.language: ans += u'Language : ' + unicode(self.language) + u'\n' + if self.timestamp is not None: + ans += u'Timestamp : ' + self.timestamp.isoformat(' ') return ans.strip() def to_html(self): @@ -289,12 +293,12 @@ class MetaInformation(object): if self.series: ans += [(_('Series'), unicode(self.series)+ ' #%s'%self.format_series_index())] ans += [(_('Language'), unicode(self.language))] + if self.timestamp is not None: + ans += [(_('Timestamp'), unicode(self.timestamp.isoformat(' ')))] for i, x in enumerate(ans): ans[i] = u'%s%s'%x return u'%s
'%u'\n'.join(ans) - - def __str__(self): return self.__unicode__().encode('utf-8') diff --git a/src/calibre/ebooks/metadata/meta.py b/src/calibre/ebooks/metadata/meta.py index d8116b33d3..1241238f26 100644 --- a/src/calibre/ebooks/metadata/meta.py +++ b/src/calibre/ebooks/metadata/meta.py @@ -31,8 +31,14 @@ def metadata_from_formats(formats): mi = MetaInformation(None, None) formats.sort(cmp=lambda x,y: cmp(METADATA_PRIORITIES[path_to_ext(x)], METADATA_PRIORITIES[path_to_ext(y)])) - for path in formats: - ext = path_to_ext(path) + extensions = list(map(path_to_ext, formats)) + if 'opf' in extensions: + opf = formats[extensions.index('opf')] + mi2 = opf_metadata(opf) + if mi2 is not None and mi2.title: + return mi2 + + for path, ext in zip(formats, extensions): stream = open(path, 'rb') try: mi.smart_update(get_metadata(stream, stream_type=ext, use_libprs_metadata=True)) diff --git a/src/calibre/ebooks/metadata/opf.xml b/src/calibre/ebooks/metadata/opf.xml index 94a8f63b3c..9dab4efbf4 100644 --- a/src/calibre/ebooks/metadata/opf.xml +++ b/src/calibre/ebooks/metadata/opf.xml @@ -10,7 +10,7 @@ ${author} ${'%s (%s)'%(__appname__, __version__)} [http://${__appname__}.kovidgoyal.net] ${mi.application_id} - + ${mi.timestamp.isoformat()} ${mi.language if mi.language else 'UND'} ${mi.category} ${mi.comments} diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index 32ba2cb45a..2e3b5ff047 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -12,6 +12,7 @@ from urllib import unquote from urlparse import urlparse from lxml import etree +from dateutil import parser from calibre.ebooks.chardet import xml_to_unicode from calibre import relpath @@ -436,6 +437,7 @@ class OPF(object): series = MetadataField('series', is_dc=False) series_index = MetadataField('series_index', is_dc=False, formatter=int, none_is=1) rating = MetadataField('rating', is_dc=False, formatter=int) + timestamp = MetadataField('date', formatter=parser.parse) def __init__(self, stream, basedir=os.getcwdu(), unquote_urls=True): diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index a7f522cad0..00da8f3e37 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -380,8 +380,10 @@ class LibraryDatabase2(LibraryDatabase): return row[loc] for prop in ('author_sort', 'authors', 'comment', 'comments', 'isbn', - 'publisher', 'rating', 'series', 'series_index', 'tags', 'title'): - setattr(self, prop, functools.partial(get_property, loc=FIELD_MAP['comments' if prop == 'comment' else prop])) + 'publisher', 'rating', 'series', 'series_index', 'tags', + 'title', 'timestamp'): + setattr(self, prop, functools.partial(get_property, + loc=FIELD_MAP['comments' if prop == 'comment' else prop])) def initialize_database(self): from calibre.resources import metadata_sqlite @@ -590,6 +592,7 @@ class LibraryDatabase2(LibraryDatabase): mi.author_sort = self.author_sort(idx, index_is_id=index_is_id) mi.comments = self.comments(idx, index_is_id=index_is_id) mi.publisher = self.publisher(idx, index_is_id=index_is_id) + mi.timestamp = self.timestamp(idx, index_is_id=index_is_id) tags = self.tags(idx, index_is_id=index_is_id) if tags: mi.tags = [i.strip() for i in tags.split(',')] @@ -884,6 +887,8 @@ class LibraryDatabase2(LibraryDatabase): self.set_isbn(id, mi.isbn, notify=False) if mi.series_index and mi.series_index > 0: self.set_series_index(id, mi.series_index, notify=False) + if getattr(mi, 'timestamp', None) is not None: + self.set_timestamp(id, mi.timestamp, notify=False) self.set_path(id, True) self.notify('metadata', [id]) @@ -1203,6 +1208,8 @@ class LibraryDatabase2(LibraryDatabase): self.set_metadata(id, mi) for path in formats: ext = os.path.splitext(path)[1][1:].lower() + if ext == 'opf': + continue stream = open(path, 'rb') self.add_format(id, ext, stream, index_is_id=True) self.conn.commit() @@ -1392,10 +1399,11 @@ books_series_link feeds f = open(os.path.join(base, sanitize_file_name(name)+'.opf'), 'wb') if not mi.authors: mi.authors = [_('Unknown')] - cdata = self.cover(id, index_is_id=True) - cname = sanitize_file_name(name)+'.jpg' - open(os.path.join(base, cname), 'wb').write(cdata) - mi.cover = cname + cdata = self.cover(int(id), index_is_id=True) + if cdata is not None: + cname = sanitize_file_name(name)+'.jpg' + open(os.path.join(base, cname), 'wb').write(cdata) + mi.cover = cname opf = OPFCreator(base, mi) opf.render(f) f.close() @@ -1472,7 +1480,7 @@ books_series_link feeds if not ext: continue ext = ext[1:].lower() - if ext not in BOOK_EXTENSIONS: + if ext not in BOOK_EXTENSIONS and ext != 'opf': continue formats.append(path) yield formats diff --git a/src/calibre/trac/plugins/download.py b/src/calibre/trac/plugins/download.py index 63bb4006e1..e63a7d8d0d 100644 --- a/src/calibre/trac/plugins/download.py +++ b/src/calibre/trac/plugins/download.py @@ -35,6 +35,7 @@ class Distribution(object): ('xdg-utils', '1.0.2', 'xdg-utils', 'xdg-utils', 'xdg-utils'), ('dbus-python', '0.82.2', 'dbus-python', 'python-dbus', 'dbus-python'), ('lxml', '2.0.5', 'lxml', 'python-lxml', 'python-lxml'), + ('python-dateutil', '1.4.1', 'python-dateutil', 'python-dateutil', 'python-dateutil') ('BeautifulSoup', '3.0.5', 'beautifulsoup', 'python-beautifulsoup', 'python-BeautifulSoup'), ('help2man', '1.36.4', 'help2man', 'help2man', 'help2man'), ]