Sycn to pluginize

This commit is contained in:
John Schember 2009-05-09 07:45:16 -04:00
commit d69b1ffbdc
23 changed files with 407 additions and 338 deletions

View File

@ -59,10 +59,9 @@ class HTMLRenderer(object):
def render_html(path_to_html, width=590, height=750):
from PyQt4.QtWebKit import QWebPage
from PyQt4.Qt import QEventLoop, QPalette, Qt, SIGNAL, QUrl, QSize, \
QApplication
if QApplication.instance() is None:
QApplication([])
from PyQt4.Qt import QEventLoop, QPalette, Qt, SIGNAL, QUrl, QSize
from calibre.gui2 import is_ok_to_use_qt
if not is_ok_to_use_qt(): return None
path_to_html = os.path.abspath(path_to_html)
with CurrentDir(os.path.dirname(path_to_html)):
page = QWebPage()

View File

@ -80,8 +80,10 @@ class EPUBInput(InputFormatPlugin):
t.set('href', guide_cover)
t.set('title', 'Title Page')
from calibre.ebooks import render_html
open('calibre_raster_cover.jpg', 'wb').write(
render_html(guide_cover).data)
renderer = render_html(guide_cover)
if renderer is not None:
open('calibre_raster_cover.jpg', 'wb').write(
renderer.data)
def convert(self, stream, options, file_ext, log, accelerators):

View File

@ -290,14 +290,6 @@ class MobiReader(object):
self.replace_page_breaks()
self.cleanup_html()
if self.processed_html.startswith('<body'):
self.processed_html = '<html><head></head>'+self.processed_html+'</html>'
self.processed_html = \
re.compile('<head>', re.IGNORECASE).sub(
'\n<head>\n'
'\t<link type="text/css" href="styles.css" />\n',
self.processed_html)
self.log.debug('Parsing HTML...')
root = html.fromstring(self.processed_html)
if root.xpath('descendant::p/descendant::p'):
@ -305,7 +297,7 @@ class MobiReader(object):
self.log.warning('Markup contains unclosed <p> tags, parsing using',
'BeatifulSoup')
root = soupparser.fromstring(self.processed_html)
if root[0].tag != 'html':
if root.tag != 'html':
self.log.warn('File does not have opening <html> tag')
nroot = html.fromstring('<html><head></head><body></body></html>')
bod = nroot.find('body')
@ -314,6 +306,35 @@ class MobiReader(object):
bod.append(child)
root = nroot
htmls = list(root.xpath('//html'))
if len(htmls) > 1:
self.log.warn('Markup contains multiple <html> tags')
# Keep only the largest head and body
bodies, heads = root.xpath('//body'), root.xpath('//head')
def sz(x): return len(list(x.iter()))
def scmp(x, y): return cmp(sz(x), sz(y))
body = list(sorted(bodies, cmp=scmp))
head = list(sorted(heads, cmp=scmp))
for x in root: root.remove(x)
if head:
root.append(head[-1])
if body:
root.append(body[-1])
for x in root.xpath('//script'):
x.getparent().remove(x)
head = root.xpath('//head')
if head:
head = head[0]
else:
head = root.makeelement('head', {})
root.insert(0, head)
head.text = '\n\t'
link = head.makeelement('link', {'type':'text/css',
'href':'styles.css'})
head.insert(0, link)
link.tail = '\n\t'
self.upshift_markup(root)
guides = root.xpath('//guide')
guide = guides[0] if guides else None

View File

