diff --git a/src/calibre/customize/ui.py b/src/calibre/customize/ui.py index af85ca523d..99c74ce5f0 100644 --- a/src/calibre/customize/ui.py +++ b/src/calibre/customize/ui.py @@ -30,7 +30,7 @@ def _config(): c.add_opt('filetype_mapping', default={}, help=_('Mapping for filetype plugins')) c.add_opt('plugin_customization', default={}, help=_('Local plugin customization')) c.add_opt('disabled_plugins', default=set([]), help=_('Disabled plugins')) - + return ConfigProxy(c) config = _config() @@ -45,7 +45,7 @@ class PluginNotFound(ValueError): def load_plugin(path_to_zip_file): ''' Load plugin from zip file or raise InvalidPlugin error - + :return: A :class:`Plugin` instance. ''' print 'Loading plugin from', path_to_zip_file @@ -61,9 +61,9 @@ def load_plugin(path_to_zip_file): if x.minimum_calibre_version > version or \ platform not in x.supported_platforms: continue - + return x - + raise InvalidPlugin(_('No valid plugin found in ')+path_to_zip_file) _initialized_plugins = [] @@ -122,8 +122,8 @@ def reread_metadata_plugins(): for ft in plugin.file_types: if not _metadata_writers.has_key(ft): _metadata_writers[ft] = [] - _metadata_writers[ft].append(plugin) - + _metadata_writers[ft].append(plugin) + def metadata_readers(): ans = set([]) for plugins in _metadata_readers.values(): @@ -136,8 +136,8 @@ def metadata_writers(): for plugins in _metadata_writers.values(): for plugin in plugins: ans.add(plugin) - return ans - + return ans + def get_file_type_metadata(stream, ftype): mi = MetaInformation(None, None) ftype = ftype.lower().strip() @@ -163,21 +163,21 @@ def set_file_type_metadata(stream, mi, ftype): plugin.set_metadata(stream, mi, ftype.lower().strip()) break except: - print 'Failed to set metadata for', repr(getattr(mi, 'title', '')) + print 'Failed to set metadata for', repr(getattr(mi, 'title', '')) traceback.print_exc() - - + + def _run_filetype_plugins(path_to_file, ft=None, occasion='preprocess'): - occasion = {'import':_on_import, 'preprocess':_on_preprocess, + occasion = {'import':_on_import, 'preprocess':_on_preprocess, 'postprocess':_on_postprocess}[occasion] customization = config['plugin_customization'] if ft is None: - ft = os.path.splitext(path_to_file)[-1].lower().replace('.', '') + ft = os.path.splitext(path_to_file)[-1].lower().replace('.', '') nfp = path_to_file for plugin in occasion.get(ft, []): if is_disabled(plugin): continue - plugin.site_customization = customization.get(plugin.name, '') + plugin.site_customization = customization.get(plugin.name, '') with plugin: try: nfp = plugin.run(path_to_file) @@ -190,13 +190,13 @@ def _run_filetype_plugins(path_to_file, ft=None, occasion='preprocess'): nfp = path_to_file return nfp -run_plugins_on_import = functools.partial(_run_filetype_plugins, +run_plugins_on_import = functools.partial(_run_filetype_plugins, occasion='import') -run_plugins_on_preprocess = functools.partial(_run_filetype_plugins, +run_plugins_on_preprocess = functools.partial(_run_filetype_plugins, occasion='preprocess') -run_plugins_on_postprocess = functools.partial(_run_filetype_plugins, +run_plugins_on_postprocess = functools.partial(_run_filetype_plugins, occasion='postprocess') - + def initialize_plugin(plugin, path_to_zip_file): try: @@ -206,7 +206,7 @@ def initialize_plugin(plugin, path_to_zip_file): tb = traceback.format_exc() raise InvalidPlugin((_('Initialization of plugin %s failed with traceback:') %tb) + '\n'+tb) - + def add_plugin(path_to_zip_file): make_config_dir() @@ -248,18 +248,18 @@ def input_format_plugins(): for plugin in _initialized_plugins: if isinstance(plugin, InputFormatPlugin): yield plugin - + def plugin_for_input_format(fmt): for plugin in input_format_plugins(): if fmt.lower() in plugin.file_types: return plugin def available_input_formats(): - formats = [] + formats = set([]) for plugin in input_format_plugins(): if not is_disabled(plugin): for format in plugin.file_types: - formats.append(format) + formats.add(format) return formats def output_format_plugins(): @@ -273,10 +273,10 @@ def plugin_for_output_format(fmt): return plugin def available_output_formats(): - formats = [] + formats = set([]) for plugin in output_format_plugins(): if not is_disabled(plugin): - formats.append(plugin.file_type) + formats.add(plugin.file_type) return formats def disable_plugin(plugin_or_name): @@ -309,21 +309,21 @@ def initialize_plugins(): except: print 'Failed to initialize plugin...' traceback.print_exc() - _initialized_plugins.sort(cmp=lambda x,y:cmp(x.priority, y.priority), reverse=True) + _initialized_plugins.sort(cmp=lambda x,y:cmp(x.priority, y.priority), reverse=True) reread_filetype_plugins() reread_metadata_plugins() - + initialize_plugins() def option_parser(): parser = OptionParser(usage=_('''\ %prog options - + Customize calibre by loading external plugins. ''')) - parser.add_option('-a', '--add-plugin', default=None, + parser.add_option('-a', '--add-plugin', default=None, help=_('Add a plugin by specifying the path to the zip file containing it.')) - parser.add_option('-r', '--remove-plugin', default=None, + parser.add_option('-r', '--remove-plugin', default=None, help=_('Remove a custom plugin by name. Has no effect on builtin plugins')) parser.add_option('--customize-plugin', default=None, help=_('Customize plugin. Specify name of plugin and customization string separated by a comma.')) @@ -377,16 +377,16 @@ def main(args=sys.argv): print for plugin in initialized_plugins(): print fmt%( - plugin.type, plugin.name, - plugin.version, is_disabled(plugin), + plugin.type, plugin.name, + plugin.version, is_disabled(plugin), plugin_customization(plugin) ) print '\t', plugin.description if plugin.is_customizable(): print '\t', plugin.customization_help() print - + return 0 - + if __name__ == '__main__': sys.exit(main()) diff --git a/src/calibre/ebooks/conversion/plumber.py b/src/calibre/ebooks/conversion/plumber.py index 41d5f0abd9..ab30e71ba1 100644 --- a/src/calibre/ebooks/conversion/plumber.py +++ b/src/calibre/ebooks/conversion/plumber.py @@ -179,10 +179,13 @@ OptionRecommendation(name='language', raise ValueError('Input file must have an extension') input_fmt = input_fmt[1:].lower() - output_fmt = os.path.splitext(output)[1] - if not output_fmt: - output_fmt = '.oeb' - output_fmt = output_fmt[1:].lower() + if os.path.exists(output) and os.path.isdir(output): + output_fmt = 'oeb' + else: + output_fmt = os.path.splitext(output)[1] + if not output_fmt: + output_fmt = '.oeb' + output_fmt = output_fmt[1:].lower() self.input_plugin = plugin_for_input_format(input_fmt) self.output_plugin = plugin_for_output_format(output_fmt) diff --git a/src/calibre/ebooks/epub/iterator.py b/src/calibre/ebooks/oeb/iterator.py similarity index 79% rename from src/calibre/ebooks/epub/iterator.py rename to src/calibre/ebooks/oeb/iterator.py index 5d47c93ea3..ec0eda908a 100644 --- a/src/calibre/ebooks/epub/iterator.py +++ b/src/calibre/ebooks/oeb/iterator.py @@ -5,20 +5,20 @@ __copyright__ = '2008 Kovid Goyal ' Iterate over the HTML files in an ebook. Useful for writing viewers. ''' -import re, os, math, copy +import re, os, math from cStringIO import StringIO from PyQt4.Qt import QFontDatabase -from calibre.ebooks.epub.from_any import MAP +from calibre.customize.ui import available_input_formats from calibre.ebooks.epub.from_html import TITLEPAGE -from calibre.ebooks.epub import config -from calibre.ebooks.metadata.opf2 import OPF +from calibre.ebooks.metadata.opf2 import OPF, OPFCreator from calibre.ptempfile import TemporaryDirectory from calibre.ebooks.chardet import xml_to_unicode -from calibre.ebooks.html_old import create_dir from calibre.utils.zipfile import safe_replace, ZipFile from calibre.utils.config import DynamicConfig +from calibre.utils.logging import Log +from calibre import CurrentDir def character_count(html): ''' @@ -50,11 +50,28 @@ class SpineItem(unicode): obj.max_page = -1 return obj -def html2opf(path, tdir, opts): - opts = copy.copy(opts) - opts.output = tdir - create_dir(path, opts) - return os.path.join(tdir, 'metadata.opf') +class FakeOpts(object): + verbose = 0 + breadth_first = False + max_levels = 5 + input_encoding = None + +def html2opf(path, tdir, log): + from calibre.ebooks.html.input import get_filelist + from calibre.ebooks.metadata.meta import get_metadata + with CurrentDir(tdir): + fl = get_filelist(path, tdir, FakeOpts(), log) + mi = get_metadata(open(path, 'rb'), 'html') + mi = OPFCreator(os.getcwdu(), mi) + mi.guide = None + entries = [(f.path, 'application/xhtml+xml') for f in fl] + mi.create_manifest(entries) + mi.create_spine([f.path for f in fl]) + + mi.render(open('metadata.opf', 'wb')) + opfpath = os.path.abspath('metadata.opf') + + return opfpath def opf2opf(path, tdir, opts): return path @@ -62,24 +79,22 @@ def opf2opf(path, tdir, opts): def is_supported(path): ext = os.path.splitext(path)[1].replace('.', '').lower() ext = re.sub(r'(x{0,1})htm(l{0,1})', 'html', ext) - return ext in list(MAP.keys())+['html', 'opf'] + return ext in available_input_formats() class EbookIterator(object): CHARACTERS_PER_PAGE = 1000 - def __init__(self, pathtoebook): + def __init__(self, pathtoebook, log=None): + self.log = log + if log is None: + self.log = Log() pathtoebook = pathtoebook.strip() self.pathtoebook = os.path.abspath(pathtoebook) self.config = DynamicConfig(name='iterator') ext = os.path.splitext(pathtoebook)[1].replace('.', '').lower() ext = re.sub(r'(x{0,1})htm(l{0,1})', 'html', ext) - map = dict(MAP) - map['html'] = html2opf - map['opf'] = opf2opf - if ext not in map.keys(): - raise UnsupportedFormatError(ext) - self.to_opf = map[ext] + self.ebook_ext = ext def search(self, text, index): text = text.lower() @@ -115,14 +130,24 @@ class EbookIterator(object): def __enter__(self): self._tdir = TemporaryDirectory('_ebook_iter') self.base = self._tdir.__enter__() - opts = config('').parse() - self.pathtoopf = self.to_opf(self.pathtoebook, self.base, opts) + if self.ebook_ext == 'opf': + self.pathtoopf = self.pathtoebook + elif self.ebook_ext == 'html': + self.pathtoopf = html2opf(self.pathtoebook, self.base, self.log) + else: + from calibre.ebooks.conversion.plumber import Plumber + plumber = Plumber(self.pathtoebook, self.base, self.log) + plumber.setup_options() + self.pathtoopf = plumber.input_plugin(open(plumber.input, 'rb'), + plumber.opts, plumber.input_fmt, self.log, + {}, self.base) + + self.opf = OPF(self.pathtoopf, os.path.dirname(self.pathtoopf)) self.spine = [SpineItem(i.path) for i in self.opf.spine] cover = self.opf.cover - if os.path.splitext(self.pathtoebook)[1].lower() in \ - ('.lit', '.mobi', '.prc') and cover: + if self.ebook_ext in ('lit', 'mobi', 'prc', 'opf') and cover: cfile = os.path.join(os.path.dirname(self.spine[0]), 'calibre_ei_cover.html') open(cfile, 'wb').write(TITLEPAGE%cover) self.spine[0:0] = [SpineItem(cfile)] @@ -131,7 +156,6 @@ class EbookIterator(object): self.opf.path_to_html_toc not in self.spine: self.spine.append(SpineItem(self.opf.path_to_html_toc)) - sizes = [i.character_count for i in self.spine] self.pages = [math.ceil(i/float(self.CHARACTERS_PER_PAGE)) for i in sizes] for p, s in zip(self.pages, self.spine): diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index 543d92904b..0b8800035a 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -17,14 +17,14 @@ from calibre.gui2.viewer.bookmarkmanager import BookmarkManager from calibre.gui2.main_window import MainWindow from calibre.gui2 import Application, ORG_NAME, APP_UID, choose_files, \ info_dialog, error_dialog -from calibre.ebooks.epub.iterator import EbookIterator -from calibre.ebooks.epub.from_any import SOURCE_FORMATS +from calibre.ebooks.oeb.iterator import EbookIterator from calibre.ebooks import DRMError from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog from calibre.constants import islinux from calibre.utils.config import Config, StringConfig from calibre.gui2.library import SearchBox from calibre.ebooks.metadata import MetaInformation +from calibre.customize.ui import available_input_formats class TOCItem(QStandardItem): @@ -362,7 +362,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer): def open_ebook(self, checked): files = choose_files(self, 'ebook viewer open dialog', _('Choose ebook'), - [(_('Ebooks'), SOURCE_FORMATS)], all_files=False, + [(_('Ebooks'), available_input_formats())], + all_files=False, select_only_single_file=True) if files: self.load_ebook(files[0]) diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 71529b79e9..c7a39cbc4b 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -445,6 +445,8 @@ class BasicNewsRecipe(object): :param progress_reporter: A Callable that takes two arguments: progress (a number between 0 and 1) and a string message. The message should be optional. ''' self.log = Log() + if options.verbose: + self.log.filter_level = self.log.DEBUG if not isinstance(self.title, unicode): self.title = unicode(self.title, 'utf-8', 'replace')