Sync to trunk.

This commit is contained in:
John Schember 2012-03-20 21:09:17 -04:00
commit ae6a168551
8 changed files with 241 additions and 97 deletions

View File

@ -0,0 +1,43 @@
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2012, Darko Miletic <darko.miletic at gmail.com>'
'''
ivanamilakovic.blogspot.com
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class IvanaMilakovic(BasicNewsRecipe):
title = u'Ivana Milaković'
__author__ = 'Darko Miletic'
description = u'Hronika mačijeg škrabala - priče, inspiracija, knjige, pisanje, prevodi...'
oldest_article = 80
max_articles_per_feed = 100
language = 'sr'
encoding = 'utf-8'
no_stylesheets = True
use_embedded_content = True
publication_type = 'blog'
extra_css = """
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
body{font-family: Arial,Tahoma,Helvetica,FreeSans,sans1,sans-serif}
img{margin-bottom: 0.8em; border: 1px solid #333333; padding: 4px }
"""
conversion_options = {
'comment' : description
, 'tags' : 'knjige, blog, srbija, sf'
, 'publisher': 'Ivana Milakovic'
, 'language' : language
}
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
feeds = [(u'Posts', u'http://ivanamilakovic.blogspot.com/feeds/posts/default')]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
return self.adeify_images(soup)

42
recipes/klubknjige.recipe Normal file
View File

@ -0,0 +1,42 @@
__license__ = 'GPL v3'
__copyright__ = '2012, Darko Miletic <darko.miletic at gmail.com>'
'''
klub-knjige.blogspot.com
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class KlubKnjige(BasicNewsRecipe):
title = 'Klub knjige'
__author__ = 'Darko Miletic'
description = 'literarni blog'
oldest_article = 30
max_articles_per_feed = 100
language = 'sr'
encoding = 'utf-8'
no_stylesheets = True
use_embedded_content = True
publication_type = 'blog'
extra_css = """
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
body{font-family: Arial,Tahoma,Helvetica,FreeSans,sans1,sans-serif}
img{margin-bottom: 0.8em; border: 1px solid #333333; padding: 4px }
"""
conversion_options = {
'comment' : description
, 'tags' : 'knjige, blog, srbija, sf'
, 'publisher': 'Klub Knjige'
, 'language' : language
}
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
feeds = [(u'Posts', u'http://klub-knjige.blogspot.com/feeds/posts/default')]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
return self.adeify_images(soup)

View File

