def open(self, connected_device, library_uuid): ''' Perform any device specific initialization. Called after the device is detected but before any other functions that communicate with the device. For example: For devices that present themselves as USB Mass storage devices, this method would be responsible for mounting the device or if the device has been automounted, for finding out where it has been mounted. The method :meth:`calibre.devices.usbms.device.Device.open` has an implementation of this function that should serve as a good example for USB Mass storage devices. This method can raise an OpenFeedback exception to display a message to the user. :param connected_device: The device that we are trying to open. It is a tuple of (vendor id, product id, bcd, manufacturer name, product name, device serial number). However, some devices have no serial number and on windows only the first three fields are present, the rest are None. :param library_uuid: The UUID of the current calibre library. Can be None if there is no library (for example when used from the command line). ''' logger.debug(sys._getframe().f_code.co_name) # At this point we know that the user has a valid network connection. # if not prefs['token']: # # if there is no access_token set, the user hasn't logged in. We can't do anything. # raise OpenFeedback('QuietThyme has not been authorized on this machine. Please open the plugin preferences to login.') response = ApiClient().auth(library_uuid, current_library_name()) #if everything is successful set is_connected to true. if not response['success']: raise OpenFeedback(response['error_msg']) else: prefs['token'] = response['data']['token'] self.is_connected = response['success'] status_response = ApiClient().status(library_uuid, current_library_name()) if not status_response['success']: raise OpenFeedback(status_response['error_msg']) #store the settings in memory self.current_library_uuid = library_uuid #mimic from #https://github.com/kovidgoyal/calibre/blob/master/src/calibre/devices/usbms/driver.py #https://github.com/kovidgoyal/calibre/blob/master/src/calibre/devices/usbms/device.py self.qt_settings = status_response['data']['settings']
def get_output_fields(self, db, opts): # Return a list of requested fields all_std_fields = {'author_sort','authors','comments','cover','formats', 'id','isbn','library_name','ondevice','pubdate','publisher', 'rating','series_index','series','size','tags','timestamp', 'title_sort','title','uuid','languages','identifiers'} all_custom_fields = set(db.custom_field_keys()) for field in list(all_custom_fields): fm = db.field_metadata[field] if fm['datatype'] == 'series': all_custom_fields.add(field+'_index') all_fields = all_std_fields.union(all_custom_fields) if opts.fields != 'all': # Make a list from opts.fields of = [x.strip() for x in opts.fields.split(',')] requested_fields = set(of) # Validate requested_fields if requested_fields - all_fields: from calibre.library import current_library_name invalid_fields = sorted(list(requested_fields - all_fields)) print("invalid --fields specified: %s" % ', '.join(invalid_fields)) print("available fields in '%s': %s" % (current_library_name(), ', '.join(sorted(list(all_fields))))) raise ValueError("unable to generate catalog with specified fields") fields = [x for x in of if x in all_fields] else: fields = sorted(all_fields, key=self._field_sorter) if not opts.connected_device['is_device_connected'] and 'ondevice' in fields: fields.pop(int(fields.index('ondevice'))) return fields
def remove_library_icon(self, name=''): try: with suppress(FileNotFoundError): os.remove(library_icon_path(name or current_library_name())) self.set_library_icon() except Exception: import traceback traceback.print_exc()
def set_cc_mapping(cc_name, field=None, combobox=None): ''' Store element to cc_name in prefs:cc_mappings ''' cc_mappings = plugin_prefs.get('cc_mappings', {}) current_library = current_library_name() if current_library in cc_mappings: cc_mappings[current_library][cc_name] = {'field': field, 'combobox': combobox} else: cc_mappings[current_library] = {cc_name: {'field': field, 'combobox': combobox}} plugin_prefs.set('cc_mappings', cc_mappings)
def set_cc_mapping(cc_name, field=None, combobox=None): """ Store element to cc_name in prefs:cc_mappings """ from calibre_plugins.marvin_manager.config import plugin_prefs cc_mappings = plugin_prefs.get("cc_mappings", {}) current_library = current_library_name() if current_library in cc_mappings: cc_mappings[current_library][cc_name] = {"field": field, "combobox": combobox} else: cc_mappings[current_library] = {cc_name: {"field": field, "combobox": combobox}} plugin_prefs.set("cc_mappings", cc_mappings)
def __setitem__(self, kind, value): if self.current_library == "": from calibre.library import current_library_name self.current_library = current_library_name() try: self.getfilenameprefs['pref_lib'][ self.current_library][kind] = value except KeyError: self.getfilenameprefs['pref_lib'][ self.current_library] = self.def_lib self.getfilenameprefs['pref_lib'][ self.current_library][kind] = value self.getfilenameprefs['pref_lib'][ self.current_library]['configured'] = True
def get_library_icon(self): try: paths = choose_images( self.gui, 'choose_library_icon', _('Select icon for library "%s"') % current_library_name()) if paths: path = paths[0] p = QIcon(path).pixmap(QSize(256, 256)) icp = library_icon_path() os.makedirs(os.path.dirname(icp), exist_ok=True) with open(icp, 'wb') as f: f.write(pixmap_to_data(p, format='PNG')) self.set_library_icon() except Exception: import traceback traceback.print_exc()
def get_cc_mapping(cc_name, element, default=None): ''' Return the element mapped to cc_name in prefs:cc_mappings ''' if element not in ['field', 'combobox']: raise ValueError("invalid element '{0}' requested for custom column '{1}'".format( element, cc_name)) ans = default cc_mappings = plugin_prefs.get('cc_mappings', {}) current_library = current_library_name() if (current_library in cc_mappings and cc_name in cc_mappings[current_library] and element in cc_mappings[current_library][cc_name]): ans = cc_mappings[current_library][cc_name][element] return ans
def get_library_config(db): from calibre.library import current_library_name prefs = prefs_a.GetFileName_Prefs(current_library_name()) try: schema = prefs[KEY_SCHEMA_VERSION] except KeyError: schema = "" if (schema != DEFAULT_SCHEMA_VERSION): migrate(db, prefs) prefs.set('configured', True) prefs.writeprefs() return prefs
def get_cc_mapping(cc_name, element, default=None): """ Return the element mapped to cc_name in prefs """ from calibre_plugins.marvin_manager.config import plugin_prefs if element not in ["field", "combobox"]: raise ValueError("invalid element '{0}' requested for custom column '{1}'".format(element, cc_name)) ans = default cc_mappings = plugin_prefs.get("cc_mappings", {}) current_library = current_library_name() if ( current_library in cc_mappings and cc_name in cc_mappings[current_library] and element in cc_mappings[current_library][cc_name] ): ans = cc_mappings[current_library][cc_name][element] return ans
def set_cc_mapping(cc_name, field=None, combobox=None): ''' Store element to cc_name in prefs:cc_mappings ''' from calibre_plugins.marvin_manager.config import plugin_prefs cc_mappings = plugin_prefs.get('cc_mappings', {}) current_library = current_library_name() if current_library in cc_mappings: cc_mappings[current_library][cc_name] = { 'field': field, 'combobox': combobox } else: cc_mappings[current_library] = { cc_name: { 'field': field, 'combobox': combobox } } plugin_prefs.set('cc_mappings', cc_mappings)
def __getitem__(self, kind=None): if self.current_library == "": from calibre.library import current_library_name self.current_library = current_library_name() try: pref_lib = self.getfilenameprefs['pref_lib'][self.current_library] except KeyError: for item in self.def_lib: self.def_lib[item] = self.getfilenameprefs[item] self.getfilenameprefs['pref_lib'][ self.current_library] = self.def_lib pref_lib = self.def_lib # Aqui leer la primera vez de base de datos pref_lib['configured'] = False print("Prefs: ", pref_lib) if kind is not None: try: return pref_lib[kind] except KeyError: return None return pref_lib
def evaluate(self, formatter, kwargs, mi, locals): from calibre.library import current_library_name return current_library_name()
def __init__(self): from calibre.library import current_library_name QWidget.__init__(self) actual_plugin = 'calibre_plugins.getfilename.action:GetFileNameAction' self.plugin_action = actual_plugin # get the prefs self.prefs = prefs_a.GetFileName_Prefs(current_library_name()) if (self.prefs['configured'] == False): try: from calibre.gui2.ui import get_gui db = get_gui().current_db self.prefs = get_library_config(db) except: try: from calibre.library import db self.prefs = get_library_config(db()) except: self.prefs = prefs_a.GetFileName_Prefs( current_library_name()) for key, col in six.iteritems(DEFAULT_MIGRATION): self.prefs.set(col, DEFAULT_LIBRARY_VALUES[key]) self.prefs.set('OPC_PREF', 'name') self.prefs.set('configured', True) self.prefs.set(KEY_SCHEMA_VERSION, DEFAULT_SCHEMA_VERSION) self.prefs.writeprefs() self.filename_col = self.prefs[NAME_PREF] self.extension_col = self.prefs[EXT_PREF] self.path_col = self.prefs[PATH_PREF] self.date_col = self.prefs[DATE_PREF] self.option_name = self.prefs[OPC_PREF] # Start Qt Gui dialog layout layout = QVBoxLayout(self) self.setLayout(layout) # -- Options -- # # --- File --- avail_columns_text = self.get_custom_columns_text() avail_columns_date = self.get_custom_columns_date() filename_group_box = QGroupBox(_('File name options:'), self) layout.addWidget(filename_group_box) filename_group_box_layout = QGridLayout() filename_group_box.setLayout(filename_group_box_layout) pos = 0 self.path_checkbox = QCheckBox(_("Include folder"), self) self.path_checkbox.setTristate(False) self.path_checkbox.setToolTip( _("It indicates it stores the folder with the filename.")) self.path_checkbox.stateChanged.connect(self.path_checkbox_clicked) filename_group_box_layout.addWidget(self.path_checkbox, pos, 0, 1, 1) pos = pos + 1 fname_column_label = QLabel(_('&File name:'), self) fname_column_label.setToolTip( _('Custom text column for storing the filename and folder if included' )) fname_col = self.filename_col self.fname_column_combo = CustomColumnComboBox(self, avail_columns_text, fname_col) fname_column_label.setBuddy(self.fname_column_combo) filename_group_box_layout.addWidget(fname_column_label, pos, 0, 1, 1) filename_group_box_layout.addWidget(self.fname_column_combo, pos, 1, 1, 2) self.fname_column_combo.currentIndexChanged.connect( self.filename_changed) pos = pos + 1 fexten_column_label = QLabel(_('File &Extension:'), self) fexten_column_label.setToolTip( _('Custom text column for storing the extension (if empty the filename is not splited). Not used if file name column is empty' )) fexten_col = self.extension_col self.fexten_column_combo = CustomColumnComboBox( self, avail_columns_text, fexten_col) fexten_column_label.setBuddy(self.fexten_column_combo) filename_group_box_layout.addWidget(fexten_column_label, pos, 0, 1, 1) filename_group_box_layout.addWidget(self.fexten_column_combo, pos, 1, 1, 2) if (self.filename_col == ""): self.fexten_column_combo.setEnabled(False) else: self.fexten_column_combo.setEnabled(True) pos = pos + 1 fpath_column_label = QLabel(_('File &Folder:'), self) fpath_column_label.setToolTip( _('Custom text column for storing the folder (if empty the filename is not splited). Not used if file name column is empty' )) fpath_col = self.path_col self.fpath_column_combo = CustomColumnComboBox(self, avail_columns_text, fpath_col) fpath_column_label.setBuddy(self.fpath_column_combo) filename_group_box_layout.addWidget(fpath_column_label, pos, 0, 1, 1) filename_group_box_layout.addWidget(self.fpath_column_combo, pos, 1, 1, 2) date_column_group = QGroupBox(self) layout.addWidget(date_column_group) date_layout = QGridLayout() date_column_group.setLayout(date_layout) fdate_column_label = QLabel(_('File &Date:'), self) fdate_column_label.setToolTip( _('Custom date column for storing the last modified date (if empty the date is not stored)' )) fdate_col = self.date_col self.fdate_column_combo = CustomColumnComboBox(self, avail_columns_date, fdate_col) fdate_column_label.setBuddy(self.fdate_column_combo) date_layout.addWidget(fdate_column_label, 2, 0, 1, 1) date_layout.addWidget(self.fdate_column_combo, 2, 1, 1, 2) if (self.option_name == 'path'): self.path_checkbox.setChecked(True) if (self.fname_column_combo.currentIndex() == 0): self.fpath_column_combo.setEnabled(False) else: self.fpath_column_combo.setEnabled(True) else: self.path_checkbox.setChecked(False) self.fpath_column_combo.setEnabled(False) layout.addStretch(1)
def initialize(self, library_path, db, listener, actions, show_gui=True, splash_screen=None): opts = self.opts self.preferences_action, self.quit_action = actions self.library_path = library_path self.content_server = None self.spare_servers = [] self.must_restart_before_config = False self.listener = Listener(listener) self.check_messages_timer = QTimer() self.check_messages_timer.timeout.connect( self.another_instance_wants_to_talk) self.check_messages_timer.start(1000) for ac in self.iactions.values(): try: ac.do_genesis() except Exception: # Ignore errors in third party plugins import traceback traceback.print_exc() if getattr(ac, 'plugin_path', None) is None: raise self.donate_action = QAction(QIcon(I('donate.png')), _('&Donate to support calibre'), self) for st in self.istores.values(): st.do_genesis() MainWindowMixin.init_main_window_mixin(self, db) # Jobs Button {{{ self.job_manager = JobManager() self.jobs_dialog = JobsDialog(self, self.job_manager) self.jobs_button = JobsButton(horizontal=True, parent=self) self.jobs_button.initialize(self.jobs_dialog, self.job_manager) # }}} LayoutMixin.init_layout_mixin(self) DeviceMixin.init_device_mixin(self) self.progress_indicator = ProgressIndicator(self) self.progress_indicator.pos = (0, 20) self.verbose = opts.verbose self.get_metadata = GetMetadata() self.upload_memory = {} self.metadata_dialogs = [] self.default_thumbnail = None self.tb_wrapper = textwrap.TextWrapper(width=40) self.viewers = collections.deque() self.system_tray_icon = SystemTrayIcon(QIcon(I('lt.png')), self) self.system_tray_icon.setToolTip('calibre') self.system_tray_icon.tooltip_requested.connect( self.job_manager.show_tooltip) systray_ok = config['systray_icon'] and not (islinux or isbsd) # System tray icons are broken on linux, see # https://bugreports.qt-project.org/browse/QTBUG-31762 if not systray_ok: self.system_tray_icon.hide() else: self.system_tray_icon.show() self.system_tray_menu = QMenu(self) self.restore_action = self.system_tray_menu.addAction( QIcon(I('page.png')), _('&Restore')) self.system_tray_menu.addAction(self.donate_action) self.donate_button.setDefaultAction(self.donate_action) self.donate_button.setStatusTip(self.donate_button.toolTip()) self.eject_action = self.system_tray_menu.addAction( QIcon(I('eject.png')), _('&Eject connected device')) self.eject_action.setEnabled(False) self.addAction(self.quit_action) self.system_tray_menu.addAction(self.quit_action) self.keyboard.register_shortcut('quit calibre', _('Quit calibre'), default_keys=('Ctrl+Q', ), action=self.quit_action) self.system_tray_icon.setContextMenu(self.system_tray_menu) self.quit_action.triggered[bool].connect(self.quit) self.donate_action.triggered[bool].connect(self.donate) self.restore_action.triggered.connect(self.show_windows) self.system_tray_icon.activated.connect( self.system_tray_icon_activated) self.esc_action = QAction(self) self.addAction(self.esc_action) self.keyboard.register_shortcut('clear current search', _('Clear the current search'), default_keys=('Esc', ), action=self.esc_action) self.esc_action.triggered.connect(self.esc) self.shift_esc_action = QAction(self) self.addAction(self.shift_esc_action) self.keyboard.register_shortcut('focus book list', _('Focus the book list'), default_keys=('Shift+Esc', ), action=self.shift_esc_action) self.shift_esc_action.triggered.connect(self.shift_esc) self.ctrl_esc_action = QAction(self) self.addAction(self.ctrl_esc_action) self.keyboard.register_shortcut('clear virtual library', _('Clear the virtual library'), default_keys=('Ctrl+Esc', ), action=self.ctrl_esc_action) self.ctrl_esc_action.triggered.connect(self.ctrl_esc) self.alt_esc_action = QAction(self) self.addAction(self.alt_esc_action) self.keyboard.register_shortcut('clear additional restriction', _('Clear the additional restriction'), default_keys=('Alt+Esc', ), action=self.alt_esc_action) self.alt_esc_action.triggered.connect( self.clear_additional_restriction) # ###################### Start spare job server ######################## QTimer.singleShot(1000, self.add_spare_server) # ###################### Location Manager ######################## self.location_manager.location_selected.connect(self.location_selected) self.location_manager.unmount_device.connect( self.device_manager.umount_device) self.location_manager.configure_device.connect( self.configure_connected_device) self.location_manager.update_device_metadata.connect( self.update_metadata_on_device) self.eject_action.triggered.connect(self.device_manager.umount_device) # ################### Update notification ################### UpdateMixin.init_update_mixin(self, opts) # ###################### Search boxes ######################## SearchRestrictionMixin.init_search_restirction_mixin(self) SavedSearchBoxMixin.init_saved_seach_box_mixin(self) # ###################### Library view ######################## LibraryViewMixin.init_library_view_mixin(self, db) SearchBoxMixin.init_search_box_mixin(self) # Requires current_db if show_gui: self.show() if splash_screen is not None: splash_screen.hide() if self.system_tray_icon.isVisible() and opts.start_in_tray: self.hide_windows() self.library_view.model().count_changed_signal.connect( self.iactions['Choose Library'].count_changed) if not gprefs.get('quick_start_guide_added', False): try: add_quick_start_guide(self.library_view, getattr(self, 'db_images', None)) except: import traceback traceback.print_exc() for view in ('library', 'memory', 'card_a', 'card_b'): v = getattr(self, '%s_view' % view) v.selectionModel().selectionChanged.connect(self.update_status_bar) v.model().count_changed_signal.connect(self.update_status_bar) self.library_view.model().count_changed() self.bars_manager.database_changed(self.library_view.model().db) self.library_view.model().database_changed.connect( self.bars_manager.database_changed, type=Qt.QueuedConnection) # ########################## Tags Browser ############################## TagBrowserMixin.init_tag_browser_mixin(self, db) # ######################## Search Restriction ########################## if db.prefs['virtual_lib_on_startup']: self.apply_virtual_library(db.prefs['virtual_lib_on_startup']) self.rebuild_vl_tabs() # ########################## Cover Flow ################################ CoverFlowMixin.init_cover_flow_mixin(self) self._calculated_available_height = min(max_available_height() - 15, self.height()) self.resize(self.width(), self._calculated_available_height) self.build_context_menus() for ac in self.iactions.values(): try: ac.gui_layout_complete() except: import traceback traceback.print_exc() if ac.plugin_path is None: raise if config['autolaunch_server']: self.start_content_server() self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection) self.read_settings() self.finalize_layout() if self.bars_manager.showing_donate: self.donate_button.start_animation() self.set_window_title() for ac in self.iactions.values(): try: ac.initialization_complete() except: import traceback traceback.print_exc() if ac.plugin_path is None: raise self.set_current_library_information(current_library_name(), db.library_id, db.field_metadata) self.keyboard.finalize() self.auto_adder = AutoAdder(gprefs['auto_add_path'], self) self.save_layout_state() # Collect cycles now gc.collect() if show_gui and self.gui_debug is not None: info_dialog( self, _('Debug mode'), '<p>' + _('You have started calibre in debug mode. After you ' 'quit calibre, the debug log will be available in ' 'the file: %s<p>The ' 'log will be displayed automatically.') % self.gui_debug, show=True) self.iactions['Connect Share'].check_smartdevice_menus() QTimer.singleShot(1, self.start_smartdevice)
def run(self, path_to_output, opts, db, notification=DummyReporter()): from calibre.library.catalogs.epub_mobi_builder import CatalogBuilder from calibre.utils.logging import default_log as log from calibre.utils.config import JSONConfig # If preset specified from the cli, insert stored options from JSON file if hasattr(opts, "preset") and opts.preset: available_presets = JSONConfig("catalog_presets") if not opts.preset in available_presets: if available_presets: print(_('Error: Preset "%s" not found.' % opts.preset)) print(_("Stored presets: %s" % ", ".join([p for p in sorted(available_presets.keys())]))) else: print(_("Error: No stored presets.")) return 1 # Copy the relevant preset values to the opts object for item in available_presets[opts.preset]: if not item in ["exclusion_rules_tw", "format", "prefix_rules_tw"]: setattr(opts, item, available_presets[opts.preset][item]) # Provide an unconnected device opts.connected_device = { "is_device_connected": False, "kind": None, "name": None, "save_template": None, "serial": None, "storage": None, } # Convert prefix_rules and exclusion_rules from JSON lists to tuples prs = [] for rule in opts.prefix_rules: prs.append(tuple(rule)) opts.prefix_rules = tuple(prs) ers = [] for rule in opts.exclusion_rules: ers.append(tuple(rule)) opts.exclusion_rules = tuple(ers) opts.log = log opts.fmt = self.fmt = path_to_output.rpartition(".")[2] # Add local options opts.creator = "%s, %s %s, %s" % (strftime("%A"), strftime("%B"), strftime("%d").lstrip("0"), strftime("%Y")) opts.creator_sort_as = "%s %s" % ("calibre", strftime("%Y-%m-%d")) opts.connected_kindle = False # Finalize output_profile op = opts.output_profile if op is None: op = "default" if opts.connected_device["name"] and "kindle" in opts.connected_device["name"].lower(): opts.connected_kindle = True if opts.connected_device["serial"] and opts.connected_device["serial"][:4] in ["B004", "B005"]: op = "kindle_dx" else: op = "kindle" opts.description_clip = 380 if op.endswith("dx") or "kindle" not in op else 100 opts.author_clip = 100 if op.endswith("dx") or "kindle" not in op else 60 opts.output_profile = op opts.basename = "Catalog" opts.cli_environment = not hasattr(opts, "sync") # Hard-wired to always sort descriptions by author, with series after non-series opts.sort_descriptions_by_author = True build_log = [] build_log.append( "%s('%s'): Generating %s %sin %s environment, locale: '%s'" % ( self.name, current_library_name(), self.fmt, "for %s " % opts.output_profile if opts.output_profile else "", "CLI" if opts.cli_environment else "GUI", calibre_langcode_to_name(canonicalize_lang(get_lang()), localize=False), ) ) # If exclude_genre is blank, assume user wants all tags as genres if opts.exclude_genre.strip() == "": # opts.exclude_genre = '\[^.\]' # build_log.append(" converting empty exclude_genre to '\[^.\]'") opts.exclude_genre = "a^" build_log.append(" converting empty exclude_genre to 'a^'") if opts.connected_device["is_device_connected"] and opts.connected_device["kind"] == "device": if opts.connected_device["serial"]: build_log.append( " connected_device: '%s' #%s%s " % ( opts.connected_device["name"], opts.connected_device["serial"][0:4], "x" * (len(opts.connected_device["serial"]) - 4), ) ) for storage in opts.connected_device["storage"]: if storage: build_log.append(" mount point: %s" % storage) else: build_log.append(" connected_device: '%s'" % opts.connected_device["name"]) try: for storage in opts.connected_device["storage"]: if storage: build_log.append(" mount point: %s" % storage) except: build_log.append(" (no mount points)") else: build_log.append(" connected_device: '%s'" % opts.connected_device["name"]) opts_dict = vars(opts) if opts_dict["ids"]: build_log.append(" book count: %d" % len(opts_dict["ids"])) sections_list = [] if opts.generate_authors: sections_list.append("Authors") if opts.generate_titles: sections_list.append("Titles") if opts.generate_series: sections_list.append("Series") if opts.generate_genres: sections_list.append("Genres") if opts.generate_recently_added: sections_list.append("Recently Added") if opts.generate_descriptions: sections_list.append("Descriptions") if not sections_list: if opts.cli_environment: opts.log.warn("*** No Section switches specified, enabling all Sections ***") opts.generate_authors = True opts.generate_titles = True opts.generate_series = True opts.generate_genres = True opts.generate_recently_added = True opts.generate_descriptions = True sections_list = ["Authors", "Titles", "Series", "Genres", "Recently Added", "Descriptions"] else: opts.log.warn("\n*** No enabled Sections, terminating catalog generation ***") return ["No Included Sections", "No enabled Sections.\nCheck E-book options tab\n'Included sections'\n"] if opts.fmt == "mobi" and sections_list == ["Descriptions"]: warning = _("\n*** Adding 'By Authors' Section required for MOBI output ***") opts.log.warn(warning) sections_list.insert(0, "Authors") opts.generate_authors = True opts.log(" Sections: %s" % ", ".join(sections_list)) opts.section_list = sections_list # Limit thumb_width to 1.0" - 2.0" try: if float(opts.thumb_width) < float(self.THUMB_SMALLEST): log.warning("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_SMALLEST)) opts.thumb_width = self.THUMB_SMALLEST if float(opts.thumb_width) > float(self.THUMB_LARGEST): log.warning("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_LARGEST)) opts.thumb_width = self.THUMB_LARGEST opts.thumb_width = "%.2f" % float(opts.thumb_width) except: log.error("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_SMALLEST)) opts.thumb_width = "1.0" # eval prefix_rules if passed from command line if type(opts.prefix_rules) is not tuple: try: opts.prefix_rules = eval(opts.prefix_rules) except: log.error("malformed --prefix-rules: %s" % opts.prefix_rules) raise for rule in opts.prefix_rules: if len(rule) != 4: log.error("incorrect number of args for --prefix-rules: %s" % repr(rule)) # eval exclusion_rules if passed from command line if type(opts.exclusion_rules) is not tuple: try: opts.exclusion_rules = eval(opts.exclusion_rules) except: log.error("malformed --exclusion-rules: %s" % opts.exclusion_rules) raise for rule in opts.exclusion_rules: if len(rule) != 3: log.error("incorrect number of args for --exclusion-rules: %s" % repr(rule)) # Display opts keys = sorted(opts_dict.keys()) build_log.append(" opts:") for key in keys: if key in [ "catalog_title", "author_clip", "connected_kindle", "creator", "cross_reference_authors", "description_clip", "exclude_book_marker", "exclude_genre", "exclude_tags", "exclusion_rules", "fmt", "genre_source_field", "header_note_source_field", "merge_comments_rule", "output_profile", "prefix_rules", "preset", "read_book_marker", "search_text", "sort_by", "sort_descriptions_by_author", "sync", "thumb_width", "use_existing_cover", "wishlist_tag", ]: build_log.append(" %s: %s" % (key, repr(opts_dict[key]))) if opts.verbose: log("\n".join(line for line in build_log)) # Capture start_time opts.start_time = time.time() self.opts = opts if opts.verbose: log.info( " Begin catalog source generation (%s)" % str(datetime.timedelta(seconds=int(time.time() - opts.start_time))) ) # Launch the Catalog builder catalog = CatalogBuilder(db, opts, self, report_progress=notification) try: catalog.build_sources() if opts.verbose: log.info( " Completed catalog source generation (%s)\n" % str(datetime.timedelta(seconds=int(time.time() - opts.start_time))) ) except (AuthorSortMismatchException, EmptyCatalogException), e: log.error(" *** Terminated catalog generation: %s ***" % e)
def run(self, path_to_output, opts, db, notification=DummyReporter()): from calibre.library import current_library_name from calibre.utils.date import isoformat from calibre.utils.html2text import html2text from calibre.utils.logging import default_log as log from lxml import etree from calibre.ebooks.metadata import authors_to_string self.fmt = path_to_output.rpartition('.')[2] self.notification = notification current_library = current_library_name() if getattr(opts, 'library_path', None): current_library = os.path.basename(opts.library_path) if opts.verbose: opts_dict = vars(opts) log("%s('%s'): Generating %s" % (self.name, current_library, self.fmt.upper())) if opts.connected_device['is_device_connected']: log(" connected_device: %s" % opts.connected_device['name']) if opts_dict['search_text']: log(" --search='%s'" % opts_dict['search_text']) if opts_dict['ids']: log(" Book count: %d" % len(opts_dict['ids'])) if opts_dict['search_text']: log(" (--search ignored when a subset of the database is specified)") if opts_dict['fields']: if opts_dict['fields'] == 'all': log(" Fields: %s" % ', '.join(FIELDS[1:])) else: log(" Fields: %s" % opts_dict['fields']) # If a list of ids are provided, don't use search_text if opts.ids: opts.search_text = None data = self.search_sort_db(db, opts) if not len(data): log.error("\nNo matching database entries for search criteria '%s'" % opts.search_text) # raise SystemExit(1) # Get the requested output fields as a list fields = self.get_output_fields(db, opts) # If connected device, add 'On Device' values to data if opts.connected_device['is_device_connected'] and 'ondevice' in fields: for entry in data: entry['ondevice'] = db.catalog_plugin_on_device_temp_mapping[entry['id']]['ondevice'] fm = {x: db.field_metadata.get(x, {}) for x in fields} if self.fmt == 'csv': outfile = codecs.open(path_to_output, 'w', 'utf8') # Write a UTF-8 BOM outfile.write('\xef\xbb\xbf') # Output the field headers outfile.write(u'%s\n' % u','.join(fields)) # Output the entry fields for entry in data: outstr = [] for field in fields: if field.startswith('#'): item = db.get_field(entry['id'], field, index_is_id=True) if isinstance(item, (list, tuple)): if fm.get(field, {}).get('display', {}).get('is_names', False): item = ' & '.join(item) else: item = ', '.join(item) elif field == 'library_name': item = current_library elif field == 'title_sort': item = entry['sort'] else: item = entry[field] if item is None: outstr.append('""') continue elif field == 'formats': fmt_list = [] for format in item: fmt_list.append(format.rpartition('.')[2].lower()) item = ', '.join(fmt_list) elif field == 'authors': item = authors_to_string(item) elif field == 'tags': item = ', '.join(item) elif field == 'isbn': # Could be 9, 10 or 13 digits, with hyphens, possibly ending in 'X' item = u'%s' % re.sub(r'[^\dX-]', '', item) elif fm.get(field, {}).get('datatype') == 'datetime': item = isoformat(item, as_utc=False) elif field == 'comments': item = item.replace(u'\r\n', u' ') item = item.replace(u'\n', u' ') elif fm.get(field, {}).get('datatype', None) == 'rating' and item: item = u'%.2g' % (item / 2.0) # Convert HTML to markdown text if type(item) is unicode: opening_tag = re.search('<(\w+)(\x20|>)', item) if opening_tag: closing_tag = re.search('<\/%s>$' % opening_tag.group(1), item) if closing_tag: item = html2text(item) outstr.append(u'"%s"' % unicode(item).replace('"', '""')) outfile.write(u','.join(outstr) + u'\n') outfile.close() elif self.fmt == 'xml': from lxml.builder import E root = E.calibredb() for r in data: record = E.record() root.append(record) for field in fields: if field.startswith('#'): val = db.get_field(r['id'], field, index_is_id=True) if not isinstance(val, (str, unicode)): val = unicode(val) item = getattr(E, field.replace('#', '_'))(val) record.append(item) for field in ('id', 'uuid', 'publisher', 'rating', 'size', 'isbn', 'ondevice', 'identifiers'): if field in fields: val = r[field] if not val: continue if not isinstance(val, (str, unicode)): if (fm.get(field, {}).get('datatype', None) == 'rating' and val): val = u'%.2g' % (val / 2.0) val = unicode(val) item = getattr(E, field)(val) record.append(item) if 'title' in fields: title = E.title(r['title'], sort=r['sort']) record.append(title) if 'authors' in fields: aus = E.authors(sort=r['author_sort']) for au in r['authors']: aus.append(E.author(au)) record.append(aus) for field in ('timestamp', 'pubdate'): if field in fields: record.append(getattr(E, field)(isoformat(r[field], as_utc=False))) if 'tags' in fields and r['tags']: tags = E.tags() for tag in r['tags']: tags.append(E.tag(tag)) record.append(tags) if 'comments' in fields and r['comments']: record.append(E.comments(r['comments'])) if 'series' in fields and r['series']: record.append(E.series(r['series'], index=str(r['series_index']))) if 'cover' in fields and r['cover']: record.append(E.cover(r['cover'].replace(os.sep, '/'))) if 'formats' in fields and r['formats']: fmt = E.formats() for f in r['formats']: fmt.append(E.format(f.replace(os.sep, '/'))) record.append(fmt) if 'library_name' in fields: record.append(E.library_name(current_library)) with open(path_to_output, 'w') as f: f.write(etree.tostring(root, encoding='utf-8', xml_declaration=True, pretty_print=True))
def writeprefs(self, value=True): if (self.current_library == ""): from calibre.library import current_library_name self.current_library = current_library_name() self.getfilenameprefs['configured'] = value
def initialize(self, library_path, db, listener, actions, show_gui=True): opts = self.opts self.preferences_action, self.quit_action = actions self.library_path = library_path self.library_broker = GuiLibraryBroker(db) self.content_server = None self.server_change_notification_timer = t = QTimer(self) self.server_changes = Queue() t.setInterval(1000), t.timeout.connect(self.handle_changes_from_server_debounced), t.setSingleShot(True) self._spare_pool = None self.must_restart_before_config = False self.listener = Listener(listener) self.check_messages_timer = QTimer() self.check_messages_timer.timeout.connect(self.another_instance_wants_to_talk) self.check_messages_timer.start(1000) for ac in self.iactions.values(): try: ac.do_genesis() except Exception: # Ignore errors in third party plugins import traceback traceback.print_exc() if getattr(ac, 'plugin_path', None) is None: raise self.donate_action = QAction(QIcon(I('donate.png')), _('&Donate to support calibre'), self) for st in self.istores.values(): st.do_genesis() MainWindowMixin.init_main_window_mixin(self, db) # Jobs Button {{{ self.job_manager = JobManager() self.jobs_dialog = JobsDialog(self, self.job_manager) self.jobs_button = JobsButton(parent=self) self.jobs_button.initialize(self.jobs_dialog, self.job_manager) # }}} LayoutMixin.init_layout_mixin(self) DeviceMixin.init_device_mixin(self) self.progress_indicator = ProgressIndicator(self) self.progress_indicator.pos = (0, 20) self.verbose = opts.verbose self.get_metadata = GetMetadata() self.upload_memory = {} self.metadata_dialogs = [] self.default_thumbnail = None self.tb_wrapper = textwrap.TextWrapper(width=40) self.viewers = collections.deque() self.system_tray_icon = None do_systray = config['systray_icon'] or opts.start_in_tray if do_systray: self.system_tray_icon = factory(app_id='com.calibre-ebook.gui').create_system_tray_icon(parent=self, title='calibre') if self.system_tray_icon is not None: self.system_tray_icon.setIcon(QIcon(I('lt.png', allow_user_override=False))) if not (iswindows or isosx): self.system_tray_icon.setIcon(QIcon.fromTheme('calibre-tray', self.system_tray_icon.icon())) self.system_tray_icon.setToolTip(self.jobs_button.tray_tooltip()) self.system_tray_icon.setVisible(True) self.jobs_button.tray_tooltip_updated.connect(self.system_tray_icon.setToolTip) elif do_systray: prints('Failed to create system tray icon, your desktop environment probably' ' does not support the StatusNotifier spec https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/') self.system_tray_menu = QMenu(self) self.toggle_to_tray_action = self.system_tray_menu.addAction(QIcon(I('page.png')), '') self.toggle_to_tray_action.triggered.connect(self.system_tray_icon_activated) self.system_tray_menu.addAction(self.donate_action) self.eject_action = self.system_tray_menu.addAction( QIcon(I('eject.png')), _('&Eject connected device')) self.eject_action.setEnabled(False) self.addAction(self.quit_action) self.system_tray_menu.addAction(self.quit_action) self.keyboard.register_shortcut('quit calibre', _('Quit calibre'), default_keys=('Ctrl+Q',), action=self.quit_action) if self.system_tray_icon is not None: self.system_tray_icon.setContextMenu(self.system_tray_menu) self.system_tray_icon.activated.connect(self.system_tray_icon_activated) self.quit_action.triggered[bool].connect(self.quit) self.donate_action.triggered[bool].connect(self.donate) self.minimize_action = QAction(_('Minimize the calibre window'), self) self.addAction(self.minimize_action) self.keyboard.register_shortcut('minimize calibre', self.minimize_action.text(), default_keys=(), action=self.minimize_action) self.minimize_action.triggered.connect(self.showMinimized) self.esc_action = QAction(self) self.addAction(self.esc_action) self.keyboard.register_shortcut('clear current search', _('Clear the current search'), default_keys=('Esc',), action=self.esc_action) self.esc_action.triggered.connect(self.esc) self.shift_esc_action = QAction(self) self.addAction(self.shift_esc_action) self.keyboard.register_shortcut('focus book list', _('Focus the book list'), default_keys=('Shift+Esc',), action=self.shift_esc_action) self.shift_esc_action.triggered.connect(self.shift_esc) self.ctrl_esc_action = QAction(self) self.addAction(self.ctrl_esc_action) self.keyboard.register_shortcut('clear virtual library', _('Clear the virtual library'), default_keys=('Ctrl+Esc',), action=self.ctrl_esc_action) self.ctrl_esc_action.triggered.connect(self.ctrl_esc) self.alt_esc_action = QAction(self) self.addAction(self.alt_esc_action) self.keyboard.register_shortcut('clear additional restriction', _('Clear the additional restriction'), default_keys=('Alt+Esc',), action=self.alt_esc_action) self.alt_esc_action.triggered.connect(self.clear_additional_restriction) # ###################### Start spare job server ######################## QTimer.singleShot(1000, self.create_spare_pool) # ###################### Location Manager ######################## self.location_manager.location_selected.connect(self.location_selected) self.location_manager.unmount_device.connect(self.device_manager.umount_device) self.location_manager.configure_device.connect(self.configure_connected_device) self.location_manager.update_device_metadata.connect(self.update_metadata_on_device) self.eject_action.triggered.connect(self.device_manager.umount_device) # ################### Update notification ################### UpdateMixin.init_update_mixin(self, opts) # ###################### Search boxes ######################## SearchRestrictionMixin.init_search_restriction_mixin(self) SavedSearchBoxMixin.init_saved_seach_box_mixin(self) # ###################### Library view ######################## LibraryViewMixin.init_library_view_mixin(self, db) SearchBoxMixin.init_search_box_mixin(self) # Requires current_db self.library_view.model().count_changed_signal.connect( self.iactions['Choose Library'].count_changed) if not gprefs.get('quick_start_guide_added', False): try: add_quick_start_guide(self.library_view) except: import traceback traceback.print_exc() for view in ('library', 'memory', 'card_a', 'card_b'): v = getattr(self, '%s_view' % view) v.selectionModel().selectionChanged.connect(self.update_status_bar) v.model().count_changed_signal.connect(self.update_status_bar) self.library_view.model().count_changed() self.bars_manager.database_changed(self.library_view.model().db) self.library_view.model().database_changed.connect(self.bars_manager.database_changed, type=Qt.QueuedConnection) # ########################## Tags Browser ############################## TagBrowserMixin.init_tag_browser_mixin(self, db) self.library_view.model().database_changed.connect(self.populate_tb_manage_menu, type=Qt.QueuedConnection) # ######################## Search Restriction ########################## if db.prefs['virtual_lib_on_startup']: self.apply_virtual_library(db.prefs['virtual_lib_on_startup']) self.rebuild_vl_tabs() # ########################## Cover Flow ################################ CoverFlowMixin.init_cover_flow_mixin(self) self._calculated_available_height = min(max_available_height()-15, self.height()) self.resize(self.width(), self._calculated_available_height) self.build_context_menus() for ac in self.iactions.values(): try: ac.gui_layout_complete() except: import traceback traceback.print_exc() if ac.plugin_path is None: raise if config['autolaunch_server']: self.start_content_server() self.read_settings() self.finalize_layout() self.bars_manager.start_animation() self.set_window_title() for ac in self.iactions.values(): try: ac.initialization_complete() except: import traceback traceback.print_exc() if ac.plugin_path is None: raise self.set_current_library_information(current_library_name(), db.library_id, db.field_metadata) register_keyboard_shortcuts() self.keyboard.finalize() if show_gui: # Note this has to come after restoreGeometry() because of # https://bugreports.qt.io/browse/QTBUG-56831 self.show() if self.system_tray_icon is not None and self.system_tray_icon.isVisible() and opts.start_in_tray: self.hide_windows() self.auto_adder = AutoAdder(gprefs['auto_add_path'], self) # Now that the gui is initialized we can restore the quickview state # The same thing will be true for any action-based operation with a # layout button from calibre.gui2.actions.show_quickview import get_quickview_action_plugin qv = get_quickview_action_plugin() if qv: qv.qv_button.restore_state() self.save_layout_state() # Collect cycles now gc.collect() QApplication.instance().shutdown_signal_received.connect(self.quit) if show_gui and self.gui_debug is not None: QTimer.singleShot(10, self.show_gui_debug_msg) self.iactions['Connect Share'].check_smartdevice_menus() QTimer.singleShot(1, self.start_smartdevice) QTimer.singleShot(100, self.update_toggle_to_tray_action)
def run(self, path_to_output, opts, db, notification=DummyReporter()): from calibre.library.catalogs.epub_mobi_builder import CatalogBuilder from calibre.utils.logging import default_log as log opts.log = log opts.fmt = self.fmt = path_to_output.rpartition('.')[2] # Add local options opts.creator = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y')) opts.creator_sort_as = '%s %s' % ('calibre', strftime('%Y-%m-%d')) opts.connected_kindle = False # Finalize output_profile op = opts.output_profile if op is None: op = 'default' if opts.connected_device['name'] and 'kindle' in opts.connected_device['name'].lower(): opts.connected_kindle = True if opts.connected_device['serial'] and \ opts.connected_device['serial'][:4] in ['B004', 'B005']: op = "kindle_dx" else: op = "kindle" opts.description_clip = 380 if op.endswith('dx') or 'kindle' not in op else 100 opts.author_clip = 100 if op.endswith('dx') or 'kindle' not in op else 60 opts.output_profile = op opts.basename = "Catalog" opts.cli_environment = not hasattr(opts, 'sync') # Hard-wired to always sort descriptions by author, with series after non-series opts.sort_descriptions_by_author = True build_log = [] build_log.append(u"%s('%s'): Generating %s %sin %s environment, locale: '%s'" % (self.name, current_library_name(), self.fmt, 'for %s ' % opts.output_profile if opts.output_profile else '', 'CLI' if opts.cli_environment else 'GUI', calibre_langcode_to_name(canonicalize_lang(get_lang()), localize=False)) ) # If exclude_genre is blank, assume user wants all tags as genres if opts.exclude_genre.strip() == '': #opts.exclude_genre = '\[^.\]' #build_log.append(" converting empty exclude_genre to '\[^.\]'") opts.exclude_genre = 'a^' build_log.append(" converting empty exclude_genre to 'a^'") if opts.connected_device['is_device_connected'] and \ opts.connected_device['kind'] == 'device': if opts.connected_device['serial']: build_log.append(u" connected_device: '%s' #%s%s " % \ (opts.connected_device['name'], opts.connected_device['serial'][0:4], 'x' * (len(opts.connected_device['serial']) - 4))) for storage in opts.connected_device['storage']: if storage: build_log.append(u" mount point: %s" % storage) else: build_log.append(u" connected_device: '%s'" % opts.connected_device['name']) try: for storage in opts.connected_device['storage']: if storage: build_log.append(u" mount point: %s" % storage) except: build_log.append(u" (no mount points)") else: build_log.append(u" connected_device: '%s'" % opts.connected_device['name']) opts_dict = vars(opts) if opts_dict['ids']: build_log.append(" book count: %d" % len(opts_dict['ids'])) sections_list = [] if opts.generate_authors: sections_list.append('Authors') if opts.generate_titles: sections_list.append('Titles') if opts.generate_series: sections_list.append('Series') if opts.generate_genres: sections_list.append('Genres') if opts.generate_recently_added: sections_list.append('Recently Added') if opts.generate_descriptions: sections_list.append('Descriptions') if not sections_list: if opts.cli_environment: opts.log.warn('*** No Section switches specified, enabling all Sections ***') opts.generate_authors = True opts.generate_titles = True opts.generate_series = True opts.generate_genres = True opts.generate_recently_added = True opts.generate_descriptions = True sections_list = ['Authors', 'Titles', 'Series', 'Genres', 'Recently Added', 'Descriptions'] else: opts.log.warn('\n*** No enabled Sections, terminating catalog generation ***') return ["No Included Sections", "No enabled Sections.\nCheck E-book options tab\n'Included sections'\n"] if opts.fmt == 'mobi' and sections_list == ['Descriptions']: warning = _("\n*** Adding 'By Authors' Section required for MOBI output ***") opts.log.warn(warning) sections_list.insert(0, 'Authors') opts.generate_authors = True opts.log(u" Sections: %s" % ', '.join(sections_list)) opts.section_list = sections_list # Limit thumb_width to 1.0" - 2.0" try: if float(opts.thumb_width) < float(self.THUMB_SMALLEST): log.warning("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_SMALLEST)) opts.thumb_width = self.THUMB_SMALLEST if float(opts.thumb_width) > float(self.THUMB_LARGEST): log.warning("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_LARGEST)) opts.thumb_width = self.THUMB_LARGEST opts.thumb_width = "%.2f" % float(opts.thumb_width) except: log.error("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_SMALLEST)) opts.thumb_width = "1.0" # eval prefix_rules if passed from command line if type(opts.prefix_rules) is not tuple: try: opts.prefix_rules = eval(opts.prefix_rules) except: log.error("malformed --prefix-rules: %s" % opts.prefix_rules) raise for rule in opts.prefix_rules: if len(rule) != 4: log.error("incorrect number of args for --prefix-rules: %s" % repr(rule)) # eval exclusion_rules if passed from command line if type(opts.exclusion_rules) is not tuple: try: opts.exclusion_rules = eval(opts.exclusion_rules) except: log.error("malformed --exclusion-rules: %s" % opts.exclusion_rules) raise for rule in opts.exclusion_rules: if len(rule) != 3: log.error("incorrect number of args for --exclusion-rules: %s" % repr(rule)) # Display opts keys = opts_dict.keys() keys.sort() build_log.append(" opts:") for key in keys: if key in ['catalog_title', 'author_clip', 'connected_kindle', 'creator', 'cross_reference_authors', 'description_clip', 'exclude_book_marker', 'exclude_genre', 'exclude_tags', 'exclusion_rules', 'fmt', 'genre_source_field', 'header_note_source_field', 'merge_comments_rule', 'output_profile', 'prefix_rules', 'read_book_marker', 'search_text', 'sort_by', 'sort_descriptions_by_author', 'sync', 'thumb_width', 'use_existing_cover', 'wishlist_tag']: build_log.append(" %s: %s" % (key, repr(opts_dict[key]))) if opts.verbose: log('\n'.join(line for line in build_log)) self.opts = opts # Launch the Catalog builder catalog = CatalogBuilder(db, opts, self, report_progress=notification) if opts.verbose: log.info(" Begin catalog source generation") try: catalog.build_sources() if opts.verbose: log.info(" Completed catalog source generation\n") except (AuthorSortMismatchException, EmptyCatalogException), e: log.error(" *** Terminated catalog generation: %s ***" % e)
def initialize(self, library_path, db, listener, actions, show_gui=True): opts = self.opts self.preferences_action, self.quit_action = actions self.library_path = library_path self.content_server = None self._spare_pool = None self.must_restart_before_config = False self.listener = Listener(listener) self.check_messages_timer = QTimer() self.check_messages_timer.timeout.connect(self.another_instance_wants_to_talk) self.check_messages_timer.start(1000) for ac in self.iactions.values(): try: ac.do_genesis() except Exception: # Ignore errors in third party plugins import traceback traceback.print_exc() if getattr(ac, 'plugin_path', None) is None: raise self.donate_action = QAction(QIcon(I('donate.png')), _('&Donate to support calibre'), self) for st in self.istores.values(): st.do_genesis() MainWindowMixin.init_main_window_mixin(self, db) # Jobs Button {{{ self.job_manager = JobManager() self.jobs_dialog = JobsDialog(self, self.job_manager) self.jobs_button = JobsButton(horizontal=True, parent=self) self.jobs_button.initialize(self.jobs_dialog, self.job_manager) # }}} LayoutMixin.init_layout_mixin(self) DeviceMixin.init_device_mixin(self) self.progress_indicator = ProgressIndicator(self) self.progress_indicator.pos = (0, 20) self.verbose = opts.verbose self.get_metadata = GetMetadata() self.upload_memory = {} self.metadata_dialogs = [] self.default_thumbnail = None self.tb_wrapper = textwrap.TextWrapper(width=40) self.viewers = collections.deque() self.system_tray_icon = None if config['systray_icon']: self.system_tray_icon = factory(app_id='com.calibre-ebook.gui').create_system_tray_icon(parent=self, title='calibre') if self.system_tray_icon is not None: self.system_tray_icon.setIcon(QIcon(I('lt.png'))) self.system_tray_icon.setToolTip(self.jobs_button.tray_tooltip()) self.system_tray_icon.setVisible(True) self.jobs_button.tray_tooltip_updated.connect(self.system_tray_icon.setToolTip) elif config['systray_icon']: prints('Failed to create system tray icon, your desktop environment probably does not support the StatusNotifier spec') self.system_tray_menu = QMenu(self) self.toggle_to_tray_action = self.system_tray_menu.addAction(QIcon(I('page.png')), '') self.toggle_to_tray_action.triggered.connect(self.system_tray_icon_activated) self.system_tray_menu.addAction(self.donate_action) self.donate_button.setDefaultAction(self.donate_action) self.donate_button.setStatusTip(self.donate_button.toolTip()) self.eject_action = self.system_tray_menu.addAction( QIcon(I('eject.png')), _('&Eject connected device')) self.eject_action.setEnabled(False) self.addAction(self.quit_action) self.system_tray_menu.addAction(self.quit_action) self.keyboard.register_shortcut('quit calibre', _('Quit calibre'), default_keys=('Ctrl+Q',), action=self.quit_action) if self.system_tray_icon is not None: self.system_tray_icon.setContextMenu(self.system_tray_menu) self.system_tray_icon.activated.connect(self.system_tray_icon_activated) self.quit_action.triggered[bool].connect(self.quit) self.donate_action.triggered[bool].connect(self.donate) self.esc_action = QAction(self) self.addAction(self.esc_action) self.keyboard.register_shortcut('clear current search', _('Clear the current search'), default_keys=('Esc',), action=self.esc_action) self.esc_action.triggered.connect(self.esc) self.shift_esc_action = QAction(self) self.addAction(self.shift_esc_action) self.keyboard.register_shortcut('focus book list', _('Focus the book list'), default_keys=('Shift+Esc',), action=self.shift_esc_action) self.shift_esc_action.triggered.connect(self.shift_esc) self.ctrl_esc_action = QAction(self) self.addAction(self.ctrl_esc_action) self.keyboard.register_shortcut('clear virtual library', _('Clear the virtual library'), default_keys=('Ctrl+Esc',), action=self.ctrl_esc_action) self.ctrl_esc_action.triggered.connect(self.ctrl_esc) self.alt_esc_action = QAction(self) self.addAction(self.alt_esc_action) self.keyboard.register_shortcut('clear additional restriction', _('Clear the additional restriction'), default_keys=('Alt+Esc',), action=self.alt_esc_action) self.alt_esc_action.triggered.connect(self.clear_additional_restriction) # ###################### Start spare job server ######################## QTimer.singleShot(1000, self.create_spare_pool) # ###################### Location Manager ######################## self.location_manager.location_selected.connect(self.location_selected) self.location_manager.unmount_device.connect(self.device_manager.umount_device) self.location_manager.configure_device.connect(self.configure_connected_device) self.location_manager.update_device_metadata.connect(self.update_metadata_on_device) self.eject_action.triggered.connect(self.device_manager.umount_device) # ################### Update notification ################### UpdateMixin.init_update_mixin(self, opts) # ###################### Search boxes ######################## SearchRestrictionMixin.init_search_restirction_mixin(self) SavedSearchBoxMixin.init_saved_seach_box_mixin(self) # ###################### Library view ######################## LibraryViewMixin.init_library_view_mixin(self, db) SearchBoxMixin.init_search_box_mixin(self) # Requires current_db if show_gui: self.show() if self.system_tray_icon is not None and self.system_tray_icon.isVisible() and opts.start_in_tray: self.hide_windows() self.library_view.model().count_changed_signal.connect( self.iactions['Choose Library'].count_changed) if not gprefs.get('quick_start_guide_added', False): try: add_quick_start_guide(self.library_view) except: import traceback traceback.print_exc() for view in ('library', 'memory', 'card_a', 'card_b'): v = getattr(self, '%s_view' % view) v.selectionModel().selectionChanged.connect(self.update_status_bar) v.model().count_changed_signal.connect(self.update_status_bar) self.library_view.model().count_changed() self.bars_manager.database_changed(self.library_view.model().db) self.library_view.model().database_changed.connect(self.bars_manager.database_changed, type=Qt.QueuedConnection) # ########################## Tags Browser ############################## TagBrowserMixin.init_tag_browser_mixin(self, db) # ######################## Search Restriction ########################## if db.prefs['virtual_lib_on_startup']: self.apply_virtual_library(db.prefs['virtual_lib_on_startup']) self.rebuild_vl_tabs() # ########################## Cover Flow ################################ CoverFlowMixin.init_cover_flow_mixin(self) self._calculated_available_height = min(max_available_height()-15, self.height()) self.resize(self.width(), self._calculated_available_height) self.build_context_menus() for ac in self.iactions.values(): try: ac.gui_layout_complete() except: import traceback traceback.print_exc() if ac.plugin_path is None: raise if config['autolaunch_server']: self.start_content_server() self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection) self.read_settings() self.finalize_layout() if self.bars_manager.showing_donate: self.donate_button.start_animation() self.set_window_title() for ac in self.iactions.values(): try: ac.initialization_complete() except: import traceback traceback.print_exc() if ac.plugin_path is None: raise self.set_current_library_information(current_library_name(), db.library_id, db.field_metadata) register_keyboard_shortcuts() self.keyboard.finalize() self.auto_adder = AutoAdder(gprefs['auto_add_path'], self) self.save_layout_state() # Collect cycles now gc.collect() if show_gui and self.gui_debug is not None: info_dialog(self, _('Debug mode'), '<p>' + _('You have started calibre in debug mode. After you ' 'quit calibre, the debug log will be available in ' 'the file: %s<p>The ' 'log will be displayed automatically.')%self.gui_debug, show=True) self.iactions['Connect Share'].check_smartdevice_menus() QTimer.singleShot(1, self.start_smartdevice) QTimer.singleShot(100, self.update_toggle_to_tray_action)
def run(self, path_to_output, opts, db, notification=DummyReporter()): from calibre.library.catalogs.epub_mobi_builder import CatalogBuilder from calibre.utils.logging import default_log as log # If preset specified from the cli, insert stored options from JSON file if hasattr(opts, 'preset') and opts.preset: available_presets = JSONConfig("catalog_presets") if not opts.preset in available_presets: if available_presets: print(_('Error: Preset "%s" not found.' % opts.preset)) print( _('Stored presets: %s' % ', '.join( [p for p in sorted(available_presets.keys())]))) else: print(_('Error: No stored presets.')) return 1 # Copy the relevant preset values to the opts object for item in available_presets[opts.preset]: if not item in [ 'exclusion_rules_tw', 'format', 'prefix_rules_tw' ]: setattr(opts, item, available_presets[opts.preset][item]) # Provide an unconnected device opts.connected_device = { 'is_device_connected': False, 'kind': None, 'name': None, 'save_template': None, 'serial': None, 'storage': None, } # Convert prefix_rules and exclusion_rules from JSON lists to tuples prs = [] for rule in opts.prefix_rules: prs.append(tuple(rule)) opts.prefix_rules = tuple(prs) ers = [] for rule in opts.exclusion_rules: ers.append(tuple(rule)) opts.exclusion_rules = tuple(ers) opts.log = log opts.fmt = self.fmt = path_to_output.rpartition('.')[2] # Add local options opts.creator = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y')) opts.creator_sort_as = '%s %s' % ('calibre', strftime('%Y-%m-%d')) opts.connected_kindle = False # Finalize output_profile op = opts.output_profile if op is None: op = 'default' if opts.connected_device['name'] and 'kindle' in opts.connected_device[ 'name'].lower(): opts.connected_kindle = True if opts.connected_device['serial'] and \ opts.connected_device['serial'][:4] in ['B004', 'B005']: op = "kindle_dx" else: op = "kindle" opts.description_clip = 380 if op.endswith( 'dx') or 'kindle' not in op else 100 opts.author_clip = 100 if op.endswith( 'dx') or 'kindle' not in op else 60 opts.output_profile = op opts.basename = "Catalog" opts.cli_environment = not hasattr(opts, 'sync') # Hard-wired to always sort descriptions by author, with series after non-series opts.sort_descriptions_by_author = True build_log = [] build_log.append( u"%s('%s'): Generating %s %sin %s environment, locale: '%s'" % (self.name, current_library_name(), self.fmt, 'for %s ' % opts.output_profile if opts.output_profile else '', 'CLI' if opts.cli_environment else 'GUI', calibre_langcode_to_name(canonicalize_lang(get_lang()), localize=False))) # If exclude_genre is blank, assume user wants all tags as genres if opts.exclude_genre.strip() == '': #opts.exclude_genre = '\[^.\]' #build_log.append(" converting empty exclude_genre to '\[^.\]'") opts.exclude_genre = 'a^' build_log.append(" converting empty exclude_genre to 'a^'") if opts.connected_device['is_device_connected'] and \ opts.connected_device['kind'] == 'device': if opts.connected_device['serial']: build_log.append(u" connected_device: '%s' #%s%s " % \ (opts.connected_device['name'], opts.connected_device['serial'][0:4], 'x' * (len(opts.connected_device['serial']) - 4))) for storage in opts.connected_device['storage']: if storage: build_log.append(u" mount point: %s" % storage) else: build_log.append(u" connected_device: '%s'" % opts.connected_device['name']) try: for storage in opts.connected_device['storage']: if storage: build_log.append(u" mount point: %s" % storage) except: build_log.append(u" (no mount points)") else: build_log.append(u" connected_device: '%s'" % opts.connected_device['name']) opts_dict = vars(opts) if opts_dict['ids']: build_log.append(" book count: %d" % len(opts_dict['ids'])) sections_list = [] if opts.generate_authors: sections_list.append('Authors') if opts.generate_titles: sections_list.append('Titles') if opts.generate_series: sections_list.append('Series') if opts.generate_genres: sections_list.append('Genres') if opts.generate_recently_added: sections_list.append('Recently Added') if opts.generate_descriptions: sections_list.append('Descriptions') if not sections_list: if opts.cli_environment: opts.log.warn( '*** No Section switches specified, enabling all Sections ***' ) opts.generate_authors = True opts.generate_titles = True opts.generate_series = True opts.generate_genres = True opts.generate_recently_added = True opts.generate_descriptions = True sections_list = [ 'Authors', 'Titles', 'Series', 'Genres', 'Recently Added', 'Descriptions' ] else: opts.log.warn( '\n*** No enabled Sections, terminating catalog generation ***' ) return [ "No Included Sections", "No enabled Sections.\nCheck E-book options tab\n'Included sections'\n" ] if opts.fmt == 'mobi' and sections_list == ['Descriptions']: warning = _( "\n*** Adding 'By Authors' Section required for MOBI output ***" ) opts.log.warn(warning) sections_list.insert(0, 'Authors') opts.generate_authors = True opts.log(u" Sections: %s" % ', '.join(sections_list)) opts.section_list = sections_list # Limit thumb_width to 1.0" - 2.0" try: if float(opts.thumb_width) < float(self.THUMB_SMALLEST): log.warning("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_SMALLEST)) opts.thumb_width = self.THUMB_SMALLEST if float(opts.thumb_width) > float(self.THUMB_LARGEST): log.warning("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_LARGEST)) opts.thumb_width = self.THUMB_LARGEST opts.thumb_width = "%.2f" % float(opts.thumb_width) except: log.error("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_SMALLEST)) opts.thumb_width = "1.0" # eval prefix_rules if passed from command line if type(opts.prefix_rules) is not tuple: try: opts.prefix_rules = eval(opts.prefix_rules) except: log.error("malformed --prefix-rules: %s" % opts.prefix_rules) raise for rule in opts.prefix_rules: if len(rule) != 4: log.error( "incorrect number of args for --prefix-rules: %s" % repr(rule)) # eval exclusion_rules if passed from command line if type(opts.exclusion_rules) is not tuple: try: opts.exclusion_rules = eval(opts.exclusion_rules) except: log.error("malformed --exclusion-rules: %s" % opts.exclusion_rules) raise for rule in opts.exclusion_rules: if len(rule) != 3: log.error( "incorrect number of args for --exclusion-rules: %s" % repr(rule)) # Display opts keys = sorted(opts_dict.keys()) build_log.append(" opts:") for key in keys: if key in [ 'catalog_title', 'author_clip', 'connected_kindle', 'creator', 'cross_reference_authors', 'description_clip', 'exclude_book_marker', 'exclude_genre', 'exclude_tags', 'exclusion_rules', 'fmt', 'genre_source_field', 'header_note_source_field', 'merge_comments_rule', 'output_profile', 'prefix_rules', 'preset', 'read_book_marker', 'search_text', 'sort_by', 'sort_descriptions_by_author', 'sync', 'thumb_width', 'use_existing_cover', 'wishlist_tag' ]: build_log.append(" %s: %s" % (key, repr(opts_dict[key]))) if opts.verbose: log('\n'.join(line for line in build_log)) # Capture start_time opts.start_time = time.time() self.opts = opts if opts.verbose: log.info(" Begin catalog source generation (%s)" % str( datetime.timedelta(seconds=int(time.time() - opts.start_time)))) # Launch the Catalog builder catalog = CatalogBuilder(db, opts, self, report_progress=notification) try: catalog.build_sources() if opts.verbose: log.info(" Completed catalog source generation (%s)\n" % str( datetime.timedelta(seconds=int(time.time() - opts.start_time)))) except (AuthorSortMismatchException, EmptyCatalogException), e: log.error(" *** Terminated catalog generation: %s ***" % e)
def library_moved(self, newloc, copy_structure=False, call_close=True, allow_rebuild=False): if newloc is None: return default_prefs = None try: olddb = self.library_view.model().db if copy_structure: default_prefs = olddb.prefs except: olddb = None try: db = LibraryDatabase(newloc, default_prefs=default_prefs) except apsw.Error: if not allow_rebuild: raise import traceback repair = question_dialog( self, _('Corrupted database'), _('The library database at %s appears to be corrupted. Do ' 'you want calibre to try and rebuild it automatically? ' 'The rebuild may not be completely successful.') % force_unicode(newloc, filesystem_encoding), det_msg=traceback.format_exc()) if repair: from calibre.gui2.dialogs.restore_library import repair_library_at if repair_library_at(newloc, parent=self): db = LibraryDatabase(newloc, default_prefs=default_prefs) else: return else: return if self.content_server is not None: self.content_server.set_database(db) self.library_path = newloc prefs['library_path'] = self.library_path self.book_on_device(None, reset=True) db.set_book_on_device_func(self.book_on_device) self.library_view.set_database(db) self.tags_view.set_database(db, self.alter_tb) self.library_view.model().set_book_on_device_func(self.book_on_device) self.status_bar.clear_message() self.search.clear() self.saved_search.clear() self.book_details.reset_info() # self.library_view.model().count_changed() db = self.library_view.model().db self.iactions['Choose Library'].count_changed(db.count()) self.set_window_title() self.apply_named_search_restriction('') # reset restriction to null self.saved_searches_changed( recount=False) # reload the search restrictions combo box if db.prefs['virtual_lib_on_startup']: self.apply_virtual_library(db.prefs['virtual_lib_on_startup']) self.rebuild_vl_tabs() for action in self.iactions.values(): action.library_changed(db) if olddb is not None: try: if call_close: olddb.close() except: import traceback traceback.print_exc() olddb.break_cycles() if self.device_connected: self.set_books_in_library(self.booklists(), reset=True) self.refresh_ondevice() self.memory_view.reset() self.card_a_view.reset() self.card_b_view.reset() self.set_current_library_information(current_library_name(), db.library_id, db.field_metadata) self.library_view.set_current_row(0) # Run a garbage collection now so that it does not freeze the # interface later gc.collect()
def run(self, path_to_output, opts, db, notification=DummyReporter()): from calibre.library.catalogs.epub_mobi_builder import CatalogBuilder from calibre.utils.logging import default_log as log from calibre.utils.config import JSONConfig # If preset specified from the cli, insert stored options from JSON file if hasattr(opts, 'preset') and opts.preset: available_presets = JSONConfig("catalog_presets") if opts.preset not in available_presets: if available_presets: print(_('Error: Preset "%s" not found.' % opts.preset)) print(_('Stored presets: %s' % ', '.join([p for p in sorted(available_presets.keys())]))) else: print(_('Error: No stored presets.')) return 1 # Copy the relevant preset values to the opts object for item in available_presets[opts.preset]: if item not in ['exclusion_rules_tw', 'format', 'prefix_rules_tw']: setattr(opts, item, available_presets[opts.preset][item]) # Provide an unconnected device opts.connected_device = { 'is_device_connected': False, 'kind': None, 'name': None, 'save_template': None, 'serial': None, 'storage': None, } # Convert prefix_rules and exclusion_rules from JSON lists to tuples prs = [] for rule in opts.prefix_rules: prs.append(tuple(rule)) opts.prefix_rules = tuple(prs) ers = [] for rule in opts.exclusion_rules: ers.append(tuple(rule)) opts.exclusion_rules = tuple(ers) opts.log = log opts.fmt = self.fmt = path_to_output.rpartition('.')[2] # Add local options opts.creator = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y')) opts.creator_sort_as = '%s %s' % ('calibre', strftime('%Y-%m-%d')) opts.connected_kindle = False # Finalize output_profile op = opts.output_profile if op is None: op = 'default' if opts.connected_device['name'] and 'kindle' in opts.connected_device['name'].lower(): opts.connected_kindle = True if opts.connected_device['serial'] and \ opts.connected_device['serial'][:4] in ['B004', 'B005']: op = "kindle_dx" else: op = "kindle" opts.description_clip = 380 if op.endswith('dx') or 'kindle' not in op else 100 opts.author_clip = 100 if op.endswith('dx') or 'kindle' not in op else 60 opts.output_profile = op opts.basename = "Catalog" opts.cli_environment = not hasattr(opts, 'sync') # Hard-wired to always sort descriptions by author, with series after non-series opts.sort_descriptions_by_author = True build_log = [] build_log.append(u"%s('%s'): Generating %s %sin %s environment, locale: '%s'" % (self.name, current_library_name(), self.fmt, 'for %s ' % opts.output_profile if opts.output_profile else '', 'CLI' if opts.cli_environment else 'GUI', calibre_langcode_to_name(canonicalize_lang(get_lang()), localize=False)) ) # If exclude_genre is blank, assume user wants all tags as genres if opts.exclude_genre.strip() == '': # opts.exclude_genre = '\[^.\]' # build_log.append(" converting empty exclude_genre to '\[^.\]'") opts.exclude_genre = 'a^' build_log.append(" converting empty exclude_genre to 'a^'") if opts.connected_device['is_device_connected'] and \ opts.connected_device['kind'] == 'device': if opts.connected_device['serial']: build_log.append(u" connected_device: '%s' #%s%s " % (opts.connected_device['name'], opts.connected_device['serial'][0:4], 'x' * (len(opts.connected_device['serial']) - 4))) for storage in opts.connected_device['storage']: if storage: build_log.append(u" mount point: %s" % storage) else: build_log.append(u" connected_device: '%s'" % opts.connected_device['name']) try: for storage in opts.connected_device['storage']: if storage: build_log.append(u" mount point: %s" % storage) except: build_log.append(u" (no mount points)") else: build_log.append(u" connected_device: '%s'" % opts.connected_device['name']) opts_dict = vars(opts) if opts_dict['ids']: build_log.append(" book count: %d" % len(opts_dict['ids'])) sections_list = [] if opts.generate_authors: sections_list.append('Authors') if opts.generate_titles: sections_list.append('Titles') if opts.generate_series: sections_list.append('Series') if opts.generate_genres: sections_list.append('Genres') if opts.generate_recently_added: sections_list.append('Recently Added') if opts.generate_descriptions: sections_list.append('Descriptions') if not sections_list: if opts.cli_environment: opts.log.warn('*** No Section switches specified, enabling all Sections ***') opts.generate_authors = True opts.generate_titles = True opts.generate_series = True opts.generate_genres = True opts.generate_recently_added = True opts.generate_descriptions = True sections_list = ['Authors', 'Titles', 'Series', 'Genres', 'Recently Added', 'Descriptions'] else: opts.log.warn('\n*** No enabled Sections, terminating catalog generation ***') return ["No Included Sections", "No enabled Sections.\nCheck E-book options tab\n'Included sections'\n"] if opts.fmt == 'mobi' and sections_list == ['Descriptions']: warning = _("\n*** Adding 'By authors' section required for MOBI output ***") opts.log.warn(warning) sections_list.insert(0, 'Authors') opts.generate_authors = True opts.log(u" Sections: %s" % ', '.join(sections_list)) opts.section_list = sections_list # Limit thumb_width to 1.0" - 2.0" try: if float(opts.thumb_width) < float(self.THUMB_SMALLEST): log.warning("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_SMALLEST)) opts.thumb_width = self.THUMB_SMALLEST if float(opts.thumb_width) > float(self.THUMB_LARGEST): log.warning("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_LARGEST)) opts.thumb_width = self.THUMB_LARGEST opts.thumb_width = "%.2f" % float(opts.thumb_width) except: log.error("coercing thumb_width from '%s' to '%s'" % (opts.thumb_width, self.THUMB_SMALLEST)) opts.thumb_width = "1.0" # eval prefix_rules if passed from command line if type(opts.prefix_rules) is not tuple: try: opts.prefix_rules = eval(opts.prefix_rules) except: log.error("malformed --prefix-rules: %s" % opts.prefix_rules) raise for rule in opts.prefix_rules: if len(rule) != 4: log.error("incorrect number of args for --prefix-rules: %s" % repr(rule)) # eval exclusion_rules if passed from command line if type(opts.exclusion_rules) is not tuple: try: opts.exclusion_rules = eval(opts.exclusion_rules) except: log.error("malformed --exclusion-rules: %s" % opts.exclusion_rules) raise for rule in opts.exclusion_rules: if len(rule) != 3: log.error("incorrect number of args for --exclusion-rules: %s" % repr(rule)) # Display opts keys = sorted(opts_dict.keys()) build_log.append(" opts:") for key in keys: if key in ['catalog_title', 'author_clip', 'connected_kindle', 'creator', 'cross_reference_authors', 'description_clip', 'exclude_book_marker', 'exclude_genre', 'exclude_tags', 'exclusion_rules', 'fmt', 'genre_source_field', 'header_note_source_field', 'merge_comments_rule', 'output_profile', 'prefix_rules', 'preset', 'read_book_marker', 'search_text', 'sort_by', 'sort_descriptions_by_author', 'sync', 'thumb_width', 'use_existing_cover', 'wishlist_tag']: build_log.append(" %s: %s" % (key, repr(opts_dict[key]))) if opts.verbose: log('\n'.join(line for line in build_log)) # Capture start_time opts.start_time = time.time() self.opts = opts if opts.verbose: log.info(" Begin catalog source generation (%s)" % str(datetime.timedelta(seconds=int(time.time() - opts.start_time)))) # Launch the Catalog builder catalog = CatalogBuilder(db, opts, self, report_progress=notification) try: catalog.build_sources() if opts.verbose: log.info(" Completed catalog source generation (%s)\n" % str(datetime.timedelta(seconds=int(time.time() - opts.start_time)))) except (AuthorSortMismatchException, EmptyCatalogException) as e: log.error(" *** Terminated catalog generation: %s ***" % e) except: log.error(" unhandled exception in catalog generator") raise else: recommendations = [] recommendations.append(('remove_fake_margins', False, OptionRecommendation.HIGH)) recommendations.append(('comments', '', OptionRecommendation.HIGH)) """ >>> Use to debug generated catalog code before pipeline conversion <<< """ GENERATE_DEBUG_EPUB = False if GENERATE_DEBUG_EPUB: catalog_debug_path = os.path.join(os.path.expanduser('~'), 'Desktop', 'Catalog debug') setattr(opts, 'debug_pipeline', os.path.expanduser(catalog_debug_path)) dp = getattr(opts, 'debug_pipeline', None) if dp is not None: recommendations.append(('debug_pipeline', dp, OptionRecommendation.HIGH)) if opts.output_profile and opts.output_profile.startswith("kindle"): recommendations.append(('output_profile', opts.output_profile, OptionRecommendation.HIGH)) recommendations.append(('book_producer', opts.output_profile, OptionRecommendation.HIGH)) if opts.fmt == 'mobi': recommendations.append(('no_inline_toc', True, OptionRecommendation.HIGH)) recommendations.append(('verbose', 2, OptionRecommendation.HIGH)) # Use existing cover or generate new cover cpath = None existing_cover = False try: search_text = 'title:"%s" author:%s' % ( opts.catalog_title.replace('"', '\\"'), 'calibre') matches = db.search(search_text, return_matches=True, sort_results=False) if matches: cpath = db.cover(matches[0], index_is_id=True, as_path=True) if cpath and os.path.exists(cpath): existing_cover = True except: pass if self.opts.use_existing_cover and not existing_cover: log.warning("no existing catalog cover found") if self.opts.use_existing_cover and existing_cover: recommendations.append(('cover', cpath, OptionRecommendation.HIGH)) log.info("using existing catalog cover") else: from calibre.ebooks.covers import calibre_cover2 log.info("replacing catalog cover") new_cover_path = PersistentTemporaryFile(suffix='.jpg') new_cover = calibre_cover2(opts.catalog_title, 'calibre') new_cover_path.write(new_cover) new_cover_path.close() recommendations.append(('cover', new_cover_path.name, OptionRecommendation.HIGH)) # Run ebook-convert from calibre.ebooks.conversion.plumber import Plumber plumber = Plumber(os.path.join(catalog.catalog_path, opts.basename + '.opf'), path_to_output, log, report_progress=notification, abort_after_input_dump=False) plumber.merge_ui_recommendations(recommendations) plumber.run() try: os.remove(cpath) except: pass if GENERATE_DEBUG_EPUB: from calibre.ebooks.epub import initialize_container from calibre.ebooks.tweak import zip_rebuilder from calibre.utils.zipfile import ZipFile input_path = os.path.join(catalog_debug_path, 'input') epub_shell = os.path.join(catalog_debug_path, 'epub_shell.zip') initialize_container(epub_shell, opf_name='content.opf') with ZipFile(epub_shell, 'r') as zf: zf.extractall(path=input_path) os.remove(epub_shell) zip_rebuilder(input_path, os.path.join(catalog_debug_path, 'input.epub')) if opts.verbose: log.info(" Catalog creation complete (%s)\n" % str(datetime.timedelta(seconds=int(time.time() - opts.start_time)))) # returns to gui2.actions.catalog:catalog_generated() return catalog.error
def postadd(self, book_id, fmt_map, db): import calibre_plugins.getfilename.prefs as prefs import calibre_plugins.getfilename.config as cfg from calibre.library import current_library_name current_library = current_library_name() ''' Se llama si on_postimport tiene el valor True ''' fich_temp = False # debug_print (traceback.format_stack()) for llamada in traceback.format_stack(): if (llamada.find("add_empty") != -1) or (llamada.find("copy_to_library") != -1) or (llamada.find("my_tools") != -1): if DEBUG: if (llamada.find("add_empty") != -1): print(("Libro vacio:", llamada)) elif (llamada.find("copy_to_library") != -1): print(("Copia a biblioteca:", llamada)) elif (llamada.find("my_tools") != -1): print(("Mis herramientas:", llamada)) else: print(("Error:", llamada)) fich_temp = True if not (fich_temp): config_col = {} value_col = {} all_cols = db.field_metadata.custom_field_metadata() try: prefs_value = prefs.GetFileName_Prefs(current_library) if (prefs_value['configured'] == False): prefs_value = cfg.get_library_config(db) except: debug_print("Error reading config for:") for fmt, file in six.iteritems(fmt_map): debug_print("- ", file) return for col, val in six.iteritems(cfg.ALL_COLUMNS): nom_column = prefs_value[val] if (nom_column != '') and not (nom_column in all_cols): nom_column = '' config_col[col] = {'nom': nom_column, 'empty': ''} value_col[col] = config_col[col]['empty'] # debug_print (config_col) if (config_col[cfg.NAME]['nom'] != ''): get_original_file = False if (prefs_value[cfg.OPC_PREF] == 'path'): include_path = True get_original_file = True else: include_path = False if (config_col[cfg.DATE]['nom'] != ''): get_original_file = True nom_fich = '' ext_fich = '' path_fich = '' date_fich = UNDEFINED_DATE if (get_original_file): fich_name = prefs_value.getTemporaryFile() # print "Name postadd: ", fich_name dictio_archi = {} with ExclusiveFile(fich_name) as file: try: dictio = json.load(file) except Exception as e: debug_print( "Skipping invalid temporary file. Excepton: ", e) dictio = {} #debug_print ('Dict buil: ', dictio) #archivos = self.prefs['procesados'] # debug_print ("archivos: ", archivos) for fmt, file in six.iteritems(fmt_map): # # Check original_path_to_file in plugin # list_ext = ['rar', 'zip'] if (get_original_file): debug_print("File: ", file, " - Format: ", fmt) try: list_ext.append(dict_fmt[fmt]) except KeyError: pass nombre_ar = os.path.basename(file) try: path_ori = dictio[nombre_ar] except KeyError: for extFile in list_ext: lis_datos = nombre_ar.split('.') aux = lis_datos.pop() lis_datos.append(extFile) nombre_ar = '.'.join(lis_datos) try: path_ori = dictio[nombre_ar] break except KeyError: pass else: path_ori = file debug_print("Fich ori: ", path_ori) if (include_path): if (config_col[cfg.PATH]['nom'] != ''): path_fich = os.path.dirname(path_ori) path_fich = os.path.normpath(path_fich) nom_fich = os.path.basename(path_ori) debug_print("NF: ", nom_fich) value_col[cfg.PATH] = path_fich else: nom_fich = os.path.abspath(path_ori) nom_fich = os.path.normpath(nom_fich) value_col[cfg.NAME] = nom_fich else: value_col[cfg.NAME] = os.path.basename(file) nom_fich = value_col[cfg.NAME] #path_ori = file if (config_col[cfg.EXT]['nom'] != ''): lis_datos = nom_fich.split('.') ext_fich = '' if (len(lis_datos) > 1): value_col[cfg.EXT] = lis_datos.pop() value_col[cfg.NAME] = '.'.join(lis_datos) if (config_col[cfg.DATE]['nom'] != ''): debug_print("Ori: ", path_ori) #value_col[cfg.DATE] = datetime.datetime.utcfromtimestamp (os.path.getmtime(file)) value_col[ cfg.DATE] = datetime.datetime.utcfromtimestamp( os.path.getmtime(path_ori)) else: value_col[cfg.DATE] = '' debug_print("File: ", nom_fich) dbA = db.new_api try: for col, column in six.iteritems(config_col): # debug_print ('col: ', col) # debug_print ('column: ', column) # debug_print ('value: ', value_col[col]) if ((value_col[col] != column['empty']) and (column['nom'] != '')): val_db = dbA.field_for(column['nom'], book_id, "") #debug_print ('Updating value:', val_db) if (val_db == column['empty']): debug_print("Updating column ", column['nom'], "for book id: ", book_id, " with value: ", value_col[col]) numF = dbA.set_field( column['nom'], {book_id: value_col[col]}) except Exception as inst: print((type(inst))) # the exception instance print((inst.args)) # arguments stored in .args print(inst) print("There was some problem updating metadata") debug_print("There was some problem updating metadata") debug_print("* instance: ", type(inst)) debug_print("* args: ", inst.args) debug_print("* Eception: ", inst)
def library_moved(self, newloc, copy_structure=False, call_close=True, allow_rebuild=False): if newloc is None: return default_prefs = None try: olddb = self.library_view.model().db if copy_structure: default_prefs = olddb.prefs from calibre.utils.formatter_functions import unload_user_template_functions unload_user_template_functions(olddb.library_id) except: olddb = None try: db = LibraryDatabase(newloc, default_prefs=default_prefs) except apsw.Error: if not allow_rebuild: raise import traceback repair = question_dialog(self, _('Corrupted database'), _('The library database at %s appears to be corrupted. Do ' 'you want calibre to try and rebuild it automatically? ' 'The rebuild may not be completely successful.') % force_unicode(newloc, filesystem_encoding), det_msg=traceback.format_exc() ) if repair: from calibre.gui2.dialogs.restore_library import repair_library_at if repair_library_at(newloc, parent=self): db = LibraryDatabase(newloc, default_prefs=default_prefs) else: return else: return if self.content_server is not None: self.content_server.set_database(db) self.library_path = newloc prefs['library_path'] = self.library_path self.book_on_device(None, reset=True) db.set_book_on_device_func(self.book_on_device) self.library_view.set_database(db) self.tags_view.set_database(db, self.alter_tb) self.library_view.model().set_book_on_device_func(self.book_on_device) self.status_bar.clear_message() self.search.clear() self.saved_search.clear() self.book_details.reset_info() # self.library_view.model().count_changed() db = self.library_view.model().db self.iactions['Choose Library'].count_changed(db.count()) self.set_window_title() self.apply_named_search_restriction('') # reset restriction to null self.saved_searches_changed(recount=False) # reload the search restrictions combo box if db.prefs['virtual_lib_on_startup']: self.apply_virtual_library(db.prefs['virtual_lib_on_startup']) self.rebuild_vl_tabs() for action in self.iactions.values(): action.library_changed(db) if olddb is not None: try: if call_close: olddb.close() except: import traceback traceback.print_exc() olddb.break_cycles() if self.device_connected: self.set_books_in_library(self.booklists(), reset=True) self.refresh_ondevice() self.memory_view.reset() self.card_a_view.reset() self.card_b_view.reset() self.set_current_library_information(current_library_name(), db.library_id, db.field_metadata) self.library_view.set_current_row(0) # Run a garbage collection now so that it does not freeze the # interface later gc.collect()
def library_icon_path(lib_name=''): return os.path.join( config_dir, 'library_icons', sanitize_file_name(lib_name or current_library_name()) + '.png')
def initialize(self, library_path, db, listener, actions, show_gui=True): opts = self.opts self.preferences_action, self.quit_action = actions self.library_path = library_path self.content_server = None self._spare_pool = None self.must_restart_before_config = False self.listener = Listener(listener) self.check_messages_timer = QTimer() self.check_messages_timer.timeout.connect( self.another_instance_wants_to_talk) self.check_messages_timer.start(1000) for ac in self.iactions.values(): try: ac.do_genesis() except Exception: # Ignore errors in third party plugins import traceback traceback.print_exc() if getattr(ac, 'plugin_path', None) is None: raise self.donate_action = QAction(QIcon(I('donate.png')), _('&Donate to support calibre'), self) for st in self.istores.values(): st.do_genesis() MainWindowMixin.init_main_window_mixin(self, db) # Jobs Button {{{ self.job_manager = JobManager() self.jobs_dialog = JobsDialog(self, self.job_manager) self.jobs_button = JobsButton(horizontal=True, parent=self) self.jobs_button.initialize(self.jobs_dialog, self.job_manager) # }}} LayoutMixin.init_layout_mixin(self) DeviceMixin.init_device_mixin(self) self.progress_indicator = ProgressIndicator(self) self.progress_indicator.pos = (0, 20) self.verbose = opts.verbose self.get_metadata = GetMetadata() self.upload_memory = {} self.metadata_dialogs = [] self.default_thumbnail = None self.tb_wrapper = textwrap.TextWrapper(width=40) self.viewers = collections.deque() self.system_tray_icon = None if config['systray_icon']: self.system_tray_icon = factory( app_id='com.calibre-ebook.gui').create_system_tray_icon( parent=self, title='calibre') if self.system_tray_icon is not None: self.system_tray_icon.setIcon( QIcon(I('lt.png', allow_user_override=False))) if not (iswindows or isosx): self.system_tray_icon.setIcon( QIcon.fromTheme('calibre-gui', self.system_tray_icon.icon())) self.system_tray_icon.setToolTip(self.jobs_button.tray_tooltip()) self.system_tray_icon.setVisible(True) self.jobs_button.tray_tooltip_updated.connect( self.system_tray_icon.setToolTip) elif config['systray_icon']: prints( 'Failed to create system tray icon, your desktop environment probably does not support the StatusNotifier spec' ) self.system_tray_menu = QMenu(self) self.toggle_to_tray_action = self.system_tray_menu.addAction( QIcon(I('page.png')), '') self.toggle_to_tray_action.triggered.connect( self.system_tray_icon_activated) self.system_tray_menu.addAction(self.donate_action) self.donate_button.clicked.connect(self.donate_action.trigger) self.donate_button.setToolTip(self.donate_action.text().replace( '&', '')) self.donate_button.setIcon(self.donate_action.icon()) self.donate_button.setStatusTip(self.donate_button.toolTip()) self.eject_action = self.system_tray_menu.addAction( QIcon(I('eject.png')), _('&Eject connected device')) self.eject_action.setEnabled(False) self.addAction(self.quit_action) self.system_tray_menu.addAction(self.quit_action) self.keyboard.register_shortcut('quit calibre', _('Quit calibre'), default_keys=('Ctrl+Q', ), action=self.quit_action) if self.system_tray_icon is not None: self.system_tray_icon.setContextMenu(self.system_tray_menu) self.system_tray_icon.activated.connect( self.system_tray_icon_activated) self.quit_action.triggered[bool].connect(self.quit) self.donate_action.triggered[bool].connect(self.donate) self.minimize_action = QAction(_('Minimize the calibre window'), self) self.addAction(self.minimize_action) self.keyboard.register_shortcut('minimize calibre', self.minimize_action.text(), default_keys=(), action=self.minimize_action) self.minimize_action.triggered.connect(self.showMinimized) self.esc_action = QAction(self) self.addAction(self.esc_action) self.keyboard.register_shortcut('clear current search', _('Clear the current search'), default_keys=('Esc', ), action=self.esc_action) self.esc_action.triggered.connect(self.esc) self.shift_esc_action = QAction(self) self.addAction(self.shift_esc_action) self.keyboard.register_shortcut('focus book list', _('Focus the book list'), default_keys=('Shift+Esc', ), action=self.shift_esc_action) self.shift_esc_action.triggered.connect(self.shift_esc) self.ctrl_esc_action = QAction(self) self.addAction(self.ctrl_esc_action) self.keyboard.register_shortcut('clear virtual library', _('Clear the virtual library'), default_keys=('Ctrl+Esc', ), action=self.ctrl_esc_action) self.ctrl_esc_action.triggered.connect(self.ctrl_esc) self.alt_esc_action = QAction(self) self.addAction(self.alt_esc_action) self.keyboard.register_shortcut('clear additional restriction', _('Clear the additional restriction'), default_keys=('Alt+Esc', ), action=self.alt_esc_action) self.alt_esc_action.triggered.connect( self.clear_additional_restriction) # ###################### Start spare job server ######################## QTimer.singleShot(1000, self.create_spare_pool) # ###################### Location Manager ######################## self.location_manager.location_selected.connect(self.location_selected) self.location_manager.unmount_device.connect( self.device_manager.umount_device) self.location_manager.configure_device.connect( self.configure_connected_device) self.location_manager.update_device_metadata.connect( self.update_metadata_on_device) self.eject_action.triggered.connect(self.device_manager.umount_device) # ################### Update notification ################### UpdateMixin.init_update_mixin(self, opts) # ###################### Search boxes ######################## SearchRestrictionMixin.init_search_restriction_mixin(self) SavedSearchBoxMixin.init_saved_seach_box_mixin(self) # ###################### Library view ######################## LibraryViewMixin.init_library_view_mixin(self, db) SearchBoxMixin.init_search_box_mixin(self) # Requires current_db if show_gui: self.show() if self.system_tray_icon is not None and self.system_tray_icon.isVisible( ) and opts.start_in_tray: self.hide_windows() self.library_view.model().count_changed_signal.connect( self.iactions['Choose Library'].count_changed) if not gprefs.get('quick_start_guide_added', False): try: add_quick_start_guide(self.library_view) except: import traceback traceback.print_exc() for view in ('library', 'memory', 'card_a', 'card_b'): v = getattr(self, '%s_view' % view) v.selectionModel().selectionChanged.connect(self.update_status_bar) v.model().count_changed_signal.connect(self.update_status_bar) self.library_view.model().count_changed() self.bars_manager.database_changed(self.library_view.model().db) self.library_view.model().database_changed.connect( self.bars_manager.database_changed, type=Qt.QueuedConnection) # ########################## Tags Browser ############################## TagBrowserMixin.init_tag_browser_mixin(self, db) # ######################## Search Restriction ########################## if db.prefs['virtual_lib_on_startup']: self.apply_virtual_library(db.prefs['virtual_lib_on_startup']) self.rebuild_vl_tabs() # ########################## Cover Flow ################################ CoverFlowMixin.init_cover_flow_mixin(self) self._calculated_available_height = min(max_available_height() - 15, self.height()) self.resize(self.width(), self._calculated_available_height) self.build_context_menus() for ac in self.iactions.values(): try: ac.gui_layout_complete() except: import traceback traceback.print_exc() if ac.plugin_path is None: raise if config['autolaunch_server']: self.start_content_server() self.read_settings() self.finalize_layout() if self.bars_manager.showing_donate: self.donate_button.start_animation() self.set_window_title() for ac in self.iactions.values(): try: ac.initialization_complete() except: import traceback traceback.print_exc() if ac.plugin_path is None: raise self.set_current_library_information(current_library_name(), db.library_id, db.field_metadata) register_keyboard_shortcuts() self.keyboard.finalize() self.auto_adder = AutoAdder(gprefs['auto_add_path'], self) self.save_layout_state() # Collect cycles now gc.collect() QApplication.instance().shutdown_signal_received.connect(self.quit) if show_gui and self.gui_debug is not None: QTimer.singleShot(10, self.show_gui_debug_msg) self.iactions['Connect Share'].check_smartdevice_menus() QTimer.singleShot(1, self.start_smartdevice) QTimer.singleShot(100, self.update_toggle_to_tray_action)
def open(self, connected_device, library_uuid): ''' Perform any device specific initialization. Called after the device is detected but before any other functions that communicate with the device. For example: For devices that present themselves as USB Mass storage devices, this method would be responsible for mounting the device or if the device has been automounted, for finding out where it has been mounted. The method :meth:`calibre.devices.usbms.device.Device.open` has an implementation of this function that should serve as a good example for USB Mass storage devices. This method can raise an OpenFeedback exception to display a message to the user. :param connected_device: The device that we are trying to open. It is a tuple of (vendor id, product id, bcd, manufacturer name, product name, device serial number). However, some devices have no serial number and on windows only the first three fields are present, the rest are None. :param library_uuid: The UUID of the current calibre library. Can be None if there is no library (for example when used from the command line). ''' logger.debug(sys._getframe().f_code.co_name) # At this point we know that the user has a valid network connection. if not prefs['token']: # if there is no access_token set, the user hasn't logged in. We can't do anything. raise OpenFeedback( 'QuietThyme has not been authorized on this machine. Please open the plugin preferences to login.' ) # response = ApiClient().auth(library_uuid, current_library_name()) # # #if everything is successful set is_connected to true. # if not response['success']: # raise OpenFeedback(response['error_msg']) # else: # prefs['token'] = response['data']['token'] # self.is_connected = response['success'] status_response = ApiClient().status(library_uuid, current_library_name()) if not status_response['success']: # if an error occurs because token is invalid (401) then remove it. errorMessage = status_response.get('data', {}).get('errorMessage', {}) if errorMessage.get( 'code', 0) == 401: #or errorMessage.get('code', 0) == 403: logger.debug( "Got 401/403 response when attempting to call status endpoint. Token is expired/invalid. Deleting it." ) # if current_token == prefs.get('token'): # the token hasnt changed, and its invalid, delete it. prefs.pop('token', None) raise OpenFeedback(status_response['error_msg']) self.is_connected = status_response['success'] #store the settings in memory self.current_library_uuid = library_uuid #mimic from #https://github.com/kovidgoyal/calibre/blob/master/src/calibre/devices/usbms/driver.py #https://github.com/kovidgoyal/calibre/blob/master/src/calibre/devices/usbms/device.py self.qt_settings = status_response['data']['settings']
def run(self, path_to_output, opts, db, notification=DummyReporter()): from calibre.library import current_library_name from calibre.utils.date import isoformat from calibre.utils.html2text import html2text from calibre.utils.logging import default_log as log from lxml import etree from calibre.ebooks.metadata import authors_to_string self.fmt = path_to_output.rpartition('.')[2] self.notification = notification current_library = current_library_name() if getattr(opts, 'library_path', None): current_library = os.path.basename(opts.library_path) if opts.verbose: opts_dict = vars(opts) log("%s('%s'): Generating %s" % (self.name, current_library, self.fmt.upper())) if opts.connected_device['is_device_connected']: log(" connected_device: %s" % opts.connected_device['name']) if opts_dict['search_text']: log(" --search='%s'" % opts_dict['search_text']) if opts_dict['ids']: log(" Book count: %d" % len(opts_dict['ids'])) if opts_dict['search_text']: log(" (--search ignored when a subset of the database is specified)" ) if opts_dict['fields']: if opts_dict['fields'] == 'all': log(" Fields: %s" % ', '.join(FIELDS[1:])) else: log(" Fields: %s" % opts_dict['fields']) # If a list of ids are provided, don't use search_text if opts.ids: opts.search_text = None data = self.search_sort_db(db, opts) if not len(data): log.error( "\nNo matching database entries for search criteria '%s'" % opts.search_text) # raise SystemExit(1) # Get the requested output fields as a list fields = self.get_output_fields(db, opts) # If connected device, add 'On Device' values to data if opts.connected_device[ 'is_device_connected'] and 'ondevice' in fields: for entry in data: entry['ondevice'] = db.catalog_plugin_on_device_temp_mapping[ entry['id']]['ondevice'] fm = {x: db.field_metadata.get(x, {}) for x in fields} if self.fmt == 'csv': outfile = codecs.open(path_to_output, 'w', 'utf8') # Write a UTF-8 BOM outfile.write('\ufeff') # Output the field headers outfile.write('%s\n' % ','.join(fields)) # Output the entry fields for entry in data: outstr = [] for field in fields: if field.startswith('#'): item = db.get_field(entry['id'], field, index_is_id=True) if isinstance(item, (list, tuple)): if fm.get(field, {}).get('display', {}).get('is_names', False): item = ' & '.join(item) else: item = ', '.join(item) elif field == 'library_name': item = current_library elif field == 'title_sort': item = entry['sort'] else: item = entry[field] if item is None: outstr.append('""') continue elif field == 'formats': fmt_list = [] for format in item: fmt_list.append(format.rpartition('.')[2].lower()) item = ', '.join(fmt_list) elif field == 'authors': item = authors_to_string(item) elif field == 'tags': item = ', '.join(item) elif field == 'isbn': # Could be 9, 10 or 13 digits, with hyphens, possibly ending in 'X' item = '%s' % re.sub(r'[^\dX-]', '', item) elif fm.get(field, {}).get('datatype') == 'datetime': item = isoformat(item, as_utc=False) elif field == 'comments': item = item.replace('\r\n', ' ') item = item.replace('\n', ' ') elif fm.get(field, {}).get('datatype', None) == 'rating' and item: item = '%.2g' % (item / 2) # Convert HTML to markdown text if isinstance(item, str): opening_tag = re.search(r'<(\w+)( |>)', item) if opening_tag: closing_tag = re.search( r'<\/%s>$' % opening_tag.group(1), item) if closing_tag: item = html2text(item) outstr.append('"%s"' % str(item).replace('"', '""')) outfile.write(','.join(outstr) + '\n') outfile.close() elif self.fmt == 'xml': from lxml.builder import E if getattr(opts, 'catalog_title', None): root = E.calibredb(title=opts.catalog_title) else: root = E.calibredb() for r in data: record = E.record() root.append(record) for field in fields: if field.startswith('#'): val = db.get_field(r['id'], field, index_is_id=True) if not isinstance(val, str): val = str(val) item = getattr(E, field.replace('#', '_'))(val) record.append(item) for field in ('id', 'uuid', 'publisher', 'rating', 'size', 'isbn', 'ondevice', 'identifiers'): if field in fields: val = r[field] if not val: continue if not isinstance(val, (bytes, str)): if (fm.get(field, {}).get('datatype', None) == 'rating' and val): val = '%.2g' % (val / 2) val = str(val) item = getattr(E, field)(val) record.append(item) if 'title' in fields: title = E.title(r['title'], sort=r['sort']) record.append(title) if 'authors' in fields: aus = E.authors(sort=r['author_sort']) for au in r['authors']: aus.append(E.author(au)) record.append(aus) for field in ('timestamp', 'pubdate'): if field in fields: record.append( getattr(E, field)(isoformat(r[field], as_utc=False))) if 'tags' in fields and r['tags']: tags = E.tags() for tag in r['tags']: tags.append(E.tag(tag)) record.append(tags) if 'comments' in fields and r['comments']: record.append(E.comments(r['comments'])) if 'series' in fields and r['series']: record.append( E.series(r['series'], index=str(r['series_index']))) if 'languages' in fields and r['languages']: record.append(E.languages(r['languages'])) if 'cover' in fields and r['cover']: record.append(E.cover(r['cover'].replace(os.sep, '/'))) if 'formats' in fields and r['formats']: fmt = E.formats() for f in r['formats']: fmt.append(E.format(f.replace(os.sep, '/'))) record.append(fmt) if 'library_name' in fields: record.append(E.library_name(current_library)) with open(path_to_output, 'wb') as f: f.write( etree.tostring(root, encoding='utf-8', xml_declaration=True, pretty_print=True))
def run(self, path_to_output, opts, db, notification=DummyReporter()): from calibre.library import current_library_name from calibre.utils.date import isoformat from calibre.utils.html2text import html2text from calibre.utils.logging import default_log as log from lxml import etree self.fmt = path_to_output.rpartition(".")[2] self.notification = notification current_library = current_library_name() if getattr(opts, "library_path", None): current_library = os.path.basename(opts.library_path) if opts.verbose: opts_dict = vars(opts) log("%s('%s'): Generating %s" % (self.name, current_library, self.fmt.upper())) if opts.connected_device["is_device_connected"]: log(" connected_device: %s" % opts.connected_device["name"]) if opts_dict["search_text"]: log(" --search='%s'" % opts_dict["search_text"]) if opts_dict["ids"]: log(" Book count: %d" % len(opts_dict["ids"])) if opts_dict["search_text"]: log(" (--search ignored when a subset of the database is specified)") if opts_dict["fields"]: if opts_dict["fields"] == "all": log(" Fields: %s" % ", ".join(FIELDS[1:])) else: log(" Fields: %s" % opts_dict["fields"]) # If a list of ids are provided, don't use search_text if opts.ids: opts.search_text = None data = self.search_sort_db(db, opts) if not len(data): log.error("\nNo matching database entries for search criteria '%s'" % opts.search_text) # raise SystemExit(1) # Get the requested output fields as a list fields = self.get_output_fields(db, opts) # If connected device, add 'On Device' values to data if opts.connected_device["is_device_connected"] and "ondevice" in fields: for entry in data: entry["ondevice"] = db.catalog_plugin_on_device_temp_mapping[entry["id"]]["ondevice"] fm = {x: db.field_metadata.get(x, {}) for x in fields} if self.fmt == "csv": outfile = codecs.open(path_to_output, "w", "utf8") # Write a UTF-8 BOM outfile.write("\xef\xbb\xbf") # Output the field headers outfile.write(u"%s\n" % u",".join(fields)) # Output the entry fields for entry in data: outstr = [] for field in fields: if field.startswith("#"): item = db.get_field(entry["id"], field, index_is_id=True) elif field == "library_name": item = current_library elif field == "title_sort": item = entry["sort"] else: item = entry[field] if item is None: outstr.append('""') continue elif field == "formats": fmt_list = [] for format in item: fmt_list.append(format.rpartition(".")[2].lower()) item = ", ".join(fmt_list) elif field in ["authors", "tags"]: item = ", ".join(item) elif field == "isbn": # Could be 9, 10 or 13 digits, with hyphens, possibly ending in 'X' item = u"%s" % re.sub(r"[^\dX-]", "", item) elif field in ["pubdate", "timestamp"]: item = isoformat(item, as_utc=False) elif field == "comments": item = item.replace(u"\r\n", u" ") item = item.replace(u"\n", u" ") elif fm.get(field, {}).get("datatype", None) == "rating" and item: item = u"%.2g" % (item / 2.0) # Convert HTML to markdown text if type(item) is unicode: opening_tag = re.search("<(\w+)(\x20|>)", item) if opening_tag: closing_tag = re.search("<\/%s>$" % opening_tag.group(1), item) if closing_tag: item = html2text(item) outstr.append(u'"%s"' % unicode(item).replace('"', '""')) outfile.write(u",".join(outstr) + u"\n") outfile.close() elif self.fmt == "xml": from lxml.builder import E root = E.calibredb() for r in data: record = E.record() root.append(record) for field in fields: if field.startswith("#"): val = db.get_field(r["id"], field, index_is_id=True) if not isinstance(val, (str, unicode)): val = unicode(val) item = getattr(E, field.replace("#", "_"))(val) record.append(item) for field in ("id", "uuid", "publisher", "rating", "size", "isbn", "ondevice", "identifiers"): if field in fields: val = r[field] if not val: continue if not isinstance(val, (str, unicode)): if fm.get(field, {}).get("datatype", None) == "rating" and val: val = u"%.2g" % (val / 2.0) val = unicode(val) item = getattr(E, field)(val) record.append(item) if "title" in fields: title = E.title(r["title"], sort=r["sort"]) record.append(title) if "authors" in fields: aus = E.authors(sort=r["author_sort"]) for au in r["authors"]: aus.append(E.author(au)) record.append(aus) for field in ("timestamp", "pubdate"): if field in fields: record.append(getattr(E, field)(isoformat(r[field], as_utc=False))) if "tags" in fields and r["tags"]: tags = E.tags() for tag in r["tags"]: tags.append(E.tag(tag)) record.append(tags) if "comments" in fields and r["comments"]: record.append(E.comments(r["comments"])) if "series" in fields and r["series"]: record.append(E.series(r["series"], index=str(r["series_index"]))) if "cover" in fields and r["cover"]: record.append(E.cover(r["cover"].replace(os.sep, "/"))) if "formats" in fields and r["formats"]: fmt = E.formats() for f in r["formats"]: fmt.append(E.format(f.replace(os.sep, "/"))) record.append(fmt) if "library_name" in fields: record.append(E.library_name(current_library)) with open(path_to_output, "w") as f: f.write(etree.tostring(root, encoding="utf-8", xml_declaration=True, pretty_print=True))