Пример #1
0
    def __init__(self, current_cover=None, parent=None):
        QDialog.__init__(self, parent)
        self.current_cover = current_cover
        self.log = Log()
        self.book = self.cover_pixmap = None

        self.setWindowTitle(_('Downloading metadata...'))
        self.setWindowIcon(QIcon(I('download-metadata.png')))

        self.stack = QStackedWidget()
        self.l = l = QVBoxLayout()
        self.setLayout(l)
        l.addWidget(self.stack)

        self.bb = QDialogButtonBox(QDialogButtonBox.Cancel
                                   | QDialogButtonBox.Ok)
        self.h = h = QHBoxLayout()
        l.addLayout(h)
        self.bb.rejected.connect(self.reject)
        self.bb.accepted.connect(self.accept)
        self.ok_button = self.bb.button(self.bb.Ok)
        self.ok_button.setEnabled(False)
        self.ok_button.clicked.connect(self.ok_clicked)
        self.prev_button = pb = QPushButton(QIcon(I('back.png')), _('&Back'),
                                            self)
        pb.clicked.connect(self.back_clicked)
        pb.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.log_button = self.bb.addButton(_('&View log'), self.bb.ActionRole)
        self.log_button.clicked.connect(self.view_log)
        self.log_button.setIcon(QIcon(I('debug.png')))
        self.prev_button.setVisible(False)
        h.addWidget(self.prev_button), h.addWidget(self.bb)

        self.identify_widget = IdentifyWidget(self.log, self)
        self.identify_widget.rejected.connect(self.reject)
        self.identify_widget.results_found.connect(self.identify_results_found)
        self.identify_widget.book_selected.connect(self.book_selected)
        self.stack.addWidget(self.identify_widget)

        self.covers_widget = CoversWidget(self.log,
                                          self.current_cover,
                                          parent=self)
        self.covers_widget.chosen.connect(self.ok_clicked)
        self.stack.addWidget(self.covers_widget)

        self.resize(850, 600)
        geom = gprefs.get('metadata_single_gui_geom', None)
        if geom is not None and geom:
            self.restoreGeometry(geom)

        self.finished.connect(self.cleanup)
Пример #2
0
    def setup_pipeline(self, *args):
        oidx = self.groups.currentIndex().row()
        output_format = self.output_format

        input_path = 'dummy.epub'
        output_path = 'dummy.' + output_format
        log = Log()
        log.outputs = []
        self.plumber = Plumber(input_path,
                               output_path,
                               log,
                               merge_plugin_recs=False)
        self.plumber.merge_plugin_recs(self.plumber.output_plugin)

        def widget_factory(cls):
            return cls(self, self.plumber.get_option_by_name,
                       self.plumber.get_option_help, self.db)

        self.setWindowTitle(
            ngettext(_('Bulk convert one book'), _('Bulk convert {} books'),
                     self.num_of_books).format(self.num_of_books))
        lf = widget_factory(LookAndFeelWidget)
        hw = widget_factory(HeuristicsWidget)
        sr = widget_factory(SearchAndReplaceWidget)
        ps = widget_factory(PageSetupWidget)
        sd = widget_factory(StructureDetectionWidget)
        toc = widget_factory(TOCWidget)
        toc.manually_fine_tune_toc.hide()

        output_widget = self.plumber.output_plugin.gui_configuration_widget(
            self, self.plumber.get_option_by_name,
            self.plumber.get_option_help, self.db)

        self.break_cycles()
        widgets = self.widgets = [lf, hw, ps, sd, toc, sr]
        if output_widget is not None:
            widgets.append(output_widget)
        for w in widgets:
            w.set_help_signal.connect(self.help.setPlainText)
            w.setVisible(False)

        self._groups_model = GroupModel(widgets)
        self.groups.setModel(self._groups_model)

        idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0
        self.groups.setCurrentIndex(self._groups_model.index(idx))
        self.show_pane(idx)
        try:
            shutil.rmtree(self.plumber.archive_input_tdir, ignore_errors=True)
        except:
            pass
Пример #3
0
def gui_catalog(fmt,
                title,
                dbspec,
                ids,
                out_file_name,
                sync,
                fmt_options,
                connected_device,
                notification=DummyReporter(),
                log=None):
    if log is None:
        log = Log()
    from calibre.library import db
    from calibre.utils.config import prefs
    prefs.refresh()
    db = db(read_only=True)
    db.catalog_plugin_on_device_temp_mapping = dbspec

    # Create a minimal OptionParser that we can append to
    parser = OptionParser()
    args = []
    parser.add_option("--verbose",
                      action="store_true",
                      dest="verbose",
                      default=True)
    opts, args = parser.parse_args()

    # Populate opts
    # opts.gui_search_text = something
    opts.catalog_title = title
    opts.connected_device = connected_device
    opts.ids = ids
    opts.search_text = None
    opts.sort_by = None
    opts.sync = sync

    # Extract the option dictionary to comma-separated lists
    for option in fmt_options:
        if isinstance(fmt_options[option], list):
            setattr(opts, option, ','.join(fmt_options[option]))
        else:
            setattr(opts, option, fmt_options[option])

    # Fetch and run the plugin for fmt
    # Returns 0 if successful, 1 if no catalog built
    plugin = plugin_for_catalog_format(fmt)
    return plugin.run(out_file_name, opts, db, notification=notification)
Пример #4
0
def main(args=sys.argv):
    log = Log()
    parser, plumber = create_option_parser(args, log)
    opts, leftover_args = parser.parse_args(args)
    if len(leftover_args) > 3:
        log.error('Extra arguments not understood:',
                  u', '.join(leftover_args[3:]))
        return 1
    for x in ('read_metadata_from_opf', 'cover'):
        if getattr(opts, x, None) is not None:
            setattr(opts, x, abspath(getattr(opts, x)))
    if opts.search_replace:
        opts.search_replace = read_sr_patterns(opts.search_replace, log)
    if opts.transform_css_rules:
        from calibre.ebooks.css_transform_rules import import_rules, validate_rule
        with open(opts.transform_css_rules, 'rb') as tcr:
            opts.transform_css_rules = rules = list(import_rules(tcr.read()))
            for rule in rules:
                title, msg = validate_rule(rule)
                if title and msg:
                    log.error('Failed to parse CSS transform rules')
                    log.error(title)
                    log.error(msg)
                    return 1

    recommendations = [(n.dest, getattr(opts,
                                        n.dest), OptionRecommendation.HIGH)
                       for n in parser.options_iter() if n.dest]
    plumber.merge_ui_recommendations(recommendations)

    try:
        plumber.run()
    except ConversionUserFeedBack as e:
        ll = {
            'info': log.info,
            'warn': log.warn,
            'error': log.error
        }.get(e.level, log.info)
        ll(e.title)
        if e.det_msg:
            log.debug(e.detmsg)
        ll(e.msg)
        raise SystemExit(1)

    log(_('Output saved to'), ' ', plumber.output)

    return 0
Пример #5
0
    def genesis(self, gui):
        log = Log()
        log.outputs = []

        self.plumber = Plumber('dummy.epub',
                               'dummy.epub',
                               log,
                               dummy=True,
                               merge_plugin_recs=False)

        def widget_factory(cls):
            plugin = getattr(cls, 'conv_plugin', None)
            if plugin is None:
                hfunc = self.plumber.get_option_help
            else:
                options = plugin.options.union(plugin.common_options)

                def hfunc(name):
                    for rec in options:
                        if rec.option == name:
                            ans = getattr(rec, 'help', None)
                            if ans is not None:
                                return ans.replace(
                                    '%default',
                                    unicode_type(rec.recommended_value))

            return cls(self, self.plumber.get_option_by_name, hfunc, None,
                       None)

        self.load_conversion_widgets()
        widgets = list(map(widget_factory, self.conversion_widgets))
        self.model = Model(widgets)
        self.list.setModel(self.model)

        for w in widgets:
            w.changed_signal.connect(self.changed_signal)
            sa = QScrollArea(self)
            sa.setWidget(w)
            sa.setWidgetResizable(True)
            self.stack.addWidget(sa)
            if isinstance(w, TOCWidget):
                w.manually_fine_tune_toc.hide()

        self.list.current_changed.connect(self.category_current_changed)
        self.list.setCurrentIndex(self.model.index(0))
