Example #1
0
 def get_valid(prompt, invalidq=lambda x: None):
     while True:
         ans = raw_input(prompt + ": ").strip().decode(enc)
         fail_message = invalidq(ans)
         if fail_message is None:
             return ans
         prints(fail_message)
Example #2
0
 def _read_version_history_html(self, forum_link):
     br = browser()
     br.set_handle_gzip(True)
     try:
         raw = br.open_novisit(forum_link).read()
         if not raw:
             return None
     except:
         traceback.print_exc()
         return None
     raw = raw.decode('utf-8', errors='replace')
     root = html.fromstring(raw)
     spoiler_nodes = root.xpath('//div[@class="smallfont" and strong="Spoiler"]')
     for spoiler_node in spoiler_nodes:
         try:
             if spoiler_node.getprevious() is None:
                 # This is a spoiler node that has been indented using [INDENT]
                 # Need to go up to parent div, then previous node to get header
                 heading_node = spoiler_node.getparent().getprevious()
             else:
                 # This is a spoiler node after a BR tag from the heading
                 heading_node = spoiler_node.getprevious().getprevious()
             if heading_node is None:
                 continue
             if heading_node.text_content().lower().find('version history') != -1:
                 div_node = spoiler_node.xpath('div')[0]
                 text = html.tostring(div_node, method='html', encoding='unicode')
                 return re.sub(r'<div\s.*?>', '<div>', text)
         except:
             if DEBUG:
                 prints('======= MobileRead Parse Error =======')
                 traceback.print_exc()
                 prints(html.tostring(spoiler_node))
     return None
Example #3
0
 def purge_broken_playlist_items(self, root):
     id_map = self.build_id_map(root)
     for pl in root.xpath('//*[local-name()="playlist"]'):
         seen = set([])
         for item in list(pl):
             id_ = item.get("id", None)
             if id_ is None or id_ in seen or id_map.get(id_, None) is None:
                 if DEBUG:
                     if id_ is None:
                         cause = "invalid id"
                     elif id_ in seen:
                         cause = "duplicate item"
                     else:
                         cause = "id not found"
                     prints(
                         "Purging broken playlist item:",
                         id_,
                         "from playlist:",
                         pl.get("title", None),
                         "because:",
                         cause,
                     )
                 item.getparent().remove(item)
                 continue
             seen.add(id_)
Example #4
0
def create_plugin_action(plugin, tool, for_toolbar, actions=None, toolbar_actions=None, plugin_menu_actions=None):
    try:
        ac = tool.create_action(for_toolbar=for_toolbar)
        if ac is None:
            raise RuntimeError('create_action() failed to return an action')
    except Exception:
        prints('Failed to create action for tool:', tool.name)
        import traceback
        traceback.print_exc()
        return
    sid = plugin_action_sid(plugin, tool, for_toolbar)
    if actions is not None and sid in actions:
        prints('The %s tool from the %s plugin has a non unique name, ignoring' % (tool.name, plugin.name))
    else:
        if actions is not None:
            actions[sid] = ac
        ac.sid = sid
        if for_toolbar:
            if toolbar_actions is not None:
                toolbar_actions[sid] = ac
                plugin_toolbar_actions.append(ac)
            ac.popup_mode = {'instant':QToolButton.InstantPopup, 'button':QToolButton.MenuButtonPopup}.get(
                tool.toolbar_button_popup_mode, QToolButton.DelayedPopup)
        else:
            if plugin_menu_actions is not None:
                plugin_menu_actions.append(ac)
    return ac
Example #5
0
def read_available_plugins(raise_error=False):
    import json, bz2
    display_plugins = []
    try:
        raw = get_https_resource_securely(INDEX_URL)
        if not raw:
            return
        raw = json.loads(bz2.decompress(raw))
    except:
        if raise_error:
            raise
        traceback.print_exc()
        return
    for plugin in itervalues(raw):
        try:
            display_plugin = DisplayPlugin(plugin)
            get_installed_plugin_status(display_plugin)
            display_plugins.append(display_plugin)
        except:
            if DEBUG:
                prints('======= Plugin Parse Error =======')
                traceback.print_exc()
                import pprint
                pprint.pprint(plugin)
    display_plugins = sorted(display_plugins, key=lambda k: k.name)
    return display_plugins
Example #6
0
def init_qt(args):
    parser = option_parser()
    opts, args = parser.parse_args(args)
    find_portable_library()
    if opts.with_library is not None:
        libpath = os.path.expanduser(opts.with_library)
        if not os.path.exists(libpath):
            os.makedirs(libpath)
        if os.path.isdir(libpath):
            prefs.set('library_path', os.path.abspath(libpath))
            prints('Using library at', prefs['library_path'])
    override = 'calibre-gui' if islinux else None
    app = Application(args, override_program_name=override)
    app.file_event_hook = EventAccumulator()
    try:
        is_x11 = app.platformName() == 'xcb'
    except Exception:
        import traceback
        traceback.print_exc()
        is_x11 = False
    # Ancient broken VNC servers cannot handle icons of size greater than 256
    # https://www.mobileread.com/forums/showthread.php?t=278447
    ic = 'lt.png' if is_x11 else 'library.png'
    app.setWindowIcon(QIcon(I(ic, allow_user_override=False)))
    return app, opts, args
Example #7
0
 def report_save_error(self, tb):
     if self.doing_terminal_save:
         prints(tb, file=sys.stderr)
         return
     error_dialog(self.gui, _('Could not save'),
                  _('Saving of the book failed. Click "Show Details"'
                    ' for more information.'), det_msg=tb, show=True)
Example #8
0
 def _update_drive_info(self, storage, location_code, name=None):
     from calibre.utils.date import isoformat, now
     from calibre.utils.config import from_json, to_json
     import uuid
     f = storage.find_path(self.calibre_file_paths['driveinfo'].split('/'))
     dinfo = {}
     if f is not None:
         try:
             stream = self.get_mtp_file(f)
             dinfo = json.load(stream, object_hook=from_json)
         except:
             prints('Failed to load existing driveinfo.calibre file, with error:')
             traceback.print_exc()
             dinfo = {}
     if dinfo.get('device_store_uuid', None) is None:
         dinfo['device_store_uuid'] = unicode(uuid.uuid4())
     if dinfo.get('device_name', None) is None:
         dinfo['device_name'] = self.current_friendly_name
     if name is not None:
         dinfo['device_name'] = name
     dinfo['location_code'] = location_code
     dinfo['last_library_uuid'] = getattr(self, 'current_library_uuid', None)
     dinfo['calibre_version'] = '.'.join([unicode(i) for i in numeric_version])
     dinfo['date_last_connected'] = isoformat(now())
     dinfo['mtp_prefix'] = storage.storage_prefix
     raw = json.dumps(dinfo, default=to_json)
     self.put_calibre_file(storage, 'driveinfo', BytesIO(raw), len(raw))
     self.driveinfo[location_code] = dinfo
Example #9
0
 def add_action(name, bar):
     if name is None:
         bar.addSeparator()
         return
     try:
         ac = actions[name]
     except KeyError:
         if DEBUG:
             prints('Unknown editor tool: %r' % name)
         return
     bar.addAction(ac)
     if name == 'insert-tag':
         w = bar.widgetForAction(ac)
         w.setPopupMode(QToolButton.MenuButtonPopup)
         w.setMenu(self.insert_tag_menu)
         w.setContextMenuPolicy(Qt.CustomContextMenu)
         w.customContextMenuRequested.connect(w.showMenu)
         self._build_insert_tag_button_menu()
     elif name == 'change-paragraph':
         m = ac.m = QMenu()
         ac.setMenu(m)
         ch = bar.widgetForAction(ac)
         ch.setPopupMode(QToolButton.InstantPopup)
         for name in tuple('h%d' % d for d in range(1, 7)) + ('p',):
             m.addAction(actions['rename-block-tag-%s' % name])
Example #10
0
    def do_update(self):
        replacements = {}

        for name, src in self.download_updates():
            try:
                key = self.name_rmap[name]
            except KeyError:
                # Plugin has been disabled
                replacements[name] = src
                continue
            try:
                obj, ver = self.load_object(src, key)
            except VersionMismatch as e:
                self.cached_version_map[name] = e.ver
                replacements[name] = src
                continue
            except:
                import traceback
                prints('Failed to load downloaded store:', name)
                traceback.print_exc()
            else:
                if self.replace_plugin(ver, name, obj, 'downloaded'):
                    replacements[name] = src

        if replacements:
            with self.cache_file:
                for name, src in replacements.iteritems():
                    self.cache_file[name] = src
Example #11
0
    def load_cache(self):
        # Load plugins from on disk cache
        remove = set()
        pat = re.compile(r'^store_version\s*=\s*(\d+)', re.M)
        for name, src in self.cache_file.iteritems():
            try:
                key = self.name_rmap[name]
            except KeyError:
                # Plugin has been disabled
                m = pat.search(src[:512])
                if m is not None:
                    try:
                        self.cached_version_map[name] = int(m.group(1))
                    except (TypeError, ValueError):
                        pass
                continue

            try:
                obj, ver = self.load_object(src, key)
            except VersionMismatch as e:
                self.cached_version_map[name] = e.ver
                continue
            except:
                import traceback
                prints('Failed to load cached store:', name)
                traceback.print_exc()
            else:
                if not self.replace_plugin(ver, name, obj, 'cached'):
                    # Builtin plugin is newer than cached
                    remove.add(name)

        if remove:
            with self.cache_file:
                for name in remove:
                    del self.cache_file[name]
