1#!/usr/local/bin/python3.8 2# vim:fileencoding=utf-8 3# License: GPLv3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net> 4 5 6import os 7 8from calibre.db.cli import integers_from_string 9from calibre.db.errors import NoSuchFormat 10from calibre.library.save_to_disk import ( 11 config, do_save_book_to_disk, get_formats, sanitize_args 12) 13from calibre.utils.formatter_functions import load_user_template_functions 14 15readonly = True 16version = 0 # change this if you change signature of implementation() 17 18 19def implementation(db, notify_changes, action, *args): 20 is_remote = notify_changes is not None 21 if action == 'all_ids': 22 return db.all_book_ids() 23 if action == 'setup': 24 book_id, formats = args 25 mi = db.get_metadata(book_id) 26 plugboards = db.pref('plugboards', {}) 27 formats = get_formats(db.formats(book_id), formats) 28 return mi, plugboards, formats, db.library_id, db.pref( 29 'user_template_functions', [] 30 ) 31 if action == 'cover': 32 return db.cover(args[0]) 33 if action == 'fmt': 34 book_id, fmt, dest = args 35 if is_remote: 36 return db.format(book_id, fmt) 37 db.copy_format_to(book_id, fmt, dest) 38 39 40def option_parser(get_parser, args): 41 parser = get_parser( 42 _( 43 '''\ 44%prog export [options] ids 45 46Export the books specified by ids (a comma separated list) to the filesystem. 47The export operation saves all formats of the book, its cover and metadata (in 48an opf file). You can get id numbers from the search command. 49''' 50 ) 51 ) 52 parser.add_option( 53 '--all', 54 default=False, 55 action='store_true', 56 help=_('Export all books in database, ignoring the list of ids.') 57 ) 58 parser.add_option( 59 '--to-dir', 60 default='.', 61 help=( 62 _('Export books to the specified folder. Default is') + ' %default' 63 ) 64 ) 65 parser.add_option( 66 '--single-dir', 67 default=False, 68 action='store_true', 69 help=_('Export all books into a single folder') 70 ) 71 parser.add_option( 72 '--progress', 73 default=False, 74 action='store_true', 75 help=_('Report progress') 76 ) 77 c = config() 78 for pref in ['asciiize', 'update_metadata', 'write_opf', 'save_cover']: 79 opt = c.get_option(pref) 80 switch = '--dont-' + pref.replace('_', '-') 81 parser.add_option( 82 switch, 83 default=True, 84 action='store_false', 85 help=opt.help + ' ' + 86 _('Specifying this switch will turn ' 87 'this behavior off.'), 88 dest=pref 89 ) 90 91 for pref in ['timefmt', 'template', 'formats']: 92 opt = c.get_option(pref) 93 switch = '--' + pref 94 parser.add_option(switch, default=opt.default, help=opt.help, dest=pref) 95 96 for pref in ('replace_whitespace', 'to_lowercase'): 97 opt = c.get_option(pref) 98 switch = '--' + pref.replace('_', '-') 99 parser.add_option(switch, default=False, action='store_true', help=opt.help) 100 101 return parser 102 103 104class DBProxy: 105 106 # Proxy to allow do_save_book_to_disk() to work with remote database 107 108 def __init__(self, dbctx): 109 self.dbctx = dbctx 110 111 def cover(self, book_id): 112 return self.dbctx.run('export', 'cover', book_id) 113 114 def copy_format_to(self, book_id, fmt, path): 115 fdata = self.dbctx.run('export', 'fmt', book_id, fmt, path) 116 if self.dbctx.is_remote: 117 if fdata is None: 118 raise NoSuchFormat(fmt) 119 with lopen(path, 'wb') as f: 120 f.write(fdata) 121 122 123def export(opts, dbctx, book_id, dest, dbproxy, length, first): 124 mi, plugboards, formats, library_id, template_funcs = dbctx.run( 125 'export', 'setup', book_id, opts.formats 126 ) 127 if dbctx.is_remote and first: 128 load_user_template_functions(library_id, template_funcs) 129 return do_save_book_to_disk( 130 dbproxy, book_id, mi, plugboards, formats, dest, opts, length 131 ) 132 133 134def main(opts, args, dbctx): 135 if len(args) < 1 and not opts.all: 136 raise SystemExit(_('You must specify some ids or the %s option') % '--all') 137 if opts.all: 138 book_ids = dbctx.run('export', 'all_ids') 139 else: 140 book_ids = set() 141 for arg in args: 142 book_ids |= set(integers_from_string(arg)) 143 dest = os.path.abspath(os.path.expanduser(opts.to_dir)) 144 dbproxy = DBProxy(dbctx) 145 dest, opts, length = sanitize_args(dest, opts) 146 total = len(book_ids) 147 for i, book_id in enumerate(book_ids): 148 export(opts, dbctx, book_id, dest, dbproxy, length, i == 0) 149 if opts.progress: 150 num = i + 1 151 print('\r {:.0%} [{}/{}]'.format(num / total, num, total), end=' '*20) 152 if opts.progress: 153 print() 154 return 0 155