def convert_document(source: Path, target: Path, cover_dir: Path):
    post = frontmatter.load(source)
    log = Log()

    title = post.get("title", "Untitled")
    author = post.get("creator", "Shannan Lekwati")

    args = [
        ("authors", author),
        ("language", post.get("lang", "en")),
        ("title", title),
    ]

    date = post.get("date")
    if date:
        args += [
            ("pubdate", str(post["date"])),
            ("timestamp", str(post["date"]))
        ]

    summary = post.get("summary")
    if summary:
        args += [ ("comments", summary) ]

    cover_image = post.get("cover", {}).get("image")
    if cover_image:
        cover_path = cover_dir.joinpath(cover_image).absolute()
        if cover_path.exists():
            args += [ ("cover", str(cover_path)) ]
        else:
            print(f"WARNING: {cover_image} in {source} does not exists", file=sys.stderr)

    with NamedTemporaryFile(suffix=source.suffix, mode="w") as f:
        f.write(f"# {title} by **{author}** \n")
        if "dedication" in post:
            f.write(f"{dedication}\n")
        f.write(post.content)
        f.flush()

        plumber = Plumber(f.name, target, log)
        recommendations = [(k, v, OptionRecommendation.HIGH) for (k,v) in args]
        plumber.merge_ui_recommendations(recommendations)
        plumber.run()
Пример #7
0
    def cli_main(self, argv):
        self.cli = True
        log = JobLog(Log())
        self.report_version(log)
        log.info("")

        allowed_exts = [".epub", ".opf", ".mobi", ".doc", ".docx", ".kpf", ".kfx-zip"]
        ext_choices = ", ".join(allowed_exts[:-1] + ["or " + allowed_exts[-1]])

        parser = argparse.ArgumentParser(prog='calibre-debug -r "KFX Output" --', description="Convert e-book to KFX format")
        parser.add_argument("-a", "--asin", action="store", help="Optional ASIN to assign to the book")
        parser.add_argument("-c", "--clean", action="store_true", help="Save the input file cleaned for conversion to KFX")
        parser.add_argument("-d", "--doc", action="store_true", help="Create personal document (PDOC) instead of book (EBOK)")
        parser.add_argument("-p", "--pages", action="store", type=int, default=-1,
                            help="Create n approximate page numbers if missing from input file (0 for auto)")
        parser.add_argument("-t", "--timeout", action="store_true", help="Stop conversions lasting over 15 minutes")
        parser.add_argument("-l", "--logs", action="store_true", help="Show log files produced during conversion")
        parser.add_argument("infile", help="Pathname of the %s file to be converted" % ext_choices)
        parser.add_argument("outfile", nargs="?", help="Optional pathname of the resulting .kfx file")
        args = parser.parse_args(argv[1:])

        input = book_name = args.infile
        intype = os.path.splitext(input)[1]

        if not os.path.isfile(input):
            raise Exception("Input file does not exist: %s" % input)

        if args.outfile:
            output = args.outfile
        else:
            output = os.path.join(os.path.dirname(input), os.path.splitext(os.path.basename(input))[0] + ".kfx")

        if not output.endswith(".kfx"):
            raise Exception("Output file must have .kfx extension")

        if intype in [".kpf", ".kfx-zip"]:
            self.convert_from_kpf_or_zip(log, book_name, input, args.asin, args.doc, args.pages, intype == ".kpf", output)
        elif intype in allowed_exts:
            self.convert_using_previewer(
                    log, book_name, input, args.asin, args.doc, args.pages, args.logs,
                    args.clean, TIMEOUT if args.timeout else None, output)
        else:
            raise Exception("Input file must be %s" % ext_choices)
Пример #8
0
def gui_polish(data):
    files = data.pop('files')
    if not data.pop('metadata'):
        data.pop('opf')
    if not data.pop('do_cover'):
        data.pop('cover', None)
    file_map = {x:x for x in files}
    opts = ALL_OPTS.copy()
    opts.update(data)
    O = namedtuple('Options', ' '.join(ALL_OPTS.iterkeys()))
    opts = O(**opts)
    log = Log(level=Log.DEBUG)
    report = []
    polish(file_map, opts, log, report.append)
    log('')
    log(REPORT)
    for msg in report:
        log(msg)
    return '\n\n'.join(report)
Пример #9
0
def gui_convert(input,
                output,
                recommendations,
                notification=DummyReporter(),
                abort_after_input_dump=False,
                log=None,
                override_input_metadata=False):
    recommendations = list(recommendations)
    recommendations.append(('verbose', 2, OptionRecommendation.HIGH))
    if log is None:
        log = Log()
    plumber = Plumber(input,
                      output,
                      log,
                      report_progress=notification,
                      abort_after_input_dump=abort_after_input_dump,
                      override_input_metadata=override_input_metadata)
    plumber.merge_ui_recommendations(recommendations)

    plumber.run()
Пример #10
0
def main(args=None):
    parser = option_parser()
    opts, args = parser.parse_args(args or sys.argv[1:])
    log = Log(level=Log.DEBUG if opts.verbose else Log.INFO)
    if not args:
        parser.print_help()
        log.error(_('You must provide the input file to polish'))
        raise SystemExit(1)
    if len(args) > 2:
        parser.print_help()
        log.error(_('Unknown extra arguments'))
        raise SystemExit(1)
    if len(args) == 1:
        inbook = args[0]
        base, ext = inbook.rpartition('.')[0::2]
        outbook = base + '_polished.' + ext
    else:
        inbook, outbook = args

    popts = ALL_OPTS.copy()
    for k, v in popts.items():
        popts[k] = getattr(opts, k, None)

    O = namedtuple('Options', ' '.join(iter(popts.keys())))
    popts = O(**popts)
    report = []
    if not tuple(
        [_f for _f in (getattr(popts, name) for name in ALL_OPTS) if _f]):
        parser.print_help()
        log.error(_('You must specify at least one action to perform'))
        raise SystemExit(1)

    polish({inbook: outbook}, popts, log, report.append)
    log('')
    log(REPORT)
    for msg in report:
        log(msg)

    log('Output written to:', outbook)
Пример #11
0
def main(args=sys.argv):
    log = Log()
    parser = option_parser()

    if len(args) < 2:
        print 'Error: No command sepecified.\n'
        print_help(parser, log)
        return 1

    command = args[1].lower().strip()

    if command in COMMANDS.keys():
        del args[1]
        return COMMANDS[command].main(args, command)
    else:
        parser.parse_args(args)
        print 'Unknown command %s.\n' % command
        print_help(parser, log)
        return 1

    # We should never get here.
    return 0
Пример #12
0
def convert_book(path_to_ebook, opf_path, cover_path, output_fmt, recs):
    from calibre.customize.conversion import OptionRecommendation
    from calibre.ebooks.conversion.plumber import Plumber
    from calibre.utils.logging import Log
    recs.append(('verbose', 2, OptionRecommendation.HIGH))
    recs.append(('read_metadata_from_opf', opf_path,
                OptionRecommendation.HIGH))
    if cover_path:
        recs.append(('cover', cover_path, OptionRecommendation.HIGH))
    log = Log()
    os.chdir(os.path.dirname(path_to_ebook))
    status_file = share_open('status', 'wb')

    def notification(percent, msg=''):
        status_file.write('{}:{}|||\n'.format(percent, msg).encode('utf-8'))
        status_file.flush()

    output_path = os.path.abspath('output.' + output_fmt.lower())
    plumber = Plumber(path_to_ebook, output_path, log,
                      report_progress=notification, override_input_metadata=True)
    plumber.merge_ui_recommendations(recs)
    plumber.run()
