Sync to trunk

This commit is contained in:
Kovid Goyal 2009-02-05 15:50:08 -08:00
commit a4aaa82564
178 changed files with 8780 additions and 1660 deletions

View File

@ -21,6 +21,11 @@ mimetypes.add_type('application/epub+zip', '.epub')
mimetypes.add_type('text/x-sony-bbeb+xml', '.lrs')
mimetypes.add_type('application/x-sony-bbeb', '.lrf')
mimetypes.add_type('application/x-dtbncx+xml', '.ncx')
mimetypes.add_type('application/adobe-page-template+xml', '.xpgt')
mimetypes.add_type('application/x-font-opentype', '.otf')
mimetypes.add_type('application/x-font-truetype', '.ttf')
mimetypes.add_type('application/oebps-package+xml', '.opf')
def to_unicode(raw, encoding='utf-8', errors='strict'):
if isinstance(raw, unicode):

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = 'calibre'
__version__ = '0.4.132'
__version__ = '0.4.133'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
'''
Various run time constants.

View File

@ -27,6 +27,7 @@ class KINDLE(USBMS):
STORAGE_CARD_VOLUME_LABEL = 'Kindle Storage Card'
EBOOK_DIR_MAIN = "documents"
EBOOK_DIR_CARD = "documents"
SUPPORTS_SUB_DIRS = True
def delete_books(self, paths, end_session=True):

View File

@ -147,6 +147,7 @@ class PRS505(Device):
def open_windows(self):
time.sleep(6)
drives = []
wmi = __import__('wmi', globals(), locals(), [], -1)
c = wmi.WMI()

View File

@ -40,7 +40,7 @@ class Device(_Device):
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
%(BCD_start)s
<match key="volume.is_partition" bool="false">
<match key="@info.parent:storage.lun" int="0">
<merge key="volume.label" type="string">%(main_memory)s</merge>
<merge key="%(app)s.mainvolume" type="string">%(deviceclass)s</merge>
</match>
@ -54,7 +54,7 @@ class Device(_Device):
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
%(BCD_start)s
<match key="volume.is_partition" bool="true">
<match key="@info.parent:storage.lun" int="1">
<merge key="volume.label" type="string">%(storage_card)s</merge>
<merge key="%(app)s.cardvolume" type="string">%(deviceclass)s</merge>
</match>
@ -172,6 +172,7 @@ class Device(_Device):
return prefix
def open_windows(self):
time.sleep(6)
drives = {}
wmi = __import__('wmi', globals(), locals(), [], -1)
c = wmi.WMI()

View File

@ -102,13 +102,23 @@ def config(defaults=None, name='epub'):
c.remove_opt('zip')
c.add_opt('output', ['-o', '--output'], default=None,
help=_('The output EPUB file. If not specified, it is derived from the input file name.'))
help=_('The output EPUB file. If not specified, it is '
'derived from the input file name.'))
c.add_opt('profile', ['--profile'], default='PRS505', choices=list(PROFILES.keys()),
help=_('Profile of the target device this EPUB is meant for. Set to None to create a device independent EPUB. The profile is used for device specific restrictions on the EPUB. Choices are: ')+str(list(PROFILES.keys())))
help=_('Profile of the target device this EPUB is meant for. '
'Set to None to create a device independent EPUB. '
'The profile is used for device specific restrictions '
'on the EPUB. Choices are: ')+str(list(PROFILES.keys())))
c.add_opt('override_css', ['--override-css'], default=None,
help=_('Either the path to a CSS stylesheet or raw CSS. This CSS will override any existing CSS declarations in the source files.'))
structure = c.add_group('structure detection', _('Control auto-detection of document structure.'))
structure('chapter', ['--chapter'], default="//*[re:match(name(), 'h[1-2]') and re:test(., 'chapter|book|section|part', 'i')] | //*[@class = 'chapter']",
help=_('Either the path to a CSS stylesheet or raw CSS. '
'This CSS will override any existing CSS '
'declarations in the source files.'))
structure = c.add_group('structure detection',
_('Control auto-detection of document structure.'))
structure('chapter', ['--chapter'],
default="//*[re:match(name(), 'h[1-2]') and "
"re:test(., 'chapter|book|section|part', 'i')] | "
"//*[@class = 'chapter']",
help=_('''\
An XPath expression to detect chapter titles. The default is to consider <h1> or
<h2> tags that contain the words "chapter","book","section" or "part" as chapter titles as
@ -118,14 +128,31 @@ use the expression "/". See the XPath Tutorial in the calibre User Manual for fu
help on using this feature.
''').replace('\n', ' '))
structure('chapter_mark', ['--chapter-mark'], choices=['pagebreak', 'rule', 'both', 'none'],
default='pagebreak', help=_('Specify how to mark detected chapters. A value of "pagebreak" will insert page breaks before chapters. A value of "rule" will insert a line before chapters. A value of "none" will disable chapter marking and a value of "both" will use both page breaks and lines to mark chapters.'))
default='pagebreak',
help=_('Specify how to mark detected chapters. A value of '
'"pagebreak" will insert page breaks before chapters. '
'A value of "rule" will insert a line before chapters. '
'A value of "none" will disable chapter marking and a '
'value of "both" will use both page breaks and lines '
'to mark chapters.'))
structure('cover', ['--cover'], default=None,
help=_('Path to the cover to be used for this book'))
structure('prefer_metadata_cover', ['--prefer-metadata-cover'], default=False,
action='store_true',
help=_('Use the cover detected from the source file in preference to the specified cover.'))
help=_('Use the cover detected from the source file in preference '
'to the specified cover.'))
structure('remove_first_image', ['--remove-first-image'], default=False,
help=_('Remove the first image from the input ebook. Useful if '
'the first image in the source file is a cover and you '
'are specifying an external cover.'))
structure('dont_split_on_page_breaks', ['--dont-split-on-page-breaks'], default=False,
help=_('Turn off splitting at page breaks. Normally, input files are automatically split at every page break into two files. This gives an output ebook that can be parsed faster and with less resources. However, splitting is slow and if your source file contains a very large number of page breaks, you should turn off splitting on page breaks.'))
help=_('Turn off splitting at page breaks. Normally, input files '
'are automatically split at every page break into '
'two files. This gives an output ebook that can be parsed '
'faster and with less resources. However, splitting is '
'slow and if your source file contains a very large '
'number of page breaks, you should turn off splitting '
'on page breaks.'))
toc = c.add_group('toc',
_('''\
Control the automatic generation of a Table of Contents. If an OPF file is detected
@ -133,21 +160,36 @@ and it specifies a Table of Contents, then that will be used rather than trying
to auto-generate a Table of Contents.
''').replace('\n', ' '))
toc('max_toc_links', ['--max-toc-links'], default=50,
help=_('Maximum number of links to insert into the TOC. Set to 0 to disable. Default is: %default. Links are only added to the TOC if less than the --toc-threshold number of chapters were detected.'))
help=_('Maximum number of links to insert into the TOC. Set to 0 '
'to disable. Default is: %default. Links are only added to the '
'TOC if less than the --toc-threshold number of chapters were detected.'))
toc('no_chapters_in_toc', ['--no-chapters-in-toc'], default=False,
help=_("Don't add auto-detected chapters to the Table of Contents."))
toc('toc_threshold', ['--toc-threshold'], default=6,
help=_('If fewer than this number of chapters is detected, then links are added to the Table of Contents. Default: %default'))
help=_('If fewer than this number of chapters is detected, then links '
'are added to the Table of Contents. Default: %default'))
toc('level1_toc', ['--level1-toc'], default=None,
help=_('XPath expression that specifies all tags that should be added to the Table of Contents at level one. If this is specified, it takes precedence over other forms of auto-detection.'))
help=_('XPath expression that specifies all tags that should be added '
'to the Table of Contents at level one. If this is specified, '
'it takes precedence over other forms of auto-detection.'))
toc('level2_toc', ['--level2-toc'], default=None,
help=_('XPath expression that specifies all tags that should be added to the Table of Contents at level two. Each entry is added under the previous level one entry.'))
help=_('XPath expression that specifies all tags that should be added '
'to the Table of Contents at level two. Each entry is added '
'under the previous level one entry.'))
toc('level3_toc', ['--level3-toc'], default=None,
help=_('XPath expression that specifies all tags that should be added to the Table of Contents at level three. Each entry is added under the previous level two entry.'))
help=_('XPath expression that specifies all tags that should be added '
'to the Table of Contents at level three. Each entry is added '
'under the previous level two entry.'))
toc('from_ncx', ['--from-ncx'], default=None,
help=_('Path to a .ncx file that contains the table of contents to use for this ebook. The NCX file should contain links relative to the directory it is placed in. See http://www.niso.org/workrooms/daisy/Z39-86-2005.html#NCX for an overview of the NCX format.'))
help=_('Path to a .ncx file that contains the table of contents to use '
'for this ebook. The NCX file should contain links relative to '
'the directory it is placed in. See '
'http://www.niso.org/workrooms/daisy/Z39-86-2005.html#NCX for '
'an overview of the NCX format.'))
toc('use_auto_toc', ['--use-auto-toc'], default=False,
help=_('Normally, if the source file already has a Table of Contents, it is used in preference to the autodetected one. With this option, the autodetected one is always used.'))
help=_('Normally, if the source file already has a Table of Contents, '
'it is used in preference to the autodetected one. '
'With this option, the autodetected one is always used.'))
layout = c.add_group('page layout', _('Control page layout'))
layout('margin_top', ['--margin-top'], default=5.0,
@ -159,18 +201,33 @@ to auto-generate a Table of Contents.
layout('margin_right', ['--margin-right'], default=5.0,
help=_('Set the right margin in pts. Default is %default'))
layout('base_font_size2', ['--base-font-size'], default=12.0,
help=_('The base font size in pts. Default is %defaultpt. Set to 0 to disable rescaling of fonts.'))
help=_('The base font size in pts. Default is %defaultpt. '
'Set to 0 to disable rescaling of fonts.'))
layout('remove_paragraph_spacing', ['--remove-paragraph-spacing'], default=False,
help=_('Remove spacing between paragraphs. Will not work if the source file forces inter-paragraph spacing.'))
help=_('Remove spacing between paragraphs. '
'Also sets a indent on paragraphs of 1.5em. '
'You can override this by adding p {text-indent: 0cm} to '
'--override-css. Spacing removal will not work if the source '
'file forces inter-paragraph spacing.'))
layout('no_justification', ['--no-justification'], default=False,
help=_('Do not force text to be justified in output.'))
layout('linearize_tables', ['--linearize-tables'], default=False,
help=_('Remove table markup, converting it into paragraphs. '
'This is useful if your source file uses a table to manage layout.'))
layout('preserve_tag_structure', ['--preserve-tag-structure'], default=False,
help=_('Preserve the HTML tag structure while splitting large HTML files. This is only neccessary if the HTML files contain CSS that uses sibling selectors. Enabling this greatly slows down processing of large HTML files.'))
help=_('Preserve the HTML tag structure while splitting large HTML files. '
'This is only neccessary if the HTML files contain CSS that '
'uses sibling selectors. Enabling this greatly slows down '
'processing of large HTML files.'))
c.add_opt('show_opf', ['--show-opf'], default=False, group='debug',
help=_('Print generated OPF file to stdout'))
c.add_opt('show_ncx', ['--show-ncx'], default=False, group='debug',
help=_('Print generated NCX file to stdout'))
c.add_opt('keep_intermediate', ['--keep-intermediate-files'], group='debug', default=False,
c.add_opt('keep_intermediate', ['--keep-intermediate-files'], group='debug',
default=False,
help=_('Keep intermediate files during processing by html2epub'))
c.add_opt('extract_to', ['--extract-to'], group='debug', default=None,
help=_('Extract the contents of the produced EPUB file to the specified directory.'))
help=_('Extract the contents of the produced EPUB file to the '
'specified directory.'))
return c

View File

@ -194,6 +194,9 @@ class HTMLProcessor(Processor, Rationalizer):
if not tag.text and not tag.get('src', False):
tag.getparent().remove(tag)
if self.opts.linearize_tables:
for tag in self.root.xpath('//table | //tr | //th | //td'):
tag.tag = 'div'
def save(self):
@ -203,6 +206,13 @@ class HTMLProcessor(Processor, Rationalizer):
# self.convert_image(img)
Processor.save(self)
def remove_first_image(self):
images = self.root.xpath('//img')
if images:
images[0].getparent().remove(images[0])
return True
return False
@ -224,10 +234,13 @@ def parse_content(filelist, opts, tdir):
resource_map, stylesheets = {}, {}
toc = TOC(base_path=tdir, type='root')
stylesheet_map = {}
first_image_removed = False
for htmlfile in filelist:
logging.getLogger('html2epub').debug('Processing %s...'%htmlfile)
hp = HTMLProcessor(htmlfile, opts, os.path.join(tdir, 'content'),
resource_map, filelist, stylesheets)
if not first_image_removed and opts.remove_first_image:
first_image_removed = hp.remove_first_image()
hp.populate_toc(toc)
hp.save()
stylesheet_map[os.path.basename(hp.save_path())] = \
@ -443,10 +456,7 @@ def convert(htmlfile, opts, notification=None, create_epub=True,
if os.path.exists(cpath):
opf.add_path_to_manifest(cpath, 'image/jpeg')
with open(opf_path, 'wb') as f:
raw = opf.render()
if not raw.startswith('<?xml '):
raw = '<?xml version="1.0" encoding="UTF-8"?>\n'+raw
f.write(raw)
f.write(opf.render())
ncx_path = os.path.join(os.path.dirname(opf_path), 'toc.ncx')
if os.path.exists(ncx_path) and os.stat(ncx_path).st_size > opts.profile.flow_size:
logger.info('Condensing NCX from %d bytes...'%os.stat(ncx_path).st_size)
@ -462,7 +472,7 @@ def convert(htmlfile, opts, notification=None, create_epub=True,
logger.info(_('Output written to ')+opts.output)
if opts.show_opf:
print open(os.path.join(tdir, 'metadata.opf')).read()
print open(opf_path, 'rb').read()
if opts.extract_to is not None:
if os.path.exists(opts.extract_to):

View File

@ -9,7 +9,7 @@ directory or zip file. All the action starts in :function:`create_dir`.
'''
import sys, re, os, shutil, logging, tempfile, cStringIO, operator, functools
from urlparse import urlparse
from urlparse import urlparse, urlunparse
from urllib import unquote
from lxml import etree
@ -98,7 +98,8 @@ class Link(object):
@classmethod
def url_to_local_path(cls, url, base):
path = url.path
path = urlunparse(('', '', url.path, url.params, url.query, ''))
path = unquote(path)
if os.path.isabs(path):
return path
return os.path.abspath(os.path.join(base, path))
@ -111,11 +112,11 @@ class Link(object):
'''
assert isinstance(url, unicode) and isinstance(base, unicode)
self.url = url
self.parsed_url = urlparse(unquote(self.url))
self.parsed_url = urlparse(self.url)
self.is_local = self.parsed_url.scheme in ('', 'file')
self.is_internal = self.is_local and not bool(self.parsed_url.path)
self.path = None
self.fragment = self.parsed_url.fragment
self.fragment = unquote(self.parsed_url.fragment)
if self.is_local and not self.is_internal:
self.path = self.url_to_local_path(self.parsed_url, base)
@ -594,15 +595,21 @@ class Processor(Parser):
'''
Populate the Table of Contents from detected chapters and links.
'''
class Adder(object):
def add_item(href, fragment, text, target, type='link'):
def __init__(self, toc):
self.next_play_order = max([x.play_order for x in toc.flat()])
def __call__(self, href, fragment, text, target, type='link'):
for entry in toc.flat():
if entry.href == href and entry.fragment == fragment:
return entry
if len(text) > 50:
text = text[:50] + u'\u2026'
return target.add_item(href, fragment, text, type=type)
self.next_play_order += 1
return target.add_item(href, fragment, text, type=type,
play_order=self.next_play_order)
add_item = Adder(toc)
name = self.htmlfile_map[self.htmlfile.path]
href = 'content/'+name
@ -629,13 +636,15 @@ class Processor(Parser):
if self.opts.level1_toc is not None:
level1 = self.opts.level1_toc(self.root)
level1_order = []
if level1:
added = {}
for elem in level1:
text, _href, frag = elem_to_link(elem, href, counter)
counter += 1
if text:
added[elem] = add_item(_href, frag, text, toc, type='chapter')
level1_order.append(add_item(_href, frag, text, toc, type='chapter'))
added[elem] = level1_order[-1]
add_item(_href, frag, 'Top', added[elem], type='chapter')
if self.opts.level2_toc is not None:
added2 = {}
@ -665,6 +674,15 @@ class Processor(Parser):
add_item(_href, frag, text, level2, type='chapter')
if level1_order: # Fix play order
next_play_order = level1_order[0].play_order
for x in level1_order:
for y in x.flat():
y.play_order = next_play_order
next_play_order += 1
if len(toc) > 0:
return
# Add chapters to TOC
@ -867,6 +885,8 @@ class Processor(Parser):
css += '\n\na { color: inherit; text-decoration: inherit; cursor: default; }\na[href] { color: blue; text-decoration: underline; cursor:pointer; }'
if self.opts.remove_paragraph_spacing:
css += '\n\np {text-indent: 1.5em; margin-top:0pt; margin-bottom:0pt; padding:0pt; border:0pt;}'
if not self.opts.no_justification:
css += '\n\nbody {text-align: justify}'
if self.opts.override_css:
css += '\n\n' + self.opts.override_css
self.override_css = self.css_parser.parseString(self.preprocess_css(css))
@ -987,7 +1007,6 @@ def merge_metadata(htmlfile, opf, opts):
mi = get_metadata(open(htmlfile, 'rb'), 'html')
except:
mi = MetaInformation(None, None)
if opts.from_opf is not None and os.access(opts.from_opf, os.R_OK):
mi.smart_update(OPF(open(opts.from_opf, 'rb'), os.path.abspath(os.path.dirname(opts.from_opf))))
for attr in ('title', 'authors', 'publisher', 'tags', 'comments'):

View File

@ -23,7 +23,7 @@ from urllib import unquote as urlunquote
from lxml import etree
from calibre.ebooks.lit.reader import DirectoryEntry
import calibre.ebooks.lit.maps as maps
from calibre.ebooks.oeb.base import OEB_DOCS, OEB_STYLES, OEB_CSS_MIME, \
from calibre.ebooks.oeb.base import OEB_DOCS, XHTML_MIME, OEB_STYLES, \
CSS_MIME, OPF_MIME, XML_NS, XML
from calibre.ebooks.oeb.base import namespace, barename, prefixname, \
urlnormalize, xpath
@ -474,7 +474,7 @@ class LitWriter(object):
name = '/data/' + item.id
data = item.data
secnum = 0
if not isinstance(data, basestring):
if isinstance(data, etree._Element):
self._add_folder(name)
rebin = ReBinary(data, item, self._oeb, map=HTML_MAP)
self._add_file(name + '/ahc', rebin.ahc, 0)
@ -483,6 +483,8 @@ class LitWriter(object):
data = rebin.content
name = name + '/content'
secnum = 1
elif isinstance(data, unicode):
data = data.encode('utf-8')
self._add_file(name, data, secnum)
item.size = len(data)
@ -493,7 +495,7 @@ class LitWriter(object):
if item.spine_position is not None:
key = 'linear' if item.linear else 'nonlinear'
manifest[key].append(item)
elif item.media_type == CSS_MIME:
elif item.media_type in OEB_STYLES:
manifest['css'].append(item)
elif item.media_type in LIT_IMAGES:
manifest['images'].append(item)
@ -506,6 +508,11 @@ class LitWriter(object):
data.write(pack('<I', len(items)))
for item in items:
id, media_type = item.id, item.media_type
if media_type in OEB_DOCS:
# Needs to have 'html' in media-type
media_type = XHTML_MIME
elif media_type in OEB_STYLES:
media_type = CSS_MIME
href = urlunquote(item.href)
item.offset = offset \
if state in ('linear', 'nonlinear') else 0
@ -525,7 +532,12 @@ class LitWriter(object):
pb3 = StringIO()
pb3cur = 0
bits = 0
linear = []
nonlinear = []
for item in self._oeb.spine:
dest = linear if item.linear else nonlinear
dest.append(item)
for item in chain(linear, nonlinear):
page_breaks = copy.copy(item.page_breaks)
if not item.linear:
page_breaks.insert(0, (0, []))

View File

@ -243,6 +243,12 @@ class MetaInformation(object):
if getattr(mi, 'cover_data', None) and mi.cover_data[0] is not None:
self.cover_data = mi.cover_data
def format_series_index(self):
try:
x = float(self.series_index)
except ValueError:
x = 1.0
return '%d'%x if int(x) == x else '%.2f'%x
def __unicode__(self):
ans = [ fmt('Title', self.title) ]
@ -265,7 +271,7 @@ class MetaInformation(object):
if self.tags:
fmt('Tags', u', '.join([unicode(t) for t in self.tags]))
if self.series:
fmt('Series', self.series + '#%s'%self.series_index)
fmt('Series', self.series + ' #%s'%self.format_series_index())
if self.language:
fmt('Language', self.language)
if self.rating is not None:
@ -280,7 +286,8 @@ class MetaInformation(object):
ans += [(_('Comments'), unicode(self.comments))]
ans += [('ISBN', unicode(self.isbn))]
ans += [(_('Tags'), u', '.join([unicode(t) for t in self.tags]))]
ans += [(_('Series'), unicode(self.series))]
if self.series:
ans += [(_('Series'), unicode(self.series))+ ' #%s'%self.format_series_index()]
ans += [(_('Language'), unicode(self.language))]
for i, x in enumerate(ans):
ans[i] = u'<tr><td><b>%s</b></td><td>%s</td></tr>'%x

View File

@ -789,7 +789,10 @@ class OPF(object):
return elem
def render(self, encoding='utf-8'):
return etree.tostring(self.root, encoding='utf-8', pretty_print=True)
raw = etree.tostring(self.root, encoding=encoding, pretty_print=True)
if not raw.lstrip().startswith('<?xml '):
raw = '<?xml version="1.0" encoding="%s"?>\n'%encoding.upper()+raw
return raw
def smart_update(self, mi):
for attr in ('author_sort', 'title_sort', 'comments', 'category',
@ -877,7 +880,8 @@ class OPFCreator(MetaInformation):
self.guide = Guide.from_opf_guide(guide_element, self.base_path)
self.guide.set_basedir(self.base_path)
def render(self, opf_stream, ncx_stream=None, ncx_manifest_entry=None):
def render(self, opf_stream=sys.stdout, ncx_stream=None,
ncx_manifest_entry=None):
from calibre.resources import opf_template
from calibre.utils.genshi.template import MarkupTemplate
template = MarkupTemplate(opf_template)

View File

@ -138,6 +138,7 @@ class MobiMLizer(object):
return result
def mobimlize_content(self, tag, text, bstate, istates):
if text or tag != 'br':
bstate.content = True
istate = istates[-1]
para = bstate.para
@ -188,11 +189,6 @@ class MobiMLizer(object):
vspace -= 1
if istate.halign != 'auto':
para.attrib['align'] = istate.halign
if istate.ids:
last = bstate.body[-1]
for id in istate.ids:
last.addprevious(etree.Element(XHTML('a'), attrib={'id': id}))
istate.ids.clear()
pstate = bstate.istate
if tag in CONTENT_TAGS:
bstate.inline = para
@ -200,6 +196,11 @@ class MobiMLizer(object):
etree.SubElement(para, XHTML(tag), attrib=istate.attrib)
elif tag in TABLE_TAGS:
para.attrib['valign'] = 'top'
if istate.ids:
last = bstate.body[-1]
for id in istate.ids:
last.addprevious(etree.Element(XHTML('a'), attrib={'id': id}))
istate.ids.clear()
if not text:
return
if not pstate or istate != pstate:

View File

@ -121,6 +121,7 @@ class BookHeader(object):
sublangid = (langcode >> 10) & 0xFF
self.language = main_language.get(langid, 'ENGLISH')
self.sublanguage = sub_language.get(sublangid, 'NEUTRAL')
self.mobi_version = struct.unpack('>I', raw[0x68:0x6c])[0]
self.first_image_index = struct.unpack('>L', raw[0x6c:0x6c+4])[0]
self.exth_flag, = struct.unpack('>L', raw[0x80:0x84])
@ -132,11 +133,8 @@ class BookHeader(object):
class MobiReader(object):
PAGE_BREAK_PAT = re.compile(r'(<[/]{0,1}mbp:pagebreak\s*[/]{0,1}>)+', re.IGNORECASE)
IMAGE_PATS = map(re.compile, (r'\shirecindex=[\'"]{0,1}(\d+)[\'"]{0,1}',
r'\srecindex=[\'"]{0,1}(\d+)[\'"]{0,1}',
r'\slorecindex=[\'"]{0,1}(\d+)[\'"]{0,1}'))
IMAGE_ATTRS = ('lowrecindex', 'recindex', 'hirecindex')
def __init__(self, filename_or_stream, verbose=False):
self.verbose = verbose
@ -247,6 +245,7 @@ class MobiReader(object):
self.processed_html = re.sub(r'<div height="0(pt|px|ex|em|%){0,1}"></div>', '', self.processed_html)
if self.book_header.ancient and '<html' not in self.mobi_html[:300].lower():
self.processed_html = '<html><p>'+self.processed_html.replace('\n\n', '<p>')+'</html>'
self.processed_html = self.processed_html.replace('\r\n', '\n')
self.processed_html = self.processed_html.replace('> <', '>\n<')
for t, c in [('b', 'bold'), ('i', 'italic')]:
self.processed_html = re.sub(r'(?i)<%s>'%t, r'<span class="%s">'%c, self.processed_html)
@ -264,6 +263,7 @@ class MobiReader(object):
'x-large' : '5',
'xx-large' : '6',
}
mobi_version = self.book_header.mobi_version
for tag in root.iter(etree.Element):
if tag.tag in ('country-region', 'place', 'placetype', 'placename',
'state', 'city'):
@ -290,6 +290,11 @@ class MobiReader(object):
align = attrib.pop('align').strip()
if align:
styles.append('text-align: %s' % align)
if mobi_version == 1 and tag.tag == 'hr':
tag.tag = 'div'
styles.append('page-break-before: always')
styles.append('display: block')
styles.append('margin: 0')
if styles:
attrib['style'] = '; '.join(styles)
@ -300,6 +305,17 @@ class MobiReader(object):
except ValueError:
if sz in size_map.keys():
attrib['size'] = size_map[sz]
if 'filepos-id' in attrib:
attrib['id'] = attrib.pop('filepos-id')
if 'filepos' in attrib:
filepos = int(attrib.pop('filepos'))
attrib['href'] = "#filepos%d" % filepos
if tag.tag == 'img':
recindex = None
for attr in self.IMAGE_ATTRS:
recindex = attrib.pop(attr, None) or recindex
if recindex is not None:
attrib['src'] = 'images/%s.jpg' % recindex
def create_opf(self, htmlfile, guide=None):
mi = self.book_header.exth.mi
@ -399,37 +415,39 @@ class MobiReader(object):
def replace_page_breaks(self):
self.processed_html = self.PAGE_BREAK_PAT.sub('<br style="page-break-after:always" />',
self.processed_html = self.PAGE_BREAK_PAT.sub(
'<div style="page-break-after: always; margin: 0; display: block" />',
self.processed_html)
def add_anchors(self):
if self.verbose:
print 'Adding anchors...'
positions = set([])
link_pattern = re.compile(r'<[^<>]+filepos=[\'"]{0,1}(\d+)[^<>]*>', re.IGNORECASE)
link_pattern = re.compile(r'''<[^<>]+filepos=['"]{0,1}(\d+)[^<>]*>''',
re.IGNORECASE)
for match in link_pattern.finditer(self.mobi_html):
positions.add(int(match.group(1)))
positions = list(positions)
positions.sort()
pos = 0
self.processed_html = ''
for end in positions:
end_tag_re = re.compile(r'<\s*/')
for end in sorted(positions):
if end == 0:
continue
oend = end
l = self.mobi_html.find('<', end)
r = self.mobi_html.find('>', end)
if r > -1 and r < l: # Move out of tag
anchor = '<a id="filepos%d"></a>'
if r > -1 and (r < l or l == end or l == -1):
p = self.mobi_html.rfind('<', 0, end + 1)
if pos < end and p > -1 and \
not end_tag_re.match(self.mobi_html[p:r]):
anchor = ' filepos-id="filepos%d"'
end = r
else:
end = r + 1
self.processed_html += self.mobi_html[pos:end] + '<a id="filepos%d" name="filepos%d"></a>'%(oend, oend)
self.processed_html += self.mobi_html[pos:end] + (anchor % oend)
pos = end
self.processed_html += self.mobi_html[pos:]
fpat = re.compile(r'filepos=[\'"]{0,1}(\d+)[\'"]{0,1}', re.IGNORECASE)
def fpos_to_href(match):
return fpat.sub('href="#filepos%d"'%int(match.group(1)), match.group())
self.processed_html = link_pattern.sub(fpos_to_href,
self.processed_html)
def extract_images(self, processed_records, output_dir):
if self.verbose:
@ -455,19 +473,6 @@ class MobiReader(object):
self.image_names.append(os.path.basename(path))
im.convert('RGB').save(open(path, 'wb'), format='JPEG')
def fix_images(match):
tag = match.group()
for pat in self.IMAGE_PATS:
m = pat.search(tag)
if m:
return pat.sub(' src="images/%s.jpg"'%m.group(1), tag)
if hasattr(self, 'processed_html'):
self.processed_html = \
re.compile(r'<img (.*?)>', re.IGNORECASE|re.DOTALL)\
.sub(fix_images, self.processed_html)
def get_metadata(stream):
mr = MobiReader(stream)
if mr.book_header.exth is None:

