diff --git a/src/calibre/gui2/actions/save_to_disk.py b/src/calibre/gui2/actions/save_to_disk.py index 9e9cbe5f54..73f4367abc 100644 --- a/src/calibre/gui2/actions/save_to_disk.py +++ b/src/calibre/gui2/actions/save_to_disk.py @@ -50,7 +50,7 @@ class SaveToDiskAction(InterfaceAction): cm('single format', _('Save only %s format to disk')% prefs['output_format'].upper(), triggered=partial(self.save_single_format_to_disk, False)) - cm('fingle dir and format', + cm('single dir and format', _('Save only %s format to disk in a single directory')% prefs['output_format'].upper(), triggered=partial(self.save_single_fmt_to_single_dir, False)) @@ -115,10 +115,7 @@ class SaveToDiskAction(InterfaceAction): opts.save_cover = False opts.write_opf = False opts.template = opts.send_template - if single_dir: - opts.template = opts.template.split('/')[-1].strip() - if not opts.template: - opts.template = '{title} - {authors}' + opts.single_dir = single_dir self._saver = Saver(self.gui, self.gui.library_view.model().db, Dispatcher(self._books_saved), rows, path, opts, spare_server=self.gui.spare_server) diff --git a/src/calibre/library/save_to_disk.py b/src/calibre/library/save_to_disk.py index 3a95086faa..2c723e0f7d 100644 --- a/src/calibre/library/save_to_disk.py +++ b/src/calibre/library/save_to_disk.py @@ -121,6 +121,9 @@ def config(defaults=None): help=_('Convert paths to lowercase.')) x('replace_whitespace', default=False, help=_('Replace whitespace with underscores.')) + x('single_dir', default=False, + help=_('Save into a single directory, ignoring the template' + ' directory structure')) return c def preprocess_template(template): @@ -131,7 +134,7 @@ def preprocess_template(template): template = template.decode(preferred_encoding, 'replace') return template -class SafeFormat(TemplateFormatter): +class Formatter(TemplateFormatter): ''' Provides a format function that substitutes '' for any missing value ''' @@ -165,7 +168,7 @@ class SafeFormat(TemplateFormatter): def get_components(template, mi, id, timefmt='%b %Y', length=250, sanitize_func=ascii_filename, replace_whitespace=False, - to_lowercase=False): + to_lowercase=False, safe_format=True): tsorder = tweaks['save_template_title_series_sorting'] format_args = FORMAT_ARGS.copy() @@ -225,8 +228,11 @@ def get_components(template, mi, id, timefmt='%b %Y', length=250, format_args[key] = unicode(format_args[key]) else: format_args[key] = '' - components = SafeFormat().safe_format(template, format_args, + if safe_format: + components = Formatter().safe_format(template, format_args, 'G_C-EXCEPTION!', mi) + else: + components = Formatter().unsafe_format(template, format_args, mi) components = [x.strip() for x in components.split('/')] components = [sanitize_func(x) for x in components if x] if not components: @@ -283,10 +289,20 @@ def do_save_book_to_disk(id_, mi, cover, plugboards, if not formats: return True, id_, mi.title - components = get_components(opts.template, mi, id_, opts.timefmt, length, + try: + components = get_components(opts.template, mi, id_, opts.timefmt, length, ascii_filename if opts.asciiize else sanitize_file_name_unicode, to_lowercase=opts.to_lowercase, - replace_whitespace=opts.replace_whitespace) + replace_whitespace=opts.replace_whitespace, safe_format=False) + except Exception, e: + raise ValueError(_('Failed to calculate path for ' + 'save to disk. Template: %s\n' + 'Error: %s'%(opts.template, e))) + if opts.single_dir: + components = components[-1:] + if not components: + raise ValueError(_('Template evaluation resulted in no' + ' path components. Template: %s')%opts.template) base_path = os.path.join(root, *components) base_name = os.path.basename(base_path) dirpath = os.path.dirname(base_path) diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index 3a93c2b650..bc25f25043 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -310,7 +310,16 @@ class TemplateFormatter(string.Formatter): ans = string.Formatter.vformat(self, fmt, args, kwargs) return self.compress_spaces.sub(' ', ans).strip() - ########## a formatter guaranteed not to throw and exception ############ + ########## a formatter that throws exceptions ############ + + def unsafe_format(self, fmt, kwargs, book): + self.kwargs = kwargs + self.book = book + self.composite_values = {} + self.locals = {} + return self.vformat(fmt, [], kwargs).strip() + + ########## a formatter guaranteed not to throw an exception ############ def safe_format(self, fmt, kwargs, error_value, book): self.kwargs = kwargs