Example #12
0
    def search(self, query, max_results=10, timeout=60):
        search_url = u'http://robot.litres.ru/pages/catalit_browser/?checkpoint=2000-01-02&'\
        'search=%s&limit=0,%s'
        search_url = search_url % (urllib2.quote(query), max_results)

        counter = max_results
        br = browser()
        br.addheaders.append(['Accept-Encoding','gzip'])

        with closing(br.open(search_url, timeout=timeout)) as r:
            ungzipResponse(r,br)
            raw= xml_to_unicode(r.read(), strip_encoding_pats=True, assume_utf8=True)[0]

            parser = etree.XMLParser(recover=True, no_network=True)
            doc = etree.fromstring(raw, parser=parser)
            for data in doc.xpath('//*[local-name() = "fb2-book"]'):
                if counter <= 0:
                    break
                counter -= 1

                try:
                    sRes = self.create_search_result(data)
                except Exception as e:
                    prints('ERROR: cannot parse search result #%s: %s'%(max_results - counter + 1, e))
                    continue
                yield sRes
Example #13
0
 def start_gui(self, db):
     from calibre.gui2.ui import Main
     main = self.main = Main(self.opts, gui_debug=self.gui_debug)
     if self.splash_screen is not None:
         self.splash_screen.show_message(_('Initializing user interface...'))
     try:
         with gprefs:  # Only write gui.json after initialization is complete
             main.initialize(self.library_path, db, self.listener, self.actions)
     finally:
         if self.splash_screen is not None:
             self.splash_screen.finish(main)
         self.splash_screen = None
     if DEBUG:
         prints('Started up in %.2f seconds'%(time.time() -
             self.startup_time), 'with', len(db.data), 'books')
     add_filesystem_book = partial(main.iactions['Add Books'].add_filesystem_book, allow_device=False)
     main.set_exception_handler()
     if len(self.args) > 1:
         files = [os.path.abspath(p) for p in self.args[1:] if not
                 os.path.isdir(p)]
         if len(files) < len(sys.argv[1:]):
             prints('Ignoring directories passed as command line arguments')
         if files:
             add_filesystem_book(files)
     for event in self.app.file_event_hook.events:
         add_filesystem_book(event)
     self.app.file_event_hook = add_filesystem_book
Example #14
0
def render_html_svg_workaround(path_to_html, log, width=590, height=750):
    from calibre.ebooks.oeb.base import SVG_NS
    raw = open(path_to_html, 'rb').read()
    data = None
    if SVG_NS in raw:
        try:
            data = extract_cover_from_embedded_svg(raw,
                   os.path.dirname(path_to_html), log)
        except:
            pass
    if data is None:
        try:
            data = extract_calibre_cover(raw, os.path.dirname(path_to_html), log)
        except:
            pass

    if data is None:
        from calibre.gui2 import is_ok_to_use_qt
        if is_ok_to_use_qt():
            data = render_html_data(path_to_html, width, height)
        else:
            from calibre.utils.ipc.simple_worker import fork_job, WorkerError
            try:
                result = fork_job('calibre.ebooks',
                                  'render_html_data',
                                  (path_to_html, width, height),
                                  no_output=True)
                data = result['result']
            except WorkerError as err:
                prints(err.orig_tb)
            except:
                traceback.print_exc()
    return data
Example #15
0
 def can_handle_windows(self, usbdevice, debug=False):
     from calibre.devices.interface import DevicePlugin
     if self.can_handle.im_func is DevicePlugin.can_handle.im_func:
         # No custom can_handle implementation
         return True
     # Delegate to the unix can_handle function, creating a unix like
     # USBDevice object
     from calibre.devices.winusb import get_usb_info
     dev = usb_info_cache.get(usbdevice)
     if dev is None:
         try:
             data = get_usb_info(usbdevice, debug=debug)
         except Exception:
             time.sleep(0.1)
             try:
                 data = get_usb_info(usbdevice, debug=debug)
             except Exception:
                 data = {}
         dev = usb_info_cache[usbdevice] = namedtuple(
             'USBDevice', 'vendor_id product_id bcd manufacturer product serial')(
             usbdevice.vendor_id, usbdevice.product_id, usbdevice.bcd,
             data.get('manufacturer') or '', data.get('product') or '', data.get('serial_number') or '')
         if debug:
             prints('USB Info for device: {}'.format(dev))
     return self.can_handle(dev, debug=debug)
Example #16
0
def ensure_single_instance(args, open_at):
    try:
        from calibre.utils.lock import singleinstance
        si = singleinstance(singleinstance_name)
    except Exception:
        import traceback
        error_dialog(None, _('Cannot start viewer'), _(
            'Failed to start viewer, could not insure only a single instance of the viewer is running. Click "Show Details" for more information'),
                    det_msg=traceback.format_exc(), show=True)
        raise SystemExit(1)
    if not si:
        if len(args) > 1:
            t = RC(print_error=True, socket_address=viewer_socket_address())
            t.start()
            t.join(3.0)
            if t.is_alive() or t.conn is None:
                error_dialog(None, _('Connect to viewer failed'), _(
                    'Unable to connect to existing viewer window, try restarting the viewer.'), show=True)
                raise SystemExit(1)
            t.conn.send((os.path.abspath(args[1]), open_at))
            t.conn.close()
            prints('Opened book in existing viewer instance')
        raise SystemExit(0)
    listener = create_listener()
    return listener
Example #17
0
def find_pages(dir, sort_on_mtime=False, verbose=False):
    '''
    Find valid comic pages in a previously un-archived comic.

    :param dir: Directory in which extracted comic lives
    :param sort_on_mtime: If True sort pages based on their last modified time.
                          Otherwise, sort alphabetically.
    '''
    extensions = {'jpeg', 'jpg', 'gif', 'png', 'webp'}
    pages = []
    for datum in os.walk(dir):
        for name in datum[-1]:
            path = os.path.abspath(os.path.join(datum[0], name))
            if '__MACOSX' in path:
                continue
            for ext in extensions:
                if path.lower().endswith('.'+ext):
                    pages.append(path)
                    break
    sep_counts = {x.replace(os.sep, '/').count('/') for x in pages}
    # Use the full path to sort unless the files are in folders of different
    # levels, in which case simply use the filenames.
    basename = os.path.basename if len(sep_counts) > 1 else lambda x: x
    if sort_on_mtime:
        key = lambda x:os.stat(x).st_mtime
    else:
        key = lambda x:numeric_sort_key(basename(x))

    pages.sort(key=key)
    if verbose:
        prints('Found comic pages...')
        prints('\t'+'\n\t'.join([os.path.relpath(p, dir) for p in pages]))
    return pages
Example #18
0
    def custom_columns_in_meta(self):
        lines = {}
        for data in self.custom_column_label_map.values():
            table, lt = self.custom_table_names(data['num'])
            if data['normalized']:
                query = '%s.value'
                if data['is_multiple']:
#                    query = 'group_concat(%s.value, "{0}")'.format(
#                                        data['multiple_seps']['cache_to_list'])
#                    if not display.get('sort_alpha', False):
                    if data['multiple_seps']['cache_to_list'] == '|':
                        query = 'sortconcat_bar(link.id, %s.value)'
                    elif data['multiple_seps']['cache_to_list'] == '&':
                        query = 'sortconcat_amper(link.id, %s.value)'
                    else:
                        prints('WARNING: unknown value in multiple_seps',
                               data['multiple_seps']['cache_to_list'])
                        query = 'sortconcat_bar(link.id, %s.value)'
                line = '''(SELECT {query} FROM {lt} AS link INNER JOIN
                    {table} ON(link.value={table}.id) WHERE link.book=books.id)
                    custom_{num}
                '''.format(query=query%table, lt=lt, table=table, num=data['num'])
                if data['datatype'] == 'series':
                    line += ''',(SELECT extra FROM {lt} WHERE {lt}.book=books.id)
                        custom_index_{num}'''.format(lt=lt, num=data['num'])
            else:
                line = '''
                (SELECT value FROM {table} WHERE book=books.id) custom_{num}
                '''.format(table=table, num=data['num'])
            lines[data['num']] = line
        return lines
Example #19
0
def update_metadata(mi, fmt, stream, plugboards, cdata, error_report=None, plugboard_cache=None):
    from calibre.ebooks.metadata.meta import set_metadata
    if error_report is not None:
        def report_error(mi, fmt, tb):
            error_report(fmt, tb)

    try:
        if plugboard_cache is not None:
            cpb = plugboard_cache[fmt]
        else:
            cpb = find_plugboard(plugboard_save_to_disk_value, fmt, plugboards)
        if cpb:
            newmi = mi.deepcopy_metadata()
            newmi.template_to_attribute(mi, cpb)
        else:
            newmi = mi
        if cdata:
            newmi.cover_data = ('jpg', cdata)
        set_metadata(stream, newmi, fmt, report_error=None if error_report is None else report_error)
    except:
        if error_report is None:
            prints('Failed to set metadata for the', fmt, 'format of', mi.title)
            traceback.print_exc()
        else:
            error_report(fmt, traceback.format_exc())