@ -3,7 +3,6 @@ __copyright__ = '2011'
'''
lemonde.fr
'''
import re
from calibre.web.feeds.recipes import BasicNewsRecipe
class LeMonde(BasicNewsRecipe):
@ -41,77 +40,8 @@ class LeMonde(BasicNewsRecipe):
remove_empty_feeds = True
filterDuplicates = True
auto_cleanup = True
def preprocess_html(self, soup):
for alink in soup.findAll('a'):
if alink.string is not None:
tstr = alink.string
alink.replaceWith(tstr)
return self.adeify_images(soup)
preprocess_regexps = [
(re.compile(r'([0-9])%'), lambda m: m.group(1) + '&nbsp;%'),
(re.compile(r'([0-9])([0-9])([0-9]) ([0-9])([0-9])([0-9])'), lambda m: m.group(1) + m.group(2) + m.group(3) + '&nbsp;' + m.group(4) + m.group(5) + m.group(6)),
(re.compile(r'([0-9]) ([0-9])([0-9])([0-9])'), lambda m: m.group(1) + '&nbsp;' + m.group(2) + m.group(3) + m.group(4)),
(re.compile(r'<span>'), lambda match: ' <span>'),
(re.compile(r'\("'), lambda match: '(&laquo;&nbsp;'),
(re.compile(r'"\)'), lambda match: '&nbsp;&raquo;)'),
(re.compile(r'&ldquo;'), lambda match: '(&laquo;&nbsp;'),
(re.compile(r'&rdquo;'), lambda match: '&nbsp;&raquo;)'),
(re.compile(r'>\''), lambda match: '>&lsquo;'),
(re.compile(r' \''), lambda match: ' &lsquo;'),
(re.compile(r'\''), lambda match: '&rsquo;'),
(re.compile(r'"<em>'), lambda match: '<em>&laquo;&nbsp;'),
(re.compile(r'"<em>"</em><em>'), lambda match: '<em>&laquo;&nbsp;'),
(re.compile(r'"<a href='), lambda match: '&laquo;&nbsp;<a href='),
(re.compile(r'</em>"'), lambda match: '&nbsp;&raquo;</em>'),
(re.compile(r'</a>"'), lambda match: '&nbsp;&raquo;</a>'),
(re.compile(r'"</'), lambda match: '&nbsp;&raquo;</'),
(re.compile(r'>"'), lambda match: '>&laquo;&nbsp;'),
(re.compile(r'"<'), lambda match: '&nbsp;&raquo;<'),
(re.compile(r'&rsquo;"'), lambda match: '&rsquo;«&nbsp;'),
(re.compile(r' "'), lambda match: ' &laquo;&nbsp;'),
(re.compile(r'" '), lambda match: '&nbsp;&raquo; '),
(re.compile(r'"\.'), lambda match: '&nbsp;&raquo;.'),
(re.compile(r'",'), lambda match: '&nbsp;&raquo;,'),
(re.compile(r'"\?'), lambda match: '&nbsp;&raquo;?'),
(re.compile(r'":'), lambda match: '&nbsp;&raquo;:'),
(re.compile(r'";'), lambda match: '&nbsp;&raquo;;'),
(re.compile(r'"\!'), lambda match: '&nbsp;&raquo;!'),
(re.compile(r' :'), lambda match: '&nbsp;:'),
(re.compile(r' ;'), lambda match: '&nbsp;;'),
(re.compile(r' \?'), lambda match: '&nbsp;?'),
(re.compile(r' \!'), lambda match: '&nbsp;!'),
(re.compile(r'\s»'), lambda match: '&nbsp;»'),
(re.compile(r'«\s'), lambda match: '«&nbsp;'),
(re.compile(r' %'), lambda match: '&nbsp;%'),
(re.compile(r'\.jpg&nbsp;&raquo; border='), lambda match: '.jpg'),
(re.compile(r'\.png&nbsp;&raquo; border='), lambda match: '.png'),
(re.compile(r' &ndash; '), lambda match: '&nbsp;&ndash; '),
(re.compile(r' '), lambda match: '&nbsp;&ndash; '),
(re.compile(r' - '), lambda match: '&nbsp;&ndash; '),
(re.compile(r' -,'), lambda match: '&nbsp;&ndash;,'),
(re.compile(r'&raquo;:'), lambda match: '&raquo;&nbsp;:'),
]
keep_only_tags = [
dict(name='div', attrs={'class':['contenu']})
]
remove_tags = [dict(name='div', attrs={'class':['LM_atome']})]
remove_tags_after = [dict(id='appel_temoignage')]
def get_article_url(self, article):
url = article.get('guid', None)
if '/chat/' in url or '.blog' in url or '/video/' in url or '/sport/' in url or '/portfolio/' in url or '/visuel/' in url :
url = None
return url
# def get_article_url(self, article):
# link = article.get('link')
# if 'blog' not in link and ('chat' not in link):
# return link
feeds = [
('A la une', 'http://www.lemonde.fr/rss/une.xml'),
@ -137,3 +67,10 @@ class LeMonde(BasicNewsRecipe):
return cover_url
def get_article_url(self, article):
url = article.get('guid', None)
if '/chat/' in url or '.blog' in url or '/video/' in url or '/sport/' in url or '/portfolio/' in url or '/visuel/' in url :
url = None
return url

View File

@ -381,12 +381,15 @@ def browser(honor_time=True, max_time=2, mobile_browser=False, user_agent=None):
user_agent = USER_AGENT_MOBILE if mobile_browser else USER_AGENT
opener.addheaders = [('User-agent', user_agent)]
proxies = get_proxies()
to_add = {}
http_proxy = proxies.get('http', None)
if http_proxy:
opener.set_proxies({'http':http_proxy})
to_add['http'] = http_proxy
https_proxy = proxies.get('https', None)
if https_proxy:
opener.set_proxies({'https':https_proxy})
to_add['https'] = https_proxy
if to_add:
opener.set_proxies(to_add)
return opener

View File

@ -255,7 +255,10 @@
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="max_view_width">
<widget class="QSpinBox" name="max_fs_width">
<property name="toolTip">
<string>Set the maximum width that the book's text and pictures will take when in fullscreen mode. This allows you to read the book text without it becoming too wide.</string>
</property>
<property name="suffix">
<string> px</string>
</property>
@ -270,10 +273,10 @@
<item row="3" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Maximum &amp;view width:</string>
<string>Maximum text width in &amp;fullscreen:</string>
</property>
<property name="buddy">
<cstring>max_view_width</cstring>
<cstring>max_fs_width</cstring>
</property>
</widget>
</item>
@ -350,7 +353,7 @@
<tabstop>serif_family</tabstop>
<tabstop>sans_family</tabstop>
<tabstop>mono_family</tabstop>
<tabstop>max_view_width</tabstop>
<tabstop>max_fs_width</tabstop>
<tabstop>opt_remember_window_size</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>

View File

@ -12,7 +12,7 @@ from PyQt4.Qt import (QSize, QSizePolicy, QUrl, SIGNAL, Qt, QTimer,
QPainter, QPalette, QBrush, QFontDatabase, QDialog,
QColor, QPoint, QImage, QRegion, QVariant, QIcon,
QFont, pyqtSignature, QAction, QByteArray, QMenu,
pyqtSignal, QSwipeGesture)
pyqtSignal, QSwipeGesture, QApplication)
from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
from calibre.utils.config import Config, StringConfig
@ -46,8 +46,10 @@ def config(defaults=None):
help=_('Remember last used window size'))
c.add_opt('user_css', default='',
help=_('Set the user CSS stylesheet. This can be used to customize the look of all books.'))
c.add_opt('max_view_width', default=6000,
help=_('Maximum width of the viewer window, in pixels.'))
c.add_opt('max_fs_width', default=800,
help=_("Set the maximum width that the book's text and pictures will take"
" when in fullscreen mode. This allows you to read the book text"
" without it becoming too wide."))
c.add_opt('fit_images', default=True,
help=_('Resize images larger than the viewer window to fit inside it'))
c.add_opt('hyphenate', default=False, help=_('Hyphenate text'))
@ -101,7 +103,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
self.standard_font.setCurrentIndex({'serif':0, 'sans':1, 'mono':2}[opts.standard_font])
self.css.setPlainText(opts.user_css)
self.css.setToolTip(_('Set the user CSS stylesheet. This can be used to customize the look of all books.'))
self.max_view_width.setValue(opts.max_view_width)
self.max_fs_width.setValue(opts.max_fs_width)
with zipfile.ZipFile(P('viewer/hyphenate/patterns.zip',
allow_user_override=False), 'r') as zf:
pats = [x.split('.')[0].replace('-', '_') for x in zf.namelist()]
@ -144,7 +146,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
c.set('user_css', unicode(self.css.toPlainText()))
c.set('remember_window_size', self.opt_remember_window_size.isChecked())
c.set('fit_images', self.opt_fit_images.isChecked())
c.set('max_view_width', int(self.max_view_width.value()))
c.set('max_fs_width', int(self.max_fs_width.value()))
c.set('hyphenate', self.hyphenate.isChecked())
c.set('remember_current_page', self.opt_remember_current_page.isChecked())
c.set('wheel_flips_pages', self.opt_wheel_flips_pages.isChecked())
@ -192,6 +194,8 @@ class Document(QWebPage): # {{{
self.loaded_javascript = False
self.js_loader = JavaScriptLoader(
dynamic_coffeescript=self.debug_javascript)
self.initial_left_margin = self.initial_right_margin = u''
self.in_fullscreen_mode = False
self.setLinkDelegationPolicy(self.DelegateAllLinks)
self.scroll_marks = []
@ -239,6 +243,9 @@ class Document(QWebPage): # {{{
self.enable_page_flip = self.page_flip_duration > 0.1
self.font_magnification_step = opts.font_magnification_step
self.wheel_flips_pages = opts.wheel_flips_pages
screen_width = QApplication.desktop().screenGeometry().width()
# Leave some space for the scrollbar and some border
self.max_fs_width = min(opts.max_fs_width, screen_width-50)
def fit_images(self):
if self.do_fit_images:
@ -274,6 +281,30 @@ class Document(QWebPage): # {{{
self.set_bottom_padding(0)
self.fit_images()
self.init_hyphenate()
self.initial_left_margin = unicode(self.javascript(
'document.body.style.marginLeft').toString())
self.initial_right_margin = unicode(self.javascript(
'document.body.style.marginRight').toString())
if self.in_fullscreen_mode:
self.switch_to_fullscreen_mode()
def switch_to_fullscreen_mode(self):
self.in_fullscreen_mode = True
self.javascript('''
var s = document.body.style;
s.maxWidth = "%dpx";
s.marginLeft = "auto";
s.marginRight = "auto";
'''%self.max_fs_width)
def switch_to_window_mode(self):
self.in_fullscreen_mode = False
self.javascript('''
var s = document.body.style;
s.maxWidth = "none";
s.marginLeft = "%s";
s.marginRight = "%s";
'''%(self.initial_left_margin, self.initial_right_margin))
@pyqtSignature("QString")
def debug(self, msg):
@ -581,8 +612,8 @@ class DocumentView(QWebView): # {{{
def config(self, parent=None):
self.document.do_config(parent)
if self.manager is not None:
self.manager.set_max_width()
if self.document.in_fullscreen_mode:
self.document.switch_to_fullscreen_mode()
self.setFocus(Qt.OtherFocusReason)
def bookmark(self):
@ -602,6 +633,9 @@ class DocumentView(QWebView): # {{{
menu.insertAction(list(menu.actions())[0], self.search_action)
menu.addSeparator()
menu.addAction(self.goto_location_action)
if self.document.in_fullscreen_mode and self.manager is not None:
menu.addSeparator()
menu.addAction(self.manager.toggle_toolbar_action)
menu.exec_(ev.globalPos())
def lookup(self, *args):

