Pull from trunk

This commit is contained in:
Kovid Goyal 2009-04-12 12:16:16 -07:00
commit 0b59ff9c09
14 changed files with 284 additions and 104 deletions

View File

@ -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

View File

@ -2,7 +2,7 @@ from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
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 \

View File

@ -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()

View File

@ -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

View File

@ -1,11 +1,10 @@
'''Read meta information from PDF files'''
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
'''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()

View File

@ -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

View File

@ -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()

View File

@ -658,7 +658,9 @@ class DeviceGUI(object):
bad = '\n'.join('<li>%s</li>'%(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:<br><ul>%s</ul>')%(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. <br><ul>%s</ul>')%(bad,))
d.exec_()
def upload_booklists(self):

View File

@ -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:

View File

@ -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)

View File

@ -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('<li>%s</li>'%title)
msg = _('<p>Could not convert %d of %d books, because no suitable source format was found.<ul>%s</ul>')%(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('<li>%s</li>'%title)
msg = _('<p>Could not convert %d of %d books, because no suitable source format was found.<ul>%s</ul>')%(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('<li>%s</li>'%title)
msg = _('<p>Could not convert %d of %d books, because no suitable source format was found.<ul>%s</ul>')%(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':

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,49 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
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' })
]