Пример #13
0
def process_epub(input_file, output_file):
    from calibre import CurrentDir
    from calibre.libunzip import extract as zipextract
    from calibre.ptempfile import TemporaryDirectory
    from calibre.utils.logging import Log

    from calibre_plugins.modify_epub.container import ExtendedContainer

    input_file = os.path.abspath(input_file)
    output_file = os.path.abspath(output_file)

    # Extract the epub into a temp directory
    with TemporaryDirectory('fb2-hyphens') as tdir:
        with CurrentDir(tdir):
            zipextract(input_file, tdir)

            # Use our own simplified wrapper around an ePub that will
            # preserve the file structure and css
            container = ExtendedContainer(tdir, Log())
            is_modified = process_epub_file(container)
            if is_modified:
                container.write(output_file)
Пример #14
0
def main(args=sys.argv):
    log = Log()
    parser, plumber = create_option_parser(args, log)
    opts, leftover_args = parser.parse_args(args)
    if len(leftover_args) > 3:
        log.error('Extra arguments not understood:',
                  u', '.join(leftover_args[3:]))
        return 1
    for x in ('read_metadata_from_opf', 'cover'):
        if getattr(opts, x, None) is not None:
            setattr(opts, x, abspath(getattr(opts, x)))
    if opts.search_replace:
        opts.search_replace = read_sr_patterns(opts.search_replace, log)

    recommendations = [(n.dest, getattr(opts, n.dest),
                        OptionRecommendation.HIGH) \
                                        for n in parser.options_iter()
                                        if n.dest]
    plumber.merge_ui_recommendations(recommendations)

    try:
        plumber.run()
    except ConversionUserFeedBack as e:
        ll = {
            'info': log.info,
            'warn': log.warn,
            'error': log.error
        }.get(e.level, log.info)
        ll(e.title)
        if e.det_msg:
            log.debug(e.detmsg)
        ll(e.msg)
        raise SystemExit(1)

    log(_('Output saved to'), ' ', plumber.output)

    return 0
Пример #15
0
    def genesis(self, gui):
        log = Log()
        log.outputs = []

        self.plumber = Plumber('dummy.epub', 'dummy.epub', log, dummy=True,
                merge_plugin_recs=False)

        def widget_factory(cls):
            return cls(self, self.plumber.get_option_by_name,
                self.plumber.get_option_help, None, None)

        self.load_conversion_widgets()
        widgets = list(map(widget_factory, self.conversion_widgets))
        self.model = Model(widgets)
        self.list.setModel(self.model)

        for w in widgets:
            w.changed_signal.connect(self.changed_signal)
            self.stack.addWidget(w)
            if isinstance(w, TOCWidget):
                w.manually_fine_tune_toc.hide()

        self.list.currentChanged = self.category_current_changed
        self.list.setCurrentIndex(self.model.index(0))
Пример #16
0
import json
from calibre.utils.logging import Log
from calibre.ebooks.metadata.book.base import Metadata
from calibre_plugins.crossref_doi_download import DoiMeta
from calibre_plugins.crossref_doi_download.doi_reader import DoiReader, get_title, get_author_list
from calibre_plugins.crossref_doi_download.doi_request import DoiQuery
from polyglot.urllib import urlencode
import calibre_plugins.crossref_doi_download.short_doi as sd
log = Log()
reader = DoiReader(log)
dm = DoiMeta('./plugin/')
br = dm.browser
onlineQuery = DoiQuery(br, log)

short_doi = '10/aabbe'
short_doi = '10.1002/9781118895047'

# cdata = br.open(doiquery).read()
# json.loads(cdata)
sd.is_short_doi(short_doi)
sd.return_full_doi(br, short_doi)
# https://doi.org/api/handles/10/aabbe

# doilink = 'https://dx.doi.org/10.1002/(SICI)1097-0258(19980815/30)17:15/16%3C1661::AID-SIM968%3E3.0.CO;2-2?noredirect'

# fullurl = 'https://api.crossref.org/works?%s' % dois
# fullurl = 'https://api.crossref.org/works?filter=doi:10/aabbe&mailto=vikoya5988%40oniaj.com'
# cdata = br.open(fullurl).read()

# data = json.loads(cdata)
# message = data['message']
Пример #17
0
def create_fetcher(options, image_map={}, log=None):
    if log is None:
        log = Log(level=Log.DEBUG) if options.verbose else Log()
    return RecursiveFetcher(options, log, image_map={})
Пример #18
0
    def run(self, opts):
        from calibre.utils.serialize import msgpack_dumps
        scripts = {}
        for x in ('console', 'gui'):
            for name in basenames[x]:
                if name in ('calibre-complete', 'calibre_postinstall'):
                    continue
                scripts[name] = x

        dest = self.j(self.RESOURCES, 'scripts.calibre_msgpack')
        if self.newer(dest, self.j(self.SRC, 'calibre', 'linux.py')):
            self.info('\tCreating ' + self.b(dest))
            with open(dest, 'wb') as f:
                f.write(msgpack_dumps(scripts))

        from calibre.web.feeds.recipes.collection import \
                serialize_builtin_recipes, iterate_over_builtin_recipe_files

        files = [x[1] for x in iterate_over_builtin_recipe_files()]

        dest = self.j(self.RESOURCES, 'builtin_recipes.xml')
        if self.newer(dest, files):
            self.info('\tCreating builtin_recipes.xml')
            xml = serialize_builtin_recipes()
            with open(dest, 'wb') as f:
                f.write(xml)

        recipe_icon_dir = self.a(
            self.j(self.RESOURCES, '..', 'recipes', 'icons'))
        dest = os.path.splitext(dest)[0] + '.zip'
        files += glob.glob(self.j(recipe_icon_dir, '*.png'))
        if self.newer(dest, files):
            self.info('\tCreating builtin_recipes.zip')
            with zipfile.ZipFile(dest, 'w', zipfile.ZIP_STORED) as zf:
                for n in sorted(files, key=self.b):
                    with open(n, 'rb') as f:
                        zf.writestr(self.b(n), f.read())

        dest = self.j(self.RESOURCES, 'ebook-convert-complete.calibre_msgpack')
        files = []
        for x in os.walk(self.j(self.SRC, 'calibre')):
            for f in x[-1]:
                if f.endswith('.py'):
                    files.append(self.j(x[0], f))
        if self.newer(dest, files):
            self.info('\tCreating ' + self.b(dest))
            complete = {}
            from calibre.ebooks.conversion.plumber import supported_input_formats
            complete['input_fmts'] = set(supported_input_formats())
            from calibre.web.feeds.recipes.collection import get_builtin_recipe_titles
            complete['input_recipes'] = [
                t + '.recipe ' for t in get_builtin_recipe_titles()
            ]
            from calibre.customize.ui import available_output_formats
            complete['output'] = set(available_output_formats())
            from calibre.ebooks.conversion.cli import create_option_parser
            from calibre.utils.logging import Log
            log = Log()
            # log.outputs = []
            for inf in supported_input_formats():
                if inf in ('zip', 'rar', 'oebzip'):
                    continue
                for ouf in available_output_formats():
                    of = ouf if ouf == 'oeb' else 'dummy.' + ouf
                    p = create_option_parser(('ec', 'dummy1.' + inf, of, '-h'),
                                             log)[0]
                    complete[(inf, ouf)] = [
                        x + ' ' for x in get_opts_from_parser(p)
                    ]

            with open(dest, 'wb') as f:
                f.write(msgpack_dumps(only_unicode_recursive(complete)))

        self.info('\tCreating template-functions.json')
        dest = self.j(self.RESOURCES, 'template-functions.json')
        function_dict = {}
        import inspect
        from calibre.utils.formatter_functions import formatter_functions
        for obj in formatter_functions().get_builtins().values():
            eval_func = inspect.getmembers(
                obj,
                lambda x: inspect.ismethod(x) and x.__name__ == 'evaluate')
            try:
                lines = [
                    l[4:] for l in inspect.getsourcelines(eval_func[0][1])[0]
                ]
            except:
                continue
            lines = ''.join(lines)
            function_dict[obj.name] = lines
        dump_json(function_dict, dest)

        self.info('\tCreating editor-functions.json')
        dest = self.j(self.RESOURCES, 'editor-functions.json')
        function_dict = {}
        from calibre.gui2.tweak_book.function_replace import builtin_functions
        for func in builtin_functions():
            try:
                src = ''.join(inspect.getsourcelines(func)[0][1:])
            except Exception:
                continue
            src = src.replace('def ' + func.__name__, 'def replace')
            imports = [
                f'from {x.__module__} import {x.__name__}'
                for x in func.imports
            ]
            if imports:
                src = '\n'.join(imports) + '\n\n' + src
            function_dict[func.name] = src
        dump_json(function_dict, dest)
        self.info('\tCreating user-manual-translation-stats.json')
        d = {}
        for lc, stats in iteritems(
                json.load(
                    open(
                        self.j(self.d(self.SRC), 'manual', 'locale',
                               'completed.json')))):
            total = sum(itervalues(stats))
            d[lc] = stats['translated'] / float(total)
        dump_json(d,
                  self.j(self.RESOURCES, 'user-manual-translation-stats.json'))

        src = self.j(self.SRC, '..', 'Changelog.txt')
        dest = self.j(self.RESOURCES, 'changelog.json')
        if self.newer(dest, [src]):
            self.info('\tCreating changelog.json')
            from setup.changelog import parse
            with open(src, encoding='utf-8') as f:
                dump_json(parse(f.read(), parse_dates=False), dest)
