mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-03 19:17:02 -05:00 
			
		
		
		
	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:
		
							parent
							
								
									bdb2d0a5d9
								
							
						
					
					
						commit
						8b7956f996
					
				@ -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.
 | 
				
			||||||
 | 
				
			|||||||
@ -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,
 | 
				
			||||||
 | 
				
			|||||||
@ -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()
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user