View File

@ -296,9 +296,11 @@ class Serializer(object):
class MobiWriter(object):
COLLAPSE_RE = re.compile(r'[ \t\r\n\v]+')
def __init__(self, compression=None, imagemax=None):
def __init__(self, compression=None, imagemax=None,
prefer_author_sort=False):
self._compression = compression or UNCOMPRESSED
self._imagemax = imagemax or OTHER_MAX_IMAGE_SIZE
self._prefer_author_sort = prefer_author_sort
def dump(self, oeb, path):
if hasattr(path, 'write'):
@ -457,12 +459,19 @@ class MobiWriter(object):
for term in oeb.metadata:
if term not in EXTH_CODES: continue
code = EXTH_CODES[term]
for item in oeb.metadata[term]:
items = oeb.metadata[term]
if term == 'creator':
if self._prefer_author_sort:
creators = [unicode(c.file_as or c) for c in items]
else:
creators = [unicode(c) for c in items]
items = ['; '.join(creators)]
for item in items:
data = self.COLLAPSE_RE.sub(' ', unicode(item))
if term == 'identifier':
if data.lower().startswith('urn:isbn:'):
data = data[9:]
elif item.get('scheme', '').lower() == 'isbn':
elif item.scheme.lower() == 'isbn':
pass
else:
continue
@ -535,6 +544,9 @@ def config(defaults=None):
help=_('Render HTML tables as blocks of text instead of actual '
'tables. This is neccessary if the HTML contains very large '
'or complex tables.'))
mobi('prefer_author_sort', ['--prefer-author-sort'], default=False,
help=_('When present, use the author sorting information for '
'generating the Mobipocket author metadata.'))
profiles = c.add_group('profiles', _('Device renderer profiles. '
'Affects conversion of font sizes, image rescaling and rasterization '
'of tables. Valid profiles are: %s.') % ', '.join(_profiles))
@ -594,7 +606,8 @@ def oeb2mobi(opts, inpath):
trimmer.transform(oeb, context)
mobimlizer = MobiMLizer(ignore_tables=opts.ignore_tables)
mobimlizer.transform(oeb, context)
writer = MobiWriter(compression=compression, imagemax=imagemax)
writer = MobiWriter(compression=compression, imagemax=imagemax,
prefer_author_sort=opts.prefer_author_sort)
writer.dump(oeb, outpath)
run_plugins_on_postprocess(outpath, 'mobi')
logger.info(_('Output written to ') + outpath)