Example #20
0
    def _upload_cover(self, path, filename, metadata, filepath):
        if metadata.thumbnail and metadata.thumbnail[-1]:
            path = path.replace('/', os.sep)
            is_main = path.startswith(self._main_prefix)
            thumbnail_dir = MEDIA_THUMBNAIL if is_main else CACHE_THUMBNAIL
            prefix = None
            if is_main:
                prefix = self._main_prefix
            else:
                if self._card_a_prefix and \
                    path.startswith(self._card_a_prefix):
                    prefix = self._card_a_prefix
                elif self._card_b_prefix and \
                        path.startswith(self._card_b_prefix):
                    prefix = self._card_b_prefix
            if prefix is None:
                prints('WARNING: Failed to find prefix for:', filepath)
                return
            thumbnail_dir = os.path.join(prefix, *thumbnail_dir.split('/'))

            relpath = os.path.relpath(filepath, prefix)
            if relpath.startswith('..\\'):
                relpath = relpath[3:]
            thumbnail_dir = os.path.join(thumbnail_dir, relpath)
            if not os.path.exists(thumbnail_dir):
                os.makedirs(thumbnail_dir)
            cpath = os.path.join(thumbnail_dir, 'main_thumbnail.jpg')
            with lopen(cpath, 'wb') as f:
                f.write(metadata.thumbnail[-1])
            debug_print('Cover uploaded to: %r'%cpath)
Example #21
0
 def unicode_listdir(root):
     root = root.encode(filesystem_encoding)
     for x in os.listdir(root):
         try:
             yield x.decode(filesystem_encoding)
         except UnicodeDecodeError:
             prints('Ignoring un-decodable file:', x)
Example #22
0
 def remove_user():
     un = get_valid_user()
     if get_input((_('Are you sure you want to remove the user %s?') % un) +
                  ' [y/n]:') != 'y':
         raise SystemExit(0)
     m.remove_user(un)
     prints(_('User %s successfully removed!') % un)
Example #23
0
def main():
    if iswindows:
        if '--multiprocessing-fork' in sys.argv:
            # We are using the multiprocessing module on windows to launch a
            # worker process
            from multiprocessing import freeze_support
            freeze_support()
            return 0
        # Close open file descriptors inherited from parent
        # On Unix this is done by the subprocess module
        os.closerange(3, 256)
    if isosx and 'CALIBRE_WORKER_ADDRESS' not in os.environ and 'CALIBRE_SIMPLE_WORKER' not in os.environ and '--pipe-worker' not in sys.argv:
        # On some OS X computers launchd apparently tries to
        # launch the last run process from the bundle
        # so launch the gui as usual
        from calibre.gui2.main import main as gui_main
        return gui_main(['calibre'])
    csw = os.environ.get('CALIBRE_SIMPLE_WORKER', None)
    if csw:
        mod, _, func = csw.partition(':')
        mod = importlib.import_module(mod)
        func = getattr(mod, func)
        func()
        return
    if '--pipe-worker' in sys.argv:
        try:
            exec (sys.argv[-1])
        except Exception:
            print('Failed to run pipe worker with command:', sys.argv[-1])
            raise
        return
    address = cPickle.loads(unhexlify(os.environ['CALIBRE_WORKER_ADDRESS']))
    key     = unhexlify(os.environ['CALIBRE_WORKER_KEY'])
    resultf = unhexlify(os.environ['CALIBRE_WORKER_RESULT']).decode('utf-8')
    with closing(Client(address, authkey=key)) as conn:
        name, args, kwargs, desc = eintr_retry_call(conn.recv)
        if desc:
            prints(desc)
            sys.stdout.flush()
        func, notification = get_func(name)
        notifier = Progress(conn)
        if notification:
            kwargs[notification] = notifier
            notifier.start()

        result = func(*args, **kwargs)
        if result is not None and os.path.exists(os.path.dirname(resultf)):
            cPickle.dump(result, open(resultf, 'wb'), -1)

        notifier.queue.put(None)

    try:
        sys.stdout.flush()
    except EnvironmentError:
        pass  # Happens sometimes on OS X for GUI processes (EPIPE)
    try:
        sys.stderr.flush()
    except EnvironmentError:
        pass  # Happens sometimes on OS X for GUI processes (EPIPE)
    return 0
Example #24
0
def command_list(args, dbpath):
    pre = get_parser('')
    pargs = [x for x in args if x.startswith('--with-library') or x.startswith('--library-path')
        or not x.startswith('-')]
    opts = pre.parse_args(sys.argv[:1] + pargs)[0]
    db = get_db(dbpath, opts)
    parser = list_option_parser(db=db)
    opts, args = parser.parse_args(sys.argv[:1] + args)
    afields = set(FIELDS)
    if db is not None:
        for f, data in db.custom_column_label_map.iteritems():
            afields.add('*'+f)
            if data['datatype'] == 'series':
                afields.add('*'+f+'_index')
    fields = [str(f.strip().lower()) for f in opts.fields.split(',')]
    if 'all' in fields:
        fields = sorted(list(afields))
    if not set(fields).issubset(afields):
        parser.print_help()
        print
        prints(_('Invalid fields. Available fields:'),
                ','.join(sorted(afields)), file=sys.stderr)
        return 1

    if not opts.sort_by in afields and opts.sort_by is not None:
        parser.print_help()
        print
        prints(_('Invalid sort field. Available fields:'), ','.join(afields),
                file=sys.stderr)
        return 1

    print do_list(db, fields, afields, opts.sort_by, opts.ascending, opts.search, opts.line_width, opts.separator,
            opts.prefix)
    return 0
Example #25
0
 def warning(self, *args, **kwargs):
     print '\n'+'_'*20, 'WARNING','_'*20
     prints(*args, **kwargs)
     print '_'*50
     print ('\n')
     self.warnings.append((args, kwargs))
     sys.stdout.flush()
Example #26
0
 def test(mi):
     mt = mi.title.lower()
     if (exact and mt == title) or \
             (not exact and title in mt):
         return True
     prints('Title test failed. Expected: \'%s\' found \'%s\''%(title, mt))
     return False
Example #27
0
 def __call__(self, book_id, mi, ok):
     if mi is True:
         self.total = book_id
     else:
         self.count += 1
         prints(u'%.1f%% %s - %s'%((self.count*100)/float(self.total),
             book_id, mi.title))
Example #28
0
 def get_valid(prompt, invalidq=lambda x: None):
     while True:
         ans = get_input(prompt + ':').strip()
         fail_message = invalidq(ans)
         if fail_message is None:
             return ans
         prints(fail_message)
Example #29
0
def find_pages(dir, sort_on_mtime=False, verbose=False):
    '''
    Find valid comic pages in a previously un-archived comic.

    :param dir: Directory in which extracted comic lives
    :param sort_on_mtime: If True sort pages based on their last modified time.
                          Otherwise, sort alphabetically.
    '''
    extensions = ['jpeg', 'jpg', 'gif', 'png']
    pages = []
    for datum in os.walk(dir):
        for name in datum[-1]:
            path = os.path.join(datum[0], name)
            if '__MACOSX' in path:
                continue
            for ext in extensions:
                if path.lower().endswith('.'+ext):
                    pages.append(path)
                    break
    if sort_on_mtime:
        comparator = lambda x, y : cmp(os.stat(x).st_mtime, os.stat(y).st_mtime)
    else:
        comparator = lambda x, y : cmp(os.path.basename(x), os.path.basename(y))

    pages.sort(cmp=comparator)
    if verbose:
        prints('Found comic pages...')
        prints('\t'+'\n\t'.join([os.path.basename(p) for p in pages]))
    return pages
Example #30
0
 def add_book(self, mi, cover_path, paths):
     if DEBUG:
         st = time.time()
     try:
         cdata = None
         if cover_path:
             with open(cover_path, 'rb') as f:
                 cdata = f.read()
             try:
                 os.remove(cover_path)
             except Exception:
                 pass
         book_id = self.dbref().create_book_entry(mi, cover=cdata)
         self.added_book_ids.add(book_id)
     except Exception:
         a = self.report.append
         a(''), a('-' * 70)
         a(_('Failed to add the book: ') + mi.title)
         [a('\t' + f) for f in paths]
         a(_('With error:')), a(traceback.format_exc())
         return
     self.add_formats(book_id, paths, mi)
     try:
         if self.add_formats_to_existing:
             self.db.update_data_for_find_identical_books(book_id, self.find_identical_books_data)
         else:
             self.added_duplicate_info.add(icu_lower(mi.title or _('Unknown')))
     except Exception:
         # Ignore this exception since all it means is that duplicate
         # detection/automerge will fail for this book.
         traceback.print_exc()
     if DEBUG:
         prints('Added', mi.title, 'to db in: %.1f' % (time.time() - st))
Example #31
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

        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()
        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()
        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)
        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)