Пример #19
0
def do_download_for_worker(book, options, merge, notification=lambda x, y: x):
    '''
    Child job, to download story when run as a worker job
    '''

    from calibre_plugins.fanficfare_plugin import FanFicFareBase
    fffbase = FanFicFareBase(options['plugin_path'])
    with fffbase:  # so the sys.path was modified while loading the
        # plug impl.
        from calibre_plugins.fanficfare_plugin.dialogs import (
            NotGoingToDownload, OVERWRITE, OVERWRITEALWAYS, UPDATE,
            UPDATEALWAYS, ADDNEW, SKIP, CALIBREONLY, CALIBREONLYSAVECOL)
        from calibre_plugins.fanficfare_plugin.fanficfare import adapters, writers, exceptions
        from calibre_plugins.fanficfare_plugin.fanficfare.epubutils import get_update_data

        from calibre_plugins.fanficfare_plugin.fff_util import (
            get_fff_adapter, get_fff_config)

        try:
            ## No need to download at all.  Can happen now due to
            ## collision moving into book for CALIBREONLY changing to
            ## ADDNEW when story URL not in library.
            if book['collision'] in (CALIBREONLY, CALIBREONLYSAVECOL):
                logger.info("Skipping CALIBREONLY 'update' down inside worker")
                return book

            book['comment'] = _('Download started...')

            configuration = get_fff_config(book['url'], options['fileform'],
                                           options['personal.ini'])

            if configuration.getConfig('use_ssl_unverified_context'):
                ## monkey patch to avoid SSL bug.  dupliated from
                ## fff_plugin.py because bg jobs run in own process
                ## space.
                import ssl
                if hasattr(ssl, '_create_unverified_context'):
                    ssl._create_default_https_context = ssl._create_unverified_context

            if not options[
                    'updateepubcover'] and 'epub_for_update' in book and book[
                        'collision'] in (UPDATE, UPDATEALWAYS):
                configuration.set("overrides", "never_make_cover", "true")

            # images only for epub, html, even if the user mistakenly
            # turned it on else where.
            if options['fileform'] not in ("epub", "html"):
                configuration.set("overrides", "include_images", "false")

            adapter = adapters.getAdapter(configuration, book['url'])
            adapter.is_adult = book['is_adult']
            adapter.username = book['username']
            adapter.password = book['password']
            adapter.setChaptersRange(book['begin'], book['end'])

            configuration.load_cookiejar(options['cookiejarfile'])
            #logger.debug("cookiejar:%s"%configuration.cookiejar)
            configuration.set_pagecache(options['pagecache'])

            story = adapter.getStoryMetadataOnly()
            if not story.getMetadata("series") and 'calibre_series' in book:
                adapter.setSeries(book['calibre_series'][0],
                                  book['calibre_series'][1])

            # set PI version instead of default.
            if 'version' in options:
                story.setMetadata('version', options['version'])

            book['title'] = story.getMetadata("title", removeallentities=True)
            book['author_sort'] = book['author'] = story.getList(
                "author", removeallentities=True)
            book['publisher'] = story.getMetadata("publisher")
            book['url'] = story.getMetadata("storyUrl", removeallentities=True)
            book['tags'] = story.getSubjectTags(removeallentities=True)
            book['comments'] = story.get_sanitized_description()
            book['series'] = story.getMetadata("series",
                                               removeallentities=True)

            if story.getMetadataRaw('datePublished'):
                book['pubdate'] = story.getMetadataRaw(
                    'datePublished').replace(tzinfo=local_tz)
            if story.getMetadataRaw('dateUpdated'):
                book['updatedate'] = story.getMetadataRaw(
                    'dateUpdated').replace(tzinfo=local_tz)
            if story.getMetadataRaw('dateCreated'):
                book['timestamp'] = story.getMetadataRaw(
                    'dateCreated').replace(tzinfo=local_tz)
            else:
                book['timestamp'] = datetime.now().replace(
                    tzinfo=local_tz)  # need *something* there for calibre.

            writer = writers.getWriter(options['fileform'], configuration,
                                       adapter)
            outfile = book['outfile']

            ## checks were done earlier, it's new or not dup or newer--just write it.
            if book['collision'] in (ADDNEW, SKIP, OVERWRITE, OVERWRITEALWAYS) or \
                    ('epub_for_update' not in book and book['collision'] in (UPDATE, UPDATEALWAYS)):

                # preserve logfile even on overwrite.
                if 'epub_for_update' in book:
                    adapter.logfile = get_update_data(
                        book['epub_for_update'])[6]
                    # change the existing entries id to notid so
                    # write_epub writes a whole new set to indicate overwrite.
                    if adapter.logfile:
                        adapter.logfile = adapter.logfile.replace(
                            "span id", "span notid")

                if book['collision'] == OVERWRITE and 'fileupdated' in book:
                    lastupdated = story.getMetadataRaw('dateUpdated')
                    fileupdated = book['fileupdated']

                    # updated doesn't have time (or is midnight), use dates only.
                    # updated does have time, use full timestamps.
                    if (lastupdated.time() == time.min and fileupdated.date() > lastupdated.date()) or \
                            (lastupdated.time() != time.min and fileupdated > lastupdated):
                        raise NotGoingToDownload(
                            _("Not Overwriting, web site is not newer."),
                            'edit-undo.png',
                            showerror=False)

                logger.info("write to %s" % outfile)
                inject_cal_cols(book, story, configuration)
                writer.writeStory(outfilename=outfile, forceOverwrite=True)

                book['comment'] = _('Download %s completed, %s chapters.') % (
                    options['fileform'], story.getMetadata("numChapters"))
                book['all_metadata'] = story.getAllMetadata(
                    removeallentities=True)
                if options['savemetacol'] != '':
                    book['savemetacol'] = story.dump_html_metadata()

            ## checks were done earlier, just update it.
            elif 'epub_for_update' in book and book['collision'] in (
                    UPDATE, UPDATEALWAYS):

                # update now handled by pre-populating the old images and
                # chapters in the adapter rather than merging epubs.
                #urlchaptercount = int(story.getMetadata('numChapters').replace(',',''))
                # returns int adjusted for start-end range.
                urlchaptercount = story.getChapterCount()
                (url, chaptercount, adapter.oldchapters, adapter.oldimgs,
                 adapter.oldcover, adapter.calibrebookmark, adapter.logfile,
                 adapter.oldchaptersmap,
                 adapter.oldchaptersdata) = get_update_data(
                     book['epub_for_update'])[0:9]

                # dup handling from fff_plugin needed for anthology updates.
                if book['collision'] == UPDATE:
                    if chaptercount == urlchaptercount:
                        if merge:
                            book['comment'] = _(
                                "Already contains %d chapters.  Reuse as is."
                            ) % chaptercount
                            book['all_metadata'] = story.getAllMetadata(
                                removeallentities=True)
                            if options['savemetacol'] != '':
                                book['savemetacol'] = story.dump_html_metadata(
                                )
                            book['outfile'] = book[
                                'epub_for_update']  # for anthology merge ops.
                            return book
                        else:  # not merge,
                            raise NotGoingToDownload(
                                _("Already contains %d chapters.") %
                                chaptercount,
                                'edit-undo.png',
                                showerror=False)
                    elif chaptercount > urlchaptercount:
                        raise NotGoingToDownload(
                            _("Existing epub contains %d chapters, web site only has %d. Use Overwrite to force update."
                              ) % (chaptercount, urlchaptercount),
                            'dialog_error.png')
                    elif chaptercount == 0:
                        raise NotGoingToDownload(
                            _("FanFicFare doesn't recognize chapters in existing epub, epub is probably from a different source. Use Overwrite to force update."
                              ), 'dialog_error.png')

                if not (book['collision'] == UPDATEALWAYS and chaptercount == urlchaptercount) \
                        and adapter.getConfig("do_update_hook"):
                    chaptercount = adapter.hookForUpdates(chaptercount)

                logger.info("Do update - epub(%d) vs url(%d)" %
                            (chaptercount, urlchaptercount))
                logger.info("write to %s" % outfile)

                inject_cal_cols(book, story, configuration)
                writer.writeStory(outfilename=outfile, forceOverwrite=True)

                book['comment'] = _('Update %s completed, added %s chapters for %s total.')%\
                    (options['fileform'],(urlchaptercount-chaptercount),urlchaptercount)
                book['all_metadata'] = story.getAllMetadata(
                    removeallentities=True)
                if options['savemetacol'] != '':
                    book['savemetacol'] = story.dump_html_metadata()
            else:
                ## Shouldn't ever get here, but hey, it happened once
                ## before with prefs['collision']
                raise Exception(
                    "Impossible state reached -- Book: %s:\nOptions:%s:" %
                    (book, options))

            if options['do_wordcount'] == SAVE_YES or (
                    options['do_wordcount'] == SAVE_YES_UNLESS_SITE
                    and not story.getMetadataRaw('numWords')):
                wordcount = get_word_count(outfile)
                logger.info("get_word_count:%s" % wordcount)
                story.setMetadata('numWords', wordcount)
                writer.writeStory(outfilename=outfile, forceOverwrite=True)
                book['all_metadata'] = story.getAllMetadata(
                    removeallentities=True)
                if options['savemetacol'] != '':
                    book['savemetacol'] = story.dump_html_metadata()

            if options['smarten_punctuation'] and options['fileform'] == "epub" \
                    and calibre_version >= (0, 9, 39):
                # for smarten punc
                from calibre.ebooks.oeb.polish.main import polish, ALL_OPTS
                from calibre.utils.logging import Log
                from collections import namedtuple

                # do smarten_punctuation from calibre's polish feature
                data = {'smarten_punctuation': True}
                opts = ALL_OPTS.copy()
                opts.update(data)
                O = namedtuple('Options', ' '.join(six.iterkeys(ALL_OPTS)))
                opts = O(**opts)

                log = Log(level=Log.DEBUG)
                polish({outfile: outfile}, opts, log, logger.info)

        except NotGoingToDownload as d:
            book['good'] = False
            book['status'] = _('Bad')
            book['showerror'] = d.showerror
            book['comment'] = unicode(d)
            book['icon'] = d.icon

        except Exception as e:
            book['good'] = False
            book['status'] = _('Error')
            book['comment'] = unicode(e)
            book['icon'] = 'dialog_error.png'
            book['status'] = _('Error')
            logger.info("Exception: %s:%s" % (book, book['comment']),
                        exc_info=True)

        #time.sleep(10)
    return book