File diff suppressed because it is too large Load Diff

View File

@ -109,6 +109,7 @@ class Stylizer(object):
STYLESHEETS = {}
def __init__(self, tree, path, oeb, profile=PROFILES['PRS505']):
self.oeb = oeb
self.profile = profile
self.logger = oeb.logger
item = oeb.manifest.hrefs[path]
@ -117,7 +118,7 @@ class Stylizer(object):
stylesheets = [HTML_CSS_STYLESHEET]
head = xpath(tree, '/h:html/h:head')[0]
parser = cssutils.CSSParser()
parser.setFetcher(lambda path: ('utf-8', oeb.container.read(path)))
parser.setFetcher(self._fetch_css_file)
for elem in head:
if elem.tag == XHTML('style') and elem.text \
and elem.get('type', CSS_MIME) in OEB_STYLES:
@ -138,8 +139,7 @@ class Stylizer(object):
if path in self.STYLESHEETS:
stylesheet = self.STYLESHEETS[path]
else:
data = XHTML_CSS_NAMESPACE
data += oeb.manifest.hrefs[path].data
data = self._fetch_css_file(path)[1]
stylesheet = parser.parseString(data, href=path)
stylesheet.namespaces['h'] = XHTML_NS
self.STYLESHEETS[path] = stylesheet
@ -167,6 +167,14 @@ class Stylizer(object):
for elem in xpath(tree, '//h:*[@style]'):
self.style(elem)._apply_style_attr()
def _fetch_css_file(self, path):
hrefs = self.oeb.manifest.hrefs
if path not in hrefs:
return (None, None)
data = hrefs[path].data
data = XHTML_CSS_NAMESPACE + data
return ('utf-8', data)
def flatten_rule(self, rule, href, index):
results = []
if isinstance(rule, CSSStyleRule):

