An option to multiple add books from inside a ZIP or RAR file. Right click the Add Books button and choose 'Add multiple books from archive'.

This commit is contained in:
Kovid Goyal 2014-03-06 22:07:18 +05:30
parent bdb2d0a5d9
commit 8b7956f996
3 changed files with 55 additions and 4 deletions

View File

@ -53,6 +53,8 @@ Add books
3. **Add books from directories, including sub-directories (Multiple books per directory, assumes every ebook file is a different book)**: Allows you to choose a directory. The directory and all its sub-directories are scanned recursively and any ebooks found are added to the library. |app| assumes that each directory contains many books. All ebook files with the same name in a directory are assumed to be the same book in different formats. Ebooks with different names are added as different books. 3. **Add books from directories, including sub-directories (Multiple books per directory, assumes every ebook file is a different book)**: Allows you to choose a directory. The directory and all its sub-directories are scanned recursively and any ebooks found are added to the library. |app| assumes that each directory contains many books. All ebook files with the same name in a directory are assumed to be the same book in different formats. Ebooks with different names are added as different books.
4. **Add multiple books from archive (ZIP/RAR)**: Allows you to add multiple ebooks that are stored inside a single ZIP or RAR file. It is a convenient shortcut that avoids having to first unzip the archive and then add the books via one of the above two options.
4. **Add empty book. (Book Entry with no formats)**: Allows you to create a blank book record. This can be used to then manually fill out the information about a book that you may not have yet in your collection. 4. **Add empty book. (Book Entry with no formats)**: Allows you to create a blank book record. This can be used to then manually fill out the information about a book that you may not have yet in your collection.
5. **Add from ISBN**: Allows you to add one or more books by entering their ISBNs. 5. **Add from ISBN**: Allows you to add one or more books by entering their ISBNs.

View File