Пример #20
0
    def set_metadata(self, stream, mi, type_):
        from calibre_plugins.kfx_output.kfxlib import (set_logger, YJ_Book, YJ_Metadata)
        from calibre.ebooks import normalize as normalize_unicode
        from calibre.ebooks.metadata import author_to_author_sort
        from calibre.utils.config_base import tweaks
        from calibre.utils.date import (is_date_undefined, isoformat)
        from calibre.utils.logging import Log
        from calibre.utils.localization import (canonicalize_lang, lang_as_iso639_1)

        def mapped_author_to_author_sort(author):
            if hasattr(mi, "author_sort_map"):
                author_sort = mi.author_sort_map.get(author)    # use mapping if provided
                if author_sort:
                    return author_sort

            return author_to_author_sort(author)

        def normalize(s):
            if not isinstance(s, type("")):
                s = s.decode("utf8", "ignore")

            return normalize_unicode(s)

        log = set_logger(Log())

        filename = stream.name if hasattr(stream, "name") else "stream"
        log.info("KFX metadata writer activated for %s" % filename)

        try:
            from calibre.ebooks.conversion.config import load_defaults
            prefs = load_defaults('kfx_output')
        except Exception:
            prefs = {}
            log.info("Failed to read default KFX Output preferences")

        md = YJ_Metadata(author_sort_fn=mapped_author_to_author_sort)

        md.title = normalize(mi.title)

        md.authors = [normalize(author) for author in mi.authors]

        if mi.publisher:
            md.publisher = normalize(mi.publisher)

        if mi.pubdate and not is_date_undefined(mi.pubdate):
            md.issue_date = str(isoformat(mi.pubdate)[:10])

        if mi.comments:
            # Strip user annotations
            a_offset = mi.comments.find('<div class="user_annotations">')
            ad_offset = mi.comments.find('<hr class="annotations_divider" />')

            if a_offset >= 0:
                mi.comments = mi.comments[:a_offset]
            if ad_offset >= 0:
                mi.comments = mi.comments[:ad_offset]

            md.description = normalize(mi.comments)

        if not mi.is_null('language'):
            lang = canonicalize_lang(mi.language)
            lang = lang_as_iso639_1(lang) or lang
            if lang:
                md.language = normalize(lang)

        if mi.cover_data[1]:
            md.cover_image_data = mi.cover_data
        elif mi.cover:
            md.cover_image_data = ("jpg", open(mi.cover, 'rb').read())

        if not tweaks.get("kfx_output_ignore_asin_metadata", False):
            value = mi.identifiers.get("mobi-asin")
            if value is not None and re.match(ASIN_RE, value):
                md.asin = value
            else:
                for ident, value in mi.identifiers.items():
                    if ident.startswith("amazon") and re.match(ASIN_RE, value):
                        md.asin = value
                        break
                else:
                    value = mi.identifiers.get("asin")
                    if value is not None and re.match(ASIN_RE, value):
                        md.asin = value

        if md.asin:
            md.cde_content_type = "EBOK"

        if prefs.get("approximate_pages", False):
            page_count = 0
            number_of_pages_field = prefs.get("number_of_pages_field", AUTO_PAGES)
            if number_of_pages_field and number_of_pages_field != AUTO_PAGES:
                number_of_pages = mi.get(number_of_pages_field, "")
                try:
                    page_count = int(number_of_pages)
                except Exception:
                    pass
        else:
            page_count = -1

        book = YJ_Book(stream, log)
        book.decode_book(set_metadata=md, set_approximate_pages=page_count)
        new_data = book.convert_to_single_kfx()
        set_logger()

        stream.seek(0)
        stream.truncate()
        stream.write(new_data)
        stream.seek(0)
