Implement replacing of files

This commit is contained in:
Kovid Goyal 2013-12-02 15:47:44 +05:30
parent f885896c31
commit 7e4da66e09
3 changed files with 72 additions and 7 deletions

View File

@ -7,9 +7,10 @@ __license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import codecs
import codecs, shutil
from urlparse import urlparse
from calibre import sanitize_file_name_unicode
from calibre.ebooks.chardet import strip_encoding_declarations
class LinkReplacer(object):
@ -122,3 +123,25 @@ def rename_files(container, file_map):
link_map[current_name] = new_name
replace_links(container, link_map, replace_in_opf=True)
def replace_file(container, name, path, basename, force_mt=None):
dirname, base = name.rpartition('/')[0::2]
nname = sanitize_file_name_unicode(basename)
if dirname:
nname = dirname + '/' + nname
with open(path, 'rb') as src:
if name != nname:
count = 0
b, e = nname.rpartition('.')[0::2]
while container.exists(nname):
count += 1
nname = b + ('_%d.%s' % (count, e))
rename_files(container, {name:nname})
mt = force_mt or container.guess_type(nname)
for itemid, q in container.manifest_id_map.iteritems():
if q == nname:
for item in container.opf_xpath('//opf:manifest/opf:item[@href and @id="%s"]' % itemid):
item.set('media-type', mt)
container.dirty(container.opf_name)
with container.open(nname, 'wb') as dest:
shutil.copyfileobj(src, dest)

View File

@ -21,7 +21,7 @@ from calibre.ebooks.oeb.polish.main import SUPPORTED, tweak_polish
from calibre.ebooks.oeb.polish.container import get_container as _gc, clone_container, guess_type
from calibre.ebooks.oeb.polish.cover import mark_as_cover, mark_as_titlepage
from calibre.ebooks.oeb.polish.pretty import fix_all_html, pretty_all
from calibre.ebooks.oeb.polish.replace import rename_files
from calibre.ebooks.oeb.polish.replace import rename_files, replace_file
from calibre.ebooks.oeb.polish.split import split, merge, AbortError
from calibre.gui2 import error_dialog, choose_files, question_dialog, info_dialog, choose_save_file
from calibre.gui2.dialogs.confirm_delete import confirm
@ -75,6 +75,7 @@ class Boss(QObject):
fl.merge_requested.connect(self.merge_requested)
fl.mark_requested.connect(self.mark_requested)
fl.export_requested.connect(self.export_requested)
fl.replace_requested.connect(self.replace_requested)
self.gui.central.current_editor_changed.connect(self.apply_current_editor_state)
self.gui.central.close_requested.connect(self.editor_close_requested)
self.gui.central.search_panel.search_triggered.connect(self.search)
@ -682,6 +683,13 @@ class Boss(QObject):
with current_container().open(name, 'rb') as src, open(path, 'wb') as dest:
shutil.copyfileobj(src, dest)
@in_thread_job
def replace_requested(self, name, path, basename, force_mt):
self.commit_all_editors_to_container()
self.add_savepoint(_('Replace %s') % name)
replace_file(current_container(), name, path, basename, force_mt)
self.apply_container_update_to_gui()
def sync_editor_to_preview(self, name, lnum):
editor = self.edit_file(name, 'html')
self.ignore_preview_to_editor_sync = True

View File

