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']
Beispiel #2
0
    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
Beispiel #3
0
    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
Beispiel #4
0
 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)
Beispiel #7
0
    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
Beispiel #8
0
 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
Beispiel #10
0
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
Beispiel #12
0
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)
Beispiel #13
0
    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
Beispiel #14
0
 def evaluate(self, formatter, kwargs, mi, locals):
     from calibre.library import current_library_name
     return current_library_name()
Beispiel #15
0
    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)
Beispiel #16
0
    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)
Beispiel #17
0
    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)
Beispiel #18
0
    def evaluate(self, formatter, kwargs, mi, locals):
        from calibre.library import current_library_name

        return current_library_name()
Beispiel #19
0
    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))
Beispiel #20
0
    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
Beispiel #21
0
    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)
Beispiel #22
0
    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)
Beispiel #23
0
    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)
Beispiel #24
0
    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)
Beispiel #25
0
 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()
Beispiel #26
0
    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
Beispiel #27
0
    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)
Beispiel #28
0
    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()
Beispiel #29
0
def library_icon_path(lib_name=''):
    return os.path.join(
        config_dir, 'library_icons',
        sanitize_file_name(lib_name or current_library_name()) + '.png')
Beispiel #30
0
    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)
Beispiel #31
0
    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']
Beispiel #32
0
    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))
Beispiel #33
0
    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))