View File

@ -5,11 +5,11 @@ import traceback, os, sys, functools, collections, re
from functools import partial
from threading import Thread
from PyQt4.Qt import QApplication, Qt, QIcon, QTimer, SIGNAL, QByteArray, \
QDoubleSpinBox, QLabel, QTextBrowser, \
QPainter, QBrush, QColor, QStandardItemModel, QPalette, \
QStandardItem, QUrl, QRegExpValidator, QRegExp, QLineEdit, \
QToolButton, QMenu, QInputDialog, QAction, QKeySequence
from PyQt4.Qt import (QApplication, Qt, QIcon, QTimer, SIGNAL, QByteArray,
QSize, QDoubleSpinBox, QLabel, QTextBrowser, QPropertyAnimation,
QPainter, QBrush, QColor, QStandardItemModel, QPalette, QStandardItem,
QUrl, QRegExpValidator, QRegExp, QLineEdit, QToolButton, QMenu,
QInputDialog, QAction, QKeySequence)
from calibre.gui2.viewer.main_ui import Ui_EbookViewer
from calibre.gui2.viewer.printing import Printing
@ -55,8 +55,6 @@ class TOC(QStandardItemModel):
self.appendRow(TOCItem(t))
self.setHorizontalHeaderItem(0, QStandardItem(_('Table of Contents')))
class Worker(Thread):
def run(self):
@ -292,6 +290,37 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu)
self.tool_bar.widgetForAction(self.action_bookmark).setPopupMode(QToolButton.MenuButtonPopup)
self.action_full_screen.setCheckable(True)
self.full_screen_label = QLabel('''
<center>
<h1>%s</h1>
<h3>%s</h3>
<h3>%s</h3>
</center>
'''%(_('Full screen mode'),
_('Right click to show controls'),
_('Press Esc to quit')),
self)
self.full_screen_label.setVisible(False)
self.full_screen_label.setStyleSheet('''
QLabel {
text-align: center;
background-color: white;
color: black;
border-width: 1px;
border-style: solid;
border-radius: 20px;
}
''')
self.toggle_toolbar_action = QAction(_('Show/hide controls'), self)
self.toggle_toolbar_action.triggered.connect(self.toggle_toolbars)
self.addAction(self.toggle_toolbar_action)
self.full_screen_label_anim = QPropertyAnimation(
self.full_screen_label, 'size')
self.esc_full_screen_action = a = QAction(self)
self.addAction(a)
a.setShortcut(Qt.Key_Escape)
a.setEnabled(False)
a.triggered.connect(self.action_full_screen.trigger)
self.print_menu = QMenu()
self.print_menu.addAction(QIcon(I('print-preview.png')), _('Print Preview'))
@ -299,7 +328,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.tool_bar.widgetForAction(self.action_print).setPopupMode(QToolButton.MenuButtonPopup)
self.connect(self.action_print, SIGNAL("triggered(bool)"), partial(self.print_book, preview=False))
self.connect(self.print_menu.actions()[0], SIGNAL("triggered(bool)"), partial(self.print_book, preview=True))
self.set_max_width()
ca = self.view.copy_action
ca.setShortcut(QKeySequence.Copy)
self.addAction(ca)
@ -313,6 +341,13 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
w = self.tool_bar.widgetForAction(self.action_open_ebook)
w.setPopupMode(QToolButton.MenuButtonPopup)
for x in ('tool_bar', 'tool_bar2'):
x = getattr(self, x)
for action in x.actions():
# So that the keyboard shortcuts for these actions will
# continue to function even when the toolbars are hidden
self.addAction(action)
self.restore_state()
def set_toc_visible(self, yes):
@ -338,9 +373,18 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
count += 1
def closeEvent(self, e):
if self.isFullScreen():
self.action_full_screen.trigger()
e.ignore()
return
self.save_state()
return MainWindow.closeEvent(self, e)
def toggle_toolbars(self):
for x in ('tool_bar', 'tool_bar2'):
x = getattr(self, x)
x.setVisible(not x.isVisible())
def save_state(self):
state = bytearray(self.saveState(self.STATE_VERSION))
vprefs['viewer_toolbar_state'] = state
@ -382,11 +426,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self._lookup = None
self.dictionary_view.setHtml(html)
def set_max_width(self):
from calibre.gui2.viewer.documentview import config
c = config().parse()
self.frame.setMaximumWidth(c.max_view_width)
def get_remember_current_page_opt(self):
from calibre.gui2.viewer.documentview import config
c = config().parse()
@ -401,6 +440,46 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
else:
self.showFullScreen()
def showFullScreen(self):
self.tool_bar.setVisible(False)
self.tool_bar2.setVisible(False)
self._original_frame_margins = (
self.centralwidget.layout().contentsMargins(),
self.frame.layout().contentsMargins())
self.frame.layout().setContentsMargins(0, 0, 0, 0)
self.centralwidget.layout().setContentsMargins(0, 0, 0, 0)
super(EbookViewer, self).showFullScreen()
QTimer.singleShot(10, self.show_full_screen_label)
def show_full_screen_label(self):
f = self.full_screen_label
self.esc_full_screen_action.setEnabled(True)
f.setVisible(True)
height = 200
width = int(0.7*self.view.width())
f.resize(width, height)
f.move((self.view.width() - width)//2, (self.view.height()-height)//2)
a = self.full_screen_label_anim
a.setDuration(500)
a.setStartValue(QSize(width, 0))
a.setEndValue(QSize(width, height))
a.start()
QTimer.singleShot(2750, self.full_screen_label.hide)
self.view.document.switch_to_fullscreen_mode()
def showNormal(self):
self.esc_full_screen_action.setEnabled(False)
self.tool_bar.setVisible(True)
self.tool_bar2.setVisible(True)
self.full_screen_label.setVisible(False)
if hasattr(self, '_original_frame_margins'):
om = self._original_frame_margins
self.centralwidget.layout().setContentsMargins(om[0])
self.frame.layout().setContentsMargins(om[1])
super(EbookViewer, self).showNormal()
self.view.document.switch_to_window_mode()
def goto(self, ref):
if ref:
tokens = ref.split('.')

View File

@ -284,6 +284,9 @@
<property name="text">
<string>Toggle full screen</string>
</property>
<property name="toolTip">
<string>Toggle full screen (F11)</string>
</property>
</action>
<action name="action_print">
<property name="icon">