Example #32
0
def get_metadata(stream, cover=True):
    with TemporaryDirectory('_pdf_metadata_read') as pdfpath:
        stream.seek(0)
        with open(os.path.join(pdfpath, 'src.pdf'), 'wb') as f:
            shutil.copyfileobj(stream, f)
        try:
            res = fork_job('calibre.ebooks.metadata.pdf', 'read_info',
                    (pdfpath, bool(cover)))
        except WorkerError as e:
            prints(e.orig_tb)
            raise RuntimeError('Failed to run pdfinfo')
        info = res['result']
        with open(res['stdout_stderr'], 'rb') as f:
            raw = f.read().strip()
            if raw:
                prints(raw)
        if info is None:
            raise ValueError('Could not read info dict from PDF')
        covpath = os.path.join(pdfpath, 'cover.jpg')
        cdata = None
        if cover and os.path.exists(covpath):
            with open(covpath, 'rb') as f:
                cdata = f.read()

    title = info.get('Title', None) or _('Unknown')
    au = info.get('Author', None)
    if au is None:
        au = [_('Unknown')]
    else:
        au = string_to_authors(au)
    mi = MetaInformation(title, au)
    # if isbn is not None:
    #    mi.isbn = isbn

    creator = info.get('Creator', None)
    if creator:
        mi.book_producer = creator

    keywords = info.get('Keywords', None)
    mi.tags = []
    if keywords:
        mi.tags = [x.strip() for x in keywords.split(',')]
        isbn = [check_isbn(x) for x in mi.tags if check_isbn(x)]
        if isbn:
            mi.isbn = isbn = isbn[0]
        mi.tags = [x for x in mi.tags if check_isbn(x) != isbn]

    subject = info.get('Subject', None)
    if subject:
        mi.tags.insert(0, subject)

    if 'xmp_metadata' in info:
        from calibre.ebooks.metadata.xmp import consolidate_metadata
        mi = consolidate_metadata(mi, info)

    # Look for recognizable identifiers in the info dict, if they were not
    # found in the XMP metadata
    for scheme, check_func in {'doi':check_doi, 'isbn':check_isbn}.iteritems():
        if scheme not in mi.get_identifiers():
            for k, v in info.iteritems():
                if k != 'xmp_metadata':
                    val = check_func(v)
                    if val:
                        mi.set_identifier(scheme, val)
                        break

    if cdata:
        mi.cover_data = ('jpeg', cdata)
    return mi
Example #33
0
    def _install_clicked(self):
        display_plugin = self._selected_display_plugin()
        if not question_dialog(
                self,
                _('Install %s') % display_plugin.name,
                '<p>' +
                _('Installing plugins is a <b>security risk</b>. '
                  'Plugins can contain a virus/malware. '
                  'Only install it if you got it from a trusted source.'
                  ' Are you sure you want to proceed?'),
                show_copy_button=False):
            return

        if display_plugin.uninstall_plugins:
            uninstall_names = list(display_plugin.uninstall_plugins)
            if DEBUG:
                prints('Uninstalling plugin: ', ', '.join(uninstall_names))
            for name_to_remove in uninstall_names:
                self._uninstall_plugin(name_to_remove)

        plugin_zip_url = display_plugin.zip_url
        if DEBUG:
            prints('Downloading plugin ZIP attachment: ', plugin_zip_url)
        self.gui.status_bar.showMessage(
            _('Downloading plugin ZIP attachment: %s') % plugin_zip_url)
        zip_path = self._download_zip(plugin_zip_url)

        if DEBUG:
            prints('Installing plugin: ', zip_path)
        self.gui.status_bar.showMessage(_('Installing plugin: %s') % zip_path)

        do_restart = False
        try:
            from calibre.customize.ui import config
            installed_plugins = frozenset(config['plugins'])
            try:
                plugin = add_plugin(zip_path)
            except NameConflict as e:
                return error_dialog(self.gui,
                                    _('Already exists'),
                                    unicode_type(e),
                                    show=True)
            # Check for any toolbars to add to.
            widget = ConfigWidget(self.gui)
            widget.gui = self.gui
            widget.check_for_add_to_toolbars(plugin,
                                             previously_installed=plugin.name
                                             in installed_plugins)
            self.gui.status_bar.showMessage(
                _('Plugin installed: %s') % display_plugin.name)
            d = info_dialog(
                self.gui,
                _('Success'),
                _('Plugin <b>{0}</b> successfully installed under <b>'
                  ' {1} plugins</b>. You may have to restart calibre '
                  'for the plugin to take effect.').format(
                      plugin.name, plugin.type),
                show_copy_button=False)
            b = d.bb.addButton(_('&Restart calibre now'), d.bb.AcceptRole)
            b.setIcon(QIcon(I('lt.png')))
            d.do_restart = False

            def rf():
                d.do_restart = True

            b.clicked.connect(rf)
            d.set_details('')
            d.exec_()
            b.clicked.disconnect()
            do_restart = d.do_restart

            display_plugin.plugin = plugin
            # We cannot read the 'actual' version information as the plugin will not be loaded yet
            display_plugin.installed_version = display_plugin.available_version
        except:
            if DEBUG:
                prints('ERROR occurred while installing plugin: %s' %
                       display_plugin.name)
                traceback.print_exc()
            error_dialog(
                self.gui,
                _('Install plugin failed'),
                _('A problem occurred while installing this plugin.'
                  ' This plugin will now be uninstalled.'
                  ' Please post the error message in details below into'
                  ' the forum thread for this plugin and restart calibre.'),
                det_msg=traceback.format_exc(),
                show=True)
            if DEBUG:
                prints('Due to error now uninstalling plugin: %s' %
                       display_plugin.name)
            remove_plugin(display_plugin.name)
            display_plugin.plugin = None

        display_plugin.uninstall_plugins = []
        if self.proxy_model.filter_criteria in [
                FILTER_NOT_INSTALLED, FILTER_UPDATE_AVAILABLE
        ]:
            self.model.beginResetModel(), self.model.endResetModel()
            self._select_and_focus_view()
        else:
            self.model.refresh_plugin(display_plugin)
            self._select_and_focus_view(change_selection=False)
        if do_restart:
            self.do_restart = True
            self.accept()
Example #34
0
def run_gui(opts, args, listener, app, gui_debug=None):
    si = singleinstance('db')
    if not si:
        ext = '.exe' if iswindows else ''
        error_dialog(
            None,
            _('Cannot start calibre'),
            _('Another calibre program that can modify calibre libraries, such as,'
              ' {} or {} is already running. You must first shut it down, before'
              ' starting the main calibre program. If you are sure no such'
              ' program is running, try restarting your computer.').format(
                  'calibre-server' + ext, 'calibredb' + ext),
            show=True)
        return 1
    initialize_file_icon_provider()
    app.load_builtin_fonts(scan_for_fonts=True)
    if not dynamic.get('welcome_wizard_was_run', False):
        from calibre.gui2.wizard import wizard
        wizard().exec_()
        dynamic.set('welcome_wizard_was_run', True)
    from calibre.gui2.ui import Main
    if isosx:
        actions = tuple(Main.create_application_menubar())
    else:
        actions = tuple(Main.get_menubar_actions())
    runner = GuiRunner(opts, args, actions, listener, app, gui_debug=gui_debug)
    ret = app.exec_()
    if getattr(runner.main, 'run_wizard_b4_shutdown', False):
        from calibre.gui2.wizard import wizard
        wizard().exec_()
    if getattr(runner.main, 'restart_after_quit', False):
        e = sys.executable if getattr(sys, 'frozen', False) else sys.argv[0]
        if getattr(runner.main, 'debug_on_restart',
                   False) or gui_debug is not None:
            run_in_debug_mode()
        else:
            import subprocess
            if hasattr(sys, 'frameworks_dir'):
                app = os.path.dirname(
                    os.path.dirname(os.path.realpath(sys.frameworks_dir)))
                prints('Restarting with:', app)
                subprocess.Popen('sleep 3s; open ' + shellquote(app),
                                 shell=True)
            else:
                os.environ[b'CALIBRE_RESTARTING_FROM_GUI'] = b'1'
                if iswindows and hasattr(winutil, 'prepare_for_restart'):
                    winutil.prepare_for_restart()
                args = ['-g'
                        ] if os.path.splitext(e)[0].endswith('-debug') else []
                prints('Restarting with:', ' '.join([e] + args))
                subprocess.Popen([e] + args)
    else:
        if iswindows:
            try:
                runner.main.system_tray_icon.hide()
            except:
                pass
    if getattr(runner.main, 'gui_debug', None) is not None:
        e = sys.executable if getattr(sys, 'frozen', False) else sys.argv[0]
        debugfile = runner.main.gui_debug
        from calibre.gui2 import open_local_file
        if iswindows:
            with open(debugfile, 'r+b') as f:
                raw = f.read()
                raw = re.sub(b'(?<!\r)\n', b'\r\n', raw)
                f.seek(0)
                f.truncate()
                f.write(raw)
        open_local_file(debugfile)
    return ret
Example #35
0
 def fail_request(self, rq, fail_code=None):
     if fail_code is None:
         fail_code = QWebEngineUrlRequestJob.Error.UrlNotFound
     rq.fail(fail_code)
     prints("Blocking FAKE_PROTOCOL request: {}".format(rq.requestUrl().toString()))