View File

@ -13,13 +13,9 @@ from urlparse import urldefrag
from lxml import etree
import cssutils
from calibre.ebooks.oeb.base import XPNSMAP, CSS_MIME, OEB_DOCS
from calibre.ebooks.oeb.base import LINK_SELECTORS, CSSURL_RE
from calibre.ebooks.oeb.base import urlnormalize
LINK_SELECTORS = []
for expr in ('//h:link/@href', '//h:img/@src', '//h:object/@data',
'//*/@xl:href'):
LINK_SELECTORS.append(etree.XPath(expr, namespaces=XPNSMAP))
class ManifestTrimmer(object):
def transform(self, oeb, context):
oeb.logger.info('Trimming unused files from manifest...')
@ -53,15 +49,13 @@ class ManifestTrimmer(object):
if found not in used:
new.add(found)
elif item.media_type == CSS_MIME:
def replacer(uri):
absuri = item.abshref(urlnormalize(uri))
if absuri in oeb.manifest.hrefs:
for match in CSSURL_RE.finditer(item.data):
href = match.group('url')
href = item.abshref(urlnormalize(href))
if href in oeb.manifest.hrefs:
found = oeb.manifest.hrefs[href]
if found not in used:
new.add(found)
return uri
sheet = cssutils.parseString(item.data, href=item.href)
cssutils.replaceUrls(sheet, replacer)
used.update(new)
unchecked = new
for item in oeb.manifest.values():

View File

@ -18,7 +18,7 @@ from calibre.gui2 import error_dialog, choose_images, pixmap_to_data, ResizableD
from calibre.ebooks.epub.from_any import SOURCE_FORMATS, config as epubconfig
from calibre.ebooks.metadata import MetaInformation
from calibre.ptempfile import PersistentTemporaryFile
from calibre.ebooks.metadata.opf import OPFCreator
from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.ebooks.metadata import authors_to_string, string_to_authors
@ -62,6 +62,7 @@ class Config(ResizableDialog, Ui_Dialog):
self.toc_title_label.setVisible(False)
self.opt_rescale_images.setVisible(False)
self.opt_ignore_tables.setVisible(False)
self.opt_prefer_author_sort.setVisible(False)
def initialize(self):
self.__w = []

View File

@ -105,6 +105,36 @@
<string>Book Cover</string>
</property>
<layout class="QGridLayout" name="_2" >
<item row="0" column="0" >
<layout class="QHBoxLayout" name="_3" >
<item>
<widget class="ImageView" name="cover" >
<property name="text" >
<string/>
</property>
<property name="pixmap" >
<pixmap resource="../images.qrc" >:/images/book.svg</pixmap>
</property>
<property name="scaledContents" >
<bool>true</bool>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0" >
<widget class="QCheckBox" name="opt_prefer_metadata_cover" >
<property name="text" >
<string>Use cover from &amp;source file</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" >
<layout class="QVBoxLayout" name="_4" >
<property name="spacing" >
@ -156,36 +186,6 @@
</item>
</layout>
</item>
<item row="2" column="0" >
<widget class="QCheckBox" name="opt_prefer_metadata_cover" >
<property name="text" >
<string>Use cover from &amp;source file</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" >
<layout class="QHBoxLayout" name="_3" >
<item>
<widget class="ImageView" name="cover" >
<property name="text" >
<string/>
</property>
<property name="pixmap" >
<pixmap resource="../images.qrc" >:/images/book.svg</pixmap>
</property>
<property name="scaledContents" >
<bool>true</bool>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<zorder>opt_prefer_metadata_cover</zorder>
<zorder></zorder>
@ -486,6 +486,27 @@
</property>
</widget>
</item>
<item row="6" column="0" >
<widget class="QCheckBox" name="opt_prefer_author_sort" >
<property name="text" >
<string>&amp;Use author sort to set author field in output</string>
</property>
</widget>
</item>
<item row="7" column="0" >
<widget class="QCheckBox" name="opt_no_justification" >
<property name="text" >
<string>No text &amp;justification</string>
</property>
</widget>
</item>
<item row="8" column="0" >
<widget class="QCheckBox" name="opt_linearize_tables" >
<property name="text" >
<string>&amp;Linearize tables</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
@ -675,11 +696,7 @@
<item row="0" column="0" colspan="2" >
<widget class="QLabel" name="label_8" >
<property name="text" >
<string>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'DejaVu Sans'; font-size:10pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">You can control how calibre detects chapters using a XPath expression. To learn how to use XPath expressions see the &lt;a href="https://calibre.kovidgoyal.net/user_manual/xpath.html">&lt;span style=" text-decoration: underline; color:#0000ff;">XPath tutorial&lt;/span>&lt;/a>&lt;/p>&lt;/body>&lt;/html></string>
<string>&lt;p>You can control how calibre detects chapters using a XPath expression. To learn how to use XPath expressions see the &lt;a href="http://calibre.kovidgoyal.net/user_manual/xpath.html">XPath tutorial&lt;/a>&lt;/p></string>
</property>
<property name="textFormat" >
<enum>Qt::RichText</enum>
@ -687,6 +704,9 @@ p, li { white-space: pre-wrap; }
<property name="wordWrap" >
<bool>true</bool>
</property>
<property name="openExternalLinks" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1" >

