mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
GwR revisions for touchscreen recipes
This commit is contained in:
parent
10ffdf8c18
commit
3fee611fc8
@ -254,6 +254,7 @@ class iPadOutput(OutputProfile):
|
|||||||
comic_screen_size = (768, 1024)
|
comic_screen_size = (768, 1024)
|
||||||
dpi = 132.0
|
dpi = 132.0
|
||||||
supports_nested_toc = False
|
supports_nested_toc = False
|
||||||
|
touchscreen = True
|
||||||
|
|
||||||
class SonyReaderOutput(OutputProfile):
|
class SonyReaderOutput(OutputProfile):
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@ class BasicNewsRecipe(Recipe):
|
|||||||
#: will remove everythong from `<!--Article ends here-->` to `</body>`.
|
#: will remove everythong from `<!--Article ends here-->` to `</body>`.
|
||||||
preprocess_regexps = []
|
preprocess_regexps = []
|
||||||
|
|
||||||
#: The CSS that is used to styles the templates, i.e., the navigation bars and
|
#: The CSS that is used to style the templates, i.e., the navigation bars and
|
||||||
#: the Tables of Contents. Rather than overriding this variable, you should
|
#: the Tables of Contents. Rather than overriding this variable, you should
|
||||||
#: use :member:`extra_css` in your recipe to customize look and feel.
|
#: use :member:`extra_css` in your recipe to customize look and feel.
|
||||||
template_css = u'''
|
template_css = u'''
|
||||||
@ -517,6 +517,21 @@ class BasicNewsRecipe(Recipe):
|
|||||||
'''
|
'''
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def extract_author(self, soup):
|
||||||
|
'''
|
||||||
|
Parse downloaded articles for author, add to OEBBook object.
|
||||||
|
:param soup:
|
||||||
|
'''
|
||||||
|
return None
|
||||||
|
|
||||||
|
def extract_description(self, soup):
|
||||||
|
'''
|
||||||
|
Parse downloaded articles for description, add to OEBBook object.
|
||||||
|
:param soup:
|
||||||
|
'''
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def postprocess_book(self, oeb, opts, log):
|
def postprocess_book(self, oeb, opts, log):
|
||||||
'''
|
'''
|
||||||
Run any needed post processing on the parsed downloaded e-book.
|
Run any needed post processing on the parsed downloaded e-book.
|
||||||
@ -544,6 +559,8 @@ 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.output_profile = options.output_profile.name
|
||||||
|
self.touchscreen = getattr(options.output_profile,'touchscreen',False)
|
||||||
|
|
||||||
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 +614,7 @@ class BasicNewsRecipe(Recipe):
|
|||||||
if self.delay > 0:
|
if self.delay > 0:
|
||||||
self.simultaneous_downloads = 1
|
self.simultaneous_downloads = 1
|
||||||
|
|
||||||
self.navbar = templates.NavBarTemplate()
|
self.navbar = templates.TouchscreenNavBarTemplate() if self.touchscreen else templates.NavBarTemplate()
|
||||||
self.failed_downloads = []
|
self.failed_downloads = []
|
||||||
self.partial_failures = []
|
self.partial_failures = []
|
||||||
|
|
||||||
@ -674,7 +691,11 @@ class BasicNewsRecipe(Recipe):
|
|||||||
def feeds2index(self, feeds):
|
def feeds2index(self, feeds):
|
||||||
templ = templates.IndexTemplate()
|
templ = templates.IndexTemplate()
|
||||||
css = self.template_css + '\n\n' +(self.extra_css if self.extra_css else '')
|
css = self.template_css + '\n\n' +(self.extra_css if self.extra_css else '')
|
||||||
return templ.generate(self.title, self.timefmt, feeds,
|
timefmt = self.timefmt
|
||||||
|
if self.touchscreen:
|
||||||
|
templ = templates.TouchscreenIndexTemplate()
|
||||||
|
timefmt = '%A, %d %b %Y'
|
||||||
|
return templ.generate(self.title, "mastheadImage.jpg", timefmt, feeds,
|
||||||
extra_css=css).render(doctype='xhtml')
|
extra_css=css).render(doctype='xhtml')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -727,6 +748,44 @@ class BasicNewsRecipe(Recipe):
|
|||||||
|
|
||||||
templ = templates.FeedTemplate()
|
templ = templates.FeedTemplate()
|
||||||
css = self.template_css + '\n\n' +(self.extra_css if self.extra_css else '')
|
css = self.template_css + '\n\n' +(self.extra_css if self.extra_css else '')
|
||||||
|
|
||||||
|
if self.touchscreen:
|
||||||
|
touchscreen_css = u'''
|
||||||
|
.summary_headline {
|
||||||
|
font-size:large; font-weight:bold; margin-top:0px; margin-bottom:0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary_byline {
|
||||||
|
font-size:small; margin-top:0px; margin-bottom:0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary_text {
|
||||||
|
margin-top:0px; margin-bottom:0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feed {
|
||||||
|
font-family:sans-serif; font-weight:bold; font-size:larger;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calibre_navbar {
|
||||||
|
font-family:monospace;
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
border-color:gray;
|
||||||
|
border-style:solid;
|
||||||
|
border-width:thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.toc {
|
||||||
|
font-size:large;
|
||||||
|
}
|
||||||
|
td.article_count {
|
||||||
|
text-align:right;
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
templ = templates.TouchscreenFeedTemplate()
|
||||||
|
css = touchscreen_css + '\n\n' + (self.extra_css if self.extra_css else '')
|
||||||
return templ.generate(feed, self.description_limiter,
|
return templ.generate(feed, self.description_limiter,
|
||||||
extra_css=css).render(doctype='xhtml')
|
extra_css=css).render(doctype='xhtml')
|
||||||
|
|
||||||
@ -868,9 +927,41 @@ class BasicNewsRecipe(Recipe):
|
|||||||
|
|
||||||
#feeds.restore_duplicates()
|
#feeds.restore_duplicates()
|
||||||
|
|
||||||
|
# GwR Populate any missing author/description fields in feed
|
||||||
for f, feed in enumerate(feeds):
|
for f, feed in enumerate(feeds):
|
||||||
html = self.feed2index(feed)
|
|
||||||
feed_dir = os.path.join(self.output_dir, 'feed_%d'%f)
|
feed_dir = os.path.join(self.output_dir, 'feed_%d'%f)
|
||||||
|
for article in feed.articles:
|
||||||
|
if article.summary == '' or article.author == '':
|
||||||
|
file = os.path.join(self.output_dir,feed_dir, article.url)
|
||||||
|
if os.path.exists(file):
|
||||||
|
with open(file, 'rb') as fi:
|
||||||
|
src = fi.read().decode('utf-8')
|
||||||
|
soup = BeautifulSoup(src)
|
||||||
|
if article.author == '':
|
||||||
|
author = self.extract_author(soup)
|
||||||
|
if author and not isinstance(author, unicode):
|
||||||
|
author = author.decode('utf-8', 'replace')
|
||||||
|
article.author = author
|
||||||
|
|
||||||
|
if article.summary == '':
|
||||||
|
summary = article.summary = self.extract_description(soup)
|
||||||
|
if summary and not isinstance(summary, unicode):
|
||||||
|
summary = summary.decode('utf-8', 'replace')
|
||||||
|
if summary and '<' in summary:
|
||||||
|
try:
|
||||||
|
s = html.fragment_fromstring(summary, create_parent=True)
|
||||||
|
summary = html.tostring(s, method='text', encoding=unicode)
|
||||||
|
except:
|
||||||
|
print 'Failed to process article summary, deleting:'
|
||||||
|
print summary.encode('utf-8')
|
||||||
|
traceback.print_exc()
|
||||||
|
summary = u''
|
||||||
|
article.text_summary = summary
|
||||||
|
|
||||||
|
|
||||||
|
for f, feed in enumerate(feeds):
|
||||||
|
feed_dir = os.path.join(self.output_dir, 'feed_%d'%f)
|
||||||
|
html = self.feed2index(feed)
|
||||||
with open(os.path.join(feed_dir, 'index.html'), 'wb') as fi:
|
with open(os.path.join(feed_dir, 'index.html'), 'wb') as fi:
|
||||||
fi.write(html)
|
fi.write(html)
|
||||||
self.create_opf(feeds)
|
self.create_opf(feeds)
|
||||||
@ -949,13 +1040,47 @@ class BasicNewsRecipe(Recipe):
|
|||||||
Create a generic cover for recipes that dont have a cover
|
Create a generic cover for recipes that dont have a cover
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
from calibre.utils.magick_draw import create_cover_page, TextLine
|
try:
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
Image, ImageDraw, ImageFont
|
||||||
|
except ImportError:
|
||||||
|
import Image, ImageDraw, ImageFont
|
||||||
|
font_path = P('fonts/liberation/LiberationSerif-Bold.ttf')
|
||||||
title = self.title if isinstance(self.title, unicode) else \
|
title = self.title if isinstance(self.title, unicode) else \
|
||||||
self.title.decode(preferred_encoding, 'replace')
|
self.title.decode(preferred_encoding, 'replace')
|
||||||
date = strftime(self.timefmt)
|
date = strftime(self.timefmt)
|
||||||
lines = [TextLine(title, 44), TextLine(date, 32)]
|
app = '['+__appname__ +' '+__version__+']'
|
||||||
img_data = create_cover_page(lines, I('library.png'), output_format='jpg')
|
|
||||||
cover_file.write(img_data)
|
COVER_WIDTH, COVER_HEIGHT = 590, 750
|
||||||
|
img = Image.new('RGB', (COVER_WIDTH, COVER_HEIGHT), 'white')
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
# Title
|
||||||
|
font = ImageFont.truetype(font_path, 44)
|
||||||
|
width, height = draw.textsize(title, font=font)
|
||||||
|
left = max(int((COVER_WIDTH - width)/2.), 0)
|
||||||
|
top = 15
|
||||||
|
draw.text((left, top), title, fill=(0,0,0), font=font)
|
||||||
|
bottom = top + height
|
||||||
|
# Date
|
||||||
|
font = ImageFont.truetype(font_path, 32)
|
||||||
|
width, height = draw.textsize(date, font=font)
|
||||||
|
left = max(int((COVER_WIDTH - width)/2.), 0)
|
||||||
|
draw.text((left, bottom+15), date, fill=(0,0,0), font=font)
|
||||||
|
# Vanity
|
||||||
|
font = ImageFont.truetype(font_path, 28)
|
||||||
|
width, height = draw.textsize(app, font=font)
|
||||||
|
left = max(int((COVER_WIDTH - width)/2.), 0)
|
||||||
|
top = COVER_HEIGHT - height - 15
|
||||||
|
draw.text((left, top), app, fill=(0,0,0), font=font)
|
||||||
|
# Logo
|
||||||
|
logo = Image.open(I('library.png'), 'r')
|
||||||
|
width, height = logo.size
|
||||||
|
left = max(int((COVER_WIDTH - width)/2.), 0)
|
||||||
|
top = max(int((COVER_HEIGHT - height)/2.), 0)
|
||||||
|
img.paste(logo, (left, top))
|
||||||
|
img = img.convert('RGB').convert('P', palette=Image.ADAPTIVE)
|
||||||
|
|
||||||
|
img.convert('RGB').save(cover_file, 'JPEG')
|
||||||
cover_file.flush()
|
cover_file.flush()
|
||||||
except:
|
except:
|
||||||
self.log.exception('Failed to generate default cover')
|
self.log.exception('Failed to generate default cover')
|
||||||
@ -1048,16 +1173,21 @@ class BasicNewsRecipe(Recipe):
|
|||||||
pw.DestroyMagickWand(x)
|
pw.DestroyMagickWand(x)
|
||||||
|
|
||||||
def create_opf(self, feeds, dir=None):
|
def create_opf(self, feeds, dir=None):
|
||||||
|
|
||||||
if dir is None:
|
if dir is None:
|
||||||
dir = self.output_dir
|
dir = self.output_dir
|
||||||
mi = MetaInformation(self.short_title() + strftime(self.timefmt), [__appname__])
|
mi = MetaInformation(self.short_title() + strftime(self.timefmt), [__appname__])
|
||||||
mi.publisher = __appname__
|
|
||||||
mi.author_sort = __appname__
|
mi.author_sort = __appname__
|
||||||
|
if self.output_profile == 'iPad':
|
||||||
|
mi = MetaInformation(self.short_title(), [strftime('%A, %d %B %Y')])
|
||||||
|
mi.author_sort = strftime('%Y-%m-%d')
|
||||||
|
mi.publisher = __appname__
|
||||||
mi.publication_type = 'periodical:'+self.publication_type
|
mi.publication_type = 'periodical:'+self.publication_type
|
||||||
mi.timestamp = nowf()
|
mi.timestamp = nowf()
|
||||||
mi.comments = self.description
|
mi.comments = self.description
|
||||||
if not isinstance(mi.comments, unicode):
|
if not isinstance(mi.comments, unicode):
|
||||||
mi.comments = mi.comments.decode('utf-8', 'replace')
|
mi.comments = mi.comments.decode('utf-8', 'replace')
|
||||||
|
mi.tags = ['News']
|
||||||
mi.pubdate = nowf()
|
mi.pubdate = nowf()
|
||||||
opf_path = os.path.join(dir, 'index.opf')
|
opf_path = os.path.join(dir, 'index.opf')
|
||||||
ncx_path = os.path.join(dir, 'index.ncx')
|
ncx_path = os.path.join(dir, 'index.ncx')
|
||||||
@ -1100,7 +1230,7 @@ class BasicNewsRecipe(Recipe):
|
|||||||
|
|
||||||
entries = ['index.html']
|
entries = ['index.html']
|
||||||
toc = TOC(base_path=dir)
|
toc = TOC(base_path=dir)
|
||||||
self.play_order_counter = 0
|
self.play_order_counter = 1
|
||||||
self.play_order_map = {}
|
self.play_order_map = {}
|
||||||
|
|
||||||
def feed_index(num, parent):
|
def feed_index(num, parent):
|
||||||
@ -1212,6 +1342,7 @@ class BasicNewsRecipe(Recipe):
|
|||||||
Create a list of articles from the list of feeds returned by :meth:`BasicNewsRecipe.get_feeds`.
|
Create a list of articles from the list of feeds returned by :meth:`BasicNewsRecipe.get_feeds`.
|
||||||
Return a list of :class:`Feed` objects.
|
Return a list of :class:`Feed` objects.
|
||||||
'''
|
'''
|
||||||
|
print "\nweb.feeds.news:parse_feeds()\n"
|
||||||
feeds = self.get_feeds()
|
feeds = self.get_feeds()
|
||||||
parsed_feeds = []
|
parsed_feeds = []
|
||||||
for obj in feeds:
|
for obj in feeds:
|
||||||
|
@ -5,7 +5,8 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
|
|
||||||
from lxml import html, etree
|
from lxml import html, etree
|
||||||
from lxml.html.builder import HTML, HEAD, TITLE, STYLE, DIV, BODY, \
|
from lxml.html.builder import HTML, HEAD, TITLE, STYLE, DIV, BODY, \
|
||||||
STRONG, BR, H1, SPAN, A, HR, UL, LI, H2, IMG, P as PT
|
STRONG, BR, H1, SPAN, A, HR, UL, LI, H2, IMG, P as PT, \
|
||||||
|
TABLE, TD, TR
|
||||||
|
|
||||||
from calibre import preferred_encoding, strftime, isbytestring
|
from calibre import preferred_encoding, strftime, isbytestring
|
||||||
|
|
||||||
@ -89,12 +90,55 @@ class NavBarTemplate(Template):
|
|||||||
|
|
||||||
self.root = HTML(head, BODY(navbar))
|
self.root = HTML(head, BODY(navbar))
|
||||||
|
|
||||||
|
class TouchscreenNavBarTemplate(Template):
|
||||||
|
|
||||||
|
def _generate(self, bottom, feed, art, number_of_articles_in_feed,
|
||||||
|
two_levels, url, __appname__, prefix='', center=True,
|
||||||
|
extra_css=None, style=None):
|
||||||
|
head = HEAD(TITLE('navbar'))
|
||||||
|
if style:
|
||||||
|
head.append(STYLE(style, type='text/css'))
|
||||||
|
if extra_css:
|
||||||
|
head.append(STYLE(extra_css, type='text/css'))
|
||||||
|
|
||||||
|
if prefix and not prefix.endswith('/'):
|
||||||
|
prefix += '/'
|
||||||
|
align = 'center' if center else 'left'
|
||||||
|
navbar = DIV(CLASS('calibre_navbar', 'calibre_rescale_100',
|
||||||
|
style='text-align:'+align))
|
||||||
|
if bottom:
|
||||||
|
navbar.append(HR())
|
||||||
|
text = 'This article was downloaded by '
|
||||||
|
p = PT(text, STRONG(__appname__), A(url, href=url), style='text-align:left')
|
||||||
|
p[0].tail = ' from '
|
||||||
|
navbar.append(BR())
|
||||||
|
navbar.append(BR())
|
||||||
|
else:
|
||||||
|
next = 'feed_%d'%(feed+1) if art == number_of_articles_in_feed - 1 \
|
||||||
|
else 'article_%d'%(art+1)
|
||||||
|
up = '../..' if art == number_of_articles_in_feed - 1 else '..'
|
||||||
|
href = '%s%s/%s/index.html'%(prefix, up, next)
|
||||||
|
navbar.text = '| '
|
||||||
|
navbar.append(A('Next', href=href))
|
||||||
|
href = '%s../index.html#article_%d'%(prefix, art)
|
||||||
|
navbar.iterchildren(reversed=True).next().tail = ' | '
|
||||||
|
navbar.append(A('Section Menu', href=href))
|
||||||
|
href = '%s../../index.html#feed_%d'%(prefix, feed)
|
||||||
|
navbar.iterchildren(reversed=True).next().tail = ' | '
|
||||||
|
navbar.append(A('Main Menu', href=href))
|
||||||
|
if art > 0 and not bottom:
|
||||||
|
href = '%s../article_%d/index.html'%(prefix, art-1)
|
||||||
|
navbar.iterchildren(reversed=True).next().tail = ' | '
|
||||||
|
navbar.append(A('Previous', href=href))
|
||||||
|
navbar.iterchildren(reversed=True).next().tail = ' | '
|
||||||
|
if not bottom:
|
||||||
|
navbar.append(HR())
|
||||||
|
|
||||||
|
self.root = HTML(head, BODY(navbar))
|
||||||
|
|
||||||
class IndexTemplate(Template):
|
class IndexTemplate(Template):
|
||||||
|
|
||||||
def _generate(self, title, datefmt, feeds, extra_css=None, style=None):
|
def _generate(self, title, masthead, datefmt, feeds, extra_css=None, style=None):
|
||||||
if isinstance(datefmt, unicode):
|
if isinstance(datefmt, unicode):
|
||||||
datefmt = datefmt.encode(preferred_encoding)
|
datefmt = datefmt.encode(preferred_encoding)
|
||||||
date = strftime(datefmt)
|
date = strftime(datefmt)
|
||||||
@ -110,12 +154,40 @@ class IndexTemplate(Template):
|
|||||||
href='feed_%d/index.html'%i)), id='feed_%d'%i)
|
href='feed_%d/index.html'%i)), id='feed_%d'%i)
|
||||||
ul.append(li)
|
ul.append(li)
|
||||||
div = DIV(
|
div = DIV(
|
||||||
H1(title, CLASS('calibre_recipe_title', 'calibre_rescale_180')),
|
PT(IMG(src=masthead,alt="masthead"),style='text-align:center'),
|
||||||
PT(date, style='text-align:right'),
|
PT(date, style='text-align:right'),
|
||||||
ul,
|
ul,
|
||||||
CLASS('calibre_rescale_100'))
|
CLASS('calibre_rescale_100'))
|
||||||
self.root = HTML(head, BODY(div))
|
self.root = HTML(head, BODY(div))
|
||||||
|
|
||||||
|
class TouchscreenIndexTemplate(Template):
|
||||||
|
|
||||||
|
def _generate(self, title, masthead, datefmt, feeds, extra_css=None, style=None):
|
||||||
|
if isinstance(datefmt, unicode):
|
||||||
|
datefmt = datefmt.encode(preferred_encoding)
|
||||||
|
date = strftime(datefmt)
|
||||||
|
masthead_img = IMG(src=masthead,alt="masthead")
|
||||||
|
head = HEAD(TITLE(title))
|
||||||
|
if style:
|
||||||
|
head.append(STYLE(style, type='text/css'))
|
||||||
|
if extra_css:
|
||||||
|
head.append(STYLE(extra_css, type='text/css'))
|
||||||
|
|
||||||
|
toc = TABLE(CLASS('toc'),width="100%",border="0",cellpadding="3px")
|
||||||
|
for i, feed in enumerate(feeds):
|
||||||
|
if feed:
|
||||||
|
tr = TR()
|
||||||
|
tr.append(TD( CLASS('toc_item'), A(feed.title, href='feed_%d/index.html'%i)))
|
||||||
|
tr.append(TD( CLASS('article_count'),'%d' % len(feed.articles)))
|
||||||
|
toc.append(tr)
|
||||||
|
|
||||||
|
div = DIV(
|
||||||
|
PT(masthead_img,style='text-align:center'),
|
||||||
|
PT(date, style='text-align:center'),
|
||||||
|
toc,
|
||||||
|
CLASS('calibre_rescale_100'))
|
||||||
|
self.root = HTML(head, BODY(div))
|
||||||
|
|
||||||
class FeedTemplate(Template):
|
class FeedTemplate(Template):
|
||||||
|
|
||||||
def _generate(self, feed, cutoff, extra_css=None, style=None):
|
def _generate(self, feed, cutoff, extra_css=None, style=None):
|
||||||
@ -166,6 +238,56 @@ class FeedTemplate(Template):
|
|||||||
|
|
||||||
self.root = HTML(head, body)
|
self.root = HTML(head, body)
|
||||||
|
|
||||||
|
class TouchscreenFeedTemplate(Template):
|
||||||
|
|
||||||
|
def _generate(self, feed, cutoff, extra_css=None, style=None):
|
||||||
|
head = HEAD(TITLE(feed.title))
|
||||||
|
if style:
|
||||||
|
head.append(STYLE(style, type='text/css'))
|
||||||
|
if extra_css:
|
||||||
|
head.append(STYLE(extra_css, type='text/css'))
|
||||||
|
body = BODY(style='page-break-before:always')
|
||||||
|
div = DIV(
|
||||||
|
H2(feed.title,
|
||||||
|
CLASS('calibre_feed_title', 'calibre_rescale_160')),
|
||||||
|
CLASS('calibre_rescale_100')
|
||||||
|
)
|
||||||
|
body.append(div)
|
||||||
|
if getattr(feed, 'image', None):
|
||||||
|
div.append(DIV(IMG(
|
||||||
|
alt = feed.image_alt if feed.image_alt else '',
|
||||||
|
src = feed.image_url
|
||||||
|
),
|
||||||
|
CLASS('calibre_feed_image')))
|
||||||
|
if getattr(feed, 'description', None):
|
||||||
|
d = DIV(feed.description, CLASS('calibre_feed_description',
|
||||||
|
'calibre_rescale_80'))
|
||||||
|
d.append(BR())
|
||||||
|
div.append(d)
|
||||||
|
|
||||||
|
toc = TABLE(CLASS('toc'),width="100%",border="0",cellpadding="3px")
|
||||||
|
for i, article in enumerate(feed.articles):
|
||||||
|
if not getattr(article, 'downloaded', False):
|
||||||
|
continue
|
||||||
|
tr = TR()
|
||||||
|
td = TD(
|
||||||
|
A(article.title, CLASS('article calibre_rescale_100',
|
||||||
|
href=article.url))
|
||||||
|
)
|
||||||
|
if article.summary:
|
||||||
|
td.append(DIV(cutoff(article.text_summary),
|
||||||
|
CLASS('article_description', 'calibre_rescale_80')))
|
||||||
|
tr.append(td)
|
||||||
|
toc.append(tr)
|
||||||
|
div.append(toc)
|
||||||
|
|
||||||
|
navbar = DIV('| ', CLASS('calibre_navbar', 'calibre_rescale_100'),style='text-align:center')
|
||||||
|
link = A('Up one level', href="../index.html")
|
||||||
|
link.tail = ' |'
|
||||||
|
navbar.append(link)
|
||||||
|
div.append(navbar)
|
||||||
|
|
||||||
|
self.root = HTML(head, body)
|
||||||
|
|
||||||
class EmbeddedContent(Template):
|
class EmbeddedContent(Template):
|
||||||
|
|
||||||
|
@ -328,6 +328,9 @@ class RecursiveFetcher(object):
|
|||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
data = self.fetch_url(iurl)
|
data = self.fetch_url(iurl)
|
||||||
|
if data == 'GIF89a\x01':
|
||||||
|
# Skip empty GIF files
|
||||||
|
continue
|
||||||
except Exception:
|
except Exception:
|
||||||
self.log.exception('Could not fetch image %s'% iurl)
|
self.log.exception('Could not fetch image %s'% iurl)
|
||||||
continue
|
continue
|
||||||
|
Loading…
x
Reference in New Issue
Block a user