mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-07 09:01:38 -04:00
GwR bookmark tweaks - add 'reading in progress' symbol
This commit is contained in:
commit
5b3a929011
BIN
resources/images/news/gamasutra_fa.png
Normal file
BIN
resources/images/news/gamasutra_fa.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 956 B |
BIN
resources/images/news/gamasutra_news.png
Normal file
BIN
resources/images/news/gamasutra_news.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 956 B |
56
resources/recipes/gamasutra_fa.recipe
Normal file
56
resources/recipes/gamasutra_fa.recipe
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
gamasutra.com
|
||||||
|
'''
|
||||||
|
|
||||||
|
import re
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Gamasutra(BasicNewsRecipe):
|
||||||
|
title = 'Gamasutra Featured articles'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'The Art and Business of Making Games'
|
||||||
|
publisher = 'Gamasutra'
|
||||||
|
category = 'news, games, IT'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 200
|
||||||
|
no_stylesheets = True
|
||||||
|
encoding = 'cp1252'
|
||||||
|
use_embedded_content = False
|
||||||
|
language = 'en'
|
||||||
|
remove_empty_feeds = True
|
||||||
|
masthead_url = 'http://www.gamasutra.com/images/gamasutra_logo.gif'
|
||||||
|
extra_css = ' body{font-family: Verdana,Arial,Helvetica,sans-serif } img{margin-bottom: 0.4em} .title{font-size: x-large; font-weight: bold} '
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
, 'linearize_tables' : True
|
||||||
|
}
|
||||||
|
preprocess_regexps = [
|
||||||
|
(re.compile(r'<head>.*?<title>', re.DOTALL|re.IGNORECASE),lambda match: '<head><title>')
|
||||||
|
,(re.compile(r'</title>.*?</head>', re.DOTALL|re.IGNORECASE),lambda match: '</title></head>')
|
||||||
|
,(re.compile(r'</head>', re.DOTALL|re.IGNORECASE),lambda match: '</head><body>')
|
||||||
|
]
|
||||||
|
remove_tags = [
|
||||||
|
dict(name=['object','embed','iframe'])
|
||||||
|
,dict(attrs={'class':'adBox'})
|
||||||
|
]
|
||||||
|
remove_tags_before = dict(attrs={'class':'title'})
|
||||||
|
remove_attributes = ['width','height','name']
|
||||||
|
|
||||||
|
feeds = [(u'Feature Articles', u'http://feeds.feedburner.com/GamasutraFeatureArticles')]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url + '?print=1'
|
||||||
|
|
||||||
|
def get_article_url(self, article):
|
||||||
|
return article.get('guid', None)
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
for item in soup.findAll(style=True):
|
||||||
|
del item['style']
|
||||||
|
return self.adeify_images(soup)
|
45
resources/recipes/gamasutra_news.recipe
Normal file
45
resources/recipes/gamasutra_news.recipe
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
gamasutra.com
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Gamasutra(BasicNewsRecipe):
|
||||||
|
title = 'Gamasutra News'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'The Art and Business of Making Games'
|
||||||
|
publisher = 'Gamasutra'
|
||||||
|
category = 'news, games, IT'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 200
|
||||||
|
no_stylesheets = True
|
||||||
|
encoding = 'cp1252'
|
||||||
|
use_embedded_content = False
|
||||||
|
language = 'en'
|
||||||
|
remove_empty_feeds = True
|
||||||
|
masthead_url = 'http://www.gamasutra.com/images/gamasutra_logo.gif'
|
||||||
|
extra_css = ' body{font-family: Verdana,Arial,Helvetica,sans-serif } img{margin-bottom: 0.4em} .newsTitle{font-size: xx-large; font-weight: bold} '
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
, 'linearize_tables' : True
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_tags = [dict(attrs={'class':['relatedNews','adBox']})]
|
||||||
|
keep_only_tags = [dict(attrs={'class':['newsTitle','newsAuth','newsDate','newsText']})]
|
||||||
|
remove_attributes = ['width','height']
|
||||||
|
|
||||||
|
feeds = [(u'News', u'http://feeds.feedburner.com/GamasutraNews')]
|
||||||
|
|
||||||
|
def get_article_url(self, article):
|
||||||
|
return article.get('guid', None)
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
for item in soup.findAll(style=True):
|
||||||
|
del item['style']
|
||||||
|
return self.adeify_images(soup)
|
@ -214,8 +214,21 @@ class InputFormatPlugin(Plugin):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def postprocess_book(self, oeb, opts, log):
|
def postprocess_book(self, oeb, opts, log):
|
||||||
|
'''
|
||||||
|
Called to allow the input plugin to perform postprocessing after
|
||||||
|
the book has been parsed.
|
||||||
|
'''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def specialize(self, oeb, opts, log, output_fmt):
|
||||||
|
'''
|
||||||
|
Called to allow the input plugin to specialize the parsed book
|
||||||
|
for a particular output format. Called after postprocess_book
|
||||||
|
and before any transforms are performed on the parsed book.
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OutputFormatPlugin(Plugin):
|
class OutputFormatPlugin(Plugin):
|
||||||
'''
|
'''
|
||||||
OutputFormatPlugins are responsible for converting an OEB document
|
OutputFormatPlugins are responsible for converting an OEB document
|
||||||
|
@ -13,6 +13,7 @@ from calibre.customize.ui import input_profiles, output_profiles, \
|
|||||||
from calibre.ebooks.conversion.preprocess import HTMLPreProcessor
|
from calibre.ebooks.conversion.preprocess import HTMLPreProcessor
|
||||||
from calibre.ptempfile import PersistentTemporaryDirectory
|
from calibre.ptempfile import PersistentTemporaryDirectory
|
||||||
from calibre.utils.date import parse_date
|
from calibre.utils.date import parse_date
|
||||||
|
from calibre.utils.zipfile import ZipFile
|
||||||
from calibre import extract, walk
|
from calibre import extract, walk
|
||||||
|
|
||||||
DEBUG_README=u'''
|
DEBUG_README=u'''
|
||||||
@ -726,6 +727,12 @@ OptionRecommendation(name='timestamp',
|
|||||||
else:
|
else:
|
||||||
os.makedirs(out_dir)
|
os.makedirs(out_dir)
|
||||||
self.dump_oeb(ret, out_dir)
|
self.dump_oeb(ret, out_dir)
|
||||||
|
if self.input_fmt == 'recipe':
|
||||||
|
zf = ZipFile(os.path.join(self.opts.debug_pipeline,
|
||||||
|
'periodical.downloaded_recipe'), 'w')
|
||||||
|
zf.add_dir(out_dir)
|
||||||
|
self.input_plugin.save_download(zf)
|
||||||
|
zf.close()
|
||||||
|
|
||||||
self.log.info('Input debug saved to:', out_dir)
|
self.log.info('Input debug saved to:', out_dir)
|
||||||
|
|
||||||
@ -780,7 +787,7 @@ OptionRecommendation(name='timestamp',
|
|||||||
self.dump_input(self.oeb, tdir)
|
self.dump_input(self.oeb, tdir)
|
||||||
if self.abort_after_input_dump:
|
if self.abort_after_input_dump:
|
||||||
return
|
return
|
||||||
if self.input_fmt == 'recipe':
|
if self.input_fmt in ('recipe', 'downloaded_recipe'):
|
||||||
self.opts_to_mi(self.user_metadata)
|
self.opts_to_mi(self.user_metadata)
|
||||||
if not hasattr(self.oeb, 'manifest'):
|
if not hasattr(self.oeb, 'manifest'):
|
||||||
self.oeb = create_oebbook(self.log, self.oeb, self.opts,
|
self.oeb = create_oebbook(self.log, self.oeb, self.opts,
|
||||||
@ -793,6 +800,8 @@ OptionRecommendation(name='timestamp',
|
|||||||
out_dir = os.path.join(self.opts.debug_pipeline, 'parsed')
|
out_dir = os.path.join(self.opts.debug_pipeline, 'parsed')
|
||||||
self.dump_oeb(self.oeb, out_dir)
|
self.dump_oeb(self.oeb, out_dir)
|
||||||
self.log('Parsed HTML written to:', out_dir)
|
self.log('Parsed HTML written to:', out_dir)
|
||||||
|
self.input_plugin.specialize(self.oeb, self.opts, self.log,
|
||||||
|
self.output_fmt)
|
||||||
|
|
||||||
pr(0., _('Running transforms on ebook...'))
|
pr(0., _('Running transforms on ebook...'))
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2010, John Schember <john@nachtimwald.com>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import cStringIO
|
|
||||||
|
|
||||||
from calibre.ebooks.pdb.formatreader import FormatReader
|
from calibre.ebooks.pdb.formatreader import FormatReader
|
||||||
from calibre.ptempfile import TemporaryFile
|
from calibre.ptempfile import TemporaryFile
|
||||||
|
@ -72,8 +72,8 @@ class PML_HTMLizer(object):
|
|||||||
'ra': ('<span id="r%s"></span><a href="#%s">', '</a>'),
|
'ra': ('<span id="r%s"></span><a href="#%s">', '</a>'),
|
||||||
'c': ('<div style="text-align: center; margin: auto;">', '</div>'),
|
'c': ('<div style="text-align: center; margin: auto;">', '</div>'),
|
||||||
'r': ('<div style="text-align: right;">', '</div>'),
|
'r': ('<div style="text-align: right;">', '</div>'),
|
||||||
't': ('<div style="margin-left: 5%;">', '</div>'),
|
't': ('<div style="text-indent: 5%;">', '</div>'),
|
||||||
'T': ('<div style="margin-left: %s;">', '</div>'),
|
'T': ('<div style="text-indent: %s;">', '</div>'),
|
||||||
'i': ('<span style="font-style: italic;">', '</span>'),
|
'i': ('<span style="font-style: italic;">', '</span>'),
|
||||||
'u': ('<span style="text-decoration: underline;">', '</span>'),
|
'u': ('<span style="text-decoration: underline;">', '</span>'),
|
||||||
'd': ('<span style="text-decoration: line-through;">', '</span>'),
|
'd': ('<span style="text-decoration: line-through;">', '</span>'),
|
||||||
|
@ -866,18 +866,24 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def READ_SYMBOL(self):
|
|
||||||
def fget(self):
|
|
||||||
return '<font style="color:black">✓</font>' if self.generateForKindle else \
|
|
||||||
'<font style="color:black">%s</font>' % self.opts.read_tag
|
|
||||||
return property(fget=fget)
|
|
||||||
@dynamic_property
|
|
||||||
def NOT_READ_SYMBOL(self):
|
def NOT_READ_SYMBOL(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
return '<font style="color:white">✓</font>' if self.generateForKindle else \
|
return '<font style="color:white">✓</font>' if self.generateForKindle else \
|
||||||
'<font style="color:white">%s</font>' % self.opts.read_tag
|
'<font style="color:white">%s</font>' % self.opts.read_tag
|
||||||
return property(fget=fget)
|
return property(fget=fget)
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
|
def READING_SYMBOL(self):
|
||||||
|
def fget(self):
|
||||||
|
return '<font style="color:black">▷</font>' if self.generateForKindle else \
|
||||||
|
'<font style="color:white">%s</font>' % self.opts.read_tag
|
||||||
|
return property(fget=fget)
|
||||||
|
@dynamic_property
|
||||||
|
def READ_SYMBOL(self):
|
||||||
|
def fget(self):
|
||||||
|
return '<font style="color:black">✓</font>' if self.generateForKindle else \
|
||||||
|
'<font style="color:black">%s</font>' % self.opts.read_tag
|
||||||
|
return property(fget=fget)
|
||||||
|
@dynamic_property
|
||||||
def FULL_RATING_SYMBOL(self):
|
def FULL_RATING_SYMBOL(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
return "★" if self.generateForKindle else "*"
|
return "★" if self.generateForKindle else "*"
|
||||||
@ -1196,7 +1202,6 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
self.timestamp, = unpack('>I', data[0x24:0x28])
|
self.timestamp, = unpack('>I', data[0x24:0x28])
|
||||||
bpar_offset, = unpack('>I', data[0x4e:0x52])
|
bpar_offset, = unpack('>I', data[0x4e:0x52])
|
||||||
#print "bpar_offset: 0x%x" % bpar_offset
|
#print "bpar_offset: 0x%x" % bpar_offset
|
||||||
bpl = bpar_offset + 0x04
|
|
||||||
lrlo = bpar_offset + 0x0c
|
lrlo = bpar_offset + 0x0c
|
||||||
self.last_read_location = int(unpack('>I', data[lrlo:lrlo+4])[0])
|
self.last_read_location = int(unpack('>I', data[lrlo:lrlo+4])[0])
|
||||||
'''
|
'''
|
||||||
@ -1285,7 +1290,9 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
d.initialize(self.opts.connected_device['save_template'])
|
d.initialize(self.opts.connected_device['save_template'])
|
||||||
bookmarks = {}
|
bookmarks = {}
|
||||||
for book in self.booksByTitle:
|
for book in self.booksByTitle:
|
||||||
myMeta = MetaInformation(book['title'],
|
original_title = book['title'][book['title'].find(':') + 2:] if book['series'] \
|
||||||
|
else book['title']
|
||||||
|
myMeta = MetaInformation(original_title,
|
||||||
authors=book['authors'])
|
authors=book['authors'])
|
||||||
myMeta.author_sort = book['author_sort']
|
myMeta.author_sort = book['author_sort']
|
||||||
bm_found = False
|
bm_found = False
|
||||||
@ -1550,12 +1557,16 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# Prefix book with read/unread symbol
|
# book with read/reading/unread symbol
|
||||||
if book['read']:
|
if book['read']:
|
||||||
# check mark
|
# check mark
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag['class'] = "read_book"
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
elif book['id'] in self.bookmarked_books:
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||||
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
else:
|
else:
|
||||||
# hidden check mark
|
# hidden check mark
|
||||||
pBookTag['class'] = "unread_book"
|
pBookTag['class'] = "unread_book"
|
||||||
@ -1704,12 +1715,16 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# book with read/unread symbol
|
# book with read/reading/unread symbol
|
||||||
if book['read']:
|
if book['read']:
|
||||||
# check mark
|
# check mark
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag['class'] = "read_book"
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
elif book['id'] in self.bookmarked_books:
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||||
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
else:
|
else:
|
||||||
# hidden check mark
|
# hidden check mark
|
||||||
pBookTag['class'] = "unread_book"
|
pBookTag['class'] = "unread_book"
|
||||||
@ -1817,12 +1832,16 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# Prefix book with read/unread symbol
|
# book with read/reading/unread symbol
|
||||||
if new_entry['read']:
|
if new_entry['read']:
|
||||||
# check mark
|
# check mark
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag['class'] = "read_book"
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
elif new_entry['id'] in self.bookmarked_books:
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||||
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
else:
|
else:
|
||||||
# hidden check mark
|
# hidden check mark
|
||||||
pBookTag['class'] = "unread_book"
|
pBookTag['class'] = "unread_book"
|
||||||
@ -1859,12 +1878,16 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# Prefix book with read/unread symbol
|
# book with read/reading/unread symbol
|
||||||
if new_entry['read']:
|
if new_entry['read']:
|
||||||
# check mark
|
# check mark
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag['class'] = "read_book"
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
elif new_entry['id'] in self.bookmarked_books:
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||||
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
else:
|
else:
|
||||||
# hidden check mark
|
# hidden check mark
|
||||||
pBookTag['class'] = "unread_book"
|
pBookTag['class'] = "unread_book"
|
||||||
@ -3529,11 +3552,18 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# Prefix book with read/unread symbol
|
# book with read/reading/unread symbol
|
||||||
if book['read']:
|
if book['read']:
|
||||||
|
# check mark
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
|
elif book['id'] in self.bookmarked_books:
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||||
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
else:
|
else:
|
||||||
|
# hidden check mark
|
||||||
pBookTag['class'] = "unread_book"
|
pBookTag['class'] = "unread_book"
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||||
ptc += 1
|
ptc += 1
|
||||||
@ -4010,18 +4040,18 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
|
|
||||||
if opts.connected_device['name']:
|
if opts.connected_device['name']:
|
||||||
if opts.connected_device['serial']:
|
if opts.connected_device['serial']:
|
||||||
build_log.append(" connected_device: '%s' #%s%s " % \
|
build_log.append(u" connected_device: '%s' #%s%s " % \
|
||||||
(opts.connected_device['name'],
|
(opts.connected_device['name'],
|
||||||
opts.connected_device['serial'][0:4],
|
opts.connected_device['serial'][0:4],
|
||||||
'x' * (len(opts.connected_device['serial']) - 4)))
|
'x' * (len(opts.connected_device['serial']) - 4)))
|
||||||
for storage in opts.connected_device['storage']:
|
for storage in opts.connected_device['storage']:
|
||||||
if storage:
|
if storage:
|
||||||
build_log.append(" mount point: %s" % storage)
|
build_log.append(u" mount point: %s" % storage)
|
||||||
else:
|
else:
|
||||||
build_log.append(" connected_device: '%s'" % opts.connected_device['name'])
|
build_log.append(u" connected_device: '%s'" % opts.connected_device['name'])
|
||||||
for storage in opts.connected_device['storage']:
|
for storage in opts.connected_device['storage']:
|
||||||
if storage:
|
if storage:
|
||||||
build_log.append(" mount point: %s" % storage)
|
build_log.append(u" mount point: %s" % storage)
|
||||||
|
|
||||||
opts_dict = vars(opts)
|
opts_dict = vars(opts)
|
||||||
if opts_dict['ids']:
|
if opts_dict['ids']:
|
||||||
|
@ -19,7 +19,7 @@ class RecipeInput(InputFormatPlugin):
|
|||||||
name = 'Recipe Input'
|
name = 'Recipe Input'
|
||||||
author = 'Kovid Goyal'
|
author = 'Kovid Goyal'
|
||||||
description = _('Download periodical content from the internet')
|
description = _('Download periodical content from the internet')
|
||||||
file_types = set(['recipe'])
|
file_types = set(['recipe', 'downloaded_recipe'])
|
||||||
|
|
||||||
recommendations = set([
|
recommendations = set([
|
||||||
('chapter', None, OptionRecommendation.HIGH),
|
('chapter', None, OptionRecommendation.HIGH),
|
||||||
@ -51,11 +51,22 @@ class RecipeInput(InputFormatPlugin):
|
|||||||
def convert(self, recipe_or_file, opts, file_ext, log,
|
def convert(self, recipe_or_file, opts, file_ext, log,
|
||||||
accelerators):
|
accelerators):
|
||||||
from calibre.web.feeds.recipes import compile_recipe
|
from calibre.web.feeds.recipes import compile_recipe
|
||||||
|
opts.output_profile.flow_size = 0
|
||||||
|
if file_ext == 'downloaded_recipe':
|
||||||
|
from calibre.utils.zipfile import ZipFile
|
||||||
|
zf = ZipFile(recipe_or_file, 'r')
|
||||||
|
zf.extractall()
|
||||||
|
zf.close()
|
||||||
|
self.recipe_source = open('download.recipe', 'rb').read()
|
||||||
|
recipe = compile_recipe(self.recipe_source)
|
||||||
|
self.recipe_object = recipe(opts, log, self.report_progress)
|
||||||
|
else:
|
||||||
|
if os.access(recipe_or_file, os.R_OK):
|
||||||
|
self.recipe_source = open(recipe_or_file, 'rb').read()
|
||||||
|
recipe = compile_recipe(self.recipe_source)
|
||||||
|
else:
|
||||||
from calibre.web.feeds.recipes.collection import \
|
from calibre.web.feeds.recipes.collection import \
|
||||||
get_builtin_recipe_by_title
|
get_builtin_recipe_by_title
|
||||||
if os.access(recipe_or_file, os.R_OK):
|
|
||||||
recipe = compile_recipe(open(recipe_or_file, 'rb').read())
|
|
||||||
else:
|
|
||||||
title = getattr(opts, 'original_recipe_input_arg', recipe_or_file)
|
title = getattr(opts, 'original_recipe_input_arg', recipe_or_file)
|
||||||
title = os.path.basename(title).rpartition('.')[0]
|
title = os.path.basename(title).rpartition('.')[0]
|
||||||
raw = get_builtin_recipe_by_title(title, log=log,
|
raw = get_builtin_recipe_by_title(title, log=log,
|
||||||
@ -63,6 +74,7 @@ class RecipeInput(InputFormatPlugin):
|
|||||||
builtin = False
|
builtin = False
|
||||||
try:
|
try:
|
||||||
recipe = compile_recipe(raw)
|
recipe = compile_recipe(raw)
|
||||||
|
self.recipe_source = raw
|
||||||
if recipe.requires_version > numeric_version:
|
if recipe.requires_version > numeric_version:
|
||||||
log.warn(
|
log.warn(
|
||||||
'Downloaded recipe needs calibre version at least: %s' % \
|
'Downloaded recipe needs calibre version at least: %s' % \
|
||||||
@ -78,8 +90,7 @@ class RecipeInput(InputFormatPlugin):
|
|||||||
if raw is None:
|
if raw is None:
|
||||||
raise ValueError('Failed to find builtin recipe: '+title)
|
raise ValueError('Failed to find builtin recipe: '+title)
|
||||||
recipe = compile_recipe(raw)
|
recipe = compile_recipe(raw)
|
||||||
|
self.recipe_source = raw
|
||||||
|
|
||||||
|
|
||||||
if recipe is None:
|
if recipe is None:
|
||||||
raise ValueError('%r is not a valid recipe file or builtin recipe' %
|
raise ValueError('%r is not a valid recipe file or builtin recipe' %
|
||||||
@ -91,15 +102,28 @@ class RecipeInput(InputFormatPlugin):
|
|||||||
raise RecipeDisabled(disabled)
|
raise RecipeDisabled(disabled)
|
||||||
ro.download()
|
ro.download()
|
||||||
self.recipe_object = ro
|
self.recipe_object = ro
|
||||||
|
|
||||||
for key, val in recipe.conversion_options.items():
|
for key, val in recipe.conversion_options.items():
|
||||||
setattr(opts, key, val)
|
setattr(opts, key, val)
|
||||||
|
|
||||||
opts.output_profile.flow_size = 0
|
|
||||||
|
|
||||||
for f in os.listdir('.'):
|
for f in os.listdir('.'):
|
||||||
if f.endswith('.opf'):
|
if f.endswith('.opf'):
|
||||||
return os.path.abspath(f)
|
return os.path.abspath(f)
|
||||||
|
|
||||||
def postprocess_book(self, oeb, opts, log):
|
def postprocess_book(self, oeb, opts, log):
|
||||||
|
if self.recipe_object is not None:
|
||||||
self.recipe_object.postprocess_book(oeb, opts, log)
|
self.recipe_object.postprocess_book(oeb, opts, log)
|
||||||
|
|
||||||
|
def specialize(self, oeb, opts, log, output_fmt):
|
||||||
|
if opts.no_inline_navbars:
|
||||||
|
from calibre.ebooks.oeb.base import XPath
|
||||||
|
for item in oeb.spine:
|
||||||
|
for div in XPath('//h:div[contains(@class, "calibre_navbar")]')(item.data):
|
||||||
|
div.getparent().remove(div)
|
||||||
|
|
||||||
|
def save_download(self, zf):
|
||||||
|
raw = self.recipe_source
|
||||||
|
if isinstance(raw, unicode):
|
||||||
|
raw = raw.encode('utf-8')
|
||||||
|
zf.writestr('download.recipe', raw)
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ class BasicNewsRecipe(Recipe):
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar {
|
.calibre_navbar {
|
||||||
font-family:monospace;
|
font-family:monospace;
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
@ -525,7 +525,6 @@ class BasicNewsRecipe(Recipe):
|
|||||||
self.username = options.username
|
self.username = options.username
|
||||||
self.password = options.password
|
self.password = options.password
|
||||||
self.lrf = options.lrf
|
self.lrf = options.lrf
|
||||||
self.include_navbars = not options.no_inline_navbars
|
|
||||||
|
|
||||||
self.output_dir = os.path.abspath(self.output_dir)
|
self.output_dir = os.path.abspath(self.output_dir)
|
||||||
if options.test:
|
if options.test:
|
||||||
@ -597,7 +596,7 @@ class BasicNewsRecipe(Recipe):
|
|||||||
if first_fetch and job_info:
|
if first_fetch and job_info:
|
||||||
url, f, a, feed_len = job_info
|
url, f, a, feed_len = job_info
|
||||||
body = soup.find('body')
|
body = soup.find('body')
|
||||||
if body is not None and self.include_navbars:
|
if body is not None:
|
||||||
templ = self.navbar.generate(False, f, a, feed_len,
|
templ = self.navbar.generate(False, f, a, feed_len,
|
||||||
not self.has_single_feed,
|
not self.has_single_feed,
|
||||||
url, __appname__,
|
url, __appname__,
|
||||||
@ -1149,7 +1148,6 @@ class BasicNewsRecipe(Recipe):
|
|||||||
body = soup.find('body')
|
body = soup.find('body')
|
||||||
if body is not None:
|
if body is not None:
|
||||||
prefix = '/'.join('..'for i in range(2*len(re.findall(r'link\d+', last))))
|
prefix = '/'.join('..'for i in range(2*len(re.findall(r'link\d+', last))))
|
||||||
if self.include_navbars:
|
|
||||||
templ = self.navbar.generate(True, num, j, len(f),
|
templ = self.navbar.generate(True, num, j, len(f),
|
||||||
not self.has_single_feed,
|
not self.has_single_feed,
|
||||||
a.orig_url, __appname__, prefix=prefix,
|
a.orig_url, __appname__, prefix=prefix,
|
||||||
|
@ -38,7 +38,7 @@ class NavBarTemplate(Template):
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="navbar calibre_rescale_70" style="text-align:${'center' if center else 'left'};">
|
<div class="calibre_navbar calibre_rescale_70" style="text-align:${'center' if center else 'left'};">
|
||||||
<hr py:if="bottom" />
|
<hr py:if="bottom" />
|
||||||
<p py:if="bottom" style="text-align:left">
|
<p py:if="bottom" style="text-align:left">
|
||||||
This article was downloaded by <b>${__appname__}</b> from <a href="${url}">${url}</a>
|
This article was downloaded by <b>${__appname__}</b> from <a href="${url}">${url}</a>
|
||||||
@ -167,7 +167,7 @@ class FeedTemplate(Template):
|
|||||||
</li>
|
</li>
|
||||||
</py:for>
|
</py:for>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="navbar calibre_rescale_70">
|
<div class="calibre_navbar calibre_rescale_70">
|
||||||
| <a href="../index.html">Up one level</a> |
|
| <a href="../index.html">Up one level</a> |
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
11
todo
11
todo
@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
* Refactor web.fetch.simple to use per connection timeouts via the timeout kwarg for mechanize.open
|
|
||||||
|
|
||||||
* Testing framework
|
|
||||||
|
|
||||||
|
|
||||||
* Add a languages column to books. Best implementation is comma separated IANA codes
|
|
||||||
* Add a hash column to the formats table (used for stanza identifier)
|
|
||||||
|
|
||||||
* Fix blockquote handling in sphinx templates
|
|
||||||
* Fix ebook-viewer going to links
|
|
Loading…
x
Reference in New Issue
Block a user