View File

@ -16,7 +16,7 @@
<iconset resource="../images.qrc" >
<normaloff>:/images/view.svg</normaloff>:/images/view.svg</iconset>
</property>
<layout class="QGridLayout" >
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" >
<widget class="QPlainTextEdit" name="log" >
<property name="undoRedoEnabled" >
@ -30,10 +30,34 @@
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QDialogButtonBox" name="buttonBox" >
<property name="standardButtons" >
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../images.qrc" />
</resources>
<connections/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>617</x>
<y>442</y>
</hint>
<hint type="destinationlabel" >
<x>206</x>
<y>-5</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -18,3 +18,5 @@ class Config(_Config):
self.opt_profile.setVisible(False)
self.opt_dont_split_on_page_breaks.setVisible(False)
self.opt_preserve_tag_structure.setVisible(False)
self.opt_linearize_tables.setVisible(False)
self.opt_no_justification.setVisible(False)

View File

@ -10,8 +10,8 @@ Scheduler for automated recipe downloads
import sys, copy, time
from datetime import datetime, timedelta, date
from PyQt4.Qt import QDialog, QApplication, QLineEdit, QPalette, SIGNAL, QBrush, \
QColor, QAbstractListModel, Qt, QVariant, QFont, QIcon, \
QFile, QObject, QTimer, QMutex, QMenu, QAction, QTime
QColor, QAbstractItemModel, Qt, QVariant, QFont, QIcon, \
QFile, QObject, QTimer, QMutex, QMenu, QAction, QTime, QModelIndex
from calibre import english_sort
from calibre.gui2.dialogs.scheduler_ui import Ui_Dialog
@ -30,6 +30,7 @@ class Recipe(object):
self.id = id
self.title = getattr(recipe_class, 'title', None)
self.description = getattr(recipe_class, 'description', None)
self.language = getattr(recipe_class, 'language', _('Unknown'))
self.last_downloaded = datetime.fromordinal(1)
self.downloading = False
self.builtin = builtin
@ -86,12 +87,12 @@ def load_recipes():
recipes.append(r)
return recipes
class RecipeModel(QAbstractListModel, SearchQueryParser):
class RecipeModel(QAbstractItemModel, SearchQueryParser):
LOCATIONS = ['all']
def __init__(self, db, *args):
QAbstractListModel.__init__(self, *args)
QAbstractItemModel.__init__(self, *args)
SearchQueryParser.__init__(self)
self.default_icon = QIcon(':/images/news.svg')
self.custom_icon = QIcon(':/images/user_profile.svg')
@ -100,7 +101,10 @@ class RecipeModel(QAbstractListModel, SearchQueryParser):
recipe = compile_recipe(x[1])
self.recipes.append(Recipe(x[0], recipe, False))
self.refresh()
self._map = list(range(len(self.recipes)))
self.bold_font = QFont()
self.bold_font.setBold(True)
self.bold_font = QVariant(self.bold_font)
def refresh(self):
sr = load_recipes()
@ -110,6 +114,34 @@ class RecipeModel(QAbstractListModel, SearchQueryParser):
recipe.last_downloaded = sr[sr.index(recipe)].last_downloaded
self.recipes.sort()
self.num_of_recipes = len(self.recipes)
self.category_map = {}
for r in self.recipes:
category = getattr(r, 'language', _('Unknown'))
if not r.builtin:
category = _('Custom')
if r.schedule is not None:
category = _('Scheduled')
if category not in self.category_map.keys():
self.category_map[category] = []
self.category_map[category].append(r)
self.categories = sorted(self.category_map.keys(), cmp=self.sort_categories)
self._map = dict(self.category_map)
def sort_categories(self, x, y):
def decorate(x):
if x == _('Scheduled'):
x = '0' + x
elif x == _('Custom'):
x = '1' + x
else:
x = '2' + x
return x
return cmp(decorate(x), decorate(y))
def universal_set(self):
@ -129,36 +161,46 @@ class RecipeModel(QAbstractListModel, SearchQueryParser):
try:
results = self.parse(unicode(query))
except ParseException:
self._map = list(range(len(self.recipes)))
self._map = dict(self.category_map)
else:
self._map = []
for i, recipe in enumerate(self.recipes):
self._map = {}
for category in self.categories:
self._map[category] = []
for recipe in self.category_map[category]:
if recipe in results:
self._map.append(i)
self._map[category].append(recipe)
self.reset()
def resort(self):
self.recipes.sort()
self.reset()
def columnCount(self, *args):
return 1
def index(self, row, column, parent):
return self.createIndex(row, column, parent.row() if parent.isValid() else -1)
def rowCount(self, *args):
return len(self._map)
def parent(self, index):
if index.internalId() == -1:
return QModelIndex()
return self.createIndex(index.internalId(), 0, -1)
def columnCount(self, parent):
if not parent.isValid() or not parent.parent().isValid():
return 1
return 0
def rowCount(self, parent):
if not parent.isValid():
return len(self.categories)
if not parent.parent().isValid():
category = self.categories[parent.row()]
return len(self._map[category])
return 0
def data(self, index, role):
recipe = self.recipes[self._map[index.row()]]
if role == Qt.FontRole:
if recipe.schedule is not None:
font = QFont()
font.setBold(True)
return QVariant(font)
if not recipe.builtin:
font = QFont()
font.setItalic(True)
return QVariant(font)
elif role == Qt.DisplayRole:
if index.parent().isValid():
category = self.categories[index.parent().row()]
recipe = self._map[category][index.row()]
if role == Qt.DisplayRole:
return QVariant(recipe.title)
elif role == Qt.UserRole:
return recipe
@ -170,7 +212,13 @@ class RecipeModel(QAbstractListModel, SearchQueryParser):
elif QFile().exists(icon_path):
icon = QIcon(icon_path)
return QVariant(icon)
else:
category = self.categories[index.row()]
if role == Qt.DisplayRole:
num = len(self._map[category])
return QVariant(category + ' [%d]'%num)
elif role == Qt.FontRole:
return self.bold_font
return NONE
def update_recipe_schedule(self, recipe):
@ -241,8 +289,7 @@ class SchedulerDialog(QDialog, Ui_Dialog):
self._model = RecipeModel(db)
self.current_recipe = None
self.recipes.setModel(self._model)
self.connect(self.recipes, SIGNAL('activated(QModelIndex)'), self.show_recipe)
self.connect(self.recipes, SIGNAL('clicked(QModelIndex)'), self.show_recipe)
self.recipes.currentChanged = self.currentChanged
self.connect(self.username, SIGNAL('textEdited(QString)'), self.set_account_info)
self.connect(self.password, SIGNAL('textEdited(QString)'), self.set_account_info)
self.connect(self.schedule, SIGNAL('stateChanged(int)'), self.do_schedule)
@ -258,11 +305,15 @@ class SchedulerDialog(QDialog, Ui_Dialog):
self.connect(self.download, SIGNAL('clicked()'), self.download_now)
self.search.setFocus(Qt.OtherFocusReason)
self.old_news.setValue(gconf['oldest_news'])
self.rnumber.setText(_('%d recipes')%self._model.rowCount(None))
self.rnumber.setText(_('%d recipes')%self._model.num_of_recipes)
for day in (_('day'), _('Monday'), _('Tuesday'), _('Wednesday'),
_('Thursday'), _('Friday'), _('Saturday'), _('Sunday')):
self.day.addItem(day)
def currentChanged(self, current, previous):
if current.parent().isValid():
self.show_recipe(current)
def download_now(self):
recipe = self._model.data(self.recipes.currentIndex(), Qt.UserRole)
self.emit(SIGNAL('download_now(PyQt_PyObject)'), recipe)
@ -305,6 +356,7 @@ class SchedulerDialog(QDialog, Ui_Dialog):
hour, minute = t.hour(), t.minute()
recipe.schedule = encode_schedule(day_of_week, hour, minute)
else:
recipe.schedule = None
if recipe in recipes:
recipes.remove(recipe)
save_recipes(recipes)
@ -359,7 +411,6 @@ class SchedulerDialog(QDialog, Ui_Dialog):
else:
self.last_downloaded.setText(_('Last downloaded: never'))
class Scheduler(QObject):
INTERVAL = 1 # minutes
@ -434,7 +485,7 @@ class Scheduler(QObject):
day_matches = day > 6 or day == now.tm_wday
tnow = now.tm_hour*60 + now.tm_min
matches = day_matches and (hour*60+minute) < tnow
if matches and nowt.toordinal() < date.today().toordinal():
if matches and recipe.last_downloaded.toordinal() < date.today().toordinal():
needs_downloading.add(recipe)
self.debug('Needs downloading:', needs_downloading)
@ -466,7 +517,7 @@ class Scheduler(QObject):
recipe = self.recipes[self.recipes.index(recipe)]
now = datetime.utcnow()
d = now - recipe.last_downloaded
if recipe.schedule is not None:
if recipe.schedule is not None and recipe.schedule < 1e4:
interval = timedelta(days=recipe.schedule)
if abs(d - interval) < timedelta(hours=1):
recipe.last_downloaded += interval

View File

@ -24,8 +24,20 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
<widget class="QListView" name="recipes" >
<property name="alternatingRowColors" >
<widget class="QTreeView" name="recipes" >
<property name="showDropIndicator" stdset="0" >
<bool>false</bool>
</property>
<property name="iconSize" >
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="animated" >
<bool>true</bool>
</property>
<property name="headerHidden" >
<bool>true</bool>
</property>
</widget>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

View File

@ -575,6 +575,7 @@ class BooksModel(QAbstractTableModel):
if column == 'rating':
val = 0 if val < 0 else 5 if val > 5 else val
val *= 2
self.db.set_rating(id, val)
elif column == 'series':
pat = re.compile(r'\[(\d+)\]')
match = pat.search(val)