@ -24,6 +24,19 @@ def asfloat(value, default):
value = default
return float(value)
def dynamic_rescale_factor(node):
classes = node.get('class', '').split(' ')
classes = [x.replace('calibre_rescale_', '') for x in classes if
x.startswith('calibre_rescale_')]
if not classes: return None
factor = 1.0
for x in classes:
try:
factor *= float(x)/100.
except ValueError:
continue
return factor
class KeyMapper(object):
def __init__(self, sbase, dbase, dkey):
@ -202,11 +215,19 @@ class CSSFlattener(object):
if 'bgcolor' in node.attrib:
cssdict['background-color'] = node.attrib['bgcolor']
del node.attrib['bgcolor']
if not self.context.disable_font_rescaling and \
'font-size' in cssdict or tag == 'body':
fsize = self.fmap[style['font-size']]
cssdict['font-size'] = "%0.5fem" % (fsize / psize)
psize = fsize
if not self.context.disable_font_rescaling:
_sbase = self.sbase if self.sbase is not None else \
self.context.source.fbase
dyn_rescale = dynamic_rescale_factor(node)
if dyn_rescale is not None:
fsize = self.fmap[_sbase]
fsize *= dyn_rescale
cssdict['font-size'] = '%0.5fem'%(fsize/psize)
psize = fsize
elif 'font-size' in cssdict or tag == 'body':
fsize = self.fmap[style['font-size']]
cssdict['font-size'] = "%0.5fem" % (fsize / psize)
psize = fsize
if cssdict:
if self.lineh and self.fbase and tag != 'body':
self.clean_edges(cssdict, style, psize)

View File

@ -25,14 +25,16 @@ class Jacket(object):
<title>%(title)s</title>
</head>
<body>
<div style="text-align:center">
<h1>%(title)s</h1>
<h2>%(jacket)s</h2>
<div>%(series)s</div>
<div>%(tags)s</div>
</div>
<div style="margin-top:2em">
%(comments)s
<div class="calibre_rescale_100">
<div style="text-align:center">
<h1 class="calibre_rescale_180">%(title)s</h1>
<h2 class="calibre_rescale_140">%(jacket)s</h2>
<div class="calibre_rescale_100">%(series)s</div>
<div class="calibre_rescale_100">%(tags)s</div>
</div>
<div style="margin-top:2em" class="calibre_rescale_100">
%(comments)s
</div>
</div>
</body>
</html>

View File

@ -369,13 +369,13 @@ class FlowSplitter(object):
for path in (
'//*[re:match(name(), "h[1-6]", "i")]',
'/html/body/div',
'//pre',
'//hr',
'//p',
'//div',
'//br',
'//li',
'/h:html/h:body/h:div',
'//h:pre',
'//h:hr',
'//h:p',
'//h:div',
'//h:br',
'//h:li',
):
elems = root.xpath(path, namespaces=NAMESPACES)
elem = pick_elem(elems)

View File

@ -27,8 +27,6 @@ def _config():
help=_('Frequently used directories'))
c.add_opt('send_to_storage_card_by_default', default=False,
help=_('Send file to storage card instead of main memory by default'))
c.add_opt('save_to_disk_single_format', default='lrf',
help=_('The format to use when saving single files to disk'))
c.add_opt('confirm_delete', default=False,
help=_('Confirm before deleting'))
c.add_opt('toolbar_icon_size', default=QSize(48, 48),
@ -100,18 +98,23 @@ def available_width():
def extension(path):
return os.path.splitext(path)[1][1:].lower()
def warning_dialog(parent, title, msg, det_msg=''):
def warning_dialog(parent, title, msg, det_msg='', show=False):
d = QMessageBox(QMessageBox.Warning, 'WARNING: '+title, msg, QMessageBox.Ok,
parent)
d.setDetailedText(det_msg)
d.setIconPixmap(QPixmap(':/images/dialog_warning.svg'))
if show:
return d.exec_()
return d
def error_dialog(parent, title, msg, det_msg=''):
def error_dialog(parent, title, msg, det_msg='', show=False):
d = QMessageBox(QMessageBox.Critical, 'ERROR: '+title, msg, QMessageBox.Ok,
parent)
d.setDetailedText(det_msg)
d.setIconPixmap(QPixmap(':/images/dialog_error.svg'))
if show:
return d.exec_()
return d
def question_dialog(parent, title, msg, det_msg=''):
@ -121,13 +124,16 @@ def question_dialog(parent, title, msg, det_msg=''):
d.setIconPixmap(QPixmap(':/images/dialog_information.svg'))
return d
def info_dialog(parent, title, msg, det_msg=''):
def info_dialog(parent, title, msg, det_msg='', show=False):
d = QMessageBox(QMessageBox.Information, title, msg, QMessageBox.NoButton,
parent)
d.setDetailedText(det_msg)
d.setIconPixmap(QPixmap(':/images/dialog_information.svg'))
if show:
return d.exec_()
return d
def qstring_to_unicode(q):
return unicode(q)

View File

@ -530,8 +530,8 @@ class DeviceGUI(object):
])
error_dialog(self, _('Failed to email books'),
_('Failed to email the following books:'),
'%s'%errors,
show=True)
'%s'%errors
)
else:
self.status_bar.showMessage(_('Sent by email:') + ', '.join(good),
5000)

