Edit Book: Allow bulk changing of file extension for selected files by right clicking ont he selected files in the file browser. Fixes #1643232 [Enhancement request : bulk rename file extensions](https://bugs.launchpad.net/calibre/+bug/1643232)

This commit is contained in:
Kovid Goyal 2016-11-20 10:03:01 +05:30
parent 67709b515a
commit 05bec937b5
2 changed files with 38 additions and 14 deletions

View File

@ -104,6 +104,10 @@ Ctrl key and clicking the files. Then right click and select :guilabel:`Bulk
rename`. Enter a prefix and what number you would like the automatic numbering rename`. Enter a prefix and what number you would like the automatic numbering
to start at, click OK and you are done. to start at, click OK and you are done.
Finally, you can bulk change the file extension for all selected files. Select
multiple files, as above, and right click and choose :guilabel:`Change the file
extension for the selected files`.
Merging files Merging files
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^

View File

@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
import os import os, posixpath
from binascii import hexlify from binascii import hexlify
from collections import OrderedDict, defaultdict, Counter from collections import OrderedDict, defaultdict, Counter
from functools import partial from functools import partial
@ -15,7 +15,7 @@ import sip
from PyQt5.Qt import ( from PyQt5.Qt import (
QWidget, QTreeWidget, QGridLayout, QSize, Qt, QTreeWidgetItem, QIcon, QFont, QWidget, QTreeWidget, QGridLayout, QSize, Qt, QTreeWidgetItem, QIcon, QFont,
QStyledItemDelegate, QStyle, QPixmap, QPainter, pyqtSignal, QMenu, QTimer, QStyledItemDelegate, QStyle, QPixmap, QPainter, pyqtSignal, QMenu, QTimer,
QDialogButtonBox, QDialog, QLabel, QLineEdit, QVBoxLayout, QScrollArea, QDialogButtonBox, QDialog, QLabel, QLineEdit, QVBoxLayout, QScrollArea, QInputDialog,
QRadioButton, QFormLayout, QSpinBox, QListWidget, QListWidgetItem, QCheckBox) QRadioButton, QFormLayout, QSpinBox, QListWidget, QListWidgetItem, QCheckBox)
from calibre import human_readable, sanitize_file_name_unicode, plugins from calibre import human_readable, sanitize_file_name_unicode, plugins
@ -460,6 +460,7 @@ class FileList(QTreeWidget):
m.addSeparator() m.addSeparator()
if num > 1: if num > 1:
m.addAction(QIcon(I('modified.png')), _('&Bulk rename the selected files'), self.request_bulk_rename) m.addAction(QIcon(I('modified.png')), _('&Bulk rename the selected files'), self.request_bulk_rename)
m.addAction(QIcon(I('modified.png')), _('Change the file extension for the selected files'), self.request_change_ext)
m.addAction(QIcon(I('trash.png')), ngettext( m.addAction(QIcon(I('trash.png')), ngettext(
'&Delete the selected file', '&Delete the {} selected files', num).format(num), self.request_delete) '&Delete the selected file', '&Delete the {} selected files', num).format(num), self.request_delete)
m.addSeparator() m.addSeparator()
@ -525,7 +526,7 @@ class FileList(QTreeWidget):
else: else:
return QTreeWidget.keyPressEvent(self, ev) return QTreeWidget.keyPressEvent(self, ev)
def request_bulk_rename(self): def request_rename_common(self):
if not current_container().SUPPORTS_FILENAMES: if not current_container().SUPPORTS_FILENAMES:
error_dialog(self, _('Cannot rename'), _( error_dialog(self, _('Cannot rename'), _(
'%s books do not support file renaming as they do not use file names' '%s books do not support file renaming as they do not use file names'
@ -535,9 +536,15 @@ class FileList(QTreeWidget):
names = {unicode(item.data(0, NAME_ROLE) or '') for item in self.selectedItems()} names = {unicode(item.data(0, NAME_ROLE) or '') for item in self.selectedItems()}
bad = names & current_container().names_that_must_not_be_changed bad = names & current_container().names_that_must_not_be_changed
if bad: if bad:
return error_dialog(self, _('Cannot rename'), error_dialog(self, _('Cannot rename'),
_('The file(s) %s cannot be renamed.') % ('<b>%s</b>' % ', '.join(bad)), show=True) _('The file(s) %s cannot be renamed.') % ('<b>%s</b>' % ', '.join(bad)), show=True)
return
names = sorted(names, key=self.index_of_name) names = sorted(names, key=self.index_of_name)
return names
def request_bulk_rename(self):
names = self.request_rename_common()
if names is not None:
categories = Counter(unicode(item.data(0, CATEGORY_ROLE) or '') for item in self.selectedItems()) categories = Counter(unicode(item.data(0, CATEGORY_ROLE) or '') for item in self.selectedItems())
fmt, num = get_bulk_rename_settings(self, len(names), category=categories.most_common(1)[0][0]) fmt, num = get_bulk_rename_settings(self, len(names), category=categories.most_common(1)[0][0])
if fmt is not None: if fmt is not None:
@ -549,6 +556,19 @@ class FileList(QTreeWidget):
name_map = {n:change_name(n, num + i) for i, n in enumerate(names)} name_map = {n:change_name(n, num + i) for i, n in enumerate(names)}
self.bulk_rename_requested.emit(name_map) self.bulk_rename_requested.emit(name_map)
def request_change_ext(self):
names = self.request_rename_common()
if names is not None:
text, ok = QInputDialog.getText(self, _('Rename files'), _('New file extension:'))
if ok and text:
ext = text.lstrip('.')
def change_name(name):
base = posixpath.splitext(name)[0]
return base + '.' + ext
name_map = {n:change_name(n) for n in names}
self.bulk_rename_requested.emit(name_map)
@property @property
def selected_names(self): def selected_names(self):
ans = {unicode(item.data(0, NAME_ROLE) or '') for item in self.selectedItems()} ans = {unicode(item.data(0, NAME_ROLE) or '') for item in self.selectedItems()}