Пример #21
0
    def setup_pipeline(self, *args):
        oidx = self.groups.currentIndex().row()
        input_format = self.input_format
        output_format = self.output_format
        output_path = 'dummy.' + output_format
        log = Log()
        log.outputs = []
        input_file = 'dummy.' + input_format
        if input_format in ARCHIVE_FMTS:
            input_file = 'dummy.html'
        self.plumber = Plumber(input_file, output_path, log)

        def widget_factory(cls):
            return cls(self.stack, self.plumber.get_option_by_name,
                       self.plumber.get_option_help, self.db, self.book_id)

        self.mw = widget_factory(MetadataWidget)
        self.setWindowTitle(_('Convert') + ' ' + unicode(self.mw.title.text()))
        lf = widget_factory(LookAndFeelWidget)
        hw = widget_factory(HeuristicsWidget)
        sr = widget_factory(SearchAndReplaceWidget)
        ps = widget_factory(PageSetupWidget)
        sd = widget_factory(StructureDetectionWidget)
        toc = widget_factory(TOCWidget)
        debug = widget_factory(DebugWidget)

        output_widget = None
        name = self.plumber.output_plugin.name.lower().replace(' ', '_')
        try:
            output_widget = importlib.import_module('calibre.gui2.convert.' +
                                                    name)
            pw = output_widget.PluginWidget
            pw.ICON = I('back.png')
            pw.HELP = _('Options specific to the output format.')
            output_widget = widget_factory(pw)
        except ImportError:
            pass
        input_widget = None
        name = self.plumber.input_plugin.name.lower().replace(' ', '_')
        try:
            input_widget = importlib.import_module('calibre.gui2.convert.' +
                                                   name)
            pw = input_widget.PluginWidget
            pw.ICON = I('forward.png')
            pw.HELP = _('Options specific to the input format.')
            input_widget = widget_factory(pw)
        except ImportError:
            pass

        while True:
            c = self.stack.currentWidget()
            if not c: break
            self.stack.removeWidget(c)

        widgets = [self.mw, lf, hw, ps, sd, toc, sr]
        if input_widget is not None:
            widgets.append(input_widget)
        if output_widget is not None:
            widgets.append(output_widget)
        widgets.append(debug)
        for w in widgets:
            self.stack.addWidget(w)
            self.connect(w, SIGNAL('set_help(PyQt_PyObject)'),
                         self.help.setPlainText)

        self._groups_model = GroupModel(widgets)
        self.groups.setModel(self._groups_model)

        idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0
        self.groups.setCurrentIndex(self._groups_model.index(idx))
        self.stack.setCurrentIndex(idx)
        try:
            shutil.rmtree(self.plumber.archive_input_tdir, ignore_errors=True)
        except:
            pass
Пример #22
0
    def run(self, opts):
        scripts = {}
        for x in ('console', 'gui'):
            for name in basenames[x]:
                if name in ('calibre-complete', 'calibre_postinstall'):
                    continue
                scripts[name] = x

        dest = self.j(self.RESOURCES, 'scripts.pickle')
        if self.newer(dest, self.j(self.SRC, 'calibre', 'linux.py')):
            self.info('\tCreating scripts.pickle')
            f = open(dest, 'wb')
            cPickle.dump(scripts, f, -1)

        from calibre.web.feeds.recipes.collection import \
                serialize_builtin_recipes, iterate_over_builtin_recipe_files

        files = [x[1] for x in iterate_over_builtin_recipe_files()]

        dest = self.j(self.RESOURCES, 'builtin_recipes.xml')
        if self.newer(dest, files):
            self.info('\tCreating builtin_recipes.xml')
            xml = serialize_builtin_recipes()
            with open(dest, 'wb') as f:
                f.write(xml)

        recipe_icon_dir = self.a(
            self.j(self.RESOURCES, '..', 'recipes', 'icons'))
        dest = os.path.splitext(dest)[0] + '.zip'
        files += glob.glob(self.j(recipe_icon_dir, '*.png'))
        if self.newer(dest, files):
            self.info('\tCreating builtin_recipes.zip')
            with zipfile.ZipFile(dest, 'w', zipfile.ZIP_STORED) as zf:
                for n in sorted(files, key=self.b):
                    with open(n, 'rb') as f:
                        zf.writestr(os.path.basename(n), f.read())

        dest = self.j(self.RESOURCES, 'ebook-convert-complete.pickle')
        files = []
        for x in os.walk(self.j(self.SRC, 'calibre')):
            for f in x[-1]:
                if f.endswith('.py'):
                    files.append(self.j(x[0], f))
        if self.newer(dest, files):
            self.info('\tCreating ebook-convert-complete.pickle')
            complete = {}
            from calibre.ebooks.conversion.plumber import supported_input_formats
            complete['input_fmts'] = set(supported_input_formats())
            from calibre.web.feeds.recipes.collection import get_builtin_recipe_titles
            complete['input_recipes'] = [
                t + '.recipe ' for t in get_builtin_recipe_titles()
            ]
            from calibre.customize.ui import available_output_formats
            complete['output'] = set(available_output_formats())
            from calibre.ebooks.conversion.cli import create_option_parser
            from calibre.utils.logging import Log
            log = Log()
            #log.outputs = []
            for inf in supported_input_formats():
                if inf in ('zip', 'rar', 'oebzip'):
                    continue
                for ouf in available_output_formats():
                    of = ouf if ouf == 'oeb' else 'dummy.' + ouf
                    p = create_option_parser(('ec', 'dummy1.' + inf, of, '-h'),
                                             log)[0]
                    complete[(inf, ouf)] = [
                        x + ' ' for x in get_opts_from_parser(p)
                    ]

            cPickle.dump(complete, open(dest, 'wb'), -1)

        self.info('\tCreating template-functions.json')
        dest = self.j(self.RESOURCES, 'template-functions.json')
        function_dict = {}
        import inspect
        from calibre.utils.formatter_functions import formatter_functions
        for obj in formatter_functions().get_builtins().values():
            eval_func = inspect.getmembers(
                obj,
                lambda x: inspect.ismethod(x) and x.__name__ == 'evaluate')
            try:
                lines = [
                    l[4:] for l in inspect.getsourcelines(eval_func[0][1])[0]
                ]
            except:
                continue
            lines = ''.join(lines)
            function_dict[obj.name] = lines
        import json
        json.dump(function_dict, open(dest, 'wb'), indent=4)
Пример #23
0
def catalog_option_parser(args):
    from calibre.customize.ui import available_catalog_formats, plugin_for_catalog_format
    from calibre.utils.logging import Log

    def add_plugin_parser_options(fmt, parser, log):

        # Fetch the extension-specific CLI options from the plugin
        plugin = plugin_for_catalog_format(fmt)
        for option in plugin.cli_options:
            if option.action:
                parser.add_option(option.option,
                                  default=option.default,
                                  dest=option.dest,
                                  action=option.action,
                                  help=option.help)
            else:
                parser.add_option(option.option,
                                  default=option.default,
                                  dest=option.dest,
                                  help=option.help)

        return plugin

    def print_help(parser, log):
        help = parser.format_help().encode(preferred_encoding, 'replace')
        log(help)

    def validate_command_line(parser, args, log):
        # calibredb catalog path/to/destination.[epub|csv|xml|...] [options]

        # Validate form
        if not len(args) or args[0].startswith('-'):
            print_help(parser, log)
            log.error("\n\nYou must specify a catalog output file of the form 'path/to/destination.extension'\n"
            "To review options for an output format, type 'calibredb catalog <.extension> --help'\n"
            "For example, 'calibredb catalog .xml --help'\n")
            raise SystemExit(1)

        # Validate plugin exists for specified output format
        output = os.path.abspath(args[0])
        file_extension = output[output.rfind('.') + 1:].lower()

        if not file_extension in available_catalog_formats():
            print_help(parser, log)
            log.error("No catalog plugin available for extension '%s'.\n" % file_extension +
                      "Catalog plugins available for %s\n" % ', '.join(available_catalog_formats()) )
            raise SystemExit(1)

        return output, file_extension

    # Entry point
    log = Log()
    parser = get_parser(_(
    '''
    %prog catalog /path/to/destination.(CSV|EPUB|MOBI|XML ...) [options]

    Export a catalog in format specified by path/to/destination extension.
    Options control how entries are displayed in the generated catalog ouput.
    '''))

    # Confirm that a plugin handler exists for specified output file extension
    # Will raise SystemExit(1) if no plugin matching file_extension
    output, fmt = validate_command_line(parser, args, log)

    # Add options common to all catalog plugins
    parser.add_option('-i', '--ids', default=None, dest='ids',
                      help=_("Comma-separated list of database IDs to catalog.\n"
                      "If declared, --search is ignored.\n"
                             "Default: all"))
    parser.add_option('-s', '--search', default=None, dest='search_text',
                      help=_("Filter the results by the search query. "
                          "For the format of the search query, please see "
                          "the search-related documentation in the User Manual.\n"
                      "Default: no filtering"))
    parser.add_option('-v','--verbose', default=False, action='store_true',
                      dest='verbose',
                      help=_('Show detailed output information. Useful for debugging'))

    # Add options specific to fmt plugin
    plugin = add_plugin_parser_options(fmt, parser, log)

    return parser, plugin, log