Example #36
0
    def job_exception(self,
                      job,
                      dialog_title=_('Conversion Error'),
                      retry_func=None):
        if not hasattr(self, '_modeless_dialogs'):
            self._modeless_dialogs = []
        minz = self.is_minimized_to_tray
        if self.isVisible():
            for x in list(self._modeless_dialogs):
                if not x.isVisible():
                    self._modeless_dialogs.remove(x)
        try:
            if 'calibre.ebooks.DRMError' in job.details:
                if not minz:
                    from calibre.gui2.dialogs.drm_error import DRMErrorMessage
                    d = DRMErrorMessage(
                        self,
                        _('Cannot convert') + ' ' +
                        job.description.split(':')[-1].partition('(')[-1][:-1])
                    d.setModal(False)
                    d.show()
                    self._modeless_dialogs.append(d)
                return

            if 'calibre.ebooks.oeb.transforms.split.SplitError' in job.details:
                title = job.description.split(':')[-1].partition('(')[-1][:-1]
                msg = _('<p><b>Failed to convert: %s') % title
                msg += '<p>' + _('''
                Many older ebook reader devices are incapable of displaying
                EPUB files that have internal components over a certain size.
                Therefore, when converting to EPUB, calibre automatically tries
                to split up the EPUB into smaller sized pieces.  For some
                files that are large undifferentiated blocks of text, this
                splitting fails.
                <p>You can <b>work around the problem</b> by either increasing the
                maximum split size under EPUB Output in the conversion dialog,
                or by turning on Heuristic Processing, also in the conversion
                dialog. Note that if you make the maximum split size too large,
                your ebook reader may have trouble with the EPUB.
                        ''')
                if not minz:
                    d = error_dialog(self,
                                     _('Conversion Failed'),
                                     msg,
                                     det_msg=job.details)
                    d.setModal(False)
                    d.show()
                    self._modeless_dialogs.append(d)
                return

            if 'calibre.ebooks.mobi.reader.mobi6.KFXError:' in job.details:
                if not minz:
                    title = job.description.split(':')[-1].partition(
                        '(')[-1][:-1]
                    msg = _('<p><b>Failed to convert: %s') % title
                    idx = job.details.index(
                        'calibre.ebooks.mobi.reader.mobi6.KFXError:')
                    msg += '<p>' + re.sub(
                        r'(https:\S+)', r'<a href="\1">{}</a>'.format(
                            _('here')),
                        job.details[idx:].partition(':')[2].strip())
                    d = error_dialog(self,
                                     _('Conversion Failed'),
                                     msg,
                                     det_msg=job.details)
                    d.setModal(False)
                    d.show()
                    self._modeless_dialogs.append(d)
                return

            if 'calibre.web.feeds.input.RecipeDisabled' in job.details:
                if not minz:
                    msg = job.details
                    msg = msg[msg.
                              find('calibre.web.feeds.input.RecipeDisabled:'):]
                    msg = msg.partition(':')[-1]
                    d = error_dialog(self, _('Recipe Disabled'),
                                     '<p>%s</p>' % msg)
                    d.setModal(False)
                    d.show()
                    self._modeless_dialogs.append(d)
                return

            if 'calibre.ebooks.conversion.ConversionUserFeedBack:' in job.details:
                if not minz:
                    import json
                    payload = job.details.rpartition(
                        'calibre.ebooks.conversion.ConversionUserFeedBack:'
                    )[-1]
                    payload = json.loads('{' + payload.partition('{')[-1])
                    d = {
                        'info': info_dialog,
                        'warn': warning_dialog,
                        'error': error_dialog
                    }.get(payload['level'], error_dialog)
                    d = d(self,
                          payload['title'],
                          '<p>%s</p>' % payload['msg'],
                          det_msg=payload['det_msg'])
                    d.setModal(False)
                    d.show()
                    self._modeless_dialogs.append(d)
                return
        except:
            pass
        if job.killed:
            return
        try:
            prints(job.details, file=sys.stderr)
        except:
            pass
        if not minz:
            self.job_error_dialog.show_error(dialog_title,
                                             _('<b>Failed</b>') + ': ' +
                                             unicode(job.description),
                                             det_msg=job.details,
                                             retry_func=retry_func)
Example #37
0
 def another_instance_wants_to_talk(self):
     try:
         msg = self.listener.queue.get_nowait()
     except Empty:
         return
     if msg.startswith('launched:'):
         import json
         try:
             argv = json.loads(msg[len('launched:'):])
         except ValueError:
             prints('Failed to decode message from other instance: %r' %
                    msg)
             if DEBUG:
                 error_dialog(
                     self,
                     'Invalid message',
                     'Received an invalid message from other calibre instance.'
                     ' Do you have multiple versions of calibre installed?',
                     det_msg='Invalid msg: %r' % msg,
                     show=True)
             argv = ()
         if isinstance(argv, (list, tuple)) and len(argv) > 1:
             files = [
                 os.path.abspath(p) for p in argv[1:]
                 if not os.path.isdir(p) and os.access(p, os.R_OK)
             ]
             if files:
                 self.iactions['Add Books'].add_filesystem_book(files)
         self.setWindowState(self.windowState() & ~Qt.WindowMinimized
                             | Qt.WindowActive)
         self.show_windows()
         self.raise_()
         self.activateWindow()
     elif msg.startswith('refreshdb:'):
         m = self.library_view.model()
         m.db.new_api.reload_from_db()
         m.db.data.refresh(clear_caches=False, do_search=False)
         m.resort()
         m.research()
         self.tags_view.recount()
     elif msg.startswith('shutdown:'):
         self.quit(confirm_quit=False)
     elif msg.startswith('bookedited:'):
         parts = msg.split(':')[1:]
         try:
             book_id, fmt, library_id = parts[:3]
             book_id = int(book_id)
             m = self.library_view.model()
             db = m.db.new_api
             if m.db.library_id == library_id and db.has_id(book_id):
                 db.format_metadata(book_id,
                                    fmt,
                                    allow_cache=False,
                                    update_db=True)
                 db.update_last_modified((book_id, ))
                 m.refresh_ids((book_id, ))
         except Exception:
             import traceback
             traceback.print_exc()
     else:
         print msg
Example #38
0
def ls(dev, path, recurse=False, human_readable_size=False, ll=False, cols=0):
    def col_split(l, cols):  # split list l into columns
        rows = len(l) / cols
        if len(l) % cols:
            rows += 1
        m = []
        for i in range(rows):
            m.append(l[i::rows])
        return m

    def row_widths(
            table):  # Calculate widths for each column in the row-wise table
        tcols = len(table[0])
        rowwidths = [0 for i in range(tcols)]
        for row in table:
            c = 0
            for item in row:
                rowwidths[c] = len(
                    item) if len(item) > rowwidths[c] else rowwidths[c]
                c += 1
        return rowwidths

    output = PolyglotBytesIO()
    if path.endswith("/") and len(path) > 1:
        path = path[:-1]
    dirs = dev.list(path, recurse)
    for dir in dirs:
        if recurse:
            prints(dir[0] + ":", file=output)
        lsoutput, lscoloutput = [], []
        files = dir[1]
        maxlen = 0
        if ll:  # Calculate column width for size column
            for file in files:
                size = len(str(file.size))
                if human_readable_size:
                    file = FileFormatter(file)
                    size = len(file.human_readable_size)
                if size > maxlen:
                    maxlen = size
        for file in files:
            file = FileFormatter(file)
            name = file.name if ll else file.isdir_name
            lsoutput.append(name)
            lscoloutput.append(name)
            if ll:
                size = str(file.size)
                if human_readable_size:
                    size = file.human_readable_size
                prints(file.mode_string, ("%" + str(maxlen) + "s") % size,
                       file.modification_time,
                       name,
                       file=output)
        if not ll and len(lsoutput) > 0:
            trytable = []
            for colwidth in range(MINIMUM_COL_WIDTH, cols):
                trycols = int(cols / colwidth)
                trytable = col_split(lsoutput, trycols)
                works = True
                for row in trytable:
                    row_break = False
                    for item in row:
                        if len(item) > colwidth - 1:
                            works, row_break = False, True
                            break
                    if row_break:
                        break
                if works:
                    break
            rowwidths = row_widths(trytable)
            trytablecol = col_split(lscoloutput, len(trytable[0]))
            for r in range(len(trytable)):
                for c in range(len(trytable[r])):
                    padding = rowwidths[c] - len(trytable[r][c])
                    prints(trytablecol[r][c],
                           "".ljust(padding),
                           end=' ',
                           file=output)
                prints(file=output)
        prints(file=output)
    listing = output.getvalue().rstrip().decode('utf-8') + "\n"
    output.close()
    return listing
Example #39
0
def inspect_mobi(path):
    from calibre.ebooks.mobi.debug.main import inspect_mobi
    prints('Inspecting:', path)
    inspect_mobi(path)
    print()
Example #40
0
 def handle_commandline_arg(self, arg):
     if arg:
         if os.path.isfile(arg) and os.access(arg, os.R_OK):
             self.load_ebook(arg)
         else:
             prints('Cannot read from:', arg, file=sys.stderr)
Example #41
0
__docformat__ = 'restructuredtext en'
"""
Provides abstraction for metadata reading.writing from a variety of ebook formats.
"""
import os, sys, re
from urllib import unquote, quote
from urlparse import urlparse

from calibre import relpath, guess_type, remove_bracketed_text, prints

from calibre.utils.config import tweaks

try:
    _author_pat = re.compile(tweaks['authors_split_regex'])
except:
    prints('Author split regexp:', tweaks['authors_split_regex'],
           'is invalid, using default')
    _author_pat = re.compile(r'(?i),?\s+(and|with)\s+')


def string_to_authors(raw):
    if not raw:
        return []
    raw = raw.replace('&&', u'\uffff')
    raw = _author_pat.sub('&', raw)
    authors = [a.strip().replace(u'\uffff', '&') for a in raw.split('&')]
    return [a for a in authors if a]


