diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index 861e1bf70c..3e2cc4da57 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -366,3 +366,10 @@ server_listen_on = '0.0.0.0' # on at your own risk! unified_title_toolbar_on_osx = False +#: Save original file when converting from same format to same format +# When calibre does a conversion from the same format to the same format, for +# example, from EPUB to EPUB, the original file is saved, so that in case the +# conversion is poor, you can tweak the settings and run it again. By setting +# this to False you can prevent calibre from saving the original file. +save_original_format = True + diff --git a/src/calibre/ebooks/conversion/plumber.py b/src/calibre/ebooks/conversion/plumber.py index d0d427bf74..31f125dd88 100644 --- a/src/calibre/ebooks/conversion/plumber.py +++ b/src/calibre/ebooks/conversion/plumber.py @@ -602,7 +602,7 @@ OptionRecommendation(name='sr3_replace', input_fmt = os.path.splitext(self.input)[1] if not input_fmt: raise ValueError('Input file must have an extension') - input_fmt = input_fmt[1:].lower() + input_fmt = input_fmt[1:].lower().replace('original_', '') self.archive_input_tdir = None if input_fmt in ARCHIVE_FMTS: self.log('Processing archive...') diff --git a/src/calibre/ebooks/oeb/iterator.py b/src/calibre/ebooks/oeb/iterator.py index 92a4febb6c..e584acc392 100644 --- a/src/calibre/ebooks/oeb/iterator.py +++ b/src/calibre/ebooks/oeb/iterator.py @@ -92,7 +92,7 @@ class EbookIterator(object): self.config = DynamicConfig(name='iterator') ext = os.path.splitext(pathtoebook)[1].replace('.', '').lower() ext = re.sub(r'(x{0,1})htm(l{0,1})', 'html', ext) - self.ebook_ext = ext + self.ebook_ext = ext.replace('original_', '') def search(self, text, index, backwards=False): text = text.lower() diff --git a/src/calibre/gui2/actions/convert.py b/src/calibre/gui2/actions/convert.py index 17fa0ad622..e3dc697a45 100644 --- a/src/calibre/gui2/actions/convert.py +++ b/src/calibre/gui2/actions/convert.py @@ -12,7 +12,7 @@ from PyQt4.Qt import QModelIndex, QMenu from calibre.gui2 import error_dialog, Dispatcher from calibre.gui2.tools import convert_single_ebook, convert_bulk_ebook -from calibre.utils.config import prefs +from calibre.utils.config import prefs, tweaks from calibre.gui2.actions import InterfaceAction from calibre.customize.ui import plugin_for_input_format @@ -118,6 +118,8 @@ class ConvertAction(InterfaceAction): def queue_convert_jobs(self, jobs, changed, bad, rows, previous, converted_func, extra_job_args=[]): for func, args, desc, fmt, id, temp_files in jobs: + func, _, same_fmt = func.partition(':') + same_fmt = same_fmt == 'same_fmt' input_file = args[0] input_fmt = os.path.splitext(input_file)[1] core_usage = 1 @@ -131,6 +133,7 @@ class ConvertAction(InterfaceAction): job = self.gui.job_manager.run_job(Dispatcher(converted_func), func, args=args, description=desc, core_usage=core_usage) + job.conversion_of_same_fmt = same_fmt args = [temp_files, fmt, id]+extra_job_args self.conversion_jobs[job] = tuple(args) @@ -166,14 +169,18 @@ class ConvertAction(InterfaceAction): if job.failed: self.gui.job_exception(job) return + same_fmt = getattr(job, 'conversion_of_same_fmt', False) fmtf = temp_files[-1].name if os.stat(fmtf).st_size < 1: raise Exception(_('Empty output file, ' 'probably the conversion process crashed')) + db = self.gui.current_db + if same_fmt and tweaks['save_original_format']: + db.save_original_format(book_id, fmt, notify=False) + with open(temp_files[-1].name, 'rb') as data: - self.gui.library_view.model().db.add_format(book_id, \ - fmt, data, index_is_id=True) + db.add_format(book_id, fmt, data, index_is_id=True) self.gui.status_bar.show_message(job.description + \ (' completed'), 2000) finally: diff --git a/src/calibre/gui2/actions/view.py b/src/calibre/gui2/actions/view.py index 6cf5c5d5af..a877a8f75f 100644 --- a/src/calibre/gui2/actions/view.py +++ b/src/calibre/gui2/actions/view.py @@ -128,7 +128,8 @@ class ViewAction(InterfaceAction): self.gui.unsetCursor() def _view_file(self, name): - ext = os.path.splitext(name)[1].upper().replace('.', '') + ext = os.path.splitext(name)[1].upper().replace('.', + '').replace('ORIGINAL_', '') viewer = 'lrfviewer' if ext == 'LRF' else 'ebook-viewer' internal = ext in config['internally_viewed_formats'] self._launch_viewer(name, viewer, internal) diff --git a/src/calibre/gui2/tools.py b/src/calibre/gui2/tools.py index 1726c791f0..021617aa4a 100644 --- a/src/calibre/gui2/tools.py +++ b/src/calibre/gui2/tools.py @@ -53,7 +53,9 @@ def convert_single_ebook(parent, db, book_ids, auto_conversion=False, # {{{ mi = db.get_metadata(book_id, True) in_file = PersistentTemporaryFile('.'+d.input_format) with in_file: - db.copy_format_to(book_id, d.input_format, in_file, + input_fmt = db.original_fmt(book_id, d.input_format).lower() + same_fmt = input_fmt == d.output_format.lower() + db.copy_format_to(book_id, input_fmt, in_file, index_is_id=True) out_file = PersistentTemporaryFile('.' + d.output_format) @@ -79,7 +81,10 @@ def convert_single_ebook(parent, db, book_ids, auto_conversion=False, # {{{ temp_files.append(d.cover_file) args = [in_file.name, out_file.name, recs] temp_files.append(out_file) - jobs.append(('gui_convert_override', args, desc, d.output_format.upper(), book_id, temp_files)) + func = 'gui_convert_override' + if same_fmt: + func += ':same_fmt' + jobs.append((func, args, desc, d.output_format.upper(), book_id, temp_files)) changed = True d.break_cycles() @@ -144,10 +149,12 @@ class QueueBulk(QProgressDialog): try: input_format = get_input_format_for_book(self.db, book_id, None)[0] + input_fmt = self.db.original_fmt(book_id, input_format).lower() + same_fmt = input_fmt == self.output_format.lower() mi, opf_file = create_opf_file(self.db, book_id) in_file = PersistentTemporaryFile('.'+input_format) with in_file: - self.db.copy_format_to(book_id, input_format, in_file, + self.db.copy_format_to(book_id, input_fmt, in_file, index_is_id=True) out_file = PersistentTemporaryFile('.' + self.output_format) @@ -192,7 +199,10 @@ class QueueBulk(QProgressDialog): args = [in_file.name, out_file.name, lrecs] temp_files.append(out_file) - self.jobs.append(('gui_convert_override', args, desc, self.output_format.upper(), book_id, temp_files)) + func = 'gui_convert_override' + if same_fmt: + func += ':same_fmt' + self.jobs.append((func, args, desc, self.output_format.upper(), book_id, temp_files)) self.changed = True self.setValue(self.i) diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index 35b25b262e..5e3d9908c1 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -661,12 +661,13 @@ class EbookViewer(MainWindow, Ui_EbookViewer): def save_current_position(self): if not self.get_remember_current_page_opt(): return - try: - pos = self.view.bookmark() - bookmark = '%d#%s'%(self.current_index, pos) - self.iterator.add_bookmark(('calibre_current_page_bookmark', bookmark)) - except: - traceback.print_exc() + if hasattr(self, 'current_index'): + try: + pos = self.view.bookmark() + bookmark = '%d#%s'%(self.current_index, pos) + self.iterator.add_bookmark(('calibre_current_page_bookmark', bookmark)) + except: + traceback.print_exc() def load_ebook(self, pathtoebook): if self.iterator is not None: diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index ea4ca373c4..8d16ffbc52 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1312,6 +1312,23 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.notify('metadata', [id]) return True + def save_original_format(self, book_id, fmt, notify=True): + fmt = fmt.upper() + if 'ORIGINAL' in fmt: + raise ValueError('Cannot save original of an original fmt') + opath = self.format_abspath(book_id, fmt, index_is_id=True) + if opath is None: + return False + nfmt = 'ORIGINAL_'+fmt + with lopen(opath, 'rb') as f: + return self.add_format(book_id, nfmt, f, index_is_id=True, notify=notify) + + def original_fmt(self, book_id, fmt): + fmt = fmt + nfmt = ('ORIGINAL_%s'%fmt).upper() + opath = self.format_abspath(book_id, nfmt, index_is_id=True) + return fmt if opath is None else nfmt + def delete_book(self, id, notify=True, commit=True, permanent=False): ''' Removes book from the result cache and the underlying database.