Пример #24
0
def create_fetcher(options, image_map={}, log=None):
    if log is None:
        log = Log()
    return RecursiveFetcher(options, log, image_map={})
Пример #25
0
def do_download_for_worker(book, options, notification=lambda x, y: x):
    '''
    Child job, to download story when run as a worker job
    '''

    from calibre_plugins.fanficfare_plugin import FanFicFareBase
    fffbase = FanFicFareBase(options['plugin_path'])
    with fffbase:

        from calibre_plugins.fanficfare_plugin.dialogs import (
            NotGoingToDownload, OVERWRITE, OVERWRITEALWAYS, UPDATE,
            UPDATEALWAYS, ADDNEW, SKIP, CALIBREONLY)
        from calibre_plugins.fanficfare_plugin.fanficfare import adapters, writers, exceptions
        from calibre_plugins.fanficfare_plugin.fanficfare.epubutils import get_update_data

        from calibre_plugins.fanficfare_plugin.fff_util import (
            get_fff_adapter, get_fff_config)

        try:
            book['comment'] = _('Download started...')

            configuration = get_fff_config(book['url'], options['fileform'],
                                           options['personal.ini'])

            if configuration.getConfig('use_ssl_unverified_context'):
                ## monkey patch to avoid SSL bug.  dupliated from
                ## fff_plugin.py because bg jobs run in own process
                ## space.
                import ssl
                if hasattr(ssl, '_create_unverified_context'):
                    ssl._create_default_https_context = ssl._create_unverified_context

            if not options[
                    'updateepubcover'] and 'epub_for_update' in book and options[
                        'collision'] in (UPDATE, UPDATEALWAYS):
                configuration.set("overrides", "never_make_cover", "true")

            # images only for epub, html, even if the user mistakenly
            # turned it on else where.
            if options['fileform'] not in ("epub", "html"):
                configuration.set("overrides", "include_images", "false")

            adapter = adapters.getAdapter(configuration, book['url'])
            adapter.is_adult = book['is_adult']
            adapter.username = book['username']
            adapter.password = book['password']
            adapter.setChaptersRange(book['begin'], book['end'])

            adapter.load_cookiejar(options['cookiejarfile'])
            logger.debug("cookiejar:%s" % adapter.cookiejar)
            adapter.set_pagecache(options['pagecache'])

            story = adapter.getStoryMetadataOnly()
            if 'calibre_series' in book:
                adapter.setSeries(book['calibre_series'][0],
                                  book['calibre_series'][1])

            # set PI version instead of default.
            if 'version' in options:
                story.setMetadata('version', options['version'])

            writer = writers.getWriter(options['fileform'], configuration,
                                       adapter)

            outfile = book['outfile']

            ## No need to download at all.  Shouldn't ever get down here.
            if options['collision'] in (CALIBREONLY):
                logger.info(
                    "Skipping CALIBREONLY 'update' down inside worker--this shouldn't be happening..."
                )
                book['comment'] = 'Metadata collected.'

            ## checks were done earlier, it's new or not dup or newer--just write it.
            elif options['collision'] in (ADDNEW, SKIP, OVERWRITE, OVERWRITEALWAYS) or \
                    ('epub_for_update' not in book and options['collision'] in (UPDATE, UPDATEALWAYS)):

                # preserve logfile even on overwrite.
                if 'epub_for_update' in book:
                    (
                        urlignore,
                        chaptercountignore,
                        oldchaptersignore,
                        oldimgsignore,
                        oldcoverignore,
                        calibrebookmarkignore,
                        # only logfile set in adapter, so others aren't used.
                        adapter.logfile) = get_update_data(
                            book['epub_for_update'])

                    # change the existing entries id to notid so
                    # write_epub writes a whole new set to indicate overwrite.
                    if adapter.logfile:
                        adapter.logfile = adapter.logfile.replace(
                            "span id", "span notid")

                logger.info("write to %s" % outfile)
                writer.writeStory(outfilename=outfile, forceOverwrite=True)
                book['comment'] = 'Download %s completed, %s chapters.' % (
                    options['fileform'], story.getMetadata("numChapters"))

            ## checks were done earlier, just update it.
            elif 'epub_for_update' in book and options['collision'] in (
                    UPDATE, UPDATEALWAYS):

                # update now handled by pre-populating the old images and
                # chapters in the adapter rather than merging epubs.
                urlchaptercount = int(
                    story.getMetadata('numChapters').replace(',', ''))
                (url, chaptercount, adapter.oldchapters, adapter.oldimgs,
                 adapter.oldcover, adapter.calibrebookmark,
                 adapter.logfile) = get_update_data(book['epub_for_update'])

                # dup handling from fff_plugin needed for anthology updates.
                if options['collision'] == UPDATE:
                    if chaptercount == urlchaptercount:
                        book['comment'] = _(
                            "Already contains %d chapters.  Reuse as is."
                        ) % chaptercount
                        book['outfile'] = book[
                            'epub_for_update']  # for anthology merge ops.
                        return book

                # dup handling from fff_plugin needed for anthology updates.
                if chaptercount > urlchaptercount:
                    raise NotGoingToDownload(
                        _("Existing epub contains %d chapters, web site only has %d. Use Overwrite to force update."
                          ) % (chaptercount, urlchaptercount),
                        'dialog_error.png')

                if not (options['collision'] == UPDATEALWAYS and chaptercount == urlchaptercount) \
                        and adapter.getConfig("do_update_hook"):
                    chaptercount = adapter.hookForUpdates(chaptercount)

                logger.info("Do update - epub(%d) vs url(%d)" %
                            (chaptercount, urlchaptercount))
                logger.info("write to %s" % outfile)

                writer.writeStory(outfilename=outfile, forceOverwrite=True)

                book['comment'] = _('Update %s completed, added %s chapters for %s total.')%\
                    (options['fileform'],(urlchaptercount-chaptercount),urlchaptercount)

            if options['smarten_punctuation'] and options['fileform'] == "epub" \
                    and calibre_version >= (0, 9, 39):
                # for smarten punc
                from calibre.ebooks.oeb.polish.main import polish, ALL_OPTS
                from calibre.utils.logging import Log
                from collections import namedtuple

                # do smarten_punctuation from calibre's polish feature
                data = {'smarten_punctuation': True}
                opts = ALL_OPTS.copy()
                opts.update(data)
                O = namedtuple('Options', ' '.join(ALL_OPTS.iterkeys()))
                opts = O(**opts)

                log = Log(level=Log.DEBUG)
                # report = []
                polish({outfile: outfile}, opts, log,
                       logger.info)  # report.append

        except NotGoingToDownload as d:
            book['good'] = False
            book['comment'] = unicode(d)
            book['icon'] = d.icon

        except Exception as e:
            book['good'] = False
            book['comment'] = unicode(e)
            book['icon'] = 'dialog_error.png'
            book['status'] = 'Error'
            logger.info("Exception: %s:%s" % (book, unicode(e)))
            traceback.print_exc()

        #time.sleep(10)
    return book