def authors_to_string(authors):
    if authors is not None:
        return ' & '.join([a.replace('&', '&&') for a in authors if a])
Example #42
0
def main(args=sys.argv):
    from calibre.constants import debug

    opts, args = option_parser().parse_args(args)
    if opts.fix_multiprocessing:
        sys.argv = [sys.argv[0], '--multiprocessing-fork']
        exec(args[-1])
        return
    debug()
    if opts.gui:
        from calibre.gui_launch import calibre
        calibre(['calibre'] + args[1:])
    elif opts.gui_debug is not None:
        run_debug_gui(opts.gui_debug)
    elif opts.viewer:
        from calibre.gui_launch import ebook_viewer
        ebook_viewer(['ebook-viewer'] + args[1:])
    elif opts.command:
        sys.argv = args
        exec(opts.command)
    elif opts.debug_device_driver:
        debug_device_driver()
    elif opts.add_simple_plugin is not None:
        add_simple_plugin(opts.add_simple_plugin)
    elif opts.paths:
        prints('CALIBRE_RESOURCES_PATH=' + sys.resources_location)
        prints('CALIBRE_EXTENSIONS_PATH=' + sys.extensions_location)
        prints('CALIBRE_PYTHON_PATH=' + os.pathsep.join(sys.path))
    elif opts.reinitialize_db is not None:
        reinit_db(opts.reinitialize_db)
    elif opts.inspect_mobi:
        for path in args[1:]:
            inspect_mobi(path)
    elif opts.edit_book:
        from calibre.gui_launch import ebook_edit
        ebook_edit(['ebook-edit'] + args[1:])
    elif opts.explode_book or opts.implode_book:
        from calibre.ebooks.tweak import explode, implode
        try:
            a1, a2 = args[1:]
        except Exception:
            raise SystemExit('Must provide exactly two arguments')
        f = explode if opts.explode_book else implode
        f(a1, a2)
    elif opts.test_build:
        from calibre.test_build import test, test_multiprocessing
        test_multiprocessing()
        test()
    elif opts.shutdown_running_calibre:
        from calibre.gui2.main import shutdown_other
        shutdown_other()
    elif opts.subset_font:
        from calibre.utils.fonts.sfnt.subset import main
        main(['subset-font'] + args[1:])
    elif opts.exec_file:
        run_script(opts.exec_file, args[1:])
    elif opts.run_plugin:
        from calibre.customize.ui import find_plugin
        plugin = find_plugin(opts.run_plugin)
        if plugin is None:
            prints(_('No plugin named %s found') % opts.run_plugin)
            raise SystemExit(1)
        plugin.cli_main([plugin.name] + args[1:])
    elif opts.diff:
        from calibre.gui2.tweak_book.diff.main import main
        main(['calibre-diff'] + args[1:])
    elif opts.default_programs:
        if not iswindows:
            raise SystemExit('Can only be run on Microsoft Windows')
        if opts.default_programs == 'register':
            from calibre.utils.winreg.default_programs import register as func
        else:
            from calibre.utils.winreg.default_programs import unregister as func
        print('Running', func.__name__, '...')
        func()
    elif opts.export_all_calibre_data:
        args = args[1:]
        from calibre.utils.exim import run_exporter
        run_exporter(args=args)
    elif opts.import_calibre_data:
        from calibre.utils.exim import run_importer
        run_importer()
    elif len(args) >= 2 and args[1].rpartition('.')[-1] in {'py', 'recipe'}:
        run_script(args[1], args[2:])
    elif len(args) >= 2 and args[1].rpartition('.')[-1] in {
            'mobi', 'azw', 'azw3', 'docx', 'odt'
    }:
        for path in args[1:]:
            ext = path.rpartition('.')[-1]
            if ext in {'docx', 'odt'}:
                from calibre.ebooks.docx.dump import dump
                dump(path)
            elif ext in {'mobi', 'azw', 'azw3'}:
                inspect_mobi(path)
            else:
                print('Cannot dump unknown filetype: %s' % path)
    elif len(args) >= 2 and os.path.exists(os.path.join(
            args[1], '__main__.py')):
        sys.path.insert(0, args[1])
        run_script(os.path.join(args[1], '__main__.py'), args[2:])
    else:
        load_user_plugins()
        from calibre import ipython
        ipython()

    return 0
Example #43
0
def main():
    with open(sys.argv[-1], 'rb') as f:
        css = f.read().decode('utf-8')
    errors = check_css([create_job(sys.argv[-1], css)])
    for error in errors:
        prints(error)
Example #44
0
def tweak(ebook_file):
    ''' Command line interface to the Tweak Book tool '''
    fmt = ebook_file.rpartition('.')[-1].lower()
    exploder, rebuilder = get_tools(fmt)
    if exploder is None:
        prints(
            'Cannot tweak %s files. Supported formats are: EPUB, HTMLZ, AZW3, MOBI'
            % fmt.upper(),
            file=sys.stderr)
        raise SystemExit(1)

    with TemporaryDirectory(
            '_tweak_' +
            os.path.basename(ebook_file).rpartition('.')[0]) as tdir:
        try:
            opf = exploder(ebook_file, tdir, question=ask_cli_question)
        except WorkerError as e:
            prints('Failed to unpack', ebook_file)
            prints(e.orig_tb)
            raise SystemExit(1)
        except Error as e:
            prints(as_unicode(e), file=sys.stderr)
            raise SystemExit(1)

        if opf is None:
            # The question was answered with No
            return

        prints('Book extracted to', tdir)
        prints('Make your tweaks and once you are done,', __appname__,
               'will rebuild', ebook_file, 'from', tdir)
        print()
        proceed = ask_cli_question('Rebuild ' + ebook_file + '?')

        if proceed:
            prints('Rebuilding', ebook_file, 'please wait ...')
            try:
                rebuilder(tdir, ebook_file)
            except WorkerError as e:
                prints('Failed to rebuild', ebook_file)
                prints(e.orig_tb)
                raise SystemExit(1)
            prints(ebook_file, 'successfully tweaked')
Example #45
0
    def create_action(self,
                      spec=None,
                      attr='qaction',
                      shortcut_name=None,
                      persist_shortcut=False):
        if spec is None:
            spec = self.action_spec
        text, icon, tooltip, shortcut = spec
        if icon is not None:
            action = QAction(QIcon(I(icon)), text, self.gui)
        else:
            action = QAction(text, self.gui)
        if attr == 'qaction':
            if hasattr(self.action_menu_clone_qaction, 'rstrip'):
                mt = unicode_type(self.action_menu_clone_qaction)
            else:
                mt = action.text()
            self.menuless_qaction = ma = QAction(action.icon(), mt, self.gui)
            ma.triggered.connect(action.trigger)
        for a in ((action, ma) if attr == 'qaction' else (action, )):
            a.setAutoRepeat(self.auto_repeat)
            text = tooltip if tooltip else text
            a.setToolTip(text)
            a.setStatusTip(text)
            a.setWhatsThis(text)
        shortcut_action = action
        desc = tooltip if tooltip else None
        if attr == 'qaction':
            shortcut_action = ma
        if shortcut is not None:
            keys = ((shortcut, ) if isinstance(shortcut, string_or_bytes) else
                    tuple(shortcut))
            if shortcut_name is None and spec[0]:
                shortcut_name = unicode_type(spec[0])

            if shortcut_name and self.action_spec[0] and not (
                    attr == 'qaction'
                    and self.popup_type == QToolButton.InstantPopup):
                try:
                    self.gui.keyboard.register_shortcut(
                        self.unique_name + ' - ' + attr,
                        shortcut_name,
                        default_keys=keys,
                        action=shortcut_action,
                        description=desc,
                        group=self.action_spec[0],
                        persist_shortcut=persist_shortcut)
                except NameConflict as e:
                    try:
                        prints(unicode_type(e))
                    except:
                        pass
                    shortcut_action.setShortcuts([
                        QKeySequence(key, QKeySequence.PortableText)
                        for key in keys
                    ])
                else:
                    self.shortcut_action_for_context_menu = shortcut_action
                    if ismacos:
                        # In Qt 5 keyboard shortcuts dont work unless the
                        # action is explicitly added to the main window
                        self.gui.addAction(shortcut_action)

        if attr is not None:
            setattr(self, attr, action)
        if attr == 'qaction' and self.action_add_menu:
            menu = QMenu()
            action.setMenu(menu)
            if self.action_menu_clone_qaction:
                menu.addAction(self.menuless_qaction)
        return action