View File

@ -399,12 +399,15 @@ class ConfigDialog(QDialog, Ui_Dialog):
default_index = self.output_format.findText(prefs['output_format'])
self.output_format.setCurrentIndex(default_index if default_index != -1 else 0)
self.book_exts = sorted(BOOK_EXTENSIONS)
for ext in self.book_exts:
self.single_format.addItem(ext.upper(), QVariant(ext))
output_formats = sorted(available_output_formats())
output_formats.remove('oeb')
for f in output_formats:
self.output_format.addItem(f.upper())
default_index = \
self.output_format.findText(prefs['output_format'].upper())
self.output_format.setCurrentIndex(default_index if default_index != -1 else 0)
single_format = config['save_to_disk_single_format']
self.single_format.setCurrentIndex(self.book_exts.index(single_format))
self.cover_browse.setValue(config['cover_flow_queue_length'])
self.systray_notifications.setChecked(not config['disable_tray_notification'])
from calibre.translations.compiled import translations
@ -426,17 +429,17 @@ class ConfigDialog(QDialog, Ui_Dialog):
self.pdf_metadata.setChecked(prefs['read_file_metadata'])
added_html = False
for ext in self.book_exts:
exts = set([])
for ext in BOOK_EXTENSIONS:
ext = ext.lower()
ext = re.sub(r'(x{0,1})htm(l{0,1})', 'html', ext)
if ext == 'lrf' or is_supported('book.'+ext):
if ext == 'html' and added_html:
continue
self.viewer.addItem(ext.upper())
self.viewer.item(self.viewer.count()-1).setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable)
self.viewer.item(self.viewer.count()-1).setCheckState(Qt.Checked if ext.upper() in config['internally_viewed_formats'] else Qt.Unchecked)
added_html = ext == 'html'
exts.add(ext)
for ext in sorted(exts):
self.viewer.addItem(ext.upper())
self.viewer.item(self.viewer.count()-1).setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable)
self.viewer.item(self.viewer.count()-1).setCheckState(Qt.Checked if ext.upper() in config['internally_viewed_formats'] else Qt.Unchecked)
self.viewer.sortItems()
self.start.setEnabled(not getattr(self.server, 'is_running', False))
self.test.setEnabled(not self.start.isEnabled())
@ -767,8 +770,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
p = {0:'normal', 1:'high', 2:'low'}[self.priority.currentIndex()]
prefs['worker_process_priority'] = p
prefs['read_file_metadata'] = bool(self.pdf_metadata.isChecked())
prefs['output_format'] = self.output_format.currentText()
config['save_to_disk_single_format'] = self.book_exts[self.single_format.currentIndex()]
prefs['output_format'] = unicode(self.output_format.currentText()).upper()
config['cover_flow_queue_length'] = self.cover_browse.value()
prefs['language'] = str(self.language.itemData(self.language.currentIndex()).toString())
config['systray_icon'] = self.systray_icon.checkState() == Qt.Checked

View File

@ -147,19 +147,6 @@
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Format for &amp;single file save:</string>
</property>
<property name="buddy">
<cstring>single_format</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="single_format"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Default network &amp;timeout:</string>
@ -169,7 +156,7 @@
</property>
</widget>
</item>
<item row="2" column="1">
<item row="1" column="1">
<widget class="QSpinBox" name="timeout">
<property name="toolTip">
<string>Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information)</string>
@ -188,10 +175,10 @@
</property>
</widget>
</item>
<item row="3" column="1">
<item row="2" column="1">
<widget class="QComboBox" name="language"/>
</item>
<item row="3" column="0">
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Choose &amp;language (requires restart):</string>
@ -201,7 +188,7 @@
</property>
</widget>
</item>
<item row="4" column="1">
<item row="3" column="1">
<widget class="QComboBox" name="priority">
<item>
<property name="text">
@ -220,7 +207,7 @@
</item>
</widget>
</item>
<item row="4" column="0">
<item row="3" column="0">
<widget class="QLabel" name="priority_label">
<property name="text">
<string>Job &amp;priority:</string>