Пример #26
0
def get_metadata(stream):
    from calibre.ebooks.metadata import MetaInformation
    from calibre.ptempfile import TemporaryDirectory
    from calibre.ebooks.mobi.reader.headers import MetadataHeader
    from calibre.ebooks.mobi.reader.mobi6 import MobiReader
    from calibre import CurrentDir

    try:
        from PIL import Image as PILImage
        PILImage
    except ImportError:
        import Image as PILImage

    stream.seek(0)
    try:
        raw = stream.read(3)
    except:
        raw = ''
    stream.seek(0)
    if raw == b'TPZ':
        from calibre.ebooks.metadata.topaz import get_metadata
        return get_metadata(stream)
    from calibre.utils.logging import Log
    log = Log()
    try:
        mi = MetaInformation(os.path.basename(stream.name), [_('Unknown')])
    except:
        mi = MetaInformation(_('Unknown'), [_('Unknown')])
    mh = MetadataHeader(stream, log)
    if mh.title and mh.title != _('Unknown'):
        mi.title = mh.title

    if mh.exth is not None:
        if mh.exth.mi is not None:
            mi = mh.exth.mi
    else:
        size = 1024**3
        if hasattr(stream, 'seek') and hasattr(stream, 'tell'):
            pos = stream.tell()
            stream.seek(0, 2)
            size = stream.tell()
            stream.seek(pos)
        if size < 4 * 1024 * 1024:
            with TemporaryDirectory('_mobi_meta_reader') as tdir:
                with CurrentDir(tdir):
                    mr = MobiReader(stream, log)
                    parse_cache = {}
                    mr.extract_content(tdir, parse_cache)
                    if mr.embedded_mi is not None:
                        mi = mr.embedded_mi
    if hasattr(mh.exth, 'cover_offset'):
        cover_index = mh.first_image_index + mh.exth.cover_offset
        data = mh.section_data(int(cover_index))
    else:
        try:
            data = mh.section_data(mh.first_image_index)
        except:
            data = ''
    buf = cStringIO.StringIO(data)
    try:
        im = PILImage.open(buf)
    except:
        log.exception('Failed to read MOBI cover')
    else:
        obuf = cStringIO.StringIO()
        im.convert('RGB').save(obuf, format='JPEG')
        mi.cover_data = ('jpg', obuf.getvalue())
    return mi
Пример #27
0
    def setup_pipeline(self, *args):
        oidx = self.groups.currentIndex().row()
        input_format = self.input_format
        output_format = self.output_format
        output_path = 'dummy.' + output_format
        log = Log()
        log.outputs = []
        input_file = 'dummy.' + input_format
        if input_format in ARCHIVE_FMTS:
            input_file = 'dummy.html'
        self.plumber = Plumber(input_file, output_path, log)

        def widget_factory(cls):
            return cls(self.stack, self.plumber.get_option_by_name,
                       self.plumber.get_option_help, self.db, self.book_id)

        self.mw = widget_factory(MetadataWidget)
        self.setWindowTitle(_('Convert') + ' ' + unicode(self.mw.title.text()))
        lf = widget_factory(LookAndFeelWidget)
        hw = widget_factory(HeuristicsWidget)
        sr = widget_factory(SearchAndReplaceWidget)
        ps = widget_factory(PageSetupWidget)
        sd = widget_factory(StructureDetectionWidget)
        toc = widget_factory(TOCWidget)
        from calibre.gui2.actions.toc_edit import SUPPORTED
        toc.manually_fine_tune_toc.setVisible(
            output_format.upper() in SUPPORTED)
        debug = widget_factory(DebugWidget)

        output_widget = self.plumber.output_plugin.gui_configuration_widget(
            self.stack, self.plumber.get_option_by_name,
            self.plumber.get_option_help, self.db, self.book_id)
        input_widget = self.plumber.input_plugin.gui_configuration_widget(
            self.stack, self.plumber.get_option_by_name,
            self.plumber.get_option_help, self.db, self.book_id)
        while True:
            c = self.stack.currentWidget()
            if not c:
                break
            self.stack.removeWidget(c)

        widgets = [self.mw, lf, hw, ps, sd, toc, sr]
        if input_widget is not None:
            widgets.append(input_widget)
        if output_widget is not None:
            widgets.append(output_widget)
        widgets.append(debug)
        for w in widgets:
            self.stack.addWidget(w)
            w.set_help_signal.connect(self.help.setPlainText)

        self._groups_model = GroupModel(widgets)
        self.groups.setModel(self._groups_model)

        idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0
        self.groups.setCurrentIndex(self._groups_model.index(idx))
        self.stack.setCurrentIndex(idx)
        try:
            shutil.rmtree(self.plumber.archive_input_tdir, ignore_errors=True)
        except:
            pass
Пример #28
0
import json
from calibre.utils.logging import Log
from calibre.ebooks.metadata.book.base import Metadata
from calibre_plugins.crossref_doi_download import DoiMeta
from calibre_plugins.crossref_doi_download.doi_reader import DoiReader, get_title, get_author_list
reader = DoiReader(Log())
dm = DoiMeta('./plugin/')
br = dm.browser

fullurl = 'https://api.crossref.org/works?query.bibliographic=Bayesian+data+analysis+Andrew+Gelman&mailto=vikoya5988%40oniaj.com'
cdata = br.open(fullurl).read()

data = json.loads(cdata)
message = data['message']
results = message['items']

identifiers = {}
# fin = map(lambda x:reader.result2meta(x,identifiers),results)

for r in results:
    reader.result2meta(r,identifiers)

result = results[1]
title = get_title(result)
authors = get_author_list(result)
mi = Metadata(title=title, authors=authors)

from calibre import ipython
ipython(locals())
Пример #29
0
def get_metadata(stream):
    from calibre.ebooks.metadata import MetaInformation
    from calibre.ptempfile import TemporaryDirectory
    from calibre.ebooks.mobi.reader.headers import MetadataHeader
    from calibre.ebooks.mobi.reader.mobi6 import MobiReader
    from calibre.utils.magick.draw import save_cover_data_to
    from calibre import CurrentDir

    stream.seek(0)
    try:
        raw = stream.read(3)
    except:
        raw = ''
    stream.seek(0)
    if raw == b'TPZ':
        from calibre.ebooks.metadata.topaz import get_metadata
        return get_metadata(stream)
    from calibre.utils.logging import Log
    log = Log()
    try:
        mi = MetaInformation(os.path.basename(stream.name), [_('Unknown')])
    except:
        mi = MetaInformation(_('Unknown'), [_('Unknown')])
    mh = MetadataHeader(stream, log)
    if mh.title and mh.title != _('Unknown'):
        mi.title = mh.title

    if mh.exth is not None:
        if mh.exth.mi is not None:
            mi = mh.exth.mi
    else:
        size = 1024**3
        if hasattr(stream, 'seek') and hasattr(stream, 'tell'):
            pos = stream.tell()
            stream.seek(0, 2)
            size = stream.tell()
            stream.seek(pos)
        if size < 4 * 1024 * 1024:
            with TemporaryDirectory('_mobi_meta_reader') as tdir:
                with CurrentDir(tdir):
                    mr = MobiReader(stream, log)
                    parse_cache = {}
                    mr.extract_content(tdir, parse_cache)
                    if mr.embedded_mi is not None:
                        mi = mr.embedded_mi
    if hasattr(mh.exth, 'cover_offset'):
        cover_index = mh.first_image_index + mh.exth.cover_offset
        data = mh.section_data(int(cover_index))
    else:
        try:
            data = mh.section_data(mh.first_image_index)
        except:
            data = ''
    if data and what(None,
                     data) in {'jpg', 'jpeg', 'gif', 'png', 'bmp', 'webp'}:
        try:
            mi.cover_data = ('jpg',
                             save_cover_data_to(data,
                                                'cover.jpg',
                                                return_data=True))
        except Exception:
            log.exception('Failed to read MOBI cover')
    return mi