Example #46
0
def run_file_dialog(
        parent=None, title=None, initial_folder=None, filename=None, save_path=None,
        allow_multiple=False, only_dirs=False, confirm_overwrite=True, save_as=False, no_symlinks=False,
        file_types=()
):
    from calibre.gui2 import sanitize_env_vars
    secret = os.urandom(32).replace(b'\0', b' ')
    pipename = '\\\\.\\pipe\\%s' % uuid4()
    data = [serialize_string('PIPENAME', pipename), serialize_secret(secret)]
    parent = parent or None
    if parent is not None:
        data.append(serialize_hwnd(get_hwnd(parent)))
    if title:
        data.append(serialize_string('TITLE', title))
    if no_symlinks:
        data.append(serialize_binary('NO_SYMLINKS', no_symlinks))
    if save_as:
        data.append(serialize_binary('SAVE_AS', save_as))
        if confirm_overwrite:
            data.append(serialize_binary('CONFIRM_OVERWRITE', confirm_overwrite))
        if save_path is not None:
            save_path = process_path(save_path)
            if os.path.exists(save_path):
                data.append(serialize_string('SAVE_PATH', save_path))
            else:
                if not initial_folder:
                    initial_folder = select_initial_dir(save_path)
                if not filename:
                    filename = os.path.basename(save_path)
    else:
        if allow_multiple:
            data.append(serialize_binary('MULTISELECT', allow_multiple))
        if only_dirs:
            data.append(serialize_binary('ONLY_DIRS', only_dirs))
    if initial_folder is not None:
        initial_folder = process_path(initial_folder)
        if os.path.isdir(initial_folder):
            data.append(serialize_string('FOLDER', initial_folder))
    if filename:
        if isinstance(filename, bytes):
            filename = filename.decode(filesystem_encoding)
        data.append(serialize_string('FILENAME', filename))
    if only_dirs:
        file_types = ()  # file types not allowed for dir only dialogs
    elif not file_types:
        file_types = [(_('All files'), ('*',))]
    if file_types:
        data.append(serialize_file_types(file_types))
    loop = Loop()
    server = PipeServer(pipename)
    with sanitize_env_vars():
        h = Helper(subprocess.Popen(
            [HELPER], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE),
               data, loop.dialog_closed.emit)
    h.start()
    loop.exec_(QEventLoop.ExcludeUserInputEvents)
    def decode(x):
        x = x or b''
        try:
            x = x.decode('utf-8')
        except Exception:
            x = repr(x)
        return x
    def get_errors():
        return decode(h.stdoutdata) + ' ' + decode(h.stderrdata)
    from calibre import prints
    from calibre.constants import DEBUG
    if DEBUG:
        prints('stdout+stderr from file dialog helper:', type('')([h.stdoutdata, h.stderrdata]))

    if h.rc != 0:
        raise Exception('File dialog failed: ' + get_errors())
    server.join(2)
    if server.is_alive():
        raise Exception('Timed out waiting for read from pipe to complete')
    if server.err_msg:
        raise Exception(server.err_msg)
    if not server.data:
        return ()
    parts = list(filter(None, server.data.split(b'\0')))
    if DEBUG:
        prints('piped data from file dialog helper:', type('')(parts))
    if len(parts) < 2:
        return ()
    if parts[0] != secret:
        raise Exception('File dialog failed, incorrect secret received: ' + get_errors())
    ans = tuple((os.path.abspath(x.decode('utf-8')) for x in parts[1:]))
    return ans
Example #47
0
 def javaScriptConsoleMessage(self, level, msg, linenumber, source_id):
     prints('%s:%s: %s' % (source_id, linenumber, msg))
Example #48
0
    def auto_add(self):
        from calibre.utils.ipc.simple_worker import fork_job, WorkerError
        from calibre.ebooks.metadata.opf2 import metadata_to_opf
        from calibre.ebooks.metadata.meta import metadata_from_filename

        files = [x for x in os.listdir(self.path) if
                    # Must not be in the process of being added to the db
                    x not in self.staging and
                    # Firefox creates 0 byte placeholder files when downloading
                    os.stat(os.path.join(self.path, x)).st_size > 0 and
                    # Must be a file
                    os.path.isfile(os.path.join(self.path, x)) and
                    # Must have read and write permissions
                    os.access(os.path.join(self.path, x), os.R_OK|os.W_OK) and
                    # Must be a known ebook file type
                    self.is_filename_allowed(x)
                ]
        data = []
        # Give any in progress copies time to complete
        time.sleep(2)

        def safe_mtime(x):
            try:
                return os.path.getmtime(os.path.join(self.path, x))
            except EnvironmentError:
                return time.time()

        for fname in sorted(files, key=safe_mtime):
            f = os.path.join(self.path, fname)

            # Try opening the file for reading, if the OS prevents us, then at
            # least on windows, it means the file is open in another
            # application for writing. We will get notified by
            # QFileSystemWatcher when writing is completed, so ignore for now.
            try:
                open(f, 'rb').close()
            except:
                continue
            tdir = tempfile.mkdtemp(dir=self.tdir)
            try:
                fork_job('calibre.ebooks.metadata.meta',
                        'forked_read_metadata', (f, tdir), no_output=True)
            except WorkerError as e:
                prints('Failed to read metadata from:', fname)
                prints(e.orig_tb)
            except:
                import traceback
                traceback.print_exc()

            # Ensure that the pre-metadata file size is present. If it isn't,
            # write 0 so that the file is rescanned
            szpath = os.path.join(tdir, 'size.txt')
            try:
                with open(szpath, 'rb') as f:
                    int(f.read())
            except:
                with open(szpath, 'wb') as f:
                    f.write(b'0')

            opfpath = os.path.join(tdir, 'metadata.opf')
            try:
                if os.stat(opfpath).st_size < 30:
                    raise Exception('metadata reading failed')
            except:
                mi = metadata_from_filename(fname)
                with open(opfpath, 'wb') as f:
                    f.write(metadata_to_opf(mi))
            self.staging.add(fname)
            data.append((fname, tdir))
        if data:
            self.callback(data)
Example #49
0
 def handle_changes_from_server(self, library_path, change_event):
     if DEBUG:
         prints('Received server change event: {} for {}'.format(change_event, library_path))
     if self.library_broker.is_gui_library(library_path):
         self.server_changes.put((library_path, change_event))
         self.server_change_notification_timer.start()
Example #50
0
 def log(self, *args, **kwargs):
     kwargs['file'] = sys.stderr
     prints(*args, **kwargs)
Example #51
0
def debug_print(*args):
    global BASE_TIME
    if BASE_TIME is None:
        BASE_TIME = time.time()
    if DEBUG:
        prints('DEBUG: %6.1f' % (time.time() - BASE_TIME), *args)
Example #52
0
 def not_single(c):
     if len(c) > 1:
         prints(c, 'is not a single character', file=sys.stderr)
         raise SystemExit(1)
Example #53
0
 def javaScriptConsoleMessage(self, msg, lineno, source_id):
     prints('JSBrowser msg():%s:%s:' % (unicode(source_id), lineno),
            unicode(msg))