View File

@ -294,7 +294,7 @@ class Main(MainWindow, Ui_MainWindow):
self.stack.setCurrentIndex(0)
try:
db = LibraryDatabase2(self.library_path)
except OSError, err:
except Exception, err:
error_dialog(self, _('Bad database location'), unicode(err)).exec_()
dir = unicode(QFileDialog.getExistingDirectory(self,
_('Choose a location for your ebook library.'), os.path.expanduser('~')))
@ -1426,10 +1426,10 @@ class Main(MainWindow, Ui_MainWindow):
def donate(self, *args):
BUTTON = '''
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="1335186">
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="">
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1">
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="hosted_button_id" value="3029467" />
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="Donate to support calibre development" />
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" />
</form>
'''
MSG = _('is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development.')

View File

@ -355,6 +355,7 @@ class LibraryDatabase2(LibraryDatabase):
if isinstance(self.dbpath, unicode):
self.dbpath = self.dbpath.encode(filesystem_encoding)
self.connect()
self.is_case_sensitive = not os.path.exists(self.dbpath.replace('metadata.db', 'MeTAdAtA.dB'))
# Upgrade database
while True:
meth = getattr(self, 'upgrade_version_%d'%self.user_version, None)

View File

@ -102,7 +102,7 @@ Device Integration
What devices does |app| support?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At the moment |app| has full support for the SONY PRS 500/505/700, Cybook Gen 3 as well as the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
At the moment |app| has full support for the SONY PRS 500/505/700, Cybook Gen 3, Amazon Kindle as well as the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
I used |app| to transfer some books to my reader, and now the SONY software hangs every time I connect the reader?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -161,7 +161,7 @@ class WorkerMother(object):
self.executable = self.gui_executable = sys.executable
self.prefix = ''
if isfrozen:
fd = getattr(sys, 'frameworks_dir')
fd = os.path.realpath(getattr(sys, 'frameworks_dir'))
contents = os.path.dirname(fd)
self.gui_executable = os.path.join(contents, 'MacOS',
os.path.basename(sys.executable))

View File

@ -196,7 +196,7 @@ class Server(object):
def calculate_month_trend(self, days=31):
stats = self.get_slice(date.today()-timedelta(days=days-1), date.today())
fig = plt.figure(2, (8, 3), 96)#, facecolor, edgecolor, frameon, FigureClass)
fig = plt.figure(2, (12, 4), 96)#, facecolor, edgecolor, frameon, FigureClass)
ax = fig.add_subplot(111)
x = list(range(days-1, -1, -1))
y = stats.daily_totals
@ -205,6 +205,17 @@ class Server(object):
ax.set_ylabel('Income ($)')
ax.hlines([stats.daily_average], 0, days-1)
ax.set_xlim([0, days-1])
text = u'''\
Total: $%(total).2f
Daily average: $%(da).2f \u00b1 %(dd).2f
Average contribution: $%(ac).2f \u00b1 %(ad).2f
Donors per day: %(dpd).2f
'''%dict(total=stats.total, da=stats.daily_average,
dd=stats.daily_deviation, ac=stats.average,
ad=stats.average_deviation,
dpd=len(stats.totals)/float(stats.period.days),
)
text = ax.annotate(text, (0.6, 0.65), textcoords='axes fraction')
fig.savefig(self.MONTH_TRENDS)
def calculate_trend(self):
@ -223,7 +234,7 @@ class Server(object):
x = [m.min for m in _months]
y = [m.total for m in _months]
ml = mdates.MonthLocator() # every month
fig = plt.figure(1, (8, 3), 96)#, facecolor, edgecolor, frameon, FigureClass)
fig = plt.figure(1, (8, 4), 96)#, facecolor, edgecolor, frameon, FigureClass)
ax = fig.add_subplot(111)
ax.bar(x, y, align='center', width=20, color='g')
ax.xaxis.set_major_locator(ml)

View File

@ -25,13 +25,12 @@
<div>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" />
<input type="hidden" name="hosted_button_id" value="3029289" />
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="Donate to support calibre development" />
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" />
<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----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-----END PKCS7-----" />
</form>
</div>
<br />
</div>
<h2>Note</h2>
<div class="note">$note</div>

View File

@ -58,9 +58,9 @@ python setup.py build &amp;&amp; sudo python setup.py install
<div>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" />
<input type="hidden" name="hosted_button_id" value="3029289" />
<input type="image" src="https://www.paypal.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="Donate to support calibre development" />
<img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" />
<input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----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-----END PKCS7-----" />
</form>
</div>
</div>

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -17,7 +17,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

View File

@ -17,7 +17,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

View File

@ -13,7 +13,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

View File

@ -8,13 +8,13 @@ msgstr ""
"Project-Id-Version: calibre\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2009-01-27 01:54+0000\n"
"PO-Revision-Date: 2009-01-23 21:22+0000\n"
"Last-Translator: Molnár Gábor <csirkus@gmail.com>\n"
"PO-Revision-Date: 2009-02-04 20:39+0000\n"
"Last-Translator: Kovid Goyal <Unknown>\n"
"Language-Team: Hungarian <hu@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
@ -142,7 +142,7 @@ msgstr "Tömörített könyvek metaadatait is olvassa be"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:196
msgid "Read metadata from ebooks in RAR archives"
msgstr ""
msgstr "Metaadatok olvasása a RAR-ral tömörített könyvekből is"
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:207
#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:217
@ -274,7 +274,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:110
msgid "Control auto-detection of document structure."
msgstr ""
msgstr "Dokumentum-struktúra automatikus felismerése."
#: /home/kovid/work/calibre/src/calibre/ebooks/epub/__init__.py:112
msgid ""
@ -832,6 +832,9 @@ msgid ""
"FONT_DELTA pts. FONT_DELTA can be a fraction.If FONT_DELTA is negative, the "
"font size is decreased."
msgstr ""
"A betűméret nővelése 2*FONT_DELTA ponttal, és a sortávolság növelése "
"FONT_DELTA ponttal. A FONT_DELTA érték törtszám is lehet, valamint ha "
"negatív az értéke, a betűméret csökkenni fog."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:125
msgid ""
@ -1086,6 +1089,8 @@ msgstr "Szerző a fájl metaadataiban. Alapértelmezés: %default"
msgid ""
"Path to output file. By default a file is created in the current directory."
msgstr ""
"A kimeneti fájl útvonala. Alapértelmezés szerint az aktuális könyvtárba "
"kerül a fájl."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:299
msgid "Number of colors for grayscale image conversion. Default: %default"
@ -1134,6 +1139,8 @@ msgid ""
"Don't sort the files found in the comic alphabetically by name. Instead use "
"the order they were added to the comic."
msgstr ""
"A képregény csomagban talált fájlokat a képregényhez való hozzáadás "
"sorrendje alapján rendezze a névsorrend helyett."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/comic/convert_from.py:317
msgid ""
@ -1185,6 +1192,10 @@ msgid ""
" \n"
"%prog converts mybook.epub to mybook.lrf"
msgstr ""
"Használat: %prog [beállítások] konyv.epub\n"
" \n"
" \n"
"A %prog a konyv.epub fájlt lrf formátumba konvertálja."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/fb2/convert_from.py:23
msgid ""
@ -1205,7 +1216,7 @@ msgstr "A létrehozott HTML kiírása a kimenetre, majd kilépés."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/fb2/convert_from.py:30
msgid "Keep generated HTML files after completing conversion to LRF."
msgstr ""
msgstr "Ne törölje a konvertálásnál keletkező átmeneti HTML fájlokat."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/feeds/convert_from.py:20
msgid "Options to control the behavior of feeds2disk"
@ -1225,7 +1236,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:319
msgid "\tParsing HTML..."
msgstr ""
msgstr "\tHTML beolvasása..."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:342
msgid "\tBaen file detected. Re-parsing..."
@ -1241,71 +1252,79 @@ msgstr "Feldolgozás: %s"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:390
msgid "\tConverting to BBeB..."
msgstr ""
msgstr "\tKonvertálás BBeB formátumba..."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:536
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:549
msgid "Could not parse file: %s"
msgstr ""
msgstr "Nem tudtam feldolgozni a fájlt: %s"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:541
msgid "%s is an empty file"
msgstr ""
msgstr "A %s fájl üres!"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:561
msgid "Failed to parse link %s %s"
msgstr ""
msgstr "A link feldolgozása nem sikerült: %s %s"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:605
msgid "Cannot add link %s to TOC"
msgstr ""
msgstr "Nem tudtam a linket hozzáadni a tartalomjegyzékhez: %s"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:957
msgid "Unable to process image %s. Error: %s"
msgstr ""
msgstr "Hiba a \"%s\" kép feldolgozása közben: %s"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:995
msgid "Unable to process interlaced PNG %s"
msgstr ""
msgstr "Nem tudtam feldolgozni a PNG képet: %s"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1010
msgid ""
"Could not process image: %s\n"
"%s"
msgstr ""
"Nem tudtam feldolgozni a képet: %s\n"
"%s"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1763
msgid ""
"An error occurred while processing a table: %s. Ignoring table markup."
msgstr ""
"Hiba történt a táblázat feldolgozása közben: %s. A táblázat formázást "
"figyelmen kívül hagyom."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1765
msgid ""
"Bad table:\n"
"%s"
msgstr ""
"Hibás táblázat:\n"
"%s"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1787
msgid "Table has cell that is too large"
msgstr ""
msgstr "A táblázatban olyan cellák vannak, amelyek túl nagyok."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1817
msgid ""
"You have to save the website %s as an html file first and then run html2lrf "
"on it."
msgstr ""
"Mentsd le a %s weboldalt egy mappába, majd a html fájlon futtasd a html2lrf "
"konvertáló programot."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1860
msgid "Could not read cover image: %s"
msgstr ""
msgstr "Nem tudtam a borító képet olvasni: %s"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1863
msgid "Cannot read from: %s"
msgstr ""
msgstr "Hiba olvasás közben: %s"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1988
msgid "Failed to process opf file"
msgstr ""
msgstr "Hiba az opf fájl feldolgozása közben"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/html/convert_from.py:1994
msgid ""
@ -1317,6 +1336,13 @@ msgid ""
"to local files recursively. Thus, you can use it to \n"
"convert a whole tree of HTML files."
msgstr ""
"Használat: %prog [beállítások] konyv.html\n"
"\n"
"\n"
"A %prog a konyv.html fájlt LRF formátumba konvertálja. \n"
"A %prog minden linket rekurzívan követ ami helyi \n"
"html fájlokra hivatkozik, tehát lehetséges sok összefüggő \n"
"html fájl konvertálása is."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lit/convert_from.py:15
msgid ""
@ -1325,16 +1351,22 @@ msgid ""
"\n"
"%prog converts mybook.lit to mybook.lrf"
msgstr ""
"Használat: %prog [beállítások] konyv.lit\n"
"\n"
"\n"
"A %prog a konyv.lit fájlt LRF formátumba konvertálja"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:136
msgid ""
"%prog book.lrf\n"
"Convert an LRF file into an LRS (XML UTF-8 encoded) file"
msgstr ""
"%prog konyv.lrf\n"
"A prog a konyv.lrf fájlt LRS formátumba (UTF-8 kódolású XML) konvertálja."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:137
msgid "Output LRS file"
msgstr ""
msgstr "Kimeneti LRS fájl"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:139
msgid "Do not save embedded image and font files to disk"
@ -1342,42 +1374,44 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:158
msgid "Parsing LRF..."
msgstr ""
msgstr "LRF fájl beolvasása..."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:161
msgid "Creating XML..."
msgstr ""
msgstr "XML létrehozása..."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:163
msgid "LRS written to "
msgstr ""
msgstr "Az LRS fájl helye: "
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrs/convert_from.py:249
msgid "Could not read from thumbnail file:"
msgstr ""
msgstr "Nem tudtam az ikon fájlt olvasni:"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrs/convert_from.py:269
msgid ""
"%prog [options] file.lrs\n"
"Compile an LRS file into an LRF file."
msgstr ""
"%prog [beállítások] konyv.lrf\n"
"LRS fájlt lefordítása LRF formátumba."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrs/convert_from.py:270
msgid "Path to output file"
msgstr ""
msgstr "A célfájl elérési útja"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrs/convert_from.py:272
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/isbndb.py:115
msgid "Verbose processing"
msgstr ""
msgstr "Informatívabb üzenetek feldolgozásnál"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrs/convert_from.py:274
msgid "Convert LRS to LRS, useful for debugging."
msgstr ""
msgstr "LRS fájl konvertálása LRS formátumba (hibakeresésnél hasznos)."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:455
msgid "Invalid LRF file. Could not set metadata."
msgstr ""
msgstr "Érvénytelen LRF fájl. Nem tudtam a metadatokat beállítani."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:580
msgid ""
@ -1391,42 +1425,44 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:587
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:43
msgid "Set the book title"
msgstr ""
msgstr "A könyv címe"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:589
msgid "Set sort key for the title"
msgstr ""
msgstr "Rendezési kulcs a címhez"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:591
msgid "Set the author"
msgstr ""
msgstr "Szerző"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:593
msgid "Set sort key for the author"
msgstr ""
msgstr "Rendezési kulcs a szerzőhöz"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:595
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:47
msgid "The category this book belongs to. E.g.: History"
msgstr ""
msgstr "A kategória, amibe a könyv tartozik. Pl.: történelem"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:598
msgid "Path to a graphic that will be set as this files' thumbnail"
msgstr ""
msgstr "A fájlhoz használandó ikon elérési útja"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:601
msgid ""
"Path to a txt file containing the comment to be stored in the lrf file."
msgstr ""
"A txt fájl útvonala, amelynek tartalma az lrf fájlhoz lesz csatolva "
"megjegyzésként."
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:605
msgid "Extract thumbnail from LRF file"
msgstr ""
msgstr "Ikon kinyerése az LRF fájlból"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:606
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/mobi.py:182
msgid "Set the publisher"
msgstr ""
msgstr "Kiadó"
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/meta.py:607
msgid "Set the book classification"

