diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 484d46dc36..a9fc342059 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -262,6 +262,17 @@ class MOBIMetadataWriter(MetadataWriterPlugin): def set_metadata(self, stream, mi, type): from calibre.ebooks.metadata.mobi import set_metadata set_metadata(stream, mi) + +class PDFMetadataWriter(MetadataWriterPlugin): + + name = 'Set PDF metadata' + file_types = set(['pdf']) + description = _('Set metadata in %s files') % 'PDF' + author = 'John Schember' + + def set_metadata(self, stream, mi, type): + from calibre.ebooks.metadata.pdf import set_metadata + set_metadata(stream, mi) from calibre.ebooks.epub.input import EPUBInput diff --git a/src/calibre/customize/ui.py b/src/calibre/customize/ui.py index 99c74ce5f0..4bd5c9b284 100644 --- a/src/calibre/customize/ui.py +++ b/src/calibre/customize/ui.py @@ -2,7 +2,7 @@ from __future__ import with_statement __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -import os, shutil, traceback, functools, sys +import os, shutil, traceback, functools, sys, re from calibre.customize import Plugin, FileTypePlugin, MetadataReaderPlugin, \ MetadataWriterPlugin @@ -55,7 +55,14 @@ def load_plugin(path_to_zip_file): for name in zf.namelist(): if name.lower().endswith('plugin.py'): locals = {} - exec zf.read(name) in locals + raw = zf.read(name) + match = re.search(r'coding[:=]\s*([-\w.]+)', raw[:300]) + encoding = 'utf-8' + if match is not None: + encoding = match.group(1) + raw = raw.decode(encoding) + raw = re.sub('\r\n', '\n', raw) + exec raw in locals for x in locals.values(): if isinstance(x, type) and issubclass(x, Plugin): if x.minimum_calibre_version > version or \ diff --git a/src/calibre/debug.py b/src/calibre/debug.py index 45ce9987e0..6444eaa691 100644 --- a/src/calibre/debug.py +++ b/src/calibre/debug.py @@ -31,6 +31,11 @@ Run an embedded python interpreter. parser.add_option('--migrate', action='store_true', default=False, help='Migrate old database. Needs two arguments. Path ' 'to library1.db and path to new library folder.') + parser.add_option('--add-simple-plugin', default=None, + help='Add a simple plugin (i.e. a plugin that consists of only a ' + '.py file), by specifying the path to the py file containing the ' + 'plugin code.') + return parser def update_zipfile(zipfile, mod, path): @@ -115,6 +120,22 @@ def debug_device_driver(): print 'Total space:', d.total_space() break +def add_simple_plugin(path_to_plugin): + import tempfile, zipfile, shutil + tdir = tempfile.mkdtemp() + open(os.path.join(tdir, 'custom_plugin.py'), + 'wb').write(open(path_to_plugin, 'rb').read()) + odir = os.getcwd() + os.chdir(tdir) + zf = zipfile.ZipFile('plugin.zip', 'w') + zf.write('custom_plugin.py') + zf.close() + from calibre.customize.ui import main + main(['calibre-customize', '-a', 'plugin.zip']) + os.chdir(odir) + shutil.rmtree(tdir) + + def main(args=sys.argv): opts, args = option_parser().parse_args(args) @@ -137,6 +158,8 @@ def main(args=sys.argv): print 'You must specify the path to library1.db and the path to the new library folder' return 1 migrate(args[1], args[2]) + elif opts.add_simple_plugin is not None: + add_simple_plugin(opts.add_simple_plugin) else: from IPython.Shell import IPShellEmbed ipshell = IPShellEmbed() diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index 68041a19cd..0a66b78014 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -57,12 +57,17 @@ class USBMS(Device): prefix = self._card_prefix if oncard else self._main_prefix ebook_dir = self.EBOOK_DIR_CARD if oncard else self.EBOOK_DIR_MAIN - # Get all books in all directories under the root ebook_dir directory - for path, dirs, files in os.walk(os.path.join(prefix, ebook_dir)): - # Filter out anything that isn't in the list of supported ebook - # types - for book_type in self.FORMATS: - for filename in fnmatch.filter(files, '*.%s' % (book_type)): + # Get all books in the ebook_dir directory + if self.SUPPORTS_SUB_DIRS: + for path, dirs, files in os.walk(os.path.join(prefix, ebook_dir)): + # Filter out anything that isn't in the list of supported ebook types + for book_type in self.FORMATS: + for filename in fnmatch.filter(files, '*.%s' % (book_type)): + bl.append(self.__class__.book_from_path(os.path.join(path, filename))) + else: + path = os.path.join(prefix, ebook_dir) + for filename in os.listdir(path): + if path_to_ext(filename) in self.FORMATS: bl.append(self.__class__.book_from_path(os.path.join(path, filename))) return bl diff --git a/src/calibre/ebooks/metadata/pdf.py b/src/calibre/ebooks/metadata/pdf.py index 6b94b07275..baf458e64b 100644 --- a/src/calibre/ebooks/metadata/pdf.py +++ b/src/calibre/ebooks/metadata/pdf.py @@ -1,11 +1,10 @@ -'''Read meta information from PDF files''' - from __future__ import with_statement __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' +'''Read meta information from PDF files''' -import sys, os, re, StringIO +import sys, os, StringIO from calibre.ebooks.metadata import MetaInformation, authors_to_string from calibre.ptempfile import TemporaryDirectory @@ -31,7 +30,7 @@ def get_metadata(stream, extract_cover=True): except: import traceback traceback.print_exc() - + try: info = PdfFileReader(stream).getDocumentInfo() if info.title: @@ -52,47 +51,56 @@ def get_metadata(stream, extract_cover=True): def set_metadata(stream, mi): stream.seek(0) - raw = stream.read() - if mi.title: - tit = mi.title.encode('utf-8') if isinstance(mi.title, unicode) else mi.title - raw = re.compile(r'<<.*?/Title\((.+?)\)', re.DOTALL).sub(lambda m: m.group().replace(m.group(1), tit), raw) - if mi.authors: - au = authors_to_string(mi.authors) - if isinstance(au, unicode): - au = au.encode('utf-8') - raw = re.compile(r'<<.*?/Author\((.+?)\)', re.DOTALL).sub(lambda m: m.group().replace(m.group(1), au), raw) + + # Use a StringIO object for the pdf because we will want to over + # write it later and if we are working on the stream directly it + # could cause some issues. + raw = StringIO.StringIO(stream.read()) + orig_pdf = PdfFileReader(raw) + + title = mi.title if mi.title else orig_pdf.documentInfo.title + author = authors_to_string(mi.authors) if mi.authors else orig_pdf.documentInfo.author + + out_pdf = PdfFileWriter(title=title, author=author) + for page in orig_pdf.pages: + out_pdf.addPage(page) + + out_str = StringIO.StringIO() + out_pdf.write(out_str) + stream.seek(0) stream.truncate() - stream.write(raw) + out_str.seek(0) + stream.write(out_str.read()) stream.seek(0) def get_cover(stream): data = StringIO.StringIO() - + try: pdf = PdfFileReader(stream) output = PdfFileWriter() - + if len(pdf.pages) >= 1: output.addPage(pdf.getPage(0)) - + with TemporaryDirectory('_pdfmeta') as tdir: cover_path = os.path.join(tdir, 'cover.pdf') - + outputStream = file(cover_path, "wb") output.write(outputStream) outputStream.close() - + wand = NewMagickWand() MagickReadImage(wand, cover_path) MagickSetImageFormat(wand, 'JPEG') MagickWriteImage(wand, '%s.jpg' % cover_path) - + img = Image.open('%s.jpg' % cover_path) - + img.save(data, 'JPEG') except: import traceback traceback.print_exc() - + return data.getvalue() diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index fa43a7af42..dbdcb1a24f 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -15,7 +15,8 @@ except ImportError: from lxml import html, etree -from calibre import entity_to_unicode +from calibre import entity_to_unicode, sanitize_file_name +from calibre.ptempfile import TemporaryDirectory from calibre.ebooks import DRMError from calibre.ebooks.chardet import ENCODING_PATS from calibre.ebooks.mobi import MobiError @@ -25,7 +26,6 @@ from calibre.ebooks.mobi.langcodes import main_language, sub_language from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata.opf2 import OPFCreator, OPF from calibre.ebooks.metadata.toc import TOC -from calibre import sanitize_file_name class EXTHHeader(object): @@ -157,6 +157,62 @@ class BookHeader(object): self.exth.mi.language = self.language +class MetadataHeader(BookHeader): + def __init__(self, stream): + self.stream = stream + + self.ident = self.identity() + self.num_sections = self.section_count() + + if self.num_sections >= 2: + header = self.header() + BookHeader.__init__(self, header, self.ident, None) + else: + self.exth = None + + def identity(self): + self.stream.seek(60) + ident = self.stream.read(8).upper() + + if ident not in ['BOOKMOBI', 'TEXTREAD']: + raise MobiError('Unknown book type: %s' % ident) + return ident + + def section_count(self): + self.stream.seek(76) + return struct.unpack('>H', self.stream.read(2))[0] + + def section_offset(self, number): + self.stream.seek(78+number*8) + return struct.unpack('>LBBBB', self.stream.read(8))[0] + + def header(self): + section_headers = [] + + # First section with the metadata + section_headers.append(self.section_offset(0)) + # Second section used to get the lengh of the first + section_headers.append(self.section_offset(1)) + + end_off = section_headers[1] + off = section_headers[0] + + self.stream.seek(off) + return self.stream.read(end_off - off) + + def section_data(self, number): + start = self.section_offset(number) + + if number == self.num_sections -1: + end = os.stat(self.stream.name).st_size + else: + end = self.section_offset(number + 1) + + self.stream.seek(start) + + return self.stream.read(end - start) + + class MobiReader(object): PAGE_BREAK_PAT = re.compile(r'(<[/]{0,1}mbp:pagebreak\s*[/]{0,1}>)+', re.IGNORECASE) IMAGE_ATTRS = ('lowrecindex', 'recindex', 'hirecindex') @@ -593,27 +649,34 @@ class MobiReader(object): im.convert('RGB').save(open(path, 'wb'), format='JPEG') def get_metadata(stream): - from calibre.utils.logging import Log - log = Log() - mr = MobiReader(stream, log) - if mr.book_header.exth is None: - mi = MetaInformation(mr.name, [_('Unknown')]) - else: - mi = mr.create_opf('dummy.html')[0] - try: - if hasattr(mr.book_header.exth, 'cover_offset'): - cover_index = mr.book_header.first_image_index + \ - mr.book_header.exth.cover_offset - data = mr.sections[int(cover_index)][0] - else: - data = mr.sections[mr.book_header.first_image_index][0] - buf = cStringIO.StringIO(data) - im = PILImage.open(buf) - obuf = cStringIO.StringIO() - im.convert('RGBA').save(obuf, format='JPEG') - mi.cover_data = ('jpg', obuf.getvalue()) - except: - log.exception() + mi = MetaInformation(os.path.basename(stream.name), [_('Unknown')]) + try: + mh = MetadataHeader(stream) + + if mh.exth is not None: + if mh.exth.mi is not None: + mi = mh.exth.mi + else: + with TemporaryDirectory('_mobi_meta_reader') as tdir: + mr = MobiReader(stream) + mr.extract_content(tdir, {}) + 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: + data = mh.section_data(mh.first_image_index) + buf = cStringIO.StringIO(data) + im = PILImage.open(buf) + obuf = cStringIO.StringIO() + im.convert('RGBA').save(obuf, format='JPEG') + mi.cover_data = ('jpg', obuf.getvalue()) + except: + import traceback + traceback.print_exc() + return mi diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 225f7a9e33..1da5bb6851 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -67,6 +67,8 @@ def _config(): c.add_opt('default_send_to_device_action', default=None, help=_('Default action to perform when send to device button is ' 'clicked')) + c.add_opt('show_donate_button', default=True, + help='Show donation button') return ConfigProxy(c) config = _config() diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 46cf9895d4..c2a61e7648 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -658,7 +658,9 @@ class DeviceGUI(object): bad = '\n'.join('
  • %s
  • '%(i,) for i in bad) d = warning_dialog(self, _('No suitable formats'), _('Could not upload the following books to the device, ' - 'as no suitable formats were found:
    ')%(bad,)) + 'as no suitable formats were found. Try changing the output ' + 'format in the upper right corner next to the red heart and ' + 're-converting.
    ')%(bad,)) d.exec_() def upload_booklists(self): diff --git a/src/calibre/gui2/dialogs/epub.py b/src/calibre/gui2/dialogs/epub.py index 0773440b01..e61d034642 100644 --- a/src/calibre/gui2/dialogs/epub.py +++ b/src/calibre/gui2/dialogs/epub.py @@ -176,19 +176,19 @@ class Config(ResizableDialog, Ui_Dialog): def get_metadata(self): title, authors = self.get_title_and_authors() mi = MetaInformation(title, authors) - publisher = unicode(self.publisher.text()) + publisher = unicode(self.publisher.text()).strip() if publisher: mi.publisher = publisher - author_sort = unicode(self.author_sort.text()) + author_sort = unicode(self.author_sort.text()).strip() if author_sort: mi.author_sort = author_sort - comments = unicode(self.comment.toPlainText()) + comments = unicode(self.comment.toPlainText()).strip() if comments: mi.comments = comments mi.series_index = int(self.series_index.value()) if self.series.currentIndex() > -1: - mi.series = unicode(self.series.currentText()) - tags = [t.strip() for t in unicode(self.tags.text()).split(',')] + mi.series = unicode(self.series.currentText()).strip() + tags = [t.strip() for t in unicode(self.tags.text()).strip().split(',')] if tags: mi.tags = tags @@ -267,6 +267,7 @@ class Config(ResizableDialog, Ui_Dialog): ).exec_() return mi = self.get_metadata() + self.user_mi = mi self.read_settings() self.cover_file = None if self.row is not None: diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index f90f98b73d..b198814c93 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -108,6 +108,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.donate_action = self.system_tray_menu.addAction( QIcon(':/images/donate.svg'), _('&Donate to support calibre')) self.donate_button.setDefaultAction(self.donate_action) + if not config['show_donate_button']: + self.donate_button.setVisible(False) self.addAction(self.quit_action) self.action_restart = QAction(_('&Restart'), self) self.addAction(self.action_restart) diff --git a/src/calibre/gui2/tools.py b/src/calibre/gui2/tools.py index d004dcb502..a3002089a9 100644 --- a/src/calibre/gui2/tools.py +++ b/src/calibre/gui2/tools.py @@ -25,7 +25,7 @@ from calibre.ebooks.lrf.comic.convert_from import config as comicconfig # Ordered list of source formats. Items closer to the beginning are # preferred for conversion over those toward the end. -PREFERRED_SOURCE_FORMATS = ['epub', 'lit', 'mobi', 'prc', 'azw', 'fb2', 'odt', 'rtf', +PREFERRED_SOURCE_FORMATS = ['epub', 'lit', 'mobi', 'prc', 'azw', 'fb2', 'odt', 'rtf', 'txt', 'pdf', 'oebzip', 'htm', 'html'] def get_dialog(fmt): @@ -43,20 +43,20 @@ def get_config(fmt): def auto_convert(fmt, parent, db, rows): changed = False jobs = [] - + total = len(rows) if total == 0: return None, None, None parent.status_bar.showMessage(_('Starting auto conversion of %d books')%total, 2000) - + i = 0 bad_rows = [] - + for i, row in enumerate(rows): row_id = db.id(row) - + temp_files = [] - + data = None in_formats = [f.lower() for f in db.formats(row).split(',')] in_formats = list(set(in_formats).intersection(available_input_formats())) @@ -88,10 +88,10 @@ def auto_convert(fmt, parent, db, rows): for row in bad_rows: title = db.title(row) res.append('
  • %s
  • '%title) - + msg = _('

    Could not convert %d of %d books, because no suitable source format was found.

    ')%(len(res), total, '\n'.join(res)) warning_dialog(parent, _('Could not convert some books'), msg).exec_() - + return jobs, changed, bad_rows def convert_single(fmt, parent, db, comics, others): @@ -120,10 +120,10 @@ def convert_single(fmt, parent, db, comics, others): temp_files.append(d.cover_file) opts.cover = d.cover_file.name temp_files.extend([d.opf_file, pt, of]) - jobs.append(('any2'+fmt, args, _('Convert book: ')+d.mi.title, + jobs.append(('any2'+fmt, args, _('Convert book: ')+d.mi.title, fmt.upper(), row_id, temp_files)) changed = True - + for row, row_id in zip(comics, comics_ids): mi = db.get_metadata(row) title = author = _('Unknown') @@ -140,7 +140,7 @@ def convert_single(fmt, parent, db, comics, others): try: data = db.format(row, _fmt.upper()) if data is not None: - break + break except: continue pt = PersistentTemporaryFile('.'+_fmt) @@ -152,12 +152,12 @@ def convert_single(fmt, parent, db, comics, others): opts.verbose = 2 args = [pt.name, opts] changed = True - jobs.append(('comic2'+fmt, args, _('Convert comic: ')+opts.title, + jobs.append(('comic2'+fmt, args, _('Convert comic: ')+opts.title, fmt.upper(), row_id, [pt, of])) - + return jobs, changed - - + + def convert_single_lrf(parent, db, comics, others): changed = False @@ -182,10 +182,10 @@ def convert_single_lrf(parent, db, comics, others): if d.cover_file: temp_files.append(d.cover_file) temp_files.extend([pt, of]) - jobs.append(('any2lrf', [cmdline], _('Convert book: ')+d.title(), + jobs.append(('any2lrf', [cmdline], _('Convert book: ')+d.title(), 'LRF', row_id, temp_files)) changed = True - + for row, row_id in zip(comics, comics_ids): mi = db.get_metadata(row) title = author = _('Unknown') @@ -202,7 +202,7 @@ def convert_single_lrf(parent, db, comics, others): try: data = db.format(row, fmt.upper()) if data is not None: - break + break except: continue if data is None: @@ -216,19 +216,20 @@ def convert_single_lrf(parent, db, comics, others): opts.verbose = 1 args = [pt.name, opts] changed = True - jobs.append(('comic2lrf', args, _('Convert comic: ')+opts.title, + jobs.append(('comic2lrf', args, _('Convert comic: ')+opts.title, 'LRF', row_id, [pt, of])) - + return jobs, changed def convert_bulk(fmt, parent, db, comics, others): if others: d = get_dialog(fmt)(parent, db) if d.exec_() != QDialog.Accepted: - others = [] + others, user_mi = [], None else: opts = d.opts opts.verbose = 2 + user_mi = d.user_mi if comics: comic_opts = ComicConf.get_bulk_conversion_options(parent) if not comic_opts: @@ -239,7 +240,7 @@ def convert_bulk(fmt, parent, db, comics, others): if total == 0: return parent.status_bar.showMessage(_('Starting Bulk conversion of %d books')%total, 2000) - + for i, row in enumerate(others+comics): row_id = db.id(row) if row in others: @@ -256,6 +257,11 @@ def convert_bulk(fmt, parent, db, comics, others): continue options = opts.copy() mi = db.get_metadata(row) + if user_mi is not None: + if user_mi.series_index == 1: + user_mi.series_index = None + mi.smart_update(user_mi) + db.set_metadata(db.id(row), mi) opf = OPFCreator(os.getcwdu(), mi) opf_file = PersistentTemporaryFile('.opf') opf.render(opf_file) @@ -291,10 +297,10 @@ def convert_bulk(fmt, parent, db, comics, others): try: data = db.format(row, _fmt.upper()) if data is not None: - break + break except: continue - + pt = PersistentTemporaryFile('.'+_fmt.lower()) pt.write(data) pt.close() @@ -304,17 +310,17 @@ def convert_bulk(fmt, parent, db, comics, others): options.verbose = 1 args = [pt.name, options] desc = _('Convert book %d of %d (%s)')%(i+1, total, repr(mi.title)) - jobs.append(('comic2'+fmt, args, desc, fmt.upper(), row_id, [pt, of])) - + jobs.append(('comic2'+fmt, args, desc, fmt.upper(), row_id, [pt, of])) + if bad_rows: res = [] for row in bad_rows: title = db.title(row) res.append('
  • %s
  • '%title) - + msg = _('

    Could not convert %d of %d books, because no suitable source format was found.

    ')%(len(res), total, '\n'.join(res)) warning_dialog(parent, _('Could not convert some books'), msg).exec_() - + return jobs, False @@ -333,7 +339,7 @@ def convert_bulk_lrf(parent, db, comics, others): if total == 0: return parent.status_bar.showMessage(_('Starting Bulk conversion of %d books')%total, 2000) - + for i, row in enumerate(others+comics): row_id = db.id(row) if row in others: @@ -388,10 +394,10 @@ def convert_bulk_lrf(parent, db, comics, others): try: data = db.format(row, fmt.upper()) if data is not None: - break + break except: continue - + pt = PersistentTemporaryFile('.'+fmt.lower()) pt.write(data) pt.close() @@ -401,17 +407,17 @@ def convert_bulk_lrf(parent, db, comics, others): options.verbose = 1 args = [pt.name, options] desc = _('Convert book %d of %d (%s)')%(i+1, total, repr(mi.title)) - jobs.append(('comic2lrf', args, desc, 'LRF', row_id, [pt, of])) - + jobs.append(('comic2lrf', args, desc, 'LRF', row_id, [pt, of])) + if bad_rows: res = [] for row in bad_rows: title = db.title(row) res.append('
  • %s
  • '%title) - + msg = _('

    Could not convert %d of %d books, because no suitable source format was found.

    ')%(len(res), total, '\n'.join(res)) warning_dialog(parent, _('Could not convert some books'), msg).exec_() - + return jobs, False def set_conversion_defaults_lrf(comic, parent, db): @@ -438,7 +444,7 @@ def _fetch_news(data, fmt): args.extend(['--password', data['password']]) args.append(data['script'] if data['script'] else data['title']) return 'feeds2'+fmt.lower(), [args], _('Fetch news from ')+data['title'], fmt.upper(), [pt] - + def fetch_scheduled_recipe(recipe, script): from calibre.gui2.dialogs.scheduler import config @@ -453,7 +459,7 @@ def fetch_scheduled_recipe(recipe, script): args.extend(['--username', x[0], '--password', x[1]]) args.append(script) return 'feeds2'+fmt, [args], _('Fetch news from ')+recipe.title, fmt.upper(), [pt] - + def auto_convert_ebook(*args): return auto_convert(*args) @@ -463,14 +469,14 @@ def convert_single_ebook(*args): return convert_single_lrf(*args) elif fmt in ('epub', 'mobi'): return convert_single(fmt, *args) - + def convert_bulk_ebooks(*args): fmt = prefs['output_format'].lower() if fmt == 'lrf': return convert_bulk_lrf(*args) elif fmt in ('epub', 'mobi'): return convert_bulk(fmt, *args) - + def set_conversion_defaults(comic, parent, db): fmt = prefs['output_format'].lower() if fmt == 'lrf': diff --git a/src/calibre/library/__init__.py b/src/calibre/library/__init__.py index c32d6732df..8c304e5dce 100644 --- a/src/calibre/library/__init__.py +++ b/src/calibre/library/__init__.py @@ -7,19 +7,19 @@ from calibre.utils.config import Config, StringConfig def server_config(defaults=None): desc=_('Settings to control the calibre content server') c = Config('server', desc) if defaults is None else StringConfig(defaults, desc) - - c.add_opt('port', ['-p', '--port'], default=8080, + + c.add_opt('port', ['-p', '--port'], default=8080, help=_('The port on which to listen. Default is %default')) - c.add_opt('timeout', ['-t', '--timeout'], default=120, + c.add_opt('timeout', ['-t', '--timeout'], default=120, help=_('The server timeout in seconds. Default is %default')) - c.add_opt('thread_pool', ['--thread-pool'], default=30, + c.add_opt('thread_pool', ['--thread-pool'], default=30, help=_('The max number of worker threads to use. Default is %default')) - c.add_opt('password', ['--password'], default=None, + c.add_opt('password', ['--password'], default=None, help=_('Set a password to restrict access. By default access is unrestricted.')) c.add_opt('username', ['--username'], default='calibre', help=_('Username for access. By default, it is: %default')) c.add_opt('develop', ['--develop'], default=False, help='Development mode. Server automatically restarts on file changes and serves code files (html, css, js) from the file system instead of calibre\'s resource system.') - c.add_opt('max_cover', ['--max-cover'], default='600x800', + c.add_opt('max_cover', ['--max-cover'], default='600x800', help=_('The maximum size for displayed covers. Default is %default.')) return c diff --git a/src/calibre/web/feeds/recipes/__init__.py b/src/calibre/web/feeds/recipes/__init__.py index a2dbcd7d24..191bf905ca 100644 --- a/src/calibre/web/feeds/recipes/__init__.py +++ b/src/calibre/web/feeds/recipes/__init__.py @@ -39,6 +39,7 @@ recipe_modules = ['recipe_' + r for r in ( 'nacional_cro', '24sata', 'dnevni_avaz', 'glas_srpske', '24sata_rs', 'krstarica', 'krstarica_en', 'tanjug', 'laprensa_ni', 'azstarnet', 'corriere_della_sera_it', 'corriere_della_sera_en', 'msdnmag_en', + 'moneynews', )] import re, imp, inspect, time, os diff --git a/src/calibre/web/feeds/recipes/recipe_moneynews.py b/src/calibre/web/feeds/recipes/recipe_moneynews.py new file mode 100644 index 0000000000..96656e490d --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_moneynews.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2009, Darko Miletic ' +''' +moneynews.newsmax.com +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class MoneyNews(BasicNewsRecipe): + title = 'Moneynews.com' + __author__ = 'Darko Miletic' + description = 'Financial news worldwide' + publisher = 'moneynews.com' + category = 'news, finances, USA, business' + oldest_article = 2 + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + encoding = 'cp1252' + + html2lrf_options = [ + '--comment', description + , '--category', category + , '--publisher', publisher + , '--ignore-tables' + ] + + html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True' + + feeds = [ + (u'Street Talk' , u'http://moneynews.newsmax.com/xml/streettalk.xml' ) + ,(u'Finance News' , u'http://moneynews.newsmax.com/xml/FinanceNews.xml' ) + ,(u'Economy' , u'http://moneynews.newsmax.com/xml/economy.xml' ) + ,(u'Companies' , u'http://moneynews.newsmax.com/xml/companies.xml' ) + ,(u'Markets' , u'http://moneynews.newsmax.com/xml/Markets.xml' ) + ,(u'Investing & Analysis' , u'http://moneynews.newsmax.com/xml/investing.xml' ) + ] + + + keep_only_tags = [dict(name='table', attrs={'class':'copy'})] + + remove_tags = [ + dict(name='td' , attrs={'id':'article_fontsize'}) + ,dict(name='table', attrs={'id':'toolbox' }) + ,dict(name='tr' , attrs={'id':'noprint3' }) + ] + \ No newline at end of file