View File

@ -20,6 +20,7 @@ from calibre.utils.search_query_parser import SearchQueryParser
from calibre.utils.pyparsing import ParseException
from calibre.gui2 import NONE, error_dialog, config as gconf
from calibre.utils.config import DynamicConfig
from calibre.ptempfile import PersistentTemporaryFile
from calibre.gui2.dialogs.user_profiles import UserProfiles
config = DynamicConfig('scheduler')
@ -522,8 +523,12 @@ class Scheduler(QObject):
self.recipes.remove(recipe)
save_recipes(self.recipes)
return
pt = PersistentTemporaryFile('_builtin.recipe')
pt.write(script)
pt.close()
script = pt.name
except ValueError:
script = recipe.title
script = recipe.title + '.recipe'
self.debug('\tQueueing:', recipe)
self.main.download_scheduled_recipe(recipe, script, self.recipe_downloaded)
self.queue.add(recipe)

View File

@ -47,6 +47,19 @@ from calibre.library.database2 import LibraryDatabase2, CoverCache
from calibre.parallel import JobKilled
from calibre.gui2.dialogs.confirm_delete import confirm
class SaveMenu(QMenu):
def __init__(self, parent):
QMenu.__init__(self, _('Save single format to disk...'), parent)
for ext in sorted(BOOK_EXTENSIONS):
action = self.addAction(ext.upper())
setattr(self, 'do_'+ext, partial(self.do, ext))
self.connect(action, SIGNAL('triggered(bool)'),
getattr(self, 'do_'+ext))
def do(self, ext, *args):
self.emit(SIGNAL('save_fmt(PyQt_PyObject)'), ext)
class Main(MainWindow, Ui_MainWindow, DeviceGUI):
'The main GUI'
@ -201,8 +214,12 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.save_menu = QMenu()
self.save_menu.addAction(_('Save to disk'))
self.save_menu.addAction(_('Save to disk in a single directory'))
self.save_menu.addAction(_('Save only %s format to disk')%\
config.get('save_to_disk_single_format').upper())
self.save_menu.addAction(_('Save only %s format to disk')%
prefs['output_format'].upper())
self.save_sub_menu = SaveMenu(self)
self.save_menu.addMenu(self.save_sub_menu)
self.connect(self.save_sub_menu, SIGNAL('save_fmt(PyQt_PyObject)'),
self.save_specific_format_disk)
self.view_menu = QMenu()
self.view_menu.addAction(_('View'))
@ -856,10 +873,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
_('Failed to download metadata for the following:'),
details, self).exec_()
else:
err = _('<b>Failed to download metadata:')+\
'</b><br><pre>'+x.tb+'</pre>'
error_dialog(self, _('Error'), err,
show=True)
err = _('Failed to download metadata:')
error_dialog(self, _('Error'), err, det_msg=x.tb).exec_()
@ -912,7 +927,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
############################## Save to disk ################################
def save_single_format_to_disk(self, checked):
self.save_to_disk(checked, True, config['save_to_disk_single_format'])
self.save_to_disk(checked, True, prefs['output_format'])
def save_specific_format_disk(self, fmt):
self.save_to_disk(False, True, fmt)
def save_to_single_dir(self, checked):
self.save_to_disk(checked, True)
@ -921,10 +939,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
rows = self.current_view().selectionModel().selectedRows()
if not rows or len(rows) == 0:
d = error_dialog(self, _('Cannot save to disk'),
_('No books selected'))
d.exec_()
return
return error_dialog(self, _('Cannot save to disk'),
_('No books selected'), show=True)
progress = ProgressDialog(_('Saving to disk...'), min=0, max=len(rows),
parent=self)
@ -1266,8 +1282,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
config['show_text_in_toolbar'] else \
Qt.ToolButtonIconOnly)
self.save_menu.actions()[2].setText(
_('Save only %s format to disk')%config.get(
'save_to_disk_single_format').upper())
_('Save only %s format to disk')%
prefs['output_format'].upper())
if self.library_path != d.database_location:
try:
newloc = d.database_location
@ -1414,8 +1430,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
return
if isinstance(job.exception, JobKilled):
return
error_dialog(self, _('Conversion Error'), job.gui_text(),
show=True)
error_dialog(self, _('Conversion Error'),
_('Failed to process')+': '+unicode(job.description),
det_msg=job.console_text()).exec_()
def initialize_database(self):