View File

@ -15,7 +15,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

View File

@ -13,7 +13,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"X-Poedit-Country: RUSSIAN FEDERATION\n"
"X-Poedit-Language: Russian\n"

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

View File

@ -13,7 +13,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n"

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

View File

@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-01-30 19:20+0000\n"
"X-Launchpad-Export-Date: 2009-02-04 21:04+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,9 @@ class BasicNewsRecipe(object, LoggingInterface):
#: The author of this recipe
__author__ = __appname__
#: The language that the news is in
language = _('Unknown')
#: Maximum number of articles to download from each feed. This is primarily
#: useful for feeds that don't have article dates. For most feeds, you should
#: use :attr:`BasicNewsRecipe.oldest_article`

View File

@ -24,7 +24,9 @@ recipe_modules = ['recipe_' + r for r in (
'joelonsoftware', 'telepolis', 'common_dreams', 'nin', 'tomshardware_de',
'pagina12', 'infobae', 'ambito', 'elargentino', 'sueddeutsche', 'the_age',
'laprensa', 'amspec', 'freakonomics', 'criticadigital', 'elcronista',
'shacknews', 'teleread',
'shacknews', 'teleread', 'granma', 'juventudrebelde', 'juventudrebelde_english',
'la_tercera', 'el_mercurio_chile', 'la_cuarta', 'lanacion_chile', 'la_segunda',
'jb_online', 'estadao', 'o_globo', 'vijesti', 'elmundo', 'the_oz',
)]
import re, imp, inspect, time, os

View File

