diff --git a/session.vim b/session.vim
index 56705f9528..6b965cff2f 100644
--- a/session.vim
+++ b/session.vim
@@ -13,4 +13,11 @@ base_dir = os.path.join(src_dir, 'calibre')
vipy.session.initialize(project_name='calibre', src_dir=src_dir,
project_dir=project_dir, base_dir=base_dir)
+
+def recipe_title_callback(raw):
+ return eval(raw.decode('utf-8'))
+
+vipy.session.add_content_browser('.r', ',r', 'Recipe',
+ vipy.session.glob_based_iterator(os.path.join(project_dir, 'resources', 'recipes', '*.recipe')),
+ vipy.session.regexp_based_matcher(r'title\s*=\s*(?P
.+)', 'title', recipe_title_callback))
EOFPY
diff --git a/src/calibre/devices/cybookg3/driver.py b/src/calibre/devices/cybookg3/driver.py
index e1d8aaa0c7..04e5e7012c 100644
--- a/src/calibre/devices/cybookg3/driver.py
+++ b/src/calibre/devices/cybookg3/driver.py
@@ -47,25 +47,24 @@ class CYBOOKG3(USBMS):
DELETE_EXTS = ['.mbp', '.dat', '_6090.t2b']
SUPPORTS_SUB_DIRS = True
- def upload_books(self, files, metadatas, ids, on_card=None,
- end_session=True):
+ def upload_books(self, files, names, on_card=None, end_session=True,
+ metadata=None):
path = self._sanity_check(on_card, files)
paths = []
- metadatas = iter(metadatas)
- ids = iter(ids)
+ names = iter(names)
+ metadata = iter(metadata)
for i, infile in enumerate(files):
- mdata, id = metadatas.next(), ids.next()
- ext = os.path.splitext(infile)[1]
- filepath = self.create_upload_path(path, mdata, ext, id)
+ mdata, fname = metadata.next(), names.next()
+ filepath = self.create_upload_path(path, mdata, fname)
paths.append(filepath)
self.put_file(infile, filepath, replace_file=True)
coverdata = None
- cover = mdata.cover
+ cover = mdata.get('cover', None)
if cover:
coverdata = cover[2]
diff --git a/src/calibre/devices/iriver/driver.py b/src/calibre/devices/iriver/driver.py
index f8e7d41600..7373996213 100644
--- a/src/calibre/devices/iriver/driver.py
+++ b/src/calibre/devices/iriver/driver.py
@@ -35,7 +35,7 @@ class IRIVER_STORY(USBMS):
SUPPORTS_SUB_DIRS = True
- def windows_sort_drives(self, drives):
+ def windows_open_callback(self, drives):
main = drives.get('main', None)
card = drives.get('carda', None)
if card and main and card < main:
diff --git a/src/calibre/devices/jetbook/driver.py b/src/calibre/devices/jetbook/driver.py
index a55e76155f..6a09c7c345 100644
--- a/src/calibre/devices/jetbook/driver.py
+++ b/src/calibre/devices/jetbook/driver.py
@@ -15,7 +15,7 @@ from itertools import cycle
from calibre.devices.usbms.driver import USBMS
from calibre.utils.filenames import ascii_filename as sanitize
-from calibre.ebooks.metadata import authors_to_string, string_to_authors
+from calibre.ebooks.metadata import string_to_authors
class JETBOOK(USBMS):
name = 'Ectaco JetBook Device Interface'
@@ -50,22 +50,23 @@ class JETBOOK(USBMS):
r'(?P.+)#(?P.+)'
)
- def upload_books(self, files, metadatas, ids, on_card=None,
- end_session=True):
- path = self._sanity_check(on_card, files)
+ def upload_books(self, files, names, on_card=False, end_session=True,
+ metadata=None):
+
+ base_path = self._sanity_check(on_card, files)
paths = []
- metadatas = iter(metadatas)
- ids = iter(ids)
+ names = iter(names)
+ metadata = iter(metadata)
for i, infile in enumerate(files):
- mdata, id = metadatas.next(), ids.next()
- ext = os.path.splitext(infile)[1]
- path = self.create_upload_path(path, mdata, ext, id)
+ mdata, fname = metadata.next(), names.next()
+ path = os.path.dirname(self.create_upload_path(base_path, mdata, fname))
- author = sanitize(authors_to_string(mdata.authors)).replace(' ', '_')
- title = sanitize(mdata.title).replace(' ', '_')
- fname = '%s#%s%s' % (author, title, ext)
+ author = sanitize(mdata.get('authors','Unknown')).replace(' ', '_')
+ title = sanitize(mdata.get('title', 'Unknown')).replace(' ', '_')
+ fileext = os.path.splitext(os.path.basename(fname))[1]
+ fname = '%s#%s%s' % (author, title, fileext)
filepath = os.path.join(path, fname)
paths.append(filepath)
diff --git a/src/calibre/devices/prs500/books.py b/src/calibre/devices/prs500/books.py
index 382dcf135d..5eb8d7f011 100644
--- a/src/calibre/devices/prs500/books.py
+++ b/src/calibre/devices/prs500/books.py
@@ -9,7 +9,6 @@ from base64 import b64decode as decode
from base64 import b64encode as encode
import re
-from calibre.ebooks.metadata import authors_to_string
from calibre.devices.interface import BookList as _BookList
from calibre.devices import strftime, strptime
@@ -263,9 +262,9 @@ class BookList(_BookList):
cid = self.max_id()+1
sourceid = str(self[0].sourceid) if len(self) else "1"
attrs = {
- "title" : info.title,
- 'titleSorter' : sortable_title(info.title),
- "author" : authors_to_string(info.authors), \
+ "title" : info["title"],
+ 'titleSorter' : sortable_title(info['title']),
+ "author" : info["authors"] if info['authors'] else 'Unknown', \
"page":"0", "part":"0", "scale":"0", \
"sourceid":sourceid, "id":str(cid), "date":"", \
"mime":mime, "path":name, "size":str(size)
@@ -274,7 +273,7 @@ class BookList(_BookList):
node.setAttributeNode(self.document.createAttribute(attr))
node.setAttribute(attr, attrs[attr])
try:
- w, h, data = info.cover
+ w, h, data = info["cover"]
except TypeError:
w, h, data = None, None, None
@@ -291,7 +290,10 @@ class BookList(_BookList):
book.datetime = ctime
self.append(book)
self.set_next_id(cid+1)
- self.set_playlists(book.id, info.tags)
+ if self.prefix and info.has_key('tags'): # Playlists only supportted in main memory
+ if info.has_key('tag order'):
+ self.tag_order.update(info['tag order'])
+ self.set_playlists(book.id, info['tags'])
def playlist_by_title(self, title):
diff --git a/src/calibre/devices/prs500/driver.py b/src/calibre/devices/prs500/driver.py
index 616a1c387d..8d2c4cc9d4 100644
--- a/src/calibre/devices/prs500/driver.py
+++ b/src/calibre/devices/prs500/driver.py
@@ -867,14 +867,14 @@ class PRS500(DeviceConfig, DevicePlugin):
self.upload_book_list(booklists[1], end_session=False)
@safe
- def upload_books(self, files, metadatas, ids, on_card=None,
- end_session=True):
+ def upload_books(self, files, names, on_card=False, end_session=True,
+ metadata=None):
card = self.card(end_session=False)
prefix = card + '/' + self.CARD_PATH_PREFIX +'/' if on_card else '/Data/media/books/'
if on_card and not self._exists(prefix)[0]:
self.mkdir(prefix[:-1], False)
paths, ctimes = [], []
- names = iter([m.title for m in metatdatas])
+ names = iter(names)
infiles = [file if hasattr(file, 'read') else open(file, 'rb') for file in files]
for f in infiles: f.seek(0, 2)
sizes = [f.tell() for f in infiles]
diff --git a/src/calibre/devices/prs505/books.py b/src/calibre/devices/prs505/books.py
index 4b8a952816..6e268e734a 100644
--- a/src/calibre/devices/prs505/books.py
+++ b/src/calibre/devices/prs505/books.py
@@ -8,7 +8,7 @@ import xml.dom.minidom as dom
from base64 import b64decode as decode
from base64 import b64encode as encode
-from calibre.ebooks.metadata import authors_to_string
+
from calibre.devices.interface import BookList as _BookList
from calibre.devices import strftime as _strftime
from calibre.devices import strptime
@@ -194,9 +194,9 @@ class BookList(_BookList):
except:
sourceid = '1'
attrs = {
- "title" : info.title,
- 'titleSorter' : sortable_title(info.title),
- "author" : authors_to_string(info.authors),
+ "title" : info["title"],
+ 'titleSorter' : sortable_title(info['title']),
+ "author" : info["authors"] if info['authors'] else _('Unknown'),
"page":"0", "part":"0", "scale":"0", \
"sourceid":sourceid, "id":str(cid), "date":"", \
"mime":mime, "path":name, "size":str(size)
@@ -205,7 +205,7 @@ class BookList(_BookList):
node.setAttributeNode(self.document.createAttribute(attr))
node.setAttribute(attr, attrs[attr])
try:
- w, h, data = info.cover
+ w, h, data = info["cover"]
except TypeError:
w, h, data = None, None, None
@@ -221,7 +221,10 @@ class BookList(_BookList):
book = Book(node, self.mountpath, [], prefix=self.prefix)
book.datetime = ctime
self.append(book)
- self.set_tags(book, info.tags)
+ if info.has_key('tags'):
+ if info.has_key('tag order'):
+ self.tag_order.update(info['tag order'])
+ self.set_tags(book, info['tags'])
def _delete_book(self, node):
nid = node.getAttribute('id')
diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py
index 17de805756..ab61f76b61 100644
--- a/src/calibre/devices/prs505/driver.py
+++ b/src/calibre/devices/prs505/driver.py
@@ -114,22 +114,20 @@ class PRS505(CLI, Device):
self.report_progress(1.0, _('Getting list of books on device...'))
return bl
- def upload_books(self, files, metadatas, ids, on_card=None,
- end_session=True):
+ def upload_books(self, files, names, on_card=None, end_session=True,
+ metadata=None):
path = self._sanity_check(on_card, files)
- paths = []
- metadatas = iter(metadatas)
- ids = iter(ids)
-
+ paths, ctimes, sizes = [], [], []
+ names = iter(names)
+ metadata = iter(metadata)
for i, infile in enumerate(files):
- mdata, id = metadatas.next(), ids.next()
- ext = os.path.splitext(infile)[1]
- filepath = self.create_upload_path(path, mdata, ext, id)
- paths.append(filepath)
+ mdata, fname = metadata.next(), names.next()
+ filepath = self.create_upload_path(path, mdata, fname)
- self.put_file(infile, filepath, replace_file=True)
+ paths.append(filepath)
+ self.put_file(infile, paths[-1], replace_file=True)
ctimes.append(os.path.getctime(paths[-1]))
sizes.append(os.stat(paths[-1]).st_size)
diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py
index 5effa0a8c6..33ba104e38 100644
--- a/src/calibre/devices/usbms/device.py
+++ b/src/calibre/devices/usbms/device.py
@@ -23,7 +23,7 @@ from calibre.devices.interface import DevicePlugin
from calibre.devices.errors import DeviceError, FreeSpaceError
from calibre.devices.usbms.deviceconfig import DeviceConfig
from calibre import iswindows, islinux, isosx, __appname__
-from calibre.utils.filenames import shorten_components_to
+from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to
class Device(DeviceConfig, DevicePlugin):
@@ -295,20 +295,20 @@ class Device(DeviceConfig, DevicePlugin):
# This is typically needed when the device has the same
# WINDOWS_MAIN_MEM and WINDOWS_CARD_A_MEM in which case
- # if the devices is connected without a crad, the above
+ # if the devices is connected without a card, the above
# will incorrectly identify the main mem as carda
# See for example the driver for the Nook
if 'main' not in drives and 'carda' in drives:
drives['main'] = drives.pop('carda')
drives = self.windows_open_callback(drives)
- drives = self.windows_sort_drives(drives)
if drives.get('main', None) is None:
raise DeviceError(
_('Unable to detect the %s disk drive. Try rebooting.') %
self.__class__.__name__)
+ drives = self.windows_sort_drives(drives)
self._main_prefix = drives.get('main')
self._card_a_prefix = drives.get('carda', None)
self._card_b_prefix = drives.get('cardb', None)
@@ -739,18 +739,54 @@ class Device(DeviceConfig, DevicePlugin):
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
return path
- def create_upload_path(self, root, mdata, ext, id):
- from calibre.library.save_to_disk import config, get_components
- opts = config().parse()
- components = get_components(opts.template, mdata, id, opts.timefmt, 250)
- components = [str(x) for x in components]
- components = shorten_components_to(250 - len(root), components)
- filepath = '%s%s' % (os.path.join(root, *components), ext)
+ def create_upload_path(self, path, mdata, fname):
+ path = os.path.abspath(path)
+ newpath = path
+ extra_components = []
+
+ if self.SUPPORTS_SUB_DIRS and self.settings().use_subdirs:
+ if 'tags' in mdata.keys():
+ for tag in mdata['tags']:
+ if tag.startswith(_('News')):
+ extra_components.append('news')
+ c = sanitize(mdata.get('title', ''))
+ if c:
+ extra_components.append(c)
+ c = sanitize(mdata.get('timestamp', ''))
+ if c:
+ extra_components.append(c)
+ break
+ elif tag.startswith('/'):
+ for c in tag.split('/'):
+ c = sanitize(c)
+ if not c: continue
+ extra_components.append(c)
+ break
+
+ if not extra_components:
+ c = sanitize(mdata.get('authors', _('Unknown')))
+ if c:
+ extra_components.append(c)
+ c = sanitize(mdata.get('title', _('Unknown')))
+ if c:
+ extra_components.append(c)
+ newpath = os.path.join(newpath, c)
+
+ fname = sanitize(fname)
+ extra_components.append(fname)
+ extra_components = [str(x) for x in extra_components]
+ def remove_trailing_periods(x):
+ ans = x
+ while ans.endswith('.'):
+ ans = ans[:-1]
+ if not ans:
+ ans = 'x'
+ return ans
+ extra_components = list(map(remove_trailing_periods, extra_components))
+ components = shorten_components_to(250 - len(path), extra_components)
+ filepath = os.path.join(path, *components)
filedir = os.path.dirname(filepath)
- if not self.SUPPORTS_SUB_DIRS or not self.settings().use_subdirs:
- filedir = root
- filepath = os.path.join(root, os.path.basename(filepath))
if not os.path.exists(filedir):
os.makedirs(filedir)
diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py
index 1228781579..8d2416511c 100644
--- a/src/calibre/devices/usbms/driver.py
+++ b/src/calibre/devices/usbms/driver.py
@@ -95,19 +95,19 @@ class USBMS(CLI, Device):
return bl
- def upload_books(self, files, metadatas, ids, on_card=None,
- end_session=True):
+ def upload_books(self, files, names, on_card=None, end_session=True,
+ metadata=None):
path = self._sanity_check(on_card, files)
paths = []
- metadatas = iter(metadatas)
- ids = iter(ids)
+ names = iter(names)
+ metadata = iter(metadata)
for i, infile in enumerate(files):
- mdata, id = metadatas.next(), ids.next()
- ext = os.path.splitext(infile)[1]
- filepath = self.create_upload_path(path, mdata, ext, id)
+ mdata, fname = metadata.next(), names.next()
+ filepath = self.create_upload_path(path, mdata, fname)
+
paths.append(filepath)
self.put_file(infile, filepath, replace_file=True)
diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py
index c33e279912..4471f285dc 100644
--- a/src/calibre/gui2/device.py
+++ b/src/calibre/gui2/device.py
@@ -223,17 +223,18 @@ class DeviceManager(Thread):
return self.create_job(self._sync_booklists, done, args=[booklists],
description=_('Send metadata to device'))
- def _upload_books(self, files, metadata, ids, on_card=None):
+ def _upload_books(self, files, names, on_card=None, metadata=None):
'''Upload books to device: '''
- return self.device.upload_books(files, metadata, ids, on_card,
- end_session=False)
+ return self.device.upload_books(files, names, on_card,
+ metadata=metadata, end_session=False)
- def upload_books(self, done, files, metadata, ids, on_card=None, titles=None):
- desc = _('Upload %d books to device')%len(files)
+ def upload_books(self, done, files, names, on_card=None, titles=None,
+ metadata=None):
+ desc = _('Upload %d books to device')%len(names)
if titles:
desc += u':' + u', '.join(titles)
- return self.create_job(self._upload_books, done, args=[files, metadata, ids],
- kwargs={'on_card':on_card}, description=desc)
+ return self.create_job(self._upload_books, done, args=[files, names],
+ kwargs={'on_card':on_card,'metadata':metadata}, description=desc)
def add_books_to_metadata(self, locations, metadata, booklists):
self.device.add_books_to_metadata(locations, metadata, booklists)
@@ -707,18 +708,18 @@ class DeviceGUI(object):
dynamic.set('news_to_be_synced', set([]))
return
metadata = self.library_view.model().get_metadata(ids,
- rows_are_ids=True, full_metadata=True)[1]
+ rows_are_ids=True)
names = []
for mi in metadata:
- prefix = ascii_filename(mi.title)
+ prefix = ascii_filename(mi['title'])
if not isinstance(prefix, unicode):
prefix = prefix.decode(preferred_encoding, 'replace')
prefix = ascii_filename(prefix)
names.append('%s_%d%s'%(prefix, id,
os.path.splitext(f.name)[1]))
- cdata = mi.cover
+ cdata = mi['cover']
if cdata:
- mi.cover = self.cover_to_thumbnail(cdata)
+ mi['cover'] = self.cover_to_thumbnail(cdata)
dynamic.set('news_to_be_synced', set([]))
if config['upload_news_to_device'] and files:
remove = ids if \
@@ -727,7 +728,8 @@ class DeviceGUI(object):
self.location_view.model().free[1] : 'carda',
self.location_view.model().free[2] : 'cardb' }
on_card = space.get(sorted(space.keys(), reverse=True)[0], None)
- self.upload_books(files, metadata, ids, on_card=on_card,
+ self.upload_books(files, names, metadata,
+ on_card=on_card,
memory=[[f.name for f in files], remove])
self.status_bar.showMessage(_('Sending news to device.'), 5000)
@@ -749,28 +751,38 @@ class DeviceGUI(object):
else:
_auto_ids = []
- metadata = self.library_view.model().get_metadata(ids, True, full_metadata=True)[1]
+ metadata = self.library_view.model().get_metadata(ids, True)
ids = iter(ids)
for mi in metadata:
- cdata = mi.cover
+ cdata = mi['cover']
if cdata:
mi['cover'] = self.cover_to_thumbnail(cdata)
metadata = iter(metadata)
files = [getattr(f, 'name', None) for f in _files]
- bad, mdata, gf, fids, remove_ids = [], [], [], [], []
+ bad, good, gf, names, remove_ids = [], [], [], [], []
for f in files:
mi = metadata.next()
id = ids.next()
if f is None:
- bad.append(mi.title)
+ bad.append(mi['title'])
else:
remove_ids.append(id)
+ good.append(mi)
gf.append(f)
- mdata.append(mi)
- fids.append(id)
+ t = mi['title']
+ if not t:
+ t = _('Unknown')
+ a = mi['authors']
+ if not a:
+ a = _('Unknown')
+ prefix = ascii_filename(t+' - '+a)
+ if not isinstance(prefix, unicode):
+ prefix = prefix.decode(preferred_encoding, 'replace')
+ prefix = ascii_filename(prefix)
+ names.append('%s_%d%s'%(prefix, id, os.path.splitext(f)[1]))
remove = remove_ids if delete_from_library else []
- self.upload_books(gf, mdata, fids, on_card, memory=(_files, remove))
+ self.upload_books(gf, names, good, on_card, memory=(_files, remove))
self.status_bar.showMessage(_('Sending books to device.'), 5000)
auto = []
@@ -833,15 +845,17 @@ class DeviceGUI(object):
cp, fs = job.result
self.location_view.model().update_devices(cp, fs)
- def upload_books(self, files, metadata, ids, on_card=None, memory=None):
+ def upload_books(self, files, names, metadata, on_card=None, memory=None):
'''
Upload books to device.
:param files: List of either paths to files or file like objects
'''
- titles = [i.title for i in metadata]
+ titles = [i['title'] for i in metadata]
job = self.device_manager.upload_books(
Dispatcher(self.books_uploaded),
- files, metadata, ids, on_card=on_card, titles=titles)
+ files, names, on_card=on_card,
+ metadata=metadata, titles=titles
+ )
self.upload_memory[job] = (metadata, on_card, memory, files)
def books_uploaded(self, job):
@@ -854,7 +868,7 @@ class DeviceGUI(object):
if isinstance(job.exception, FreeSpaceError):
where = 'in main memory.' if 'memory' in str(job.exception) \
else 'on the storage card.'
- titles = '\n'.join([''+mi.title+'' \
+ titles = '\n'.join([''+mi['title']+'' \
for mi in metadata])
d = error_dialog(self, _('No space on device'),
_('Cannot upload books to device there '
diff --git a/src/calibre/gui2/dialogs/config/add_save.ui b/src/calibre/gui2/dialogs/config/add_save.ui
index ef1a867cd2..513be73e54 100644
--- a/src/calibre/gui2/dialogs/config/add_save.ui
+++ b/src/calibre/gui2/dialogs/config/add_save.ui
@@ -70,7 +70,7 @@
-
- Here you can control how calibre will save your books when you click the Save to Disk or Send to Device buttons:
+ Here you can control how calibre will save your books when you click the Save to Disk button:
true
diff --git a/src/calibre/manual/conversion.rst b/src/calibre/manual/conversion.rst
index 72e30eedbb..ccdb8d6cdd 100644
--- a/src/calibre/manual/conversion.rst
+++ b/src/calibre/manual/conversion.rst
@@ -497,6 +497,7 @@ TXT input supports a number of options to differentiate how paragraphs are detec
Convert PDF documents
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
PDF documents are one of the worst formats to convert from. They are a fixed page size and text placement format.
Meaning, it is very difficult to determine where one paragraph ends and another begins. |app| will try to unwrap