mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
Edit Book: Allow editing an unzipped EPUB (a folder) as a book
Useful if you wish to store your ebook in a version control system and edit it directly with the calibre ebook editor
This commit is contained in:
parent
f8670c524c
commit
c8ad7b203a
@ -27,7 +27,7 @@ from calibre.ebooks.oeb.polish.replace import rename_files, replace_file, get_re
|
|||||||
from calibre.ebooks.oeb.polish.split import split, merge, AbortError, multisplit
|
from calibre.ebooks.oeb.polish.split import split, merge, AbortError, multisplit
|
||||||
from calibre.ebooks.oeb.polish.toc import remove_names_from_toc, find_existing_toc, create_inline_toc
|
from calibre.ebooks.oeb.polish.toc import remove_names_from_toc, find_existing_toc, create_inline_toc
|
||||||
from calibre.ebooks.oeb.polish.utils import link_stylesheets, setup_cssutils_serialization as scs
|
from calibre.ebooks.oeb.polish.utils import link_stylesheets, setup_cssutils_serialization as scs
|
||||||
from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog, choose_save_file, open_url
|
from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog, choose_save_file, open_url, choose_dir
|
||||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||||
from calibre.gui2.tweak_book import (
|
from calibre.gui2.tweak_book import (
|
||||||
set_current_container, current_container, tprefs, actions, editors,
|
set_current_container, current_container, tprefs, actions, editors,
|
||||||
@ -243,7 +243,7 @@ class Boss(QObject):
|
|||||||
return get_container(dest, tdir=tdir)
|
return get_container(dest, tdir=tdir)
|
||||||
self.gui.blocking_job('import_book', _('Importing book, please wait...'), self.book_opened, func, src, dest, tdir=self.mkdtemp())
|
self.gui.blocking_job('import_book', _('Importing book, please wait...'), self.book_opened, func, src, dest, tdir=self.mkdtemp())
|
||||||
|
|
||||||
def open_book(self, path=None, edit_file=None, clear_notify_data=True):
|
def open_book(self, path=None, edit_file=None, clear_notify_data=True, open_folder=False):
|
||||||
'''
|
'''
|
||||||
Open the ebook at ``path`` for editing. Will show an error if the ebook is not in a supported format or the current book has unsaved changes.
|
Open the ebook at ``path`` for editing. Will show an error if the ebook is not in a supported format or the current book has unsaved changes.
|
||||||
|
|
||||||
@ -256,14 +256,24 @@ class Boss(QObject):
|
|||||||
if not self._check_before_open():
|
if not self._check_before_open():
|
||||||
return
|
return
|
||||||
if not hasattr(path, 'rpartition'):
|
if not hasattr(path, 'rpartition'):
|
||||||
path = choose_files(self.gui, 'open-book-for-tweaking', _('Choose book'),
|
if open_folder:
|
||||||
|
path = choose_dir(self.gui, 'open-book-folder-for-tweaking', _('Choose book folder'))
|
||||||
|
if path:
|
||||||
|
path = [path]
|
||||||
|
else:
|
||||||
|
path = choose_files(self.gui, 'open-book-for-tweaking', _('Choose book'),
|
||||||
[(_('Books'), [x.lower() for x in SUPPORTED])], all_files=False, select_only_single_file=True)
|
[(_('Books'), [x.lower() for x in SUPPORTED])], all_files=False, select_only_single_file=True)
|
||||||
|
|
||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
path = path[0]
|
path = path[0]
|
||||||
|
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return error_dialog(self.gui, _('File not found'), _(
|
||||||
|
'The file %s does not exist.') % path, show=True)
|
||||||
|
isdir = os.path.isdir(path)
|
||||||
ext = path.rpartition('.')[-1].upper()
|
ext = path.rpartition('.')[-1].upper()
|
||||||
if ext not in SUPPORTED:
|
if ext not in SUPPORTED and not isdir:
|
||||||
from calibre.ebooks.oeb.polish.import_book import IMPORTABLE
|
from calibre.ebooks.oeb.polish.import_book import IMPORTABLE
|
||||||
if ext.lower() in IMPORTABLE:
|
if ext.lower() in IMPORTABLE:
|
||||||
return self.import_book(path)
|
return self.import_book(path)
|
||||||
@ -271,9 +281,6 @@ class Boss(QObject):
|
|||||||
_('Tweaking is only supported for books in the %s formats.'
|
_('Tweaking is only supported for books in the %s formats.'
|
||||||
' Convert your book to one of these formats first.') % _(' and ').join(sorted(SUPPORTED)),
|
' Convert your book to one of these formats first.') % _(' and ').join(sorted(SUPPORTED)),
|
||||||
show=True)
|
show=True)
|
||||||
if not os.path.exists(path):
|
|
||||||
return error_dialog(self.gui, _('File not found'), _(
|
|
||||||
'The file %s does not exist.') % path, show=True)
|
|
||||||
|
|
||||||
for name in tuple(editors):
|
for name in tuple(editors):
|
||||||
self.close_editor(name)
|
self.close_editor(name)
|
||||||
@ -972,7 +979,7 @@ class Boss(QObject):
|
|||||||
ed.is_modified = False
|
ed.is_modified = False
|
||||||
path_to_ebook = os.path.abspath(c.path_to_ebook)
|
path_to_ebook = os.path.abspath(c.path_to_ebook)
|
||||||
destdir = os.path.dirname(path_to_ebook)
|
destdir = os.path.dirname(path_to_ebook)
|
||||||
if not os.path.exists(destdir):
|
if not c.is_dir and not os.path.exists(destdir):
|
||||||
info_dialog(self.gui, _('Path does not exist'), _(
|
info_dialog(self.gui, _('Path does not exist'), _(
|
||||||
'The file you are editing (%s) no longer exists. You have to choose a new save location.') % path_to_ebook,
|
'The file you are editing (%s) no longer exists. You have to choose a new save location.') % path_to_ebook,
|
||||||
show_copy_button=False, show=True)
|
show_copy_button=False, show=True)
|
||||||
@ -996,6 +1003,9 @@ class Boss(QObject):
|
|||||||
|
|
||||||
def save_copy(self):
|
def save_copy(self):
|
||||||
c = current_container()
|
c = current_container()
|
||||||
|
if c.is_dir:
|
||||||
|
return error_dialog(self.gui, _('Cannot save a copy'), _(
|
||||||
|
'Saving a copy of a folder based book is not supported'), show=True)
|
||||||
ext = c.path_to_ebook.rpartition('.')[-1]
|
ext = c.path_to_ebook.rpartition('.')[-1]
|
||||||
path = choose_save_file(self.gui, 'tweak_book_save_copy', _(
|
path = choose_save_file(self.gui, 'tweak_book_save_copy', _(
|
||||||
'Choose path'), filters=[(_('Book (%s)') % ext.upper(), [ext.lower()])], all_files=False)
|
'Choose path'), filters=[(_('Book (%s)') % ext.upper(), [ext.lower()])], all_files=False)
|
||||||
|
@ -19,7 +19,16 @@ from calibre.utils import join_with_timeout
|
|||||||
from calibre.utils.filenames import atomic_rename, format_permissions
|
from calibre.utils.filenames import atomic_rename, format_permissions
|
||||||
from calibre.utils.ipc import RC
|
from calibre.utils.ipc import RC
|
||||||
|
|
||||||
|
def save_dir_container(container, path):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
if not os.path.isdir(path):
|
||||||
|
raise ValueError('%s is not a folder, cannot save a directory based container to it' % path)
|
||||||
|
container.commit(path)
|
||||||
|
|
||||||
def save_container(container, path):
|
def save_container(container, path):
|
||||||
|
if container.is_dir:
|
||||||
|
return save_dir_container(container, path)
|
||||||
temp = PersistentTemporaryFile(
|
temp = PersistentTemporaryFile(
|
||||||
prefix=('_' if iswindows else '.'), suffix=os.path.splitext(path)[1], dir=os.path.dirname(path))
|
prefix=('_' if iswindows else '.'), suffix=os.path.splitext(path)[1], dir=os.path.dirname(path))
|
||||||
if hasattr(os, 'fchmod'):
|
if hasattr(os, 'fchmod'):
|
||||||
|
@ -327,6 +327,8 @@ class Main(MainWindow):
|
|||||||
'new-file', (), _('Create a new file in the current book'))
|
'new-file', (), _('Create a new file in the current book'))
|
||||||
self.action_import_files = treg('document-import.png', _('&Import files into book'), self.boss.add_files, 'new-files', (), _('Import files into book'))
|
self.action_import_files = treg('document-import.png', _('&Import files into book'), self.boss.add_files, 'new-files', (), _('Import files into book'))
|
||||||
self.action_open_book = treg('document_open.png', _('Open &book'), self.boss.open_book, 'open-book', 'Ctrl+O', _('Open a new book'))
|
self.action_open_book = treg('document_open.png', _('Open &book'), self.boss.open_book, 'open-book', 'Ctrl+O', _('Open a new book'))
|
||||||
|
self.action_open_book_folder = treg('mimetypes/dir.png', _('Open &folder (unzipped EPUB) as book'), partial(self.boss.open_book, open_folder=True),
|
||||||
|
'open-folder-as-book', (), _('Open a folder (unzipped EPUB) as a book'))
|
||||||
# Qt does not generate shortcut overrides for cmd+arrow on os x which
|
# Qt does not generate shortcut overrides for cmd+arrow on os x which
|
||||||
# means these shortcuts interfere with editing
|
# means these shortcuts interfere with editing
|
||||||
self.action_global_undo = treg('back.png', _('&Revert to before'), self.boss.do_global_undo, 'global-undo', () if isosx else 'Ctrl+Left',
|
self.action_global_undo = treg('back.png', _('&Revert to before'), self.boss.do_global_undo, 'global-undo', () if isosx else 'Ctrl+Left',
|
||||||
@ -503,6 +505,7 @@ class Main(MainWindow):
|
|||||||
f.addAction(self.action_open_book)
|
f.addAction(self.action_open_book)
|
||||||
f.addAction(self.action_new_book)
|
f.addAction(self.action_new_book)
|
||||||
f.addAction(self.action_import_book)
|
f.addAction(self.action_import_book)
|
||||||
|
f.addAction(self.action_open_book_folder)
|
||||||
self.recent_books_menu = f.addMenu(_('&Recently opened books'))
|
self.recent_books_menu = f.addMenu(_('&Recently opened books'))
|
||||||
self.update_recent_books()
|
self.update_recent_books()
|
||||||
f.addSeparator()
|
f.addSeparator()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user