Example #54
0
def find_icons():
    global icon_data
    if icon_data is not None:
        return icon_data
    base_dirs = [(os.environ.get('XDG_DATA_HOME')
                  or os.path.expanduser('~/.local/share')) + '/icons']
    base_dirs += [os.path.expanduser('~/.icons')]
    base_dirs += [
        os.path.join(b, 'icons') for b in os.environ.get(
            'XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(os.pathsep)
    ] + ['/usr/share/pixmaps']
    ans = defaultdict(list)
    sz_pat = re.compile(r'/((?:\d+x\d+)|scalable)/')
    cache_file = os.path.join(cache_dir(), 'icon-theme-cache.calibre_msgpack')
    exts = {'.svg', '.png', '.xpm'}

    def read_icon_theme_dir(dirpath):
        ans = defaultdict(list)
        for path in walk(dirpath):
            bn = os.path.basename(path)
            name, ext = os.path.splitext(bn)
            if ext in exts:
                sz = sz_pat.findall(path)
                if sz:
                    sz = sz[-1]
                    if sz == 'scalable':
                        sz = 100000
                    else:
                        sz = int(sz.partition('x')[0])
                    idx = len(ans[name])
                    ans[name].append((-sz, idx, sz, path))
        for icons in itervalues(ans):
            icons.sort(key=list)
        return {k: (-v[0][2], v[0][3]) for k, v in iteritems(ans)}

    try:
        with open(cache_file, 'rb') as f:
            cache = f.read()
        cache = msgpack_loads(cache)
        mtimes, cache = defaultdict(int, cache['mtimes']), defaultdict(
            dict, cache['data'])
    except Exception:
        mtimes, cache = defaultdict(int), defaultdict(dict)

    seen_dirs = set()
    changed = False

    for loc in base_dirs:
        try:
            subdirs = os.listdir(loc)
        except EnvironmentError:
            continue
        for dname in subdirs:
            d = os.path.join(loc, dname)
            if os.path.isdir(d):
                try:
                    mtime = os.stat(d).st_mtime
                except EnvironmentError:
                    continue
                seen_dirs.add(d)
                if mtime != mtimes[d]:
                    changed = True
                    try:
                        cache[d] = read_icon_theme_dir(d)
                    except Exception:
                        prints(
                            'Failed to read icon theme dir: %r with error:' %
                            d)
                        import traceback
                        traceback.print_exc()
                    mtimes[d] = mtime
                for name, data in iteritems(cache[d]):
                    ans[name].append(data)
    for removed in set(mtimes) - seen_dirs:
        mtimes.pop(removed), cache.pop(removed)
        changed = True

    if changed:
        data = msgpack_dumps({'data': cache, 'mtimes': mtimes})
        try:
            with open(cache_file, 'wb') as f:
                f.write(data)
        except Exception:
            import traceback
            traceback.print_exc()

    for icons in itervalues(ans):
        icons.sort(key=list)
    icon_data = {k: v[0][1] for k, v in iteritems(ans)}
    return icon_data
Example #55
0
def main(args=sys.argv):
    parser = option_parser()
    opts, args = parser.parse_args(args)
    if len(args) < 2:
        parser.print_help()
        prints(_('No file specified'), file=sys.stderr)
        return 1
    path = args[1]
    stream_type = os.path.splitext(path)[1].replace('.', '').lower()

    trying_to_set = False
    for pref in config().option_set.preferences:
        if pref.name in ('to_opf', 'get_cover'):
            continue
        if getattr(opts, pref.name) is not None:
            trying_to_set = True
            break
    with open(path, 'rb') as stream:
        mi = get_metadata(stream, stream_type, force_read_metadata=True)
    if trying_to_set:
        prints(_('Original metadata')+'::')
    metadata = unicode_type(mi)
    if trying_to_set:
        metadata = '\t'+'\n\t'.join(metadata.split('\n'))
    prints(metadata, safe_encode=True)

    if trying_to_set:
        with open(path, 'r+b') as stream:
            do_set_metadata(opts, mi, stream, stream_type)
            stream.seek(0)
            stream.flush()
            lrf = None
            if stream_type == 'lrf':
                if opts.lrf_bookid is not None:
                    lrf = LRFMetaFile(stream)
                    lrf.book_id = opts.lrf_bookid
            mi = get_metadata(stream, stream_type, force_read_metadata=True)
        prints('\n' + _('Changed metadata') + '::')
        metadata = unicode_type(mi)
        metadata = '\t'+'\n\t'.join(metadata.split('\n'))
        prints(metadata, safe_encode=True)
        if lrf is not None:
            prints('\tBookID:', lrf.book_id)

    if opts.to_opf is not None:
        from calibre.ebooks.metadata.opf2 import OPFCreator
        opf = OPFCreator(getcwd(), mi)
        with open(opts.to_opf, 'wb') as f:
            opf.render(f)
        prints(_('OPF created in'), opts.to_opf)

    if opts.get_cover is not None:
        if mi.cover_data and mi.cover_data[1]:
            with open(opts.get_cover, 'wb') as f:
                f.write(mi.cover_data[1])
                prints(_('Cover saved to'), f.name)
        else:
            prints(_('No cover found'), file=sys.stderr)

    return 0
Example #56
0
        root = container.parsed(spine_name)
        head = root.xpath('//*[local-name()="head"][1]')[0]
        href = container.name_to_href(name, spine_name)
        etree.SubElement(head,
                         XHTML('link'),
                         rel='stylesheet',
                         type='text/css',
                         href=href).tail = '\n'
        container.dirty(spine_name)
    return True


if __name__ == '__main__':
    from calibre.ebooks.oeb.polish.container import get_container
    from calibre.ebooks.oeb.polish.stats import StatsCollector
    from calibre.utils.logging import default_log
    default_log.filter_level = default_log.DEBUG
    inbook = sys.argv[-1]
    ebook = get_container(inbook, default_log)
    report = []
    stats = StatsCollector(ebook, do_embed=True)
    embed_all_fonts(ebook, stats, report.append)
    outbook, ext = inbook.rpartition('.')[0::2]
    outbook += '_subset.' + ext
    ebook.commit(outbook)
    prints('\nReport:')
    for msg in report:
        prints(msg)
    print()
    prints('Output written to:', outbook)
Example #57
0
    def __init__(self, path):
        self.handle_map = {}

        import win32file, winerror
        from pywintypes import error
        from collections import defaultdict

        if isbytestring(path):
            path = path.decode(filesystem_encoding)

        if not os.path.exists(path):
            return

        names = os.listdir(path)
        name_to_fileid = {
            x: windows_get_fileid(os.path.join(path, x))
            for x in names
        }
        fileid_to_names = defaultdict(set)
        for name, fileid in iteritems(name_to_fileid):
            fileid_to_names[fileid].add(name)

        for x in names:
            f = os.path.normcase(os.path.abspath(os.path.join(path, x)))
            if not os.path.isfile(f):
                continue
            try:
                # Ensure the file is not read-only
                win32file.SetFileAttributes(f, win32file.FILE_ATTRIBUTE_NORMAL)
            except:
                pass

            try:
                h = win32file.CreateFileW(f, win32file.GENERIC_READ,
                                          win32file.FILE_SHARE_DELETE, None,
                                          win32file.OPEN_EXISTING,
                                          win32file.FILE_FLAG_SEQUENTIAL_SCAN,
                                          0)
            except error as e:
                if getattr(e, 'winerror',
                           0) == winerror.ERROR_SHARING_VIOLATION:
                    # The file could be a hardlink to an already opened file,
                    # in which case we use the same handle for both files
                    fileid = name_to_fileid[x]
                    found = False
                    if fileid is not None:
                        for other in fileid_to_names[fileid]:
                            other = os.path.normcase(
                                os.path.abspath(os.path.join(path, other)))
                            if other in self.handle_map:
                                self.handle_map[f] = self.handle_map[other]
                                found = True
                                break
                    if found:
                        continue

                self.close_handles()
                if getattr(e, 'winerror',
                           0) == winerror.ERROR_SHARING_VIOLATION:
                    err = IOError(errno.EACCES,
                                  _('File is open in another process'))
                    err.filename = f
                    raise err
                prints('CreateFile failed for: %r' % f)
                raise
            except:
                self.close_handles()
                prints('CreateFile failed for: %r' % f)
                raise
            self.handle_map[f] = h
Example #58
0
 def javaScriptAlert(self, frame, msg):
     if self.view() is not None:
         return QWebPage.javaScriptAlert(self, frame, msg)
     prints('JSBrowser alert():', unicode(msg))
Example #59
0
 def dump_fonts(self):
     self.join()
     for family in self.font_families:
         prints(family)
         for font in self.fonts_for_family(family):
             prints('\t%s: %s'%(font['full_name'], font['path']))
             prints(end='\t')
             for key in ('font-stretch', 'font-weight', 'font-style'):
                 prints('%s: %s'%(key, font[key]), end=' ')
             prints()
             prints('\tSub-family:', font['wws_subfamily_name'] or
                     font['preferred_subfamily_name'] or
                     font['subfamily_name'])
             prints()
         prints()
Example #60
0
def main(args=sys.argv):
    from calibre.constants import debug
    debug()

    opts, args = option_parser().parse_args(args)
    if opts.gui:
        from calibre.gui2.main import main
        print_basic_debug_info()
        main(['calibre'])
    elif opts.gui_debug is not None:
        run_debug_gui(opts.gui_debug)
    elif opts.show_gui_debug:
        import time, re
        time.sleep(1)
        from calibre.gui2 import open_local_file
        if iswindows:
            with open(opts.show_gui_debug, 'r+b') as f:
                raw = f.read()
                raw = re.sub('(?<!\r)\n', '\r\n', raw)
                f.seek(0)
                f.truncate()
                f.write(raw)
        open_local_file(opts.show_gui_debug)
    elif opts.viewer:
        from calibre.gui2.viewer.main import main
        main(['ebook-viewer'] + args[1:])
    elif opts.py_console:
        from calibre.utils.pyconsole.main import main
        main()
    elif opts.command:
        sys.argv = args
        exec(opts.command)
    elif opts.debug_device_driver:
        debug_device_driver()
    elif opts.add_simple_plugin is not None:
        add_simple_plugin(opts.add_simple_plugin)
    elif opts.paths:
        prints('CALIBRE_RESOURCES_PATH=' + sys.resources_location)
        prints('CALIBRE_EXTENSIONS_PATH=' + sys.extensions_location)
        prints('CALIBRE_PYTHON_PATH=' + os.pathsep.join(sys.path))
    elif opts.reinitialize_db is not None:
        reinit_db(opts.reinitialize_db)
    elif opts.inspect_mobi:
        for path in args[1:]:
            inspect_mobi(path)
    elif opts.edit_book:
        from calibre.gui2.tweak_book.main import main
        main(['ebook-edit'] + args[1:])
    elif opts.explode_book:
        from calibre.ebooks.tweak import tweak
        tweak(opts.explode_book)
    elif opts.test_build:
        from calibre.test_build import test
        test()
    elif opts.shutdown_running_calibre:
        from calibre.gui2.main import shutdown_other
        shutdown_other()
    elif opts.subset_font:
        from calibre.utils.fonts.sfnt.subset import main
        main(['subset-font'] + [opts.subset_font] + args[1:])
    elif opts.exec_file:
        run_script(opts.exec_file, args[1:])
    elif opts.run_plugin:
        from calibre.customize.ui import find_plugin
        plugin = find_plugin(opts.run_plugin)
        if plugin is None:
            prints(_('No plugin named %s found') % opts.run_plugin)
            raise SystemExit(1)
        plugin.cli_main([plugin.name] + args[1:])
    elif opts.diff:
        from calibre.gui2.tweak_book.diff.main import main
        main(['calibre-diff'] + args[1:])
    elif len(args) >= 2 and args[1].rpartition('.')[-1] in {'py', 'recipe'}:
        run_script(args[1], args[2:])
    elif len(args) >= 2 and args[1].rpartition('.')[-1] in {
            'mobi', 'azw', 'azw3', 'docx'
    }:
        for path in args[1:]:
            ext = path.rpartition('.')[-1]
            if ext == 'docx':
                from calibre.ebooks.docx.dump import dump
                dump(path)
            elif ext in {'mobi', 'azw', 'azw3'}:
                inspect_mobi(path)
            else:
                print('Cannot dump unknown filetype: %s' % path)
    else:
        from calibre import ipython
        ipython()

    return 0