GwR bookmark tweaks - add 'reading in progress' symbol

This commit is contained in:
GRiker 2010-02-25 07:03:57 -07:00
commit 5b3a929011
13 changed files with 247 additions and 84 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

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

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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">&#x2713;</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">&#x2713;</font>' if self.generateForKindle else \ return '<font style="color:white">&#x2713;</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">&#x25b7;</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">&#x2713;</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 "&#9733;" if self.generateForKindle else "*" return "&#9733;" 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,14 +3552,21 @@ 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
# Add the book title # Add the book title
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
@ -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']:

View File

@ -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,55 +51,79 @@ 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
from calibre.web.feeds.recipes.collection import \ opts.output_profile.flow_size = 0
get_builtin_recipe_by_title if file_ext == 'downloaded_recipe':
if os.access(recipe_or_file, os.R_OK): from calibre.utils.zipfile import ZipFile
recipe = compile_recipe(open(recipe_or_file, 'rb').read()) 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: else:
title = getattr(opts, 'original_recipe_input_arg', recipe_or_file) if os.access(recipe_or_file, os.R_OK):
title = os.path.basename(title).rpartition('.')[0] self.recipe_source = open(recipe_or_file, 'rb').read()
raw = get_builtin_recipe_by_title(title, log=log, recipe = compile_recipe(self.recipe_source)
download_recipe=not opts.dont_download_recipe) else:
builtin = False from calibre.web.feeds.recipes.collection import \
try: get_builtin_recipe_by_title
recipe = compile_recipe(raw) title = getattr(opts, 'original_recipe_input_arg', recipe_or_file)
if recipe.requires_version > numeric_version: title = os.path.basename(title).rpartition('.')[0]
log.warn(
'Downloaded recipe needs calibre version at least: %s' % \
('.'.join(recipe.requires_version)))
builtin = True
except:
log.exception('Failed to compile downloaded recipe. Falling '
'back to builtin one')
builtin = True
if builtin:
raw = get_builtin_recipe_by_title(title, log=log, raw = get_builtin_recipe_by_title(title, log=log,
download_recipe=False) download_recipe=not opts.dont_download_recipe)
if raw is None: builtin = False
raise ValueError('Failed to find builtin recipe: '+title) try:
recipe = compile_recipe(raw) recipe = compile_recipe(raw)
self.recipe_source = raw
if recipe.requires_version > numeric_version:
log.warn(
'Downloaded recipe needs calibre version at least: %s' % \
('.'.join(recipe.requires_version)))
builtin = True
except:
log.exception('Failed to compile downloaded recipe. Falling '
'back to builtin one')
builtin = True
if builtin:
raw = get_builtin_recipe_by_title(title, log=log,
download_recipe=False)
if raw is None:
raise ValueError('Failed to find builtin recipe: '+title)
recipe = compile_recipe(raw)
self.recipe_source = raw
if recipe is None:
raise ValueError('%r is not a valid recipe file or builtin recipe' %
recipe_or_file)
ro = recipe(opts, log, self.report_progress)
disabled = getattr(ro, 'recipe_disabled', None)
if disabled is not None:
raise RecipeDisabled(disabled)
ro.download()
self.recipe_object = ro
if recipe is None:
raise ValueError('%r is not a valid recipe file or builtin recipe' %
recipe_or_file)
ro = recipe(opts, log, self.report_progress)
disabled = getattr(ro, 'recipe_disabled', None)
if disabled is not None:
raise RecipeDisabled(disabled)
ro.download()
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):
self.recipe_object.postprocess_book(oeb, opts, log) if self.recipe_object is not None:
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)

View File

@ -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,13 +1148,12 @@ 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, center=self.center_navbar)
center=self.center_navbar) elem = BeautifulSoup(templ.render(doctype='xhtml').decode('utf-8')).find('div')
elem = BeautifulSoup(templ.render(doctype='xhtml').decode('utf-8')).find('div') body.insert(len(body.contents), elem)
body.insert(len(body.contents), elem)
with open(last, 'wb') as fi: with open(last, 'wb') as fi:
fi.write(unicode(soup).encode('utf-8')) fi.write(unicode(soup).encode('utf-8'))

View File

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

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