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''%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'),
]