@ -18,6 +18,7 @@ class Ambito(BasicNewsRecipe):
no_stylesheets = True
use_embedded_content = False
encoding = 'iso--8859-1'
language = _('Spanish')
cover_url = 'http://www.ambito.com/img/logo_.jpg'
html2lrf_options = [

View File

@ -11,7 +11,8 @@ from calibre.web.feeds.news import BasicNewsRecipe
class TheAmericanSpectator(BasicNewsRecipe):
title = 'The American Spectator'
__author__ = 'Darko Miletic'
description = 'news from USA'
language = _('English')
description = 'News from USA'
oldest_article = 7
max_articles_per_feed = 100
no_stylesheets = True

View File

@ -8,6 +8,7 @@ class AssociatedPress(BasicNewsRecipe):
description = 'Global news'
__author__ = 'Kovid Goyal'
use_embedded_content = False
language = _('English')
max_articles_per_feed = 15
html2lrf_options = ['--force-page-break-before-tag="chapter"']

View File

@ -13,6 +13,7 @@ class ArsTechnica(BasicNewsRecipe):
title = 'Ars Technica'
description = 'The art of technology'
oldest_article = 7
language = _('English')
no_stylesheets = True
__author__ = 'Michael Warner'
max_articles_per_feed = 100

View File

@ -14,7 +14,7 @@ class TheAtlantic(BasicNewsRecipe):
__author__ = 'Kovid Goyal'
description = 'Current affairs and politics focussed on the US'
INDEX = 'http://www.theatlantic.com/doc/current'
language = _('English')
remove_tags_before = dict(name='div', id='storytop')
remove_tags = [dict(name='div', id=['seealso', 'storybottom', 'footer', 'ad_banner_top', 'sidebar'])]
no_stylesheets = True

View File

@ -6,12 +6,13 @@ __copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
b92.net
'''
import string,re
import re
from calibre.web.feeds.news import BasicNewsRecipe
class B92(BasicNewsRecipe):
title = u'B92'
__author__ = 'Darko Miletic'
language = _('Serbian')
description = 'Dnevne vesti iz Srbije i sveta'
oldest_article = 7
max_articles_per_feed = 100

View File

@ -15,6 +15,7 @@ class Barrons(BasicNewsRecipe):
title = 'Barron\'s'
max_articles_per_feed = 50
needs_subscription = True
language = _('English')
__author__ = 'Kovid Goyal'
description = 'Weekly publication for investors from the publisher of the Wall Street Journal'
timefmt = ' [%a, %b %d, %Y]'

View File

@ -13,6 +13,7 @@ class BBC(BasicNewsRecipe):
__author__ = 'Kovid Goyal'
description = 'Global news and current affairs from the British Broadcasting Corporation'
no_stylesheets = True
language = _('English')
remove_tags = [dict(name='div', attrs={'class':'footer'})]
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'

View File

@ -13,6 +13,7 @@ class BusinessWeek(BasicNewsRecipe):
title = 'Business Week'
description = 'Business News, Stock Market and Financial Advice'
__author__ = 'ChuckEggDotCom'
language = _('English')
oldest_article = 7
max_articles_per_feed = 10

View File

@ -8,6 +8,7 @@ class ChristianScienceMonitor(BasicNewsRecipe):
description = 'Providing context and clarity on national and international news, peoples and cultures'
max_articles_per_feed = 20
__author__ = 'Kovid Goyal'
language = _('English')
no_stylesheets = True
use_embedded_content = False

View File

@ -15,6 +15,7 @@ class Clarin(BasicNewsRecipe):
description = 'Noticias de Argentina y mundo'
oldest_article = 2
max_articles_per_feed = 100
language = _('Spanish')
use_embedded_content = False
no_stylesheets = True
cover_url = strftime('http://www.clarin.com/diario/%Y/%m/%d/portada.jpg')

View File

@ -12,6 +12,7 @@ class CNN(BasicNewsRecipe):
description = 'Global news'
timefmt = ' [%d %b %Y]'
__author__ = 'Kovid Goyal'
language = _('English')
no_stylesheets = True
use_embedded_content = False
oldest_article = 15

View File

@ -5,6 +5,7 @@ class CommonDreams(BasicNewsRecipe):
title = u'Common Dreams'
description = u'Progressive news and views'
__author__ = u'XanthanGum'
language = _('English')
oldest_article = 7
max_articles_per_feed = 100

View File

@ -14,6 +14,7 @@ class CriticaDigital(BasicNewsRecipe):
description = 'Noticias de Argentina'
oldest_article = 2
max_articles_per_feed = 100
language = _('Spanish')
no_stylesheets = True
use_embedded_content = False
encoding = 'cp1252'

View File

@ -6,6 +6,7 @@ class Cyberpresse(BasicNewsRecipe):
title = u'Cyberpresse'
__author__ = 'balok'
description = 'Canadian news in French'
language = _('French')
oldest_article = 7
max_articles_per_feed = 100
no_stylesheets = True

View File

@ -12,6 +12,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
class DailyTelegraph(BasicNewsRecipe):
title = u'Daily Telegraph'
__author__ = u'AprilHare'
language = _('English')
description = u'News from down under'
oldest_article = 2
max_articles_per_feed = 10

View File

@ -9,6 +9,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
class DeStandaard(BasicNewsRecipe):
title = u'De Standaard'
__author__ = u'Darko Miletic'
language = _('French')
description = u'News from Belgium'
oldest_article = 7
max_articles_per_feed = 100

View File

@ -14,6 +14,7 @@ class DiscoverMagazine(BasicNewsRecipe):
description = u'Science, Technology and the Future'
__author__ = 'Mike Diaz'
oldest_article = 33
language = _('English')
max_articles_per_feed = 20
feeds = [
(u'Technology', u'http://discovermagazine.com/topics/technology/rss.xml'),

View File

@ -14,6 +14,7 @@ from urllib2 import quote
class Economist(BasicNewsRecipe):
title = 'The Economist'
language = _('English')
__author__ = "Kovid Goyal"
description = 'Global news and current affairs from a European perspective'
oldest_article = 7.0

View File

@ -0,0 +1,48 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
emol.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class ElMercurio(BasicNewsRecipe):
title = 'El Mercurio online'
language = _('Spanish')
__author__ = 'Darko Miletic'
description = 'El sitio de noticias online de Chile'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'cp1252'
cover_url = 'http://www.emol.com/especiales/logo_emol/logo_emol.gif'
html2lrf_options = [
'--comment' , description
, '--category' , 'news, Chile'
, '--publisher' , title
]
keep_only_tags = [
dict(name='div', attrs={'class':'despliegue-txt_750px'})
,dict(name='div', attrs={'id':'div_cuerpo_participa'})
]
remove_tags = [
dict(name='div', attrs={'class':'contenedor_despliegue-col-left300'})
,dict(name='div', attrs={'id':['div_centro_dn_opc','div_cabezera','div_secciones','div_contenidos','div_pie','nav']})
]
feeds = [
(u'Noticias de ultima hora', u'http://www.emol.com/rss20/rss.asp?canal=0')
,(u'Nacional', u'http://www.emol.com/rss20/rss.asp?canal=1')
,(u'Mundo', u'http://www.emol.com/rss20/rss.asp?canal=2')
,(u'Deportes', u'http://www.emol.com/rss20/rss.asp?canal=4')
,(u'Magazine', u'http://www.emol.com/rss20/rss.asp?canal=6')
,(u'Tecnologia', u'http://www.emol.com/rss20/rss.asp?canal=5')
,(u'La Musica', u'http://www.emol.com/rss20/rss.asp?canal=7')
]

View File

@ -11,6 +11,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
class ElPais(BasicNewsRecipe):
title = u'EL PAIS'
language = _('Spanish')
oldest_article = 7
max_articles_per_feed = 100

View File

@ -12,6 +12,7 @@ class ElArgentino(BasicNewsRecipe):
title = 'ElArgentino.com'
__author__ = 'Darko Miletic'
description = 'Informacion Libre las 24 horas'
language = _('Spanish')
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True

View File

@ -13,6 +13,7 @@ class ElCronista(BasicNewsRecipe):
__author__ = 'Darko Miletic'
description = 'Noticias de Argentina'
oldest_article = 2
language = _('Spanish')
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False

View File

@ -0,0 +1,46 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
elmundo.es
'''
from calibre.web.feeds.news import BasicNewsRecipe
class ElMundo(BasicNewsRecipe):
title = 'El Mundo'
__author__ = 'Darko Miletic'
description = 'News from Spain'
language = _('Spanish')
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'iso8859_15'
cover_url = 'http://estaticos02.cache.el-mundo.net/papel/imagenes/v2.0/logoverde.gif'
html2lrf_options = [
'--comment' , description
, '--category' , 'news, Spain'
, '--publisher' , title
]
keep_only_tags = [dict(name='div', attrs={'class':'noticia'})]
remove_tags = [
dict(name='div', attrs={'class':['herramientas','publicidad_google','video','herramientasarriba','contenido_noticia_02']})
,dict(name='div', attrs={'id':'modulo_multimedia' })
,dict(name=['object','script','link', 'a'])
,dict(name='ul', attrs={'class':'herramientas'})
]
feeds = [
(u'Portada' , u'http://rss.elmundo.es/rss/descarga.htm?data2=4' )
,(u'Television' , u'http://rss.elmundo.es/rss/descarga.htm?data2=76')
,(u'Espana' , u'http://rss.elmundo.es/rss/descarga.htm?data2=8' )
,(u'Internacional' , u'http://rss.elmundo.es/rss/descarga.htm?data2=9' )
,(u'Cultura' , u'http://rss.elmundo.es/rss/descarga.htm?data2=6' )
,(u'Ciencia/Ecologia', u'http://rss.elmundo.es/rss/descarga.htm?data2=5' )
,(u'Comunicacion' , u'http://rss.elmundo.es/rss/descarga.htm?data2=26')
]

View File

@ -13,6 +13,7 @@ class Engadget(BasicNewsRecipe):
title = u'Engadget'
__author__ = 'Darko Miletic'
description = 'Tech news'
language = _('English')
oldest_article = 7
max_articles_per_feed = 100
no_stylesheets = True

View File

@ -14,6 +14,7 @@ class ESPN(BasicNewsRecipe):
title = 'ESPN'
description = 'Sports news'
__author__ = 'Kovid Goyal'
language = _('English')
needs_subscription = True
remove_tags = [dict(name='font', attrs={'class':'footer'}), dict(name='hr', noshade='noshade')]

View File

@ -0,0 +1,55 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
estadao.com.br
'''
from calibre.web.feeds.news import BasicNewsRecipe
class Estadao(BasicNewsRecipe):
title = 'O Estado de S. Paulo'
__author__ = 'Darko Miletic'
description = 'News from Brasil'
language = _('Spanish')
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
encoding = 'utf8'
cover_url = 'http://www.estadao.com.br/img/logo_estadao.png'
html2lrf_options = [
'--comment' , description
, '--category' , 'news, Brasil'
, '--publisher' , title
]
keep_only_tags = [dict(name='div', attrs={'id':'c1'})]
remove_tags = [
dict(name=['script','object','form','ul'])
,dict(name='div', attrs={'id':['votacao','estadaohoje']})
,dict(name='p', attrs={'id':'ctrl_texto'})
,dict(name='p', attrs={'class':'texto'})
]
feeds = [
(u'Manchetes Estadao', u'http://www.estadao.com.br/rss/manchetes.xml')
,(u'Ultimas noticias', u'http://www.estadao.com.br/rss/ultimas.xml')
,(u'Nacional', u'http://www.estadao.com.br/rss/nacional.xml')
,(u'Internacional', u'http://www.estadao.com.br/rss/internacional.xml')
,(u'Cidades', u'http://www.estadao.com.br/rss/cidades.xml')
,(u'Esportes', u'http://www.estadao.com.br/rss/esportes.xml')
,(u'Arte & Lazer', u'http://www.estadao.com.br/rss/arteelazer.xml')
,(u'Economia', u'http://www.estadao.com.br/rss/economia.xml')
,(u'Vida &', u'http://www.estadao.com.br/rss/vidae.xml')
]
def preprocess_html(self, soup):
ifr = soup.find('iframe')
if ifr:
ifr.extract()
return soup

View File

@ -11,8 +11,9 @@ class FazNet(BasicNewsRecipe):
title = 'FAZ NET'
__author__ = 'Kovid Goyal'
description = '"Frankfurter Allgemeine Zeitung'
description = 'Frankfurter Allgemeine Zeitung'
use_embedded_content = False
language = _('German')
max_articles_per_feed = 30
preprocess_regexps = [

View File

@ -13,6 +13,7 @@ class FinancialTimes(BasicNewsRecipe):
__author__ = 'Darko Miletic'
description = 'Financial world news'
oldest_article = 2
language = _('English')
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False

View File

@ -7,6 +7,7 @@ class Forbes(BasicNewsRecipe):
__author__ = 'Darko Miletic'
oldest_article = 30
max_articles_per_feed = 100
language = _('English')
no_stylesheets = True
html2lrf_options = ['--base-font-size', '10']

View File

@ -10,6 +10,7 @@ class Freakonomics(BasicNewsRecipe):
title = 'Freakonomics Blog'
description = 'The Hidden side of everything'
__author__ = 'Kovid Goyal'
language = _('English')
feeds = [('Blog', 'http://freakonomics.blogs.nytimes.com/feed/atom/')]

View File

@ -15,6 +15,7 @@ class FTheiseDe(BasicNewsRecipe):
__author__ = 'Oliver Niesner'
use_embedded_content = False
timefmt = ' [%d %b %Y]'
language = _('German')
max_articles_per_feed = 40
no_stylesheets = True

View File

@ -12,6 +12,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
class Fudzilla(BasicNewsRecipe):
title = u'Fudzilla'
__author__ = 'Darko Miletic'
language = _('English')
description = 'Tech news'
oldest_article = 7
max_articles_per_feed = 100

Some files were not shown because too many files have changed in this diff Show More