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<authors>.+)#(?P<title>.+)' ) - 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(['<li>'+mi.title+'</li>' \ + titles = '\n'.join(['<li>'+mi['title']+'</li>' \ for mi in metadata]) d = error_dialog(self, _('No space on device'), _('<p>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 @@ <item row="0" column="0" colspan="2"> <widget class="QLabel" name="label"> <property name="text"> - <string>Here you can control how calibre will save your books when you click the Save to Disk or Send to Device buttons:</string> + <string>Here you can control how calibre will save your books when you click the Save to Disk button:</string> </property> <property name="wordWrap"> <bool>true</bool> 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