@ -86,6 +86,7 @@ class FileList(QTreeWidget):
merge_requested = pyqtSignal(object, object, object)
mark_requested = pyqtSignal(object, object)
export_requested = pyqtSignal(object, object)
replace_requested = pyqtSignal(object, object, object, object)
def __init__(self, parent=None):
QTreeWidget.__init__(self, parent)
@ -304,6 +305,7 @@ class FileList(QTreeWidget):
m = QMenu(self)
sel = self.selectedItems()
num = len(sel)
container = current_container()
if num > 0:
m.addAction(QIcon(I('trash.png')), _('&Delete selected files'), self.request_delete)
m.addSeparator()
@ -312,13 +314,16 @@ class FileList(QTreeWidget):
cn = unicode(ci.data(0, NAME_ROLE).toString())
mt = unicode(ci.data(0, MIME_ROLE).toString())
cat = unicode(ci.data(0, CATEGORY_ROLE).toString())
m.addAction(QIcon(I('modified.png')), _('&Rename %s') % (elided_text(cn)), self.edit_current_item)
n = elided_text(cn.rpartition('/')[-1])
m.addAction(QIcon(I('modified.png')), _('&Rename %s') % n, self.edit_current_item)
if is_raster_image(mt):
m.addAction(QIcon(I('default_cover.png')), _('Mark %s as cover image') % elided_text(cn), partial(self.mark_as_cover, cn))
m.addAction(QIcon(I('default_cover.png')), _('Mark %s as cover image') % n, partial(self.mark_as_cover, cn))
elif current_container().SUPPORTS_TITLEPAGES and mt in OEB_DOCS and cat == 'text':
m.addAction(QIcon(I('default_cover.png')), _('Mark %s as title/cover page') % elided_text(cn), partial(self.mark_as_titlepage, cn))
m.addAction(QIcon(I('default_cover.png')), _('Mark %s as cover page') % n, partial(self.mark_as_titlepage, cn))
m.addSeparator()
m.addAction(QIcon(I('save.png')), _('Export %s') % elided_text(cn), partial(self.export, cn))
m.addAction(QIcon(I('save.png')), _('Export %s') % n, partial(self.export, cn))
if cn not in container.names_that_must_not_be_changed and cn not in container.names_that_must_not_be_removed and mt not in OEB_FONTS:
m.addAction(_('Replace %s with file...') % n, partial(self.replace, cn))
m.addSeparator()
selected_map = defaultdict(list)
@ -453,6 +458,32 @@ class FileList(QTreeWidget):
if path:
self.export_requested.emit(name, path)
def replace(self, name):
c = current_container()
mt = c.mime_map[name]
oext = name.rpartition('.')[-1].lower()
filters = [oext]
fname = _('Files')
if mt in OEB_DOCS:
fname = _('HTML Files')
filters = 'html htm xhtm xhtml shtml'.split()
elif is_raster_image(mt):
fname = _('Images')
filters = 'jpeg jpg gif png'.split()
path = choose_files(self, 'tweak_book_import_file', _('Choose file'), filters=[(fname, filters)], select_only_single_file=True)
if not path:
return
path = path[0]
ext = path.rpartition('.')[-1].lower()
force_mt = None
if mt in OEB_DOCS:
force_mt = c.guess_type('a.html')
nname = os.path.basename(path)
nname, ext = nname.rpartition('.')[0::2]
nname = nname + '.' + ext.lower()
self.replace_requested.emit(name, path, nname, force_mt)
class NewFileDialog(QDialog): # {{{
def __init__(self, initial_choice='html', parent=None):
@ -578,6 +609,7 @@ class FileListWidget(QWidget):
merge_requested = pyqtSignal(object, object, object)
mark_requested = pyqtSignal(object, object)
export_requested = pyqtSignal(object, object)
replace_requested = pyqtSignal(object, object, object, object)
def __init__(self, parent=None):
QWidget.__init__(self, parent)
@ -585,7 +617,9 @@ class FileListWidget(QWidget):
self.file_list = FileList(self)
self.layout().addWidget(self.file_list)
self.layout().setContentsMargins(0, 0, 0, 0)
for x in ('delete_requested', 'reorder_spine', 'rename_requested', 'edit_file', 'merge_requested', 'mark_requested', 'export_requested'):
for x in ('delete_requested', 'reorder_spine', 'rename_requested',
'edit_file', 'merge_requested', 'mark_requested',
'export_requested', 'replace_requested'):
getattr(self.file_list, x).connect(getattr(self, x))
for x in ('delete_done', 'select_name'):
setattr(self, x, getattr(self.file_list, x))