View File

@ -42,7 +42,7 @@
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
<height>75</height>
</size>
</property>
<property name="verticalScrollBarPolicy">

View File

@ -7,6 +7,7 @@ from PyQt4.Qt import QMainWindow, QString, Qt, QFont, QCoreApplication, SIGNAL,\
QAction, QMenu, QMenuBar, QIcon
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
from calibre.utils.config import OptionParser
from calibre.gui2 import error_dialog
def option_parser(usage='''\
Usage: %prog [options]
@ -79,9 +80,8 @@ class MainWindow(QMainWindow):
traceback.print_exception(type, value, tb, file=sio)
fe = sio.getvalue()
print >>sys.stderr, fe
msg = '<p><b>' + unicode(str(value), 'utf8', 'replace') + '</b></p>'
msg += '<p>Detailed <b>traceback</b>:<pre>'+fe+'</pre>'
d = ConversionErrorDialog(self, _('ERROR: Unhandled exception'), msg)
d.exec_()
msg = unicode(str(value), 'utf8', 'replace')
error_dialog(self, _('ERROR: Unhandled exception'), msg, det_msg=fe,
show=True)
except:
pass

View File

@ -7,16 +7,16 @@ __docformat__ = 'restructuredtext en'
Logic for setting up conversion jobs
'''
import cPickle, os
import cPickle
from PyQt4.Qt import QDialog
from calibre.ptempfile import PersistentTemporaryFile
from calibre.gui2 import warning_dialog
from calibre.gui2.convert import load_specifics
from calibre.gui2.convert.single import NoSupportedInputFormats
from calibre.gui2.convert.single import Config as SingleConfig
from calibre.gui2.convert.bulk import BulkConfig
from calibre.utils.config import prefs
def convert_single_ebook(parent, db, book_ids, auto_conversion=False, out_format=None):
changed = False
@ -126,33 +126,18 @@ def convert_bulk_ebook(parent, db, book_ids, out_format=None):
return jobs, changed, bad
def _fetch_news(data, fmt):
pt = PersistentTemporaryFile(suffix='_feeds2%s.%s'%(fmt.lower(), fmt.lower()))
pt.close()
args = ['feeds2%s'%fmt.lower(), '--output', pt.name, '--debug']
if data['username']:
args.extend(['--username', data['username']])
if data['password']:
args.extend(['--password', data['password']])
args.append(data['script'] if data['script'] else data['title'])
return 'fconvert_bulk_ebookseeds2'+fmt.lower(), [args], _('Fetch news from ')+data['title'], fmt.upper(), [pt]
def fetch_scheduled_recipe(recipe, script):
from calibre.gui2.dialogs.scheduler import config
fmt = prefs['output_format'].lower()
pt = PersistentTemporaryFile(suffix='_feeds2%s.%s'%(fmt.lower(), fmt.lower()))
pt = PersistentTemporaryFile(suffix='_recipe_out.%s'%fmt.lower())
pt.close()
args = ['feeds2%s'%fmt.lower(), '--output', pt.name, '--debug']
args = ['ebook-convert', script, pt.name, '-vv']
if recipe.needs_subscription:
x = config.get('recipe_account_info_%s'%recipe.id, False)
if not x:
raise ValueError(_('You must set a username and password for %s')%recipe.title)
args.extend(['--username', x[0], '--password', x[1]])
args.append(script)
return 'feeds2'+fmt, [args], _('Fetch news from ')+recipe.title, fmt.upper(), [pt]
def fetch_news(data):
fmt = prefs['output_format'].lower()
return _fetch_news(data, fmt)
return 'ebook-convert', [args], _('Fetch news from ')+recipe.title, fmt.upper(), [pt]

View File

@ -19,7 +19,6 @@ from calibre.gui2 import Application, ORG_NAME, APP_UID, choose_files, \
info_dialog, error_dialog
from calibre.ebooks.oeb.iterator import EbookIterator
from calibre.ebooks import DRMError
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
from calibre.constants import islinux
from calibre.utils.config import Config, StringConfig
from calibre.gui2.library import SearchBox
@ -543,8 +542,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
if isinstance(worker.exception, DRMError):
error_dialog(self, _('DRM Error'), _('<p>This book is protected by <a href="%s">DRM</a>')%'http://wiki.mobileread.com/wiki/DRM').exec_()
else:
ConversionErrorDialog(self, _('Could not open ebook'),
_('<b>%s</b><br/><p>%s</p>')%(worker.exception, worker.traceback.replace('\n', '<br>')), show=True)
error_dialog(self, _('Could not open ebook'),
unicode(worker.exception), det_msg=worker.traceback, show=True)
self.close_progress_indicator()
else:
self.metadata.show_opf(self.iterator.opf)

View File

@ -18,9 +18,13 @@ class RecipeInput(InputFormatPlugin):
file_types = set(['recipe'])
recommendations = set([
('chapter_mark', 'none', OptionRecommendation.HIGH),
('chapter', None, OptionRecommendation.HIGH),
('dont_split_on_page_breaks', True, OptionRecommendation.HIGH),
('use_auto_toc', False, OptionRecommendation.HIGH),
('input_encoding', None, OptionRecommendation.HIGH),
('input_profile', 'default', OptionRecommendation.HIGH),
('page_breaks_before', None, OptionRecommendation.HIGH),
('insert_metadata', False, OptionRecommendation.HIGH),
])
options = set([

View File

@ -231,23 +231,23 @@ class BasicNewsRecipe(Recipe):
#: use :member:`extra_css` in your recipe to customize look and feel.
template_css = u'''
.article_date {
font-size: x-small; color: gray; font-family: monospace;
color: gray; font-family: monospace;
}
.article_description {
font-size: small; font-family: sans; text-indent: 0pt;
font-family: sans; text-indent: 0pt;
}
a.article {
font-weight: bold; font-size: large;
font-weight: bold;
}
a.feed {
font-weight: bold; font-size: large;
font-weight: bold;
}
.navbar {
font-family:monospace; font-size:8pt
font-family:monospace;
}
'''
@ -579,8 +579,9 @@ class BasicNewsRecipe(Recipe):
def feeds2index(self, feeds):
templ = templates.IndexTemplate()
css = self.template_css + '\n\n' +(self.extra_css if self.extra_css else '')
return templ.generate(self.title, self.timefmt, feeds,
extra_css=self.extra_css).render(doctype='xhtml')
extra_css=css).render(doctype='xhtml')
@classmethod
def description_limiter(cls, src):
@ -631,8 +632,9 @@ class BasicNewsRecipe(Recipe):
templ = templates.FeedTemplate()
css = self.template_css + '\n\n' +(self.extra_css if self.extra_css else '')
return templ.generate(feed, self.description_limiter,
extra_css=self.extra_css).render(doctype='xhtml')
extra_css=css).render(doctype='xhtml')
def _fetch_article(self, url, dir, f, a, num_of_feeds):

View File

@ -10,6 +10,7 @@ class DNAIndia(BasicNewsRecipe):
description = 'Mumbai news, India news, World news, breaking news'
__author__ = 'Kovid Goyal'
language = _('English')
encoding = 'cp1252'
feeds = [
('Top News', 'http://www.dnaindia.com/syndication/rss_topnews.xml'),

View File

@ -5,7 +5,6 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import re, string, time
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import BeautifulSoup
class Newsweek(BasicNewsRecipe):
@ -15,7 +14,12 @@ class Newsweek(BasicNewsRecipe):
no_stylesheets = True
language = _('English')
extra_css = '#content { font:serif 12pt; }\n.story {font:12pt}\n.HorizontalHeader {font:18pt}\n.deck {font:16pt}'
extra_css = '''
#content { font-size:normal; font-family: serif }
.story { font-size:normal }
.HorizontalHeader {font-size:xx-large}
.deck {font-size:x-large}
'''
keep_only_tags = [dict(name='div', id='content')]
remove_tags = [

View File

@ -38,7 +38,7 @@ class NavBarTemplate(Template):
</style>
</head>
<body>
<div class="navbar" style="text-align:${'center' if center else 'left'};">
<div class="navbar calibre_rescale_70" style="text-align:${'center' if center else 'left'};">
<hr py:if="bottom" />
<p py:if="bottom" style="text-align:left">
This article was downloaded by <b>${__appname__}</b> from <a href="${url}">${url}</a>
@ -99,15 +99,17 @@ class IndexTemplate(Template):
</style>
</head>
<body>
<h1 class="calibre_recipe_title">${title}</h1>
<p style="text-align:right">${date}</p>
<ul class="calibre_feed_list">
<py:for each="i, feed in enumerate(feeds)">
<li py:if="feed" id="feed_${str(i)}">
<a class="feed" href="${'feed_%d/index.html'%i}">${feed.title}</a>
</li>
</py:for>
</ul>
<div class="calibre_rescale_100">
<h1 class="calibre_recipe_title calibre_rescale_180">${title}</h1>
<p style="text-align:right">${date}</p>
<ul class="calibre_feed_list">
<py:for each="i, feed in enumerate(feeds)">
<li py:if="feed" id="feed_${str(i)}">
<a class="feed calibre_rescale_120" href="${'feed_%d/index.html'%i}">${feed.title}</a>
</li>
</py:for>
</ul>
</div>
</body>
</html>
''')
@ -143,29 +145,32 @@ class FeedTemplate(Template):
</style>
</head>
<body style="page-break-before:always">
<h2 class="calibre_feed_title">${feed.title}</h2>
<div class="calibre_rescale_100">
<h2 class="calibre_feed_title calibre_rescale_160">${feed.title}</h2>
<py:if test="getattr(feed, 'image', None)">
<div class="calibre_feed_image">
<img alt="${feed.image_alt}" src="${feed.image_url}" />
</div>
</py:if>
<div class="calibre_feed_description" py:if="getattr(feed, 'description', None)">
<div class="calibre_feed_description calibre_rescale_80" py:if="getattr(feed, 'description', None)">
${feed.description}<br />
</div>
<ul class="calibre_article_list">
<py:for each="i, article in enumerate(feed.articles)">
<li id="${'article_%d'%i}" py:if="getattr(article, 'downloaded', False)" style="padding-bottom:0.5em">
<a class="article" href="${article.url}">${article.title}</a>
<li id="${'article_%d'%i}" py:if="getattr(article, 'downloaded',
False)" style="padding-bottom:0.5em" class="calibre_rescale_100">
<a class="article calibre_rescale_120" href="${article.url}">${article.title}</a>
<span class="article_date">${article.localtime.strftime(" [%a, %d %b %H:%M]")}</span>
<div class="article_decription" py:if="article.summary">
<div class="article_decription calibre_rescale_70" py:if="article.summary">
${Markup(cutoff(article.text_summary))}
</div>
</li>
</py:for>
</ul>
<div class="navbar" style="text-align:center; font-family:monospace; font-size:8pt">
<div class="navbar calibre_rescale_70">
| <a href="../index.html">Up one level</a> |
</div>
</div>
</body>
</html>
''')

View File

@ -53,9 +53,15 @@ def save_soup(soup, target):
ns = BeautifulSoup('<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />')
nm = ns.find('meta')
metas = soup.findAll('meta', content=True)
added = False
for meta in metas:
if 'charset' in meta.get('content', '').lower():
meta.replaceWith(nm)
added = True
if not added:
head = soup.find('head')
if head is not None:
head.insert(0, nm)
selfdir = os.path.dirname(target)
@ -67,6 +73,7 @@ def save_soup(soup, target):
html = unicode(soup)
with open(target, 'wb') as f:
idx = html.find('hoping')
f.write(html.encode('utf-8'))
class response(str):