@ -66,6 +66,12 @@ class AddAction(InterfaceAction):
'sub directories (Multiple books per directory, assumes every ' 'sub directories (Multiple books per directory, assumes every '
'ebook file is a different book)')).triggered.connect( 'ebook file is a different book)')).triggered.connect(
self.add_recursive_multiple) self.add_recursive_multiple)
arm = self.add_archive_menu = self.add_menu.addMenu(_('Add multiple books from archive (ZIP/RAR)'))
self.create_menu_action(arm, 'recursive-single-archive', _(
'One book per directory in the archive')).triggered.connect(partial(self.add_archive, True))
self.create_menu_action(arm, 'recursive-multiple-archive', _(
'Multiple books per directory in the archive')).triggered.connect(partial(self.add_archive, False))
self.add_menu.addSeparator()
self.add_menu.addSeparator() self.add_menu.addSeparator()
ma('add-empty', _('Add Empty book. (Book entry with no formats)'), ma('add-empty', _('Add Empty book. (Book entry with no formats)'),
shortcut='Shift+Ctrl+E').triggered.connect(self.add_empty) shortcut='Shift+Ctrl+E').triggered.connect(self.add_empty)
@ -135,11 +141,21 @@ class AddAction(InterfaceAction):
if current_idx.isValid(): if current_idx.isValid():
view.model().current_changed(current_idx, current_idx) view.model().current_changed(current_idx, current_idx)
def add_archive(self, single):
paths = choose_files(
self.gui, 'recursive-archive-add', _('Choose archive file'),
filters=[(_('Archives'), ('zip', 'rar'))], all_files=False, select_only_single_file=True)
if paths:
self.do_add_recursive(paths[0], single)
def add_recursive(self, single): def add_recursive(self, single):
root = choose_dir(self.gui, 'recursive book import root dir dialog', root = choose_dir(self.gui, 'recursive book import root dir dialog',
'Select root folder') _('Select root folder'))
if not root: if not root:
return return
self.do_add_recursive(root, single)
def do_add_recursive(self, root, single):
from calibre.gui2.add import Adder from calibre.gui2.add import Adder
self._adder = Adder(self.gui, self._adder = Adder(self.gui,
self.gui.library_view.model().db, self.gui.library_view.model().db,

View File

@ -7,6 +7,7 @@ from functools import partial
from PyQt4.Qt import QThread, QObject, Qt, QProgressDialog, pyqtSignal, QTimer from PyQt4.Qt import QThread, QObject, Qt, QProgressDialog, pyqtSignal, QTimer
from calibre.ptempfile import PersistentTemporaryDirectory
from calibre.gui2.dialogs.progress import ProgressDialog from calibre.gui2.dialogs.progress import ProgressDialog
from calibre.gui2 import (error_dialog, info_dialog, gprefs, from calibre.gui2 import (error_dialog, info_dialog, gprefs,
warning_dialog, available_width) warning_dialog, available_width)
@ -54,10 +55,11 @@ class RecursiveFind(QThread): # {{{
update = pyqtSignal(object) update = pyqtSignal(object)
found = pyqtSignal(object) found = pyqtSignal(object)
def __init__(self, parent, db, root, single): def __init__(self, parent, db, root, single, tdir=None):
QThread.__init__(self, parent) QThread.__init__(self, parent)
self.db = db self.db = db
self.path = root self.path = root
self.tdir = tdir
self.single_book_per_directory = single self.single_book_per_directory = single
self.canceled = False self.canceled = False
@ -72,7 +74,34 @@ class RecursiveFind(QThread): # {{{
self.books += list(self.db.find_books_in_directory(dirpath[0], self.books += list(self.db.find_books_in_directory(dirpath[0],
self.single_book_per_directory)) self.single_book_per_directory))
def extract(self):
if self.path.lower().endswith('.zip'):
from calibre.utils.zipfile import ZipFile
try:
with ZipFile(self.path) as zf:
zf.extractall(self.tdir)
except Exception:
prints('Corrupt ZIP file, trying to use local headers')
from calibre.utils.localunzip import extractall
extractall(self.path, self.tdir)
elif self.path.lower().endswith('.rar'):
from calibre.utils.unrar import extract
extract(self.path, self.tdir)
else:
raise ValueError('Can only process ZIP or RAR archives')
def run(self): def run(self):
if self.tdir is not None:
try:
self.extract()
except Exception as err:
import traceback
traceback.print_exc()
msg = as_unicode(err)
self.found.emit(msg)
return
self.path = self.tdir
root = os.path.abspath(self.path) root = os.path.abspath(self.path)
try: try:
self.walk(root) self.walk(root)
@ -263,12 +292,16 @@ class Adder(QObject): # {{{
self.pd.canceled_signal.connect(self.canceled) self.pd.canceled_signal.connect(self.canceled)
def add_recursive(self, root, single=True): def add_recursive(self, root, single=True):
self.path = root if os.path.exists(root) and os.path.isfile(root) and root.lower().rpartition('.')[-1] in {'zip', 'rar'}:
self.path = tdir = PersistentTemporaryDirectory('_arcv_')
else:
self.path = root
tdir = None
self.pd.set_msg(_('Searching in all sub-directories...')) self.pd.set_msg(_('Searching in all sub-directories...'))
self.pd.set_min(0) self.pd.set_min(0)
self.pd.set_max(0) self.pd.set_max(0)
self.pd.value = 0 self.pd.value = 0
self.rfind = RecursiveFind(self, self.db, root, single) self.rfind = RecursiveFind(self, self.db, root, single, tdir=tdir)
self.rfind.update.connect(self.pd.set_msg, type=Qt.QueuedConnection) self.rfind.update.connect(self.pd.set_msg, type=Qt.QueuedConnection)
self.rfind.found.connect(self.add, type=Qt.QueuedConnection) self.rfind.found.connect(self.add, type=Qt.QueuedConnection)
self.rfind.start() self.rfind.start()