sync with Kovid's branch
69
recipes/aktualne.cz.recipe
Normal file
@ -0,0 +1,69 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class aktualneRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'aktualne.cz'
|
||||
publisher = u'Centrum holdings'
|
||||
description = 'aktuálně.cz'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
|
||||
feeds = [
|
||||
(u'Domácí', u'http://aktualne.centrum.cz/feeds/rss/domaci/?photo=0'),
|
||||
(u'Zprávy', u'http://aktualne.centrum.cz/feeds/rss/zpravy/?photo=0'),
|
||||
(u'Praha', u'http://aktualne.centrum.cz/feeds/rss/domaci/regiony/praha/?photo=0'),
|
||||
(u'Ekonomika', u'http://aktualne.centrum.cz/feeds/rss/ekonomika/?photo=0'),
|
||||
(u'Finance', u'http://aktualne.centrum.cz/feeds/rss/finance/?photo=0'),
|
||||
(u'Blogy a názory', u'http://blog.aktualne.centrum.cz/export-all.php')
|
||||
]
|
||||
|
||||
|
||||
language = 'cs'
|
||||
cover_url = 'http://img.aktualne.centrum.cz/design/akt4/o/l/logo-akt-ciste.png'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
|
||||
remove_attributes = []
|
||||
remove_tags_before = dict(name='h1', attrs={'class':['titulek-clanku']})
|
||||
filter_regexps = [r'img.aktualne.centrum.cz']
|
||||
remove_tags = [dict(name='div', attrs={'id':['social-bookmark']}),
|
||||
dict(name='div', attrs={'class':['box1', 'svazane-tagy']}),
|
||||
dict(name='div', attrs={'class':'itemcomment id0'}),
|
||||
dict(name='div', attrs={'class':'hlavicka'}),
|
||||
dict(name='div', attrs={'class':'hlavni-menu'}),
|
||||
dict(name='div', attrs={'class':'top-standard-brand-obal'}),
|
||||
dict(name='div', attrs={'class':'breadcrumb'}),
|
||||
dict(name='div', attrs={'id':'start-standard'}),
|
||||
dict(name='div', attrs={'id':'forum'}),
|
||||
dict(name='span', attrs={'class':'akce'}),
|
||||
dict(name='span', attrs={'class':'odrazka vetsi'}),
|
||||
dict(name='div', attrs={'class':'boxP'}),
|
||||
dict(name='div', attrs={'class':'box2'})]
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'<div class="(contenttitle"|socialni-site|wiki|facebook-promo|facebook-like-button"|meta-akce).*', re.DOTALL|re.IGNORECASE), lambda match: '</body>'),
|
||||
(re.compile(r'<div class="[^"]*poutak-clanek-trojka".*', re.DOTALL|re.IGNORECASE), lambda match: '</body>')]
|
||||
|
||||
keep_only_tags = []
|
||||
|
||||
visited_urls = {}
|
||||
def get_article_url(self, article):
|
||||
url = BasicNewsRecipe.get_article_url(self, article)
|
||||
if url in self.visited_urls:
|
||||
self.log.debug('Ignoring duplicate: ' + url)
|
||||
return None
|
||||
else:
|
||||
self.visited_urls[url] = True
|
||||
self.log.debug('Accepting: ' + url)
|
||||
return url
|
||||
|
||||
def encoding(self, source):
|
||||
if source.newurl.find('blog.aktualne') >= 0:
|
||||
enc = 'utf-8'
|
||||
else:
|
||||
enc = 'iso-8859-2'
|
||||
self.log.debug('Called encoding ' + enc + " " + str(source.newurl))
|
||||
return source.decode(enc, 'replace')
|
||||
|
48
recipes/antyweb.recipe
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AntywebRecipe(BasicNewsRecipe):
|
||||
encoding = 'utf-8'
|
||||
__license__ = 'GPL v3'
|
||||
__author__ = u'Artur Stachecki <artur.stachecki@gmail.com>'
|
||||
language = 'pl'
|
||||
version = 1
|
||||
title = u'Antyweb'
|
||||
category = u'News'
|
||||
description = u'Blog o internecie i nowych technologiach'
|
||||
cover_url=''
|
||||
remove_empty_feeds= True
|
||||
auto_cleanup = False
|
||||
no_stylesheets=True
|
||||
use_embedded_content = False
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
remove_javascript = True
|
||||
simultaneous_downloads = 3
|
||||
|
||||
keep_only_tags =[]
|
||||
keep_only_tags.append(dict(name = 'h1', attrs = { 'class' : 'mm-article-title'}))
|
||||
keep_only_tags.append(dict(name = 'div', attrs = {'class' : 'mm-article-content'}))
|
||||
|
||||
|
||||
remove_tags =[]
|
||||
remove_tags.append(dict(name = 'h2', attrs = {'class' : 'widgettitle'}))
|
||||
remove_tags.append(dict(name = 'img', attrs = {'class' : 'alignleft'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'float: right;margin-left:1em;margin-bottom: 0.5em;padding-bottom: 3px; width: 72px;'}))
|
||||
remove_tags.append(dict(name = 'img', attrs = {'src' : 'http://antyweb.pl/wp-content/uploads/2011/09/HOSTERSI_testy_pasek600x30.gif'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'podwpisowe'}))
|
||||
|
||||
|
||||
extra_css = '''
|
||||
body {font-family: verdana, arial, helvetica, geneva, sans-serif ;}
|
||||
'''
|
||||
|
||||
feeds = [
|
||||
(u'Artykuly', u'feed://feeds.feedburner.com/Antyweb?format=xml'),
|
||||
]
|
||||
def preprocess_html(self, soup):
|
||||
for alink in soup.findAll('a'):
|
||||
if alink.string is not None:
|
||||
tstr = alink.string
|
||||
alink.replaceWith(tstr)
|
||||
return soup
|
50
recipes/bankier_pl.recipe
Normal file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__author__ = 'teepel <teepel44@gmail.com>'
|
||||
|
||||
'''
|
||||
bankier.pl
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class bankier(BasicNewsRecipe):
|
||||
title = u'Bankier.pl'
|
||||
__author__ = 'teepel <teepel44@gmail.com>'
|
||||
language = 'pl'
|
||||
description ='Polski portal finansowy. Informacje o: gospodarka, inwestowanie, finanse osobiste, prowadzenie firmy, kursy walut, notowania akcji, fundusze.'
|
||||
masthead_url='http://www.bankier.pl/gfx/hd-mid-02.gif'
|
||||
INDEX='http://bankier.pl/'
|
||||
remove_empty_feeds= True
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
remove_javascript=True
|
||||
no_stylesheets=True
|
||||
simultaneous_downloads = 5
|
||||
|
||||
keep_only_tags =[]
|
||||
keep_only_tags.append(dict(name = 'div', attrs = {'align' : 'left'}))
|
||||
|
||||
remove_tags =[]
|
||||
remove_tags.append(dict(name = 'table', attrs = {'cellspacing' : '2'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'align' : 'center'}))
|
||||
remove_tags.append(dict(name = 'img', attrs = {'src' : '/gfx/hd-mid-02.gif'}))
|
||||
#remove_tags.append(dict(name = 'a', attrs = {'target' : '_blank'}))
|
||||
#remove_tags.append(dict(name = 'br', attrs = {'clear' : 'all'}))
|
||||
|
||||
feeds = [
|
||||
(u'Wiadomości dnia', u'http://feeds.feedburner.com/bankier-wiadomosci-dnia'),
|
||||
(u'Finanse osobiste', u'http://feeds.feedburner.com/bankier-finanse-osobiste'),
|
||||
(u'Firma', u'http://feeds.feedburner.com/bankier-firma'),
|
||||
(u'Giełda', u'http://feeds.feedburner.com/bankier-gielda'),
|
||||
(u'Rynek walutowy', u'http://feeds.feedburner.com/bankier-rynek-walutowy'),
|
||||
(u'Komunikaty ze spółek', u'http://feeds.feedburner.com/bankier-espi'),
|
||||
]
|
||||
def print_version(self, url):
|
||||
segment = url.split('.')
|
||||
urlPart = segment[2]
|
||||
segments = urlPart.split('-')
|
||||
urlPart2 = segments[-1]
|
||||
return 'http://www.bankier.pl/wiadomosci/print.html?article_id=' + urlPart2
|
||||
|
55
recipes/blesk.recipe
Normal file
@ -0,0 +1,55 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class bleskRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Blesk'
|
||||
publisher = u''
|
||||
description = 'blesk.cz'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
use_embedded_content = False
|
||||
|
||||
feeds = [
|
||||
(u'Zprávy', u'http://www.blesk.cz/rss/7'),
|
||||
(u'Blesk', u'http://www.blesk.cz/rss/1'),
|
||||
(u'Sex a tabu', u'http://www.blesk.cz/rss/2'),
|
||||
(u'Celebrity', u'http://www.blesk.cz/rss/5'),
|
||||
(u'Cestování', u'http://www.blesk.cz/rss/12')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://img.blesk.cz/images/blesk/blesk-logo.png'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
extra_css = """
|
||||
"""
|
||||
|
||||
remove_attributes = []
|
||||
remove_tags_before = dict(name='div', attrs={'id':['boxContent']})
|
||||
remove_tags_after = dict(name='div', attrs={'class':['artAuthors']})
|
||||
remove_tags = [dict(name='div', attrs={'class':['link_clanek']}),
|
||||
dict(name='div', attrs={'id':['partHeader']}),
|
||||
dict(name='div', attrs={'id':['top_bottom_box', 'lista_top']})]
|
||||
preprocess_regexps = [(re.compile(r'<div class="(textovytip|related)".*', re.DOTALL|re.IGNORECASE), lambda match: '</body>')]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'articleContent'})]
|
||||
|
||||
visited_urls = {}
|
||||
def get_article_url(self, article):
|
||||
url = BasicNewsRecipe.get_article_url(self, article)
|
||||
if url in self.visited_urls:
|
||||
self.log.debug('Ignoring duplicate: ' + url)
|
||||
return None
|
||||
else:
|
||||
self.visited_urls[url] = True
|
||||
self.log.debug('Accepting: ' + url)
|
||||
return url
|
||||
|
||||
|
||||
|
||||
|
45
recipes/buchreport.recipe
Normal file
@ -0,0 +1,45 @@
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
'''Calibre recipe to convert the RSS feeds of the Buchreport to an ebook.'''
|
||||
|
||||
class Buchreport(BasicNewsRecipe) :
|
||||
__author__ = 'a.peter'
|
||||
__copyright__ = 'a.peter'
|
||||
__license__ = 'GPL v3'
|
||||
description = 'Buchreport'
|
||||
version = 4
|
||||
title = u'Buchreport'
|
||||
timefmt = ' [%d.%m.%Y]'
|
||||
encoding = 'cp1252'
|
||||
language = 'de'
|
||||
|
||||
|
||||
extra_css = 'body { margin-left: 0.00em; margin-right: 0.00em; } \
|
||||
article, articledate, articledescription { text-align: left; } \
|
||||
h1 { text-align: left; font-size: 140%; font-weight: bold; } \
|
||||
h2 { text-align: left; font-size: 100%; font-weight: bold; font-style: italic; } \
|
||||
h3 { text-align: left; font-size: 100%; font-weight: regular; font-style: italic; } \
|
||||
h4, h5, h6 { text-align: left; font-size: 100%; font-weight: bold; }'
|
||||
|
||||
oldest_article = 7.0
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
use_embedded_content = False
|
||||
publication_type = 'newspaper'
|
||||
|
||||
remove_tags_before = dict(name='h2')
|
||||
remove_tags_after = [
|
||||
dict(name='div', attrs={'style':["padding-top:10px;clear:both"]})
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'style':["padding-top:10px;clear:both"]}),
|
||||
dict(name='iframe'),
|
||||
dict(name='img')
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'Buchreport', u'http://www.buchreport.de/index.php?id=5&type=100')
|
||||
]
|
||||
|
||||
def get_masthead_url(self):
|
||||
return 'http://www.buchreport.de/fileadmin/template/img/buchreport_logo.jpg'
|
@ -1,5 +1,5 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
__copyright__ = '2009-2012, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
www.business-standard.com
|
||||
'''
|
||||
@ -14,10 +14,12 @@ class BusinessStandard(BasicNewsRecipe):
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
auto_cleanup = False
|
||||
encoding = 'cp1252'
|
||||
publisher = 'Business Standard Limited'
|
||||
category = 'news, business, money, india, world'
|
||||
language = 'en_IN'
|
||||
masthead_url = 'http://feeds.business-standard.com/images/logo_08.jpg'
|
||||
|
||||
conversion_options = {
|
||||
'comments' : description
|
||||
@ -26,7 +28,7 @@ class BusinessStandard(BasicNewsRecipe):
|
||||
,'publisher' : publisher
|
||||
,'linearize_tables': True
|
||||
}
|
||||
keep_only_tags=[dict(attrs={'class':'TableClas'})]
|
||||
#keep_only_tags=[dict(name='td', attrs={'class':'TableClas'})]
|
||||
remove_tags = [
|
||||
dict(name=['object','link','script','iframe','base','meta'])
|
||||
,dict(attrs={'class':'rightDiv2'})
|
||||
@ -45,3 +47,8 @@ class BusinessStandard(BasicNewsRecipe):
|
||||
,(u'Management & Mktg' , u'http://feeds.business-standard.com/rss/7_0.xml' )
|
||||
,(u'Opinion' , u'http://feeds.business-standard.com/rss/5_0.xml' )
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
l, s, tp = url.rpartition('/')
|
||||
t, k, autono = l.rpartition('/')
|
||||
return 'http://www.business-standard.com/india/printpage.php?autono=' + autono + '&tp=' + tp
|
||||
|
68
recipes/ceska_pozice.recipe
Normal file
@ -0,0 +1,68 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class ceskaPoziceRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Česká pozice'
|
||||
description = 'Česká pozice'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 20
|
||||
|
||||
feeds = [
|
||||
(u'Všechny články', u'http://www.ceskapozice.cz/rss.xml'),
|
||||
(u'Domov', u'http://www.ceskapozice.cz/taxonomy/term/16/feed'),
|
||||
(u'Chrono', u'http://www.ceskapozice.cz/chrono/feed'),
|
||||
(u'Evropa', u'http://www.ceskapozice.cz/taxonomy/term/17/feed')
|
||||
]
|
||||
|
||||
|
||||
language = 'cs'
|
||||
cover_url = 'http://www.ceskapozice.cz/sites/default/files/cpozice_logo.png'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
domain = u'http://www.ceskapozice.cz'
|
||||
use_embedded_content = False
|
||||
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'class':['block-ad', 'region region-content-ad']}),
|
||||
dict(name='ul', attrs={'class':'links'}),
|
||||
dict(name='div', attrs={'id':['comments', 'back-to-top']}),
|
||||
dict(name='div', attrs={'class':['next-page', 'region region-content-ad']}),
|
||||
dict(name='cite')]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'content'})]
|
||||
|
||||
visited_urls = {}
|
||||
def get_article_url(self, article):
|
||||
url = BasicNewsRecipe.get_article_url(self, article)
|
||||
if url in self.visited_urls:
|
||||
self.log.debug('Ignoring duplicate: ' + url)
|
||||
return None
|
||||
else:
|
||||
self.visited_urls[url] = True
|
||||
self.log.debug('Accepting: ' + url)
|
||||
return url
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
self.append_page(soup, soup.body, 3)
|
||||
return soup
|
||||
|
||||
def append_page(self, soup, appendtag, position):
|
||||
pager = soup.find('div', attrs={'class':'paging-bottom'})
|
||||
if pager:
|
||||
nextbutton = pager.find('li', attrs={'class':'pager-next'})
|
||||
if nextbutton:
|
||||
nexturl = self.domain + nextbutton.a['href']
|
||||
soup2 = self.index_to_soup(nexturl)
|
||||
texttag = soup2.find('div', attrs={'class':'main-body'})
|
||||
for it in texttag.findAll('div', attrs={'class':'region region-content-ad'}):
|
||||
it.extract()
|
||||
for it in texttag.findAll('cite'):
|
||||
it.extract()
|
||||
newpos = len(texttag.contents)
|
||||
self.append_page(soup2, texttag, newpos)
|
||||
texttag.extract()
|
||||
appendtag.insert(position, texttag)
|
||||
pager.extract()
|
||||
|
30
recipes/ceske_noviny.recipe
Normal file
@ -0,0 +1,30 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class ceskenovinyRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'České Noviny'
|
||||
description = 'ceskenoviny.cz'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
|
||||
feeds = [
|
||||
(u'Domácí', u'http://www.ceskenoviny.cz/sluzby/rss/domov.php')
|
||||
#,(u'Hlavní události', u'http://www.ceskenoviny.cz/sluzby/rss/index.php')
|
||||
#,(u'Přehled zpráv', u'http://www.ceskenoviny.cz/sluzby/rss/zpravy.php')
|
||||
#,(u'Ze světa', u'http://www.ceskenoviny.cz/sluzby/rss/svet.php')
|
||||
#,(u'Kultura', u'http://www.ceskenoviny.cz/sluzby/rss/kultura.php')
|
||||
#,(u'IT', u'http://www.ceskenoviny.cz/sluzby/rss/pocitace.php')
|
||||
]
|
||||
|
||||
|
||||
language = 'cs'
|
||||
cover_url = 'http://i4.cn.cz/grafika/cn_logo-print.gif'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
|
||||
remove_attributes = []
|
||||
filter_regexps = [r'img.aktualne.centrum.cz']
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'clnk'})]
|
26
recipes/cesky_rozhlas_6.recipe
Normal file
@ -0,0 +1,26 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class cro6Recipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Český rozhlas 6'
|
||||
description = 'Český rozhlas 6'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
|
||||
feeds = [
|
||||
(u'Český rozhlas 6', u'http://www.rozhlas.cz/export/cro6/')
|
||||
]
|
||||
|
||||
|
||||
language = 'cs'
|
||||
cover_url = 'http://www.rozhlas.cz/img/e5/logo/cro6.png'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
|
||||
remove_attributes = []
|
||||
remove_tags = [dict(name='div', attrs={'class':['audio-play-all', 'poradHeaders', 'actions']}),
|
||||
dict(name='p', attrs={'class':['para-last']})]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'article'})]
|
39
recipes/demagog.cz.recipe
Normal file
@ -0,0 +1,39 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class demagogRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Demagog.cz'
|
||||
publisher = u''
|
||||
description = 'demagog.cz'
|
||||
oldest_article = 6
|
||||
max_articles_per_feed = 20
|
||||
use_embedded_content = False
|
||||
remove_empty_feeds = True
|
||||
|
||||
feeds = [
|
||||
(u'Aktuality', u'http://demagog.cz/rss')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://demagog.cz/content/images/demagog.cz.png'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
extra_css = """
|
||||
.vyrok_suhrn{margin-top:50px; }
|
||||
.vyrok{margin-bottom:30px; }
|
||||
"""
|
||||
|
||||
remove_tags = [dict(name='a', attrs={'class':'vyrok_odovodnenie_tgl'}),
|
||||
dict(name='img', attrs={'class':'vyrok_fotografia'})]
|
||||
remove_tags_before = dict(name='h1')
|
||||
remove_tags_after = dict(name='div', attrs={'class':'vyrok_text_after'})
|
||||
preprocess_regexps = [(re.compile(r'(<div class="vyrok_suhrn">)', re.DOTALL|re.IGNORECASE), lambda match: '\1<hr>')]
|
||||
|
||||
|
||||
|
||||
|
36
recipes/denik.cz.recipe
Normal file
@ -0,0 +1,36 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class ceskyDenikRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'denik.cz'
|
||||
publisher = u''
|
||||
description = u'Český deník'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
use_embedded_content = False
|
||||
remove_empty_feeds = True
|
||||
|
||||
feeds = [
|
||||
(u'Z domova', u'http://www.denik.cz/rss/z_domova.html')
|
||||
,(u'Pražský deník - Moje Praha', u'http://prazsky.denik.cz/rss/zpravy_region.html')
|
||||
#,(u'Zahraničí', u'http://www.denik.cz/rss/ze_sveta.html')
|
||||
#,(u'Kultura', u'http://www.denik.cz/rss/kultura.html')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://g.denik.cz/images/loga/denik.png'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
extra_css = """
|
||||
"""
|
||||
|
||||
remove_tags = []
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'content'})]
|
||||
#remove_tags_before = dict(name='h1')
|
||||
remove_tags_after = dict(name='p', attrs={'class':'clanek-autor'})
|
||||
|
||||
|
28
recipes/denik_referendum.recipe
Normal file
@ -0,0 +1,28 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class denikReferendumRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Den\u00edk Referendum'
|
||||
publisher = u''
|
||||
description = ''
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
|
||||
feeds = [
|
||||
(u'Deník Referendum', u'http://feeds.feedburner.com/DenikReferendum')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
remove_attributes = []
|
||||
remove_tags_after = dict(name='div', attrs={'class':['text']})
|
||||
remove_tags = [dict(name='div', attrs={'class':['box boxLine', 'box noprint', 'box']}),
|
||||
dict(name='h3', attrs={'class':'head alt'})]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':['content']})]
|
@ -7,6 +7,7 @@ class AdvancedUserRecipe1332847053(BasicNewsRecipe):
|
||||
title = u'Editoriali'
|
||||
__author__ = 'faber1971'
|
||||
description = 'Leading articles on Italy by the best Italian editorials'
|
||||
language = 'it'
|
||||
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
|
35
recipes/f1_ultra.recipe
Normal file
@ -0,0 +1,35 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class f1ultra(BasicNewsRecipe):
|
||||
title = u'Formuła 1 - F1 ultra'
|
||||
__license__ = 'GPL v3'
|
||||
__author__ = 'MrStefan <mrstefaan@gmail.com>, Artur Stachecki <artur.stachecki@gmail.com>'
|
||||
language = 'pl'
|
||||
description =u'Formuła 1, Robert Kubica, F3, GP2 oraz inne serie wyścigowe.'
|
||||
masthead_url='http://www.f1ultra.pl/templates/f1ultra/images/logo.gif'
|
||||
remove_empty_feeds= True
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
remove_javascript=True
|
||||
no_stylesheets=True
|
||||
|
||||
keep_only_tags =[(dict(name = 'div', attrs = {'id' : 'main'}))]
|
||||
remove_tags_after =[dict(attrs = {'style' : 'margin-top:5px;margin-bottom:5px;display: inline;'})]
|
||||
remove_tags =[(dict(attrs = {'class' : ['buttonheading', 'avPlayerContainer', 'createdate']}))]
|
||||
remove_tags.append(dict(attrs = {'title' : ['PDF', 'Drukuj', 'Email']}))
|
||||
remove_tags.append(dict(name = 'form', attrs = {'method' : 'post'}))
|
||||
remove_tags.append(dict(name = 'hr', attrs = {'size' : '2'}))
|
||||
|
||||
preprocess_regexps = [(re.compile(r'align="left"'), lambda match: ''),
|
||||
(re.compile(r'align="right"'), lambda match: ''),
|
||||
(re.compile(r'width=\"*\"'), lambda match: ''),
|
||||
(re.compile(r'\<table .*?\>'), lambda match: '')]
|
||||
|
||||
|
||||
extra_css = '''.contentheading { font-size: 1.4em; font-weight: bold; }
|
||||
img { display: block; clear: both;}
|
||||
'''
|
||||
remove_attributes = ['width','height','position','float','padding-left','padding-right','padding','text-align']
|
||||
|
||||
feeds = [(u'F1 Ultra', u'http://www.f1ultra.pl/index.php?option=com_rd_rss&id=1&Itemid=245')]
|
@ -8,6 +8,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1349086293(BasicNewsRecipe):
|
||||
title = u'Foreign Policy'
|
||||
language = 'en'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'International News'
|
||||
publisher = 'Washingtonpost.Newsweek Interactive, LLC'
|
||||
|
@ -8,7 +8,6 @@ krakow.gazeta.pl
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class gw_krakow(BasicNewsRecipe):
|
||||
title = u'Gazeta.pl Kraków'
|
||||
@ -46,7 +45,7 @@ class gw_krakow(BasicNewsRecipe):
|
||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_buttons'}))
|
||||
|
||||
remove_tags_after = [dict(name = 'div', attrs = {'id' : 'gazeta_article_share'})]
|
||||
|
||||
|
||||
feeds = [(u'Wiadomości', u'http://rss.gazeta.pl/pub/rss/krakow.xml')]
|
||||
|
||||
def skip_ad_pages(self, soup):
|
||||
|
@ -8,7 +8,6 @@ warszawa.gazeta.pl
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class gw_wawa(BasicNewsRecipe):
|
||||
title = u'Gazeta.pl Warszawa'
|
||||
@ -43,7 +42,7 @@ class gw_wawa(BasicNewsRecipe):
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'gazeta_article_related_new'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'gazetaVideoPlayer'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'gazeta_article_miniatures'}))
|
||||
|
||||
|
||||
feeds = [(u'Wiadomości', u'http://rss.gazeta.pl/pub/rss/warszawa.xml')]
|
||||
|
||||
def skip_ad_pages(self, soup):
|
||||
|
BIN
recipes/icons/antyweb.png
Normal file
After Width: | Height: | Size: 668 B |
BIN
recipes/icons/bankier_pl.png
Normal file
After Width: | Height: | Size: 190 B |
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 786 B |
BIN
recipes/icons/f1_ultra.png
Normal file
After Width: | Height: | Size: 490 B |
BIN
recipes/icons/myapple_pl.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
recipes/icons/telepolis_pl.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
36
recipes/ihned.cz.recipe
Normal file
@ -0,0 +1,36 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class ihnedRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'iHNed.cz'
|
||||
publisher = u''
|
||||
description = 'ihned.cz'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
use_embedded_content = False
|
||||
|
||||
feeds = [
|
||||
(u'Zprávy', u'http://zpravy.ihned.cz/?m=rss'),
|
||||
(u'Hospodářské noviny', u'http://hn.ihned.cz/?p=500000_rss'),
|
||||
(u'Byznys', u'http://byznys.ihned.cz/?m=rss'),
|
||||
(u'Life', u'http://life.ihned.cz/?m=rss'),
|
||||
(u'Dialog', u'http://dialog.ihned.cz/?m=rss')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://rss.ihned.cz/img/0/0_hp09/ihned.cz.gif'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
extra_css = """
|
||||
"""
|
||||
|
||||
remove_attributes = []
|
||||
remove_tags_before = dict(name='div', attrs={'id':['heading']})
|
||||
remove_tags_after = dict(name='div', attrs={'id':['next-authors']})
|
||||
remove_tags = [dict(name='ul', attrs={'id':['comm']}),
|
||||
dict(name='div', attrs={'id':['r-big']}),
|
||||
dict(name='div', attrs={'class':['tools tools-top']})]
|
59
recipes/insider.recipe
Normal file
@ -0,0 +1,59 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
import re
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class insider(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = 'Insider'
|
||||
language = 'cs'
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'class':'article-related-content'})
|
||||
,dict(name='div', attrs={'class':'calendar'})
|
||||
,dict(name='span', attrs={'id':'labelHolder'})
|
||||
]
|
||||
|
||||
no_stylesheets = True
|
||||
keep_only_tags = [dict(name='div', attrs={'class':['doubleBlock textContentFormat']})]
|
||||
|
||||
preprocess_regexps = [(re.compile(r'T.mata:.*', re.DOTALL|re.IGNORECASE), lambda m: '</body>')]
|
||||
needs_subscription = True
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
br.open('http://www.denikinsider.cz/')
|
||||
br.select_form(nr=0)
|
||||
br['login-name'] = self.username
|
||||
br['login-password'] = self.password
|
||||
res = br.submit()
|
||||
raw = res.read()
|
||||
if u'Odhlásit se' not in raw:
|
||||
raise ValueError('Failed to login to insider.cz'
|
||||
'Check your username and password.')
|
||||
return br
|
||||
|
||||
def parse_index(self):
|
||||
articles = []
|
||||
|
||||
soup = self.index_to_soup('http://www.denikinsider.cz')
|
||||
titles = soup.findAll('span', attrs={'class':'homepageArticleTitle'})
|
||||
if titles is None:
|
||||
raise ValueError('Could not find category content')
|
||||
|
||||
articles = []
|
||||
seen_titles = set([])
|
||||
for title in titles:
|
||||
if title.string in seen_titles:
|
||||
continue
|
||||
article = title.parent
|
||||
seen_titles.add(title.string)
|
||||
url = article['href']
|
||||
if url.startswith('/'):
|
||||
url = 'http://www.denikinsider.cz/'+url
|
||||
self.log('\tFound article:', title, 'at', url)
|
||||
articles.append({'title':title.string, 'url':url, 'description':'',
|
||||
'date':''})
|
||||
return [(self.title, articles)]
|
||||
|
||||
|
32
recipes/kudy_z_nudy.recipe
Normal file
@ -0,0 +1,32 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class kudyznudyRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Kudy z nudy'
|
||||
publisher = u''
|
||||
description = 'kudyznudy.cz'
|
||||
oldest_article = 3
|
||||
max_articles_per_feed = 20
|
||||
use_embedded_content = False
|
||||
|
||||
feeds = [
|
||||
(u'Praha nejnovější', u'http://www.kudyznudy.cz/RSS/Charts.aspx?Type=Newest&Lang=cs-CZ&RegionId=1')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://www.kudyznudy.cz/App_Themes/KzN/Images/Containers/Header/HeaderLogoKZN.png'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
extra_css = """
|
||||
"""
|
||||
|
||||
remove_attributes = []
|
||||
remove_tags_before = dict(name='div', attrs={'class':['C_WholeContentPadding']})
|
||||
remove_tags_after = dict(name='div', attrs={'class':['SurroundingsContainer']})
|
||||
remove_tags = [dict(name='div', attrs={'class':['Details', 'buttons', 'SurroundingsContainer', 'breadcrumb']})]
|
||||
|
||||
keep_only_tags = []
|
40
recipes/lidovky.recipe
Normal file
@ -0,0 +1,40 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class lnRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'lidovky'
|
||||
publisher = u''
|
||||
description = 'lidovky.cz'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
|
||||
feeds = [
|
||||
(u'Události', u'http://www.lidovky.cz/export/rss.asp?r=ln_domov'),
|
||||
(u'Svět', u'http://www.lidovky.cz/export/rss.asp?r=ln_zahranici'),
|
||||
(u'Byznys', u'http://www.lidovky.cz/export/rss.asp?c=ln_byznys'),
|
||||
(u'Věda', u'http://www.lidovky.cz/export/rss.asp?r=ln_veda'),
|
||||
(u'Názory', u'http://www.lidovky.cz/export/rss.asp?r=ln_nazory'),
|
||||
(u'Relax', u'http://www.lidovky.cz/export/rss.asp?c=ln_relax')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://g.lidovky.cz/o/lidovky_ln3b/lidovky-logo.png'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
remove_attributes = []
|
||||
remove_tags_before = dict(name='div', attrs={'id':['content']})
|
||||
remove_tags_after = dict(name='div', attrs={'class':['authors']})
|
||||
preprocess_regexps = [(re.compile(r'<div id="(fb-root)".*', re.DOTALL|re.IGNORECASE), lambda match: '</body>')]
|
||||
|
||||
keep_only_tags = []
|
||||
|
||||
|
||||
|
||||
|
||||
|
29
recipes/metropol_tv.recipe
Normal file
@ -0,0 +1,29 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class metropolRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Metropol TV'
|
||||
publisher = u''
|
||||
description = 'metropol.cz'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
use_embedded_content = False
|
||||
|
||||
feeds = [
|
||||
(u'Metropolcv.cz', u'http://www.metropol.cz/rss/')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://www.metropol.cz/public/css/../images/logo/metropoltv.png'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
extra_css = """
|
||||
"""
|
||||
|
||||
remove_attributes = []
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':['art-full']})]
|
49
recipes/myapple_pl.recipe
Normal file
@ -0,0 +1,49 @@
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class MyAppleRecipe(BasicNewsRecipe):
|
||||
__license__ = 'GPL v3'
|
||||
__author__ = u'Artur Stachecki <artur.stachecki@gmail.com>'
|
||||
language = 'pl'
|
||||
version = 1
|
||||
|
||||
title = u'MyApple.pl'
|
||||
category = u'News'
|
||||
description = u' Największy w Polsce serwis zajmujący się tematyką związaną z Apple i wszelkimi produktami tej firmy.'
|
||||
cover_url=''
|
||||
remove_empty_feeds= True
|
||||
no_stylesheets=True
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100000
|
||||
recursions = 0
|
||||
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
simultaneous_downloads = 3
|
||||
|
||||
keep_only_tags =[]
|
||||
keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'article_content'}))
|
||||
|
||||
remove_tags =[]
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'article_author_date_comment_container'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'fullwidth'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'cmslinks'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'googleads-468'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'comments'}))
|
||||
|
||||
|
||||
extra_css = '''
|
||||
body {font-family: verdana, arial, helvetica, geneva, sans-serif ;}
|
||||
td.contentheading{font-size: large; font-weight: bold;}
|
||||
'''
|
||||
|
||||
feeds = [
|
||||
('News', 'feed://myapple.pl/external.php?do=rss&type=newcontent§ionid=1&days=120&count=10'),
|
||||
]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for alink in soup.findAll('a'):
|
||||
if alink.string is not None:
|
||||
tstr = alink.string
|
||||
alink.replaceWith(tstr)
|
||||
return soup
|
30
recipes/nadacni_fond_proti_korupci.recipe
Normal file
@ -0,0 +1,30 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class nfpkRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Nadační fond proti korupci'
|
||||
publisher = u''
|
||||
description = 'nfpk.cz'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 20
|
||||
use_embedded_content = False
|
||||
remove_empty_feeds = True
|
||||
|
||||
feeds = [
|
||||
(u'Aktuality', u'http://feeds.feedburner.com/nfpk')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://www.nfpk.cz/_templates/nfpk/_images/logo.gif'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
extra_css = """
|
||||
"""
|
||||
|
||||
remove_attributes = []
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'content'})]
|
||||
|
56
recipes/nepszabadsag.recipe
Normal file
@ -0,0 +1,56 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
'''
|
||||
Fetch Népszabadság
|
||||
'''
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class nepszabadsag(BasicNewsRecipe):
|
||||
title = u'N\u00e9pszabads\u00e1g'
|
||||
description = ''
|
||||
__author__ = 'bubak'
|
||||
use_embedded_content = False
|
||||
timefmt = ' [%d %b %Y]'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 20
|
||||
no_stylesheets = True
|
||||
language = 'hu'
|
||||
#delay = 1
|
||||
#timeout = 10
|
||||
simultaneous_downloads = 5
|
||||
|
||||
#encoding = 'utf-8'
|
||||
remove_javascript = True
|
||||
cover_url = 'http://nol.hu/_design/image/logo_nol_live.jpg'
|
||||
|
||||
feeds = [
|
||||
(u'Belföld', u'http://nol.hu/feed/belfold.rss')
|
||||
#,(u'Külföld', u'http://nol.hu/feed/kulfold.rss')
|
||||
#,(u'Gazdaság', u'http://nol.hu/feed/gazdasag.rss')
|
||||
#,(u'Kultúra', u'http://nol.hu/feed/kult.rss')
|
||||
]
|
||||
|
||||
extra_css = '''
|
||||
'''
|
||||
|
||||
remove_attributes = []
|
||||
remove_tags_before = dict(name='div', attrs={'class':['d-source']})
|
||||
remove_tags_after = dict(name='div', attrs={'class':['tags']})
|
||||
remove_tags = [dict(name='div', attrs={'class':['h']}),
|
||||
dict(name='tfoot')]
|
||||
|
||||
|
||||
keep_only_tags = [dict(name='table', attrs={'class':'article-box'})]
|
||||
|
||||
# NS sends an ad page sometimes but not frequently enough, TBD
|
||||
def AAskip_ad_pages(self, soup):
|
||||
if ('advertisement' in soup.find('title').string.lower()):
|
||||
href = soup.find('a').get('href')
|
||||
self.log.debug('Skipping to: ' + href)
|
||||
new = self.browser.open(href).read().decode('utf-8', 'ignore')
|
||||
#ipython(locals())
|
||||
self.log.debug('Finished: ' + href)
|
||||
return new
|
||||
else:
|
||||
return None
|
||||
|
32
recipes/neviditelny_pes.recipe
Normal file
@ -0,0 +1,32 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class pesRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Neviditelný pes'
|
||||
publisher = u''
|
||||
description = u'Neviditelný pes'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
use_embedded_content = False
|
||||
remove_empty_feeds = True
|
||||
|
||||
feeds = [
|
||||
(u'Neviditelný pes', u'http://neviditelnypes.lidovky.cz/export/rss.asp?c=pes_neviditelny')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://g.zpravy.cz/o/pes/logo_pes.jpg'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
extra_css = """
|
||||
"""
|
||||
|
||||
remove_tags = []
|
||||
remove_tags_before = dict(name='div', attrs={'id':'art-full'})
|
||||
remove_tags_after = dict(name='div', attrs={'id':'authors'})
|
||||
|
||||
|
50
recipes/novinky.cz.recipe
Normal file
@ -0,0 +1,50 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class novinkyRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'novinky.cz'
|
||||
publisher = u'seznam.cz'
|
||||
description = 'novinky.cz'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
|
||||
feeds = [
|
||||
(u'Domácí', u'http://www.novinky.cz/rss2/domaci/'),
|
||||
(u'Praha', u'http://www.novinky.cz/rss2/vase-zpravy/praha/'),
|
||||
(u'Ekonomika', u'http://www.novinky.cz/rss2/ekonomika/'),
|
||||
(u'Finance', u'http://www.novinky.cz/rss2/finance/'),
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'utf-8'
|
||||
language = 'cs'
|
||||
cover_url = 'http://www.novinky.cz/static/images/logo.gif'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'id':['pictureInnerBox']}),
|
||||
dict(name='div', attrs={'id':['discussionEntry']}),
|
||||
dict(name='span', attrs={'id':['mynews-hits', 'mynews-author']}),
|
||||
dict(name='div', attrs={'class':['related']}),
|
||||
dict(name='div', attrs={'id':['multimediaInfo']})]
|
||||
remove_tags_before = dict(name='div',attrs={'class':['articleHeader']})
|
||||
remove_tags_after = dict(name='div',attrs={'class':'related'})
|
||||
|
||||
keep_only_tags = []
|
||||
|
||||
# This source has identical articles under different links
|
||||
# which are redirected to the common url. I've found
|
||||
# just this API method that has the real URL
|
||||
visited_urls = {}
|
||||
def encoding(self, source):
|
||||
url = source.newurl
|
||||
if url in self.visited_urls:
|
||||
self.log.debug('Ignoring duplicate: ' + url)
|
||||
return None
|
||||
else:
|
||||
self.visited_urls[url] = True
|
||||
self.log.debug('Accepting: ' + url)
|
||||
return source.decode('utf-8', 'replace')
|
||||
|
38
recipes/parlamentni_listy.recipe
Normal file
@ -0,0 +1,38 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class plRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Parlamentn\u00ed Listy'
|
||||
publisher = u''
|
||||
description = ''
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
|
||||
feeds = [
|
||||
(u'Parlamentní listy.cz', u'http://www.parlamentnilisty.cz/export/rss.aspx')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://www.parlamentnilisty.cz/design/listy-logo2.png'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
remove_attributes = []
|
||||
remove_tags = [dict(name='div', attrs={'class':['articledetailboxin','crumbs', 'relatedarticles articledetailbox']}),
|
||||
dict(name='div', attrs={'class':['socialshare-1 noprint', 'socialshare-2 noprint']}),
|
||||
dict(name='div', attrs={'id':'widget'}),
|
||||
dict(name='div', attrs={'class':'article-discussion-box noprint'})]
|
||||
preprocess_regexps = [(re.compile(r'<(span|strong)[^>]*>\s*Ptejte se politik.*', re.DOTALL|re.IGNORECASE), lambda match: '</body>')]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':['article-detail']})]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
40
recipes/piratska_strana.recipe
Normal file
@ -0,0 +1,40 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class cpsRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Piratská strana'
|
||||
publisher = u''
|
||||
description = ''
|
||||
oldest_article = 3
|
||||
max_articles_per_feed = 20
|
||||
use_embedded_content = False
|
||||
remove_empty_feeds = True
|
||||
|
||||
feeds = [
|
||||
(u'Články', u'http://www.pirati.cz/rss.xml')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://www.pirati.cz/sites/all/themes/addari-cps/images/headbg.jpg'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
extra_css = """
|
||||
"""
|
||||
|
||||
remove_attributes = []
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'postarea'})]
|
||||
remove_tags = [dict(name='div', attrs={'class':['breadcrumb', 'submitted', 'links-readmore']}),
|
||||
dict(name='div', attrs={'id':['comments']})]
|
||||
remove_tags_before = dict(name='font', attrs={'size':'+3'})
|
||||
remove_tags_after = [dict(name='iframe')]
|
||||
|
||||
conversion_options = {'linearize_tables' : True}
|
||||
|
||||
|
||||
|
||||
|
||||
|
34
recipes/piratske_noviny.recipe
Normal file
@ -0,0 +1,34 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class nfpkRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Piratské noviny'
|
||||
publisher = u''
|
||||
description = 'nfpk.cz'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 20
|
||||
use_embedded_content = False
|
||||
remove_empty_feeds = True
|
||||
|
||||
feeds = [
|
||||
(u'Aktuality', u'http://www.piratskenoviny.cz/run/rss.php')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://www.piratskenoviny.cz/imgs/piratske-noviny.gif'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
extra_css = """
|
||||
"""
|
||||
|
||||
remove_attributes = []
|
||||
remove_tags_before = dict(name='font', attrs={'size':'+3'})
|
||||
remove_tags_after = [dict(name='iframe')]
|
||||
conversion_options = {'linearize_tables' : True}
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@ class AdvancedUserRecipe1348063712(BasicNewsRecipe):
|
||||
title = u'Portfolio.hu - English Edition'
|
||||
__author__ = 'laca'
|
||||
oldest_article = 7
|
||||
language = 'en_HUN'
|
||||
language = 'en_HU'
|
||||
masthead_url = 'http://www.portfolio.hu/img/sit/angolfejlec2010.jpg'
|
||||
use_embedded_content = False
|
||||
auto_cleanup = True
|
||||
|
64
recipes/pravo.recipe
Normal file
@ -0,0 +1,64 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class pravo(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = 'Právo'
|
||||
language = 'cs'
|
||||
|
||||
remove_tags_before = dict(name='div', attrs={'class':'rubrika-ostat'})
|
||||
remove_tags_after = dict(name='td', attrs={'class':'rubrika'})
|
||||
remove_tags = [dict(name='td', attrs={'width':'273'})
|
||||
,dict(name='td', attrs={'class':'rubrika'})
|
||||
,dict(name='div', attrs={'class':'rubrika-ostat'})
|
||||
]
|
||||
extra_css = '.nadpis {font-weight: bold; font-size: 130%;} .medium {text-align: justify;}'
|
||||
cover_url = 'http://pravo.novinky.cz/images/horni_6_logo.gif'
|
||||
cover_margins = (0, 100, '#ffffff')
|
||||
conversion_options = {'linearize_tables' : True}
|
||||
|
||||
no_stylesheets = True
|
||||
|
||||
# our variables
|
||||
seen_titles = set([])
|
||||
# only yesterday's articles are online
|
||||
parent_url = 'http://pravo.novinky.cz/minule/'
|
||||
feeds = [
|
||||
('Hlavní stránka', 'http://pravo.novinky.cz/minule/index.php'),
|
||||
('Zpravodajství', 'http://pravo.novinky.cz/minule/zpravodajstvi.php'),
|
||||
('Komentáře', 'http://pravo.novinky.cz/minule/komentare.php'),
|
||||
('Praha a střední Čechy', 'http://pravo.novinky.cz/minule/praha_stredni_cechy.php')
|
||||
]
|
||||
|
||||
|
||||
def parse_index(self):
|
||||
articles = []
|
||||
|
||||
for feed in self.feeds:
|
||||
articles.append(self.parse_page(feed))
|
||||
return articles
|
||||
|
||||
def parse_page(self, (feed_title, url)):
|
||||
articles = []
|
||||
|
||||
soup = self.index_to_soup(url)
|
||||
titles = soup.findAll('a', attrs={'class':'nadpis'})
|
||||
if titles is None:
|
||||
raise ValueError('Could not find any articles on page ' + url)
|
||||
|
||||
articles = []
|
||||
for article in titles:
|
||||
title = article.string
|
||||
if title in self.seen_titles:
|
||||
continue
|
||||
self.seen_titles.add(title)
|
||||
url = article['href']
|
||||
if not url.startswith('http'):
|
||||
url = self.parent_url + url
|
||||
self.log('\tFound article:', title, 'at', url)
|
||||
articles.append({'title':title.string, 'url':url, 'description':'',
|
||||
'date':''})
|
||||
return (feed_title, articles)
|
||||
|
37
recipes/respekt.recipe
Normal file
@ -0,0 +1,37 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
class respektRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Respekt'
|
||||
publisher = u'Respekt'
|
||||
description = 'Respekt'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
|
||||
feeds = [
|
||||
(u'Všechny články', u'http://respekt.ihned.cz/index.php?p=R00000_rss')
|
||||
,(u'Blogy', u'http://blog.respekt.ihned.cz/?p=Rb00VR_rss')
|
||||
#,(u'Respekt DJ', u'http://respekt.ihned.cz/index.php?p=R00RDJ_rss')
|
||||
]
|
||||
|
||||
|
||||
encoding = 'cp1250'
|
||||
language = 'cs'
|
||||
cover_url = 'http://respekt.ihned.cz/img/R/respekt_logo.png'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'class':['d-tools', 'actions']})]
|
||||
remove_tags_before = dict(name='div',attrs={'id':['detail']})
|
||||
remove_tags_after = dict(name='div',attrs={'class':'d-tools'})
|
||||
preprocess_regexps = [(re.compile(r'<div class="paid-zone".*', re.DOTALL|re.IGNORECASE), lambda match: 'Za zbytek článku je nutno platit. </body>'),
|
||||
(re.compile(r'.*<div class="mm-ow">', re.DOTALL|re.IGNORECASE), lambda match: '<body>'),
|
||||
(re.compile(r'<div class="col3">.*', re.DOTALL|re.IGNORECASE), lambda match: '</body>')]
|
||||
|
||||
keep_only_tags = []
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@ class AdvancedUserRecipe1303841067(BasicNewsRecipe):
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
language = 'de_DE'
|
||||
language = 'de'
|
||||
|
||||
#conversion_options = {'base_font_size': 20}
|
||||
|
||||
|
67
recipes/telepolis_pl.recipe
Normal file
@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
|
||||
|
||||
class telepolis(BasicNewsRecipe):
|
||||
title = u'Telepolis.pl'
|
||||
__author__ = 'Artur Stachecki <artur.stachecki@gmail.com>'
|
||||
language = 'pl'
|
||||
description = u'Twój telekomunikacyjny serwis informacyjny.\
|
||||
Codzienne informacje, testy i artykuły,\
|
||||
promocje, baza telefonów oraz centrum rozrywki'
|
||||
oldest_article = 7
|
||||
masthead_url = 'http://telepolis.pl/i/telepolis-logo2.gif'
|
||||
max_articles_per_feed = 100
|
||||
simultaneous_downloads = 5
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
|
||||
remove_tags = []
|
||||
remove_tags.append(dict(attrs={'alt': 'TELEPOLIS.pl'}))
|
||||
|
||||
preprocess_regexps = [(re.compile(r'<: .*? :>'),
|
||||
lambda match: ''),
|
||||
(re.compile(r'<b>Zobacz:</b>.*?</a>', re.DOTALL),
|
||||
lambda match: ''),
|
||||
(re.compile(r'<-ankieta.*?>'),
|
||||
lambda match: ''),
|
||||
(re.compile(r'\(Q\!\)'),
|
||||
lambda match: ''),
|
||||
(re.compile(r'\(plik.*?\)'),
|
||||
lambda match: ''),
|
||||
(re.compile(r'<br.*?><br.*?>', re.DOTALL),
|
||||
lambda match: '')
|
||||
]
|
||||
|
||||
extra_css = '''.tb { font-weight: bold; font-size: 20px;}'''
|
||||
|
||||
feeds = [
|
||||
(u'Wiadomości', u'http://www.telepolis.pl/rss/news.php'),
|
||||
(u'Artykuły', u'http://www.telepolis.pl/rss/artykuly.php')
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
if 'news.php' in url:
|
||||
print_url = url.replace('news.php', 'news_print.php')
|
||||
else:
|
||||
print_url = url.replace('artykuly.php', 'art_print.php')
|
||||
return print_url
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for image in soup.findAll('img'):
|
||||
if 'm.jpg' in image['src']:
|
||||
image_big = image['src']
|
||||
image_big = image_big.replace('m.jpg', '.jpg')
|
||||
image['src'] = image_big
|
||||
logo = soup.find('tr')
|
||||
logo.extract()
|
||||
for tag in soup.findAll('tr'):
|
||||
for strings in ['Wiadomość wydrukowana', 'copyright']:
|
||||
if strings in self.tag_to_string(tag):
|
||||
tag.extract()
|
||||
return self.adeify_images(soup)
|
44
recipes/tyden.cz.recipe
Normal file
@ -0,0 +1,44 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import unicode_literals
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class tydenRecipe(BasicNewsRecipe):
|
||||
__author__ = 'bubak'
|
||||
title = u'Tyden.cz'
|
||||
publisher = u''
|
||||
description = ''
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 20
|
||||
|
||||
feeds = [
|
||||
(u'Domácí', u'http://www.tyden.cz/rss/rss.php?rubrika_id=6'),
|
||||
(u'Politika', u'http://www.tyden.cz/rss/rss.php?rubrika_id=173'),
|
||||
(u'Kauzy', u'http://www.tyden.cz/rss/rss.php?rubrika_id=340')
|
||||
]
|
||||
|
||||
|
||||
#encoding = 'iso-8859-2'
|
||||
language = 'cs'
|
||||
cover_url = 'http://www.tyden.cz/img/tyden-logo.png'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
remove_attributes = []
|
||||
remove_tags_before = dict(name='p', attrs={'id':['breadcrumbs']})
|
||||
remove_tags_after = dict(name='p', attrs={'class':['author']})
|
||||
|
||||
visited_urls = {}
|
||||
def get_article_url(self, article):
|
||||
url = BasicNewsRecipe.get_article_url(self, article)
|
||||
if url in self.visited_urls:
|
||||
self.log.debug('Ignoring duplicate: ' + url)
|
||||
return None
|
||||
else:
|
||||
self.visited_urls[url] = True
|
||||
self.log.debug('Accepting: ' + url)
|
||||
return url
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@ class AdvancedUserRecipe1347997197(BasicNewsRecipe):
|
||||
title = u'XpatLoop.com'
|
||||
__author__ = 'laca'
|
||||
oldest_article = 7
|
||||
language = 'en_HUN'
|
||||
language = 'en_HU'
|
||||
auto_cleanup = True
|
||||
masthead_url = 'http://www.xpatloop.com/images/cms/xs_logo.gif'
|
||||
use_embedded_content = False
|
||||
|
@ -16,7 +16,7 @@ class ZeitDe(BasicNewsRecipe):
|
||||
category = 'news, Germany'
|
||||
timefmt = ' [%a, %d %b %Y]'
|
||||
publication_type = 'newspaper'
|
||||
language = 'de_DE'
|
||||
language = 'de'
|
||||
encoding = 'UTF-8'
|
||||
|
||||
oldest_article = 7
|
||||
|
@ -308,6 +308,10 @@ class FileTypePlugin(Plugin): # {{{
|
||||
#: to the database
|
||||
on_import = False
|
||||
|
||||
#: If True, this plugin is run after books are added
|
||||
#: to the database
|
||||
on_postimport = False
|
||||
|
||||
#: If True, this plugin is run just before a conversion
|
||||
on_preprocess = False
|
||||
|
||||
@ -337,6 +341,16 @@ class FileTypePlugin(Plugin): # {{{
|
||||
# Default implementation does nothing
|
||||
return path_to_ebook
|
||||
|
||||
def postimport(self, book_id, book_format, db):
|
||||
'''
|
||||
Called post import, i.e., after the book file has been added to the database.
|
||||
|
||||
:param book_id: Database id of the added book.
|
||||
:param book_format: The file type of the book that was added.
|
||||
:param db: Library database.
|
||||
'''
|
||||
pass # Default implementation does nothing
|
||||
|
||||
# }}}
|
||||
|
||||
class MetadataReaderPlugin(Plugin): # {{{
|
||||
@ -447,8 +461,8 @@ class CatalogPlugin(Plugin): # {{{
|
||||
# Return a list of requested fields, with opts.sort_by first
|
||||
all_std_fields = set(
|
||||
['author_sort','authors','comments','cover','formats',
|
||||
'id','isbn','ondevice','pubdate','publisher','rating',
|
||||
'series_index','series','size','tags','timestamp',
|
||||
'id','isbn','library_name','ondevice','pubdate','publisher',
|
||||
'rating','series_index','series','size','tags','timestamp',
|
||||
'title_sort','title','uuid','languages','identifiers'])
|
||||
all_custom_fields = set(db.custom_field_keys())
|
||||
for field in list(all_custom_fields):
|
||||
@ -460,6 +474,16 @@ class CatalogPlugin(Plugin): # {{{
|
||||
if opts.fields != 'all':
|
||||
# Make a list from opts.fields
|
||||
requested_fields = set(opts.fields.split(','))
|
||||
|
||||
# Validate requested_fields
|
||||
if requested_fields - all_fields:
|
||||
from calibre.library import current_library_name
|
||||
invalid_fields = sorted(list(requested_fields - all_fields))
|
||||
print("invalid --fields specified: %s" % ', '.join(invalid_fields))
|
||||
print("available fields in '%s': %s" %
|
||||
(current_library_name(), ', '.join(sorted(list(all_fields)))))
|
||||
raise ValueError("unable to generate catalog with specified fields")
|
||||
|
||||
fields = list(all_fields & requested_fields)
|
||||
else:
|
||||
fields = list(all_fields)
|
||||
|
@ -104,14 +104,17 @@ def is_disabled(plugin):
|
||||
# File type plugins {{{
|
||||
|
||||
_on_import = {}
|
||||
_on_postimport = {}
|
||||
_on_preprocess = {}
|
||||
_on_postprocess = {}
|
||||
|
||||
def reread_filetype_plugins():
|
||||
global _on_import
|
||||
global _on_postimport
|
||||
global _on_preprocess
|
||||
global _on_postprocess
|
||||
_on_import = {}
|
||||
_on_postimport = {}
|
||||
_on_preprocess = {}
|
||||
_on_postprocess = {}
|
||||
|
||||
@ -122,6 +125,10 @@ def reread_filetype_plugins():
|
||||
if not _on_import.has_key(ft):
|
||||
_on_import[ft] = []
|
||||
_on_import[ft].append(plugin)
|
||||
if plugin.on_postimport:
|
||||
if not _on_postimport.has_key(ft):
|
||||
_on_postimport[ft] = []
|
||||
_on_postimport[ft].append(plugin)
|
||||
if plugin.on_preprocess:
|
||||
if not _on_preprocess.has_key(ft):
|
||||
_on_preprocess[ft] = []
|
||||
@ -163,6 +170,22 @@ run_plugins_on_preprocess = functools.partial(_run_filetype_plugins,
|
||||
occasion='preprocess')
|
||||
run_plugins_on_postprocess = functools.partial(_run_filetype_plugins,
|
||||
occasion='postprocess')
|
||||
|
||||
def run_plugins_on_postimport(db, book_id, fmt):
|
||||
customization = config['plugin_customization']
|
||||
fmt = fmt.lower()
|
||||
for plugin in _on_postimport.get(fmt, []):
|
||||
if is_disabled(plugin):
|
||||
continue
|
||||
plugin.site_customization = customization.get(plugin.name, '')
|
||||
with plugin:
|
||||
try:
|
||||
plugin.postimport(book_id, fmt, db)
|
||||
except:
|
||||
print ('Running file type plugin %s failed with traceback:'%
|
||||
plugin.name)
|
||||
traceback.print_exc()
|
||||
|
||||
# }}}
|
||||
|
||||
# Plugin customization {{{
|
||||
|
@ -19,9 +19,9 @@ class TECLAST_K3(USBMS):
|
||||
PRODUCT_ID = [0x3203]
|
||||
BCD = [0x0000, 0x0100]
|
||||
|
||||
VENDOR_NAME = ['TECLAST', 'IMAGIN', 'RK28XX', 'PER3274B']
|
||||
VENDOR_NAME = ['TECLAST', 'IMAGIN', 'RK28XX', 'PER3274B', 'BEBOOK']
|
||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['DIGITAL_PLAYER', 'TL-K5',
|
||||
'EREADER', 'USB-MSC', 'PER3274B']
|
||||
'EREADER', 'USB-MSC', 'PER3274B', 'BEBOOK']
|
||||
|
||||
MAIN_MEMORY_VOLUME_LABEL = 'K3 Main Memory'
|
||||
STORAGE_CARD_VOLUME_LABEL = 'K3 Storage Card'
|
||||
|
@ -263,8 +263,11 @@ def generate_masthead(title, output_path=None, width=600, height=60):
|
||||
masthead_font_family = recs.get('masthead_font', 'Default')
|
||||
|
||||
if masthead_font_family != 'Default':
|
||||
from calibre.utils.fonts.scanner import font_scanner
|
||||
faces = font_scanner.fonts_for_family(masthead_font_family)
|
||||
from calibre.utils.fonts.scanner import font_scanner, NoFonts
|
||||
try:
|
||||
faces = font_scanner.fonts_for_family(masthead_font_family)
|
||||
except NoFonts:
|
||||
faces = []
|
||||
if faces:
|
||||
font_path = faces[0]['path']
|
||||
|
||||
|
@ -1103,10 +1103,14 @@ OptionRecommendation(name='search_replace',
|
||||
from calibre.ebooks.oeb.transforms.unsmarten import UnsmartenPunctuation
|
||||
UnsmartenPunctuation()(self.oeb, self.opts)
|
||||
|
||||
mobi_file_type = getattr(self.opts, 'mobi_file_type', 'old')
|
||||
needs_old_markup = (self.output_plugin.file_type == 'lit' or
|
||||
(self.output_plugin.file_type == 'mobi' and mobi_file_type
|
||||
== 'old'))
|
||||
flattener = CSSFlattener(fbase=fbase, fkey=fkey,
|
||||
lineh=line_height,
|
||||
untable=self.output_plugin.file_type in ('mobi','lit'),
|
||||
unfloat=self.output_plugin.file_type in ('mobi', 'lit'),
|
||||
untable=needs_old_markup,
|
||||
unfloat=needs_old_markup,
|
||||
page_break_on_body=self.output_plugin.file_type in ('mobi',
|
||||
'lit'),
|
||||
specializer=partial(self.output_plugin.specialize_css_for_output,
|
||||
|
@ -322,6 +322,10 @@ class MobiMLizer(object):
|
||||
istates.append(istate)
|
||||
left = 0
|
||||
display = style['display']
|
||||
if display == 'table-cell':
|
||||
display = 'inline'
|
||||
elif display.startswith('table'):
|
||||
display = 'block'
|
||||
isblock = (not display.startswith('inline') and style['display'] !=
|
||||
'none')
|
||||
isblock = isblock and style['float'] == 'none'
|
||||
|
@ -143,7 +143,7 @@ class EbookIterator(BookmarksMixin):
|
||||
|
||||
cover = self.opf.cover
|
||||
if cover and self.ebook_ext in {'lit', 'mobi', 'prc', 'opf', 'fb2',
|
||||
'azw', 'azw3'}:
|
||||
'azw', 'azw3'}:
|
||||
cfile = os.path.join(self.base, 'calibre_iterator_cover.html')
|
||||
rcpath = os.path.relpath(cover, self.base).replace(os.sep, '/')
|
||||
chtml = (TITLEPAGE%prepare_string_for_xml(rcpath, True)).encode('utf-8')
|
||||
|
@ -93,6 +93,7 @@ gprefs.defaults['tag_browser_dont_collapse'] = []
|
||||
gprefs.defaults['edit_metadata_single_layout'] = 'default'
|
||||
gprefs.defaults['default_author_link'] = 'http://en.wikipedia.org/w/index.php?search={author}'
|
||||
gprefs.defaults['preserve_date_on_ctl'] = True
|
||||
gprefs.defaults['manual_add_auto_convert'] = False
|
||||
gprefs.defaults['cb_fullscreen'] = False
|
||||
gprefs.defaults['worker_max_time'] = 0
|
||||
gprefs.defaults['show_files_after_save'] = True
|
||||
|
@ -320,15 +320,23 @@ class ChooseLibraryAction(InterfaceAction):
|
||||
_('Path to library too long. Must be less than'
|
||||
' %d characters.')%LibraryDatabase2.WINDOWS_LIBRARY_PATH_LIMIT,
|
||||
show=True)
|
||||
if not os.path.exists(loc):
|
||||
error_dialog(self.gui, _('Not found'),
|
||||
_('Cannot rename as no library was found at %s. '
|
||||
'Try switching to this library first, then switch back '
|
||||
'and retry the renaming.')%loc, show=True)
|
||||
return
|
||||
try:
|
||||
os.rename(loc, newloc)
|
||||
except:
|
||||
import traceback
|
||||
det_msg = 'Location: %r New Location: %r\n%s'%(loc, newloc,
|
||||
traceback.format_exc())
|
||||
error_dialog(self.gui, _('Rename failed'),
|
||||
_('Failed to rename the library at %s. '
|
||||
'The most common cause for this is if one of the files'
|
||||
' in the library is open in another program.') % loc,
|
||||
det_msg=traceback.format_exc(), show=True)
|
||||
det_msg=det_msg, show=True)
|
||||
return
|
||||
self.stats.rename(location, newloc)
|
||||
self.build_menus()
|
||||
|
@ -267,8 +267,8 @@ class ViewAction(InterfaceAction):
|
||||
|
||||
def _view_books(self, rows):
|
||||
if not rows or len(rows) == 0:
|
||||
self._launch_viewer()
|
||||
return
|
||||
return error_dialog(self.gui, _('Cannot view'),
|
||||
_('No books selected'), show=True)
|
||||
|
||||
if not self._view_check(len(rows)):
|
||||
return
|
||||
|
@ -42,6 +42,7 @@ class DuplicatesAdder(QObject): # {{{
|
||||
# here we add all the formats for dupe book record created above
|
||||
self.db_adder.add_formats(id, formats)
|
||||
self.db_adder.number_of_books_added += 1
|
||||
self.db_adder.auto_convert_books.add(id)
|
||||
self.count += 1
|
||||
self.added.emit(self.count)
|
||||
single_shot(self.add_one)
|
||||
@ -107,8 +108,16 @@ class DBAdder(QObject): # {{{
|
||||
self.input_queue = Queue()
|
||||
self.output_queue = Queue()
|
||||
self.merged_books = set([])
|
||||
self.auto_convert_books = set()
|
||||
|
||||
def end(self):
|
||||
if (gprefs['manual_add_auto_convert'] and
|
||||
self.auto_convert_books):
|
||||
from calibre.gui2.ui import get_gui
|
||||
gui = get_gui()
|
||||
gui.iactions['Convert Books'].auto_convert_auto_add(
|
||||
self.auto_convert_books)
|
||||
|
||||
self.input_queue.put((None, None, None))
|
||||
|
||||
def start(self):
|
||||
@ -152,7 +161,6 @@ class DBAdder(QObject): # {{{
|
||||
fmts[-1] = fmt
|
||||
return fmts
|
||||
|
||||
|
||||
def add(self, id, opf, cover, name):
|
||||
formats = self.ids.pop(id)
|
||||
if opf.endswith('.error'):
|
||||
@ -219,6 +227,7 @@ class DBAdder(QObject): # {{{
|
||||
self.duplicates.append((mi, cover, orig_formats))
|
||||
else:
|
||||
self.add_formats(id_, formats)
|
||||
self.auto_convert_books.add(id_)
|
||||
self.number_of_books_added += 1
|
||||
else:
|
||||
self.names.append(name)
|
||||
|
@ -1057,6 +1057,7 @@ class DeviceBooksModel(BooksModel): # {{{
|
||||
|
||||
booklist_dirtied = pyqtSignal()
|
||||
upload_collections = pyqtSignal(object)
|
||||
resize_rows = pyqtSignal()
|
||||
|
||||
def __init__(self, parent):
|
||||
BooksModel.__init__(self, parent)
|
||||
@ -1163,6 +1164,11 @@ class DeviceBooksModel(BooksModel): # {{{
|
||||
return flags
|
||||
|
||||
def search(self, text, reset=True):
|
||||
# This should not be here, but since the DeviceBooksModel does not
|
||||
# implement count_changed and I am too lazy to fix that, this kludge
|
||||
# will have to do
|
||||
self.resize_rows.emit()
|
||||
|
||||
if not text or not text.strip():
|
||||
self.map = list(range(len(self.db)))
|
||||
else:
|
||||
|
@ -90,6 +90,7 @@ class BooksView(QTableView): # {{{
|
||||
|
||||
def __init__(self, parent, modelcls=BooksModel, use_edit_metadata_dialog=True):
|
||||
QTableView.__init__(self, parent)
|
||||
self.row_sizing_done = False
|
||||
|
||||
if not tweaks['horizontal_scrolling_per_column']:
|
||||
self.setHorizontalScrollMode(self.ScrollPerPixel)
|
||||
@ -141,6 +142,8 @@ class BooksView(QTableView): # {{{
|
||||
self.display_parent = parent
|
||||
self._model = modelcls(self)
|
||||
self.setModel(self._model)
|
||||
self._model.count_changed_signal.connect(self.do_row_sizing,
|
||||
type=Qt.QueuedConnection)
|
||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.setSortingEnabled(True)
|
||||
self.selectionModel().currentRowChanged.connect(self._model.current_changed)
|
||||
@ -521,13 +524,17 @@ class BooksView(QTableView): # {{{
|
||||
self.apply_state(old_state, max_sort_levels=max_levels)
|
||||
self.column_header.blockSignals(False)
|
||||
|
||||
# Resize all rows to have the correct height
|
||||
if self.model().rowCount(QModelIndex()) > 0:
|
||||
self.resizeRowToContents(0)
|
||||
self.verticalHeader().setDefaultSectionSize(self.rowHeight(0))
|
||||
self.do_row_sizing()
|
||||
|
||||
self.was_restored = True
|
||||
|
||||
def do_row_sizing(self):
|
||||
# Resize all rows to have the correct height
|
||||
if not self.row_sizing_done and self.model().rowCount(QModelIndex()) > 0:
|
||||
self.resizeRowToContents(0)
|
||||
self.verticalHeader().setDefaultSectionSize(self.rowHeight(0))
|
||||
self.row_sizing_done = True
|
||||
|
||||
def resize_column_to_fit(self, column):
|
||||
col = self.column_map.index(column)
|
||||
self.column_resized(col, self.columnWidth(col), self.columnWidth(col))
|
||||
@ -943,6 +950,8 @@ class DeviceBooksView(BooksView): # {{{
|
||||
def __init__(self, parent):
|
||||
BooksView.__init__(self, parent, DeviceBooksModel,
|
||||
use_edit_metadata_dialog=False)
|
||||
self._model.resize_rows.connect(self.do_row_sizing,
|
||||
type=Qt.QueuedConnection)
|
||||
self.can_add_columns = False
|
||||
self.columns_resized = False
|
||||
self.resize_on_select = False
|
||||
|
@ -867,6 +867,7 @@ class Cover(ImageView): # {{{
|
||||
|
||||
def __init__(self, parent):
|
||||
ImageView.__init__(self, parent)
|
||||
self.show_size = True
|
||||
self.dialog = parent
|
||||
self._cdata = None
|
||||
self.cover_changed.connect(self.set_pixmap_from_data)
|
||||
|
@ -28,6 +28,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
r('swap_author_names', prefs)
|
||||
r('add_formats_to_existing', prefs)
|
||||
r('preserve_date_on_ctl', gprefs)
|
||||
r('manual_add_auto_convert', gprefs)
|
||||
choices = [
|
||||
(_('Ignore duplicate incoming formats'), 'ignore'),
|
||||
(_('Overwrite existing duplicate formats'), 'overwrite'),
|
||||
|
@ -6,7 +6,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>753</width>
|
||||
<width>1035</width>
|
||||
<height>547</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -24,6 +24,36 @@
|
||||
<string>The Add &Process</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_add_formats_to_existing">
|
||||
<property name="toolTip">
|
||||
<string>Automerge: If books with similar titles and authors found, merge the incoming formats automatically into
|
||||
existing book records. The box to the right controls what happens when an existing record already has
|
||||
the incoming format. Note that this option also affects the Copy to library action.
|
||||
|
||||
Title match ignores leading indefinite articles ("the", "a", "an"), punctuation, case, etc. Author match is exact.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Automerge added books if they already exist in the calibre library:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QComboBox" name="opt_automerge">
|
||||
<property name="toolTip">
|
||||
<string>Automerge: If books with similar titles and authors found, merge the incoming formats automatically into
|
||||
existing book records. This box controls what happens when an existing record already has
|
||||
the incoming format:
|
||||
|
||||
Ignore duplicate incoming files - means that existing files in your calibre library will not be replaced
|
||||
Overwrite existing duplicate files - means that existing files in your calibre library will be replaced
|
||||
Create new record for each duplicate file - means that a new book entry will be created for each duplicate file
|
||||
|
||||
Title matching ignores leading indefinite articles ("the", "a", "an"), punctuation, case, etc.
|
||||
Author matching is exact.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
@ -68,44 +98,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="opt_preserve_date_on_ctl">
|
||||
<property name="text">
|
||||
<string>When using the "&Copy to library" action to copy books between libraries, preserve the date</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_add_formats_to_existing">
|
||||
<property name="toolTip">
|
||||
<string>Automerge: If books with similar titles and authors found, merge the incoming formats automatically into
|
||||
existing book records. The box to the right controls what happens when an existing record already has
|
||||
the incoming format. Note that this option also affects the Copy to library action.
|
||||
|
||||
Title match ignores leading indefinite articles ("the", "a", "an"), punctuation, case, etc. Author match is exact.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Automerge added books if they already exist in the calibre library:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QComboBox" name="opt_automerge">
|
||||
<property name="toolTip">
|
||||
<string>Automerge: If books with similar titles and authors found, merge the incoming formats automatically into
|
||||
existing book records. This box controls what happens when an existing record already has
|
||||
the incoming format:
|
||||
|
||||
Ignore duplicate incoming files - means that existing files in your calibre library will not be replaced
|
||||
Overwrite existing duplicate files - means that existing files in your calibre library will be replaced
|
||||
Create new record for each duplicate file - means that a new book entry will be created for each duplicate file
|
||||
|
||||
Title matching ignores leading indefinite articles ("the", "a", "an"), punctuation, case, etc.
|
||||
Author matching is exact.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_230">
|
||||
<property name="text">
|
||||
<string>&Tags to apply when adding a book:</string>
|
||||
@ -115,14 +108,14 @@ Author matching is exact.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<item row="5" column="2">
|
||||
<widget class="QLineEdit" name="opt_new_book_tags">
|
||||
<property name="toolTip">
|
||||
<string>A comma-separated list of tags that will be applied to books added to the library</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="3">
|
||||
<item row="6" column="0" colspan="3">
|
||||
<widget class="QGroupBox" name="metadata_box">
|
||||
<property name="title">
|
||||
<string>&Configure metadata from file name</string>
|
||||
@ -144,6 +137,20 @@ Author matching is exact.</string>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="opt_preserve_date_on_ctl">
|
||||
<property name="text">
|
||||
<string>When using the "&Copy to library" action to copy books between libraries, preserve the date</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_manual_add_auto_convert">
|
||||
<property name="text">
|
||||
<string>Automatically &convert added books to the current output format</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_4">
|
||||
|
@ -16,8 +16,6 @@ from calibre.gui2 import NONE, FunctionDispatcher
|
||||
from calibre.gui2.store.search_result import SearchResult
|
||||
from calibre.gui2.store.search.download_thread import DetailsThreadPool, \
|
||||
CoverThreadPool
|
||||
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \
|
||||
REGEXP_MATCH
|
||||
from calibre.utils.icu import sort_key
|
||||
from calibre.utils.search_query_parser import SearchQueryParser
|
||||
|
||||
@ -153,7 +151,7 @@ class Matches(QAbstractItemModel):
|
||||
mod_query = query
|
||||
# Remove filter identifiers
|
||||
# Remove the prefix.
|
||||
for loc in ('all', 'author', 'authors', 'title'):
|
||||
for loc in ('all', 'author', 'author2', 'authors', 'title', 'title2'):
|
||||
query = re.sub(r'%s:"(?P<a>[^\s"]+)"' % loc, '\g<a>', query)
|
||||
query = query.replace('%s:' % loc, '')
|
||||
# Remove the prefix and search text.
|
||||
@ -301,11 +299,16 @@ class Matches(QAbstractItemModel):
|
||||
|
||||
|
||||
class SearchFilter(SearchQueryParser):
|
||||
CONTAINS_MATCH = 0
|
||||
EQUALS_MATCH = 1
|
||||
REGEXP_MATCH = 2
|
||||
IN_MATCH = 3
|
||||
|
||||
USABLE_LOCATIONS = [
|
||||
'all',
|
||||
'affiliate',
|
||||
'author',
|
||||
'author2',
|
||||
'authors',
|
||||
'cover',
|
||||
'download',
|
||||
@ -315,6 +318,7 @@ class SearchFilter(SearchQueryParser):
|
||||
'formats',
|
||||
'price',
|
||||
'title',
|
||||
'title2',
|
||||
'store',
|
||||
]
|
||||
|
||||
@ -331,6 +335,26 @@ class SearchFilter(SearchQueryParser):
|
||||
def universal_set(self):
|
||||
return self.srs
|
||||
|
||||
def _match(self, query, value, matchkind):
|
||||
for t in value:
|
||||
try: ### ignore regexp exceptions, required because search-ahead tries before typing is finished
|
||||
t = icu_lower(t)
|
||||
if matchkind == self.EQUALS_MATCH:
|
||||
if query == t:
|
||||
return True
|
||||
elif matchkind == self.REGEXP_MATCH:
|
||||
if re.search(query, t, re.I|re.UNICODE):
|
||||
return True
|
||||
elif matchkind == self.CONTAINS_MATCH:
|
||||
if query in t:
|
||||
return True
|
||||
elif matchkind == self.IN_MATCH:
|
||||
if t in query:
|
||||
return True
|
||||
except re.error:
|
||||
pass
|
||||
return False
|
||||
|
||||
def get_matches(self, location, query):
|
||||
query = query.strip()
|
||||
location = location.lower().strip()
|
||||
@ -341,17 +365,17 @@ class SearchFilter(SearchQueryParser):
|
||||
elif location == 'formats':
|
||||
location = 'format'
|
||||
|
||||
matchkind = CONTAINS_MATCH
|
||||
matchkind = self.CONTAINS_MATCH
|
||||
if len(query) > 1:
|
||||
if query.startswith('\\'):
|
||||
query = query[1:]
|
||||
elif query.startswith('='):
|
||||
matchkind = EQUALS_MATCH
|
||||
matchkind = self.EQUALS_MATCH
|
||||
query = query[1:]
|
||||
elif query.startswith('~'):
|
||||
matchkind = REGEXP_MATCH
|
||||
matchkind = self.REGEXP_MATCH
|
||||
query = query[1:]
|
||||
if matchkind != REGEXP_MATCH: ### leave case in regexps because it can be significant e.g. \S \W \D
|
||||
if matchkind != self.REGEXP_MATCH: ### leave case in regexps because it can be significant e.g. \S \W \D
|
||||
query = query.lower()
|
||||
|
||||
if location not in self.USABLE_LOCATIONS:
|
||||
@ -372,6 +396,8 @@ class SearchFilter(SearchQueryParser):
|
||||
}
|
||||
for x in ('author', 'download', 'format'):
|
||||
q[x+'s'] = q[x]
|
||||
q['author2'] = q['author']
|
||||
q['title2'] = q['title']
|
||||
|
||||
# make the price in query the same format as result
|
||||
if location == 'price':
|
||||
@ -415,16 +441,19 @@ class SearchFilter(SearchQueryParser):
|
||||
### Can't separate authors because comma is used for name sep and author sep
|
||||
### Exact match might not get what you want. For that reason, turn author
|
||||
### exactmatch searches into contains searches.
|
||||
if locvalue == 'author' and matchkind == EQUALS_MATCH:
|
||||
m = CONTAINS_MATCH
|
||||
if locvalue == 'author' and matchkind == self.EQUALS_MATCH:
|
||||
m = self.CONTAINS_MATCH
|
||||
else:
|
||||
m = matchkind
|
||||
|
||||
if locvalue == 'format':
|
||||
vals = accessor(sr).split(',')
|
||||
elif locvalue in ('author2', 'title2'):
|
||||
m = self.IN_MATCH
|
||||
vals = re.sub(r'(^|\s)(and|not|or|a|the|is|of|,)(\s|$)', ' ', accessor(sr)).split(' ')
|
||||
else:
|
||||
vals = [accessor(sr)]
|
||||
if _match(query, vals, m):
|
||||
if self._match(query, vals, m):
|
||||
matches.add(sr)
|
||||
break
|
||||
except ValueError: # Unicode errors
|
||||
|
@ -13,7 +13,7 @@ from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QTimer, QCheckBox, QLabel,
|
||||
QVBoxLayout, QIcon, QWidget, QTabWidget, QGridLayout,
|
||||
QComboBox)
|
||||
|
||||
from calibre.gui2 import JSONConfig, info_dialog
|
||||
from calibre.gui2 import JSONConfig, info_dialog, error_dialog
|
||||
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
|
||||
from calibre.gui2.progress_indicator import ProgressIndicator
|
||||
from calibre.gui2.store.config.chooser.chooser_widget import StoreChooserWidget
|
||||
@ -31,6 +31,8 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
self.setupUi(self)
|
||||
|
||||
self.config = JSONConfig('store/search')
|
||||
self.search_title.initialize('store_search_search_title')
|
||||
self.search_author.initialize('store_search_search_author')
|
||||
self.search_edit.initialize('store_search_search')
|
||||
|
||||
# Loads variables that store various settings.
|
||||
@ -60,13 +62,24 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
self.setup_store_checks()
|
||||
|
||||
# Set the search query
|
||||
# Title
|
||||
self.search_title.setText(query)
|
||||
self.search_title.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLengthWithIcon)
|
||||
self.search_title.setMinimumContentsLength(25)
|
||||
# Author
|
||||
self.search_author.setText(query)
|
||||
self.search_author.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLengthWithIcon)
|
||||
self.search_author.setMinimumContentsLength(25)
|
||||
# Keyword
|
||||
self.search_edit.setText(query)
|
||||
self.search_edit.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLengthWithIcon)
|
||||
self.search_edit.setMinimumContentsLength(25)
|
||||
|
||||
# Create and add the progress indicator
|
||||
self.pi = ProgressIndicator(self, 24)
|
||||
self.top_layout.addWidget(self.pi)
|
||||
self.button_layout.takeAt(0)
|
||||
self.button_layout.setAlignment(Qt.AlignCenter)
|
||||
self.button_layout.insertWidget(0, self.pi, 0, Qt.AlignCenter)
|
||||
|
||||
self.adv_search_button.setIcon(QIcon(I('search.png')))
|
||||
self.configure.setIcon(QIcon(I('config.png')))
|
||||
@ -152,8 +165,18 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
self.results_view.model().clear_results()
|
||||
|
||||
# Don't start a search if there is nothing to search for.
|
||||
query = unicode(self.search_edit.text())
|
||||
query = []
|
||||
if self.search_title.text():
|
||||
query.append(u'title2:"~%s"' % unicode(self.search_title.text()).replace('"', ' '))
|
||||
if self.search_author.text():
|
||||
query.append(u'author2:"%s"' % unicode(self.search_author.text()).replace('"', ' '))
|
||||
if self.search_edit.text():
|
||||
query.append(unicode(self.search_edit.text()))
|
||||
query = " ".join(query)
|
||||
if not query.strip():
|
||||
error_dialog(self, _('No query'),
|
||||
_('You must enter a title, author or keyword to'
|
||||
' search for.'), show=True)
|
||||
return
|
||||
# Give the query to the results model so it can do
|
||||
# futher filtering.
|
||||
@ -188,7 +211,7 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
query = query.replace('>', '')
|
||||
query = query.replace('<', '')
|
||||
# Remove the prefix.
|
||||
for loc in ('all', 'author', 'authors', 'title'):
|
||||
for loc in ('all', 'author', 'author2', 'authors', 'title', 'title2'):
|
||||
query = re.sub(r'%s:"(?P<a>[^\s"]+)"' % loc, '\g<a>', query)
|
||||
query = query.replace('%s:' % loc, '')
|
||||
# Remove the prefix and search text.
|
||||
|
@ -14,49 +14,74 @@
|
||||
<string>Get Books</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset>
|
||||
<iconset resource="../../../../../resources/images.qrc">
|
||||
<normaloff>:/images/store.png</normaloff>:/images/store.png</iconset>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="top_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Query:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="adv_search_button">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="HistoryLineEdit" name="search_edit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="search">
|
||||
<property name="text">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="2">
|
||||
<widget class="HistoryLineEdit" name="search_title">
|
||||
<property name="placeholderText">
|
||||
<string>Search by title</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>&Author:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>search_author</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="HistoryLineEdit" name="search_author">
|
||||
<property name="placeholderText">
|
||||
<string>Search by author</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QToolButton" name="adv_search_button">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>&Keyword:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>search_edit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="HistoryLineEdit" name="search_edit">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Search by any keyword</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="4">
|
||||
<widget class="QSplitter" name="store_splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
@ -82,8 +107,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>193</width>
|
||||
<height>127</height>
|
||||
<width>204</width>
|
||||
<height>141</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
@ -202,7 +227,7 @@
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="4" column="0" colspan="3">
|
||||
<layout class="QHBoxLayout" name="bottom_layout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
@ -234,7 +259,47 @@
|
||||
<item>
|
||||
<widget class="QPushButton" name="close">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
<string>&Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>&Title:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>search_title</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3" rowspan="3">
|
||||
<layout class="QVBoxLayout" name="button_layout">
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="search">
|
||||
<property name="text">
|
||||
<string>&Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -255,17 +320,19 @@
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>search_title</tabstop>
|
||||
<tabstop>search_author</tabstop>
|
||||
<tabstop>adv_search_button</tabstop>
|
||||
<tabstop>search_edit</tabstop>
|
||||
<tabstop>search</tabstop>
|
||||
<tabstop>results_view</tabstop>
|
||||
<tabstop>store_list</tabstop>
|
||||
<tabstop>select_all_stores</tabstop>
|
||||
<tabstop>select_invert_stores</tabstop>
|
||||
<tabstop>select_none_stores</tabstop>
|
||||
<tabstop>results_view</tabstop>
|
||||
<tabstop>configure</tabstop>
|
||||
<tabstop>open_external</tabstop>
|
||||
<tabstop>close</tabstop>
|
||||
<tabstop>adv_search_button</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../../../../resources/images.qrc"/>
|
||||
|
@ -59,6 +59,10 @@ def config(defaults=None):
|
||||
help=_('Show reading position in fullscreen mode.'))
|
||||
c.add_opt('fullscreen_scrollbar', default=True, action='store_false',
|
||||
help=_('Show the scrollbar in fullscreen mode.'))
|
||||
c.add_opt('start_in_fullscreen', default=False, action='store_true',
|
||||
help=_('Start viewer in full screen mode'))
|
||||
c.add_opt('show_fullscreen_help', default=True, action='store_false',
|
||||
help=_('Show full screen usage help'))
|
||||
c.add_opt('cols_per_screen', default=1)
|
||||
c.add_opt('use_book_margins', default=False, action='store_true')
|
||||
c.add_opt('top_margin', default=20)
|
||||
@ -206,6 +210,8 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
self.opt_fit_images.setChecked(opts.fit_images)
|
||||
self.opt_fullscreen_clock.setChecked(opts.fullscreen_clock)
|
||||
self.opt_fullscreen_scrollbar.setChecked(opts.fullscreen_scrollbar)
|
||||
self.opt_start_in_fullscreen.setChecked(opts.start_in_fullscreen)
|
||||
self.opt_show_fullscreen_help.setChecked(opts.show_fullscreen_help)
|
||||
self.opt_fullscreen_pos.setChecked(opts.fullscreen_pos)
|
||||
self.opt_cols_per_screen.setValue(opts.cols_per_screen)
|
||||
self.opt_override_book_margins.setChecked(not opts.use_book_margins)
|
||||
@ -279,6 +285,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
c.set('fullscreen_clock', self.opt_fullscreen_clock.isChecked())
|
||||
c.set('fullscreen_pos', self.opt_fullscreen_pos.isChecked())
|
||||
c.set('fullscreen_scrollbar', self.opt_fullscreen_scrollbar.isChecked())
|
||||
c.set('show_fullscreen_help', self.opt_show_fullscreen_help.isChecked())
|
||||
c.set('cols_per_screen', int(self.opt_cols_per_screen.value()))
|
||||
c.set('use_book_margins', not
|
||||
self.opt_override_book_margins.isChecked())
|
||||
|
@ -388,20 +388,34 @@ QToolBox::tab:hover {
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_fullscreen_pos">
|
||||
<property name="text">
|
||||
<string>Show reading &position in full screen mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_fullscreen_scrollbar">
|
||||
<property name="text">
|
||||
<string>Show &scrollbar in full screen mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_start_in_fullscreen">
|
||||
<property name="text">
|
||||
<string>&Start viewer in full screen mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_show_fullscreen_help">
|
||||
<property name="text">
|
||||
<string>Show &help message when starting full screen mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_6">
|
||||
|
@ -16,6 +16,7 @@ from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
|
||||
|
||||
from calibre.gui2.viewer.flip import SlideFlip
|
||||
from calibre.gui2.shortcuts import Shortcuts
|
||||
from calibre.gui2 import open_url
|
||||
from calibre import prints
|
||||
from calibre.customize.ui import all_viewer_plugins
|
||||
from calibre.gui2.viewer.keys import SHORTCUTS
|
||||
@ -144,6 +145,8 @@ class Document(QWebPage): # {{{
|
||||
self.fullscreen_clock = opts.fullscreen_clock
|
||||
self.fullscreen_scrollbar = opts.fullscreen_scrollbar
|
||||
self.fullscreen_pos = opts.fullscreen_pos
|
||||
self.start_in_fullscreen = opts.start_in_fullscreen
|
||||
self.show_fullscreen_help = opts.show_fullscreen_help
|
||||
self.use_book_margins = opts.use_book_margins
|
||||
self.cols_per_screen = opts.cols_per_screen
|
||||
self.side_margin = opts.side_margin
|
||||
@ -481,7 +484,12 @@ class DocumentView(QWebView): # {{{
|
||||
d = self.document
|
||||
self.unimplemented_actions = list(map(self.pageAction,
|
||||
[d.DownloadImageToDisk, d.OpenLinkInNewWindow, d.DownloadLinkToDisk,
|
||||
d.OpenImageInNewWindow, d.OpenLink, d.Reload]))
|
||||
d.OpenImageInNewWindow, d.OpenLink, d.Reload, d.InspectElement]))
|
||||
|
||||
self.search_online_action = QAction(QIcon(I('search.png')), '', self)
|
||||
self.search_online_action.setShortcut(Qt.CTRL+Qt.Key_E)
|
||||
self.search_online_action.triggered.connect(self.search_online)
|
||||
self.addAction(self.search_online_action)
|
||||
self.dictionary_action = QAction(QIcon(I('dictionary.png')),
|
||||
_('&Lookup in dictionary'), self)
|
||||
self.dictionary_action.setShortcut(Qt.CTRL+Qt.Key_L)
|
||||
@ -526,6 +534,10 @@ class DocumentView(QWebView): # {{{
|
||||
self.goto_location_action.setMenu(self.goto_location_menu)
|
||||
self.grabGesture(Qt.SwipeGesture)
|
||||
|
||||
self.restore_fonts_action = QAction(_('Normal font size'), self)
|
||||
self.restore_fonts_action.setCheckable(True)
|
||||
self.restore_fonts_action.triggered.connect(self.restore_font_size)
|
||||
|
||||
def goto_next_section(self, *args):
|
||||
if self.manager is not None:
|
||||
self.manager.goto_next_section()
|
||||
@ -582,6 +594,15 @@ class DocumentView(QWebView): # {{{
|
||||
if self.manager is not None:
|
||||
self.manager.selection_changed(unicode(self.document.selectedText()))
|
||||
|
||||
def _selectedText(self):
|
||||
t = unicode(self.selectedText()).strip()
|
||||
if not t:
|
||||
return u''
|
||||
if len(t) > 40:
|
||||
t = t[:40] + u'...'
|
||||
t = t.replace(u'&', u'&&')
|
||||
return _("S&earch Google for '%s'")%t
|
||||
|
||||
def contextMenuEvent(self, ev):
|
||||
mf = self.document.mainFrame()
|
||||
r = mf.hitTestContent(ev.pos())
|
||||
@ -591,17 +612,48 @@ class DocumentView(QWebView): # {{{
|
||||
menu = self.document.createStandardContextMenu()
|
||||
for action in self.unimplemented_actions:
|
||||
menu.removeAction(action)
|
||||
text = unicode(self.selectedText())
|
||||
if text:
|
||||
menu.insertAction(list(menu.actions())[0], self.dictionary_action)
|
||||
menu.insertAction(list(menu.actions())[0], self.search_action)
|
||||
|
||||
if not img.isNull():
|
||||
menu.addAction(self.view_image_action)
|
||||
menu.addSeparator()
|
||||
menu.addAction(self.goto_location_action)
|
||||
if self.document.in_fullscreen_mode and self.manager is not None:
|
||||
|
||||
text = self._selectedText()
|
||||
if text and img.isNull():
|
||||
self.search_online_action.setText(text)
|
||||
menu.addAction(self.search_online_action)
|
||||
menu.addAction(self.dictionary_action)
|
||||
menu.addAction(self.search_action)
|
||||
|
||||
if not text and img.isNull():
|
||||
menu.addSeparator()
|
||||
menu.addAction(self.manager.toggle_toolbar_action)
|
||||
if self.manager.action_back.isEnabled():
|
||||
menu.addAction(self.manager.action_back)
|
||||
if self.manager.action_forward.isEnabled():
|
||||
menu.addAction(self.manager.action_forward)
|
||||
menu.addAction(self.goto_location_action)
|
||||
|
||||
if self.manager is not None:
|
||||
menu.addSeparator()
|
||||
menu.addAction(self.manager.action_table_of_contents)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addAction(self.manager.action_font_size_larger)
|
||||
self.restore_fonts_action.setChecked(self.multiplier == 1)
|
||||
menu.addAction(self.restore_fonts_action)
|
||||
menu.addAction(self.manager.action_font_size_smaller)
|
||||
|
||||
menu.addSeparator()
|
||||
inspectAction = self.pageAction(self.document.InspectElement)
|
||||
menu.addAction(inspectAction)
|
||||
|
||||
if not text and img.isNull() and self.manager is not None:
|
||||
menu.addSeparator()
|
||||
if self.document.in_fullscreen_mode and self.manager is not None:
|
||||
menu.addAction(self.manager.toggle_toolbar_action)
|
||||
menu.addAction(self.manager.action_full_screen)
|
||||
|
||||
menu.addSeparator()
|
||||
menu.addAction(self.manager.action_quit)
|
||||
|
||||
menu.exec_(ev.globalPos())
|
||||
|
||||
def lookup(self, *args):
|
||||
@ -616,6 +668,12 @@ class DocumentView(QWebView): # {{{
|
||||
if t:
|
||||
self.manager.search.set_search_string(t)
|
||||
|
||||
def search_online(self):
|
||||
t = unicode(self.selectedText()).strip()
|
||||
if t:
|
||||
url = 'https://www.google.com/search?q=' + QUrl().toPercentEncoding(t)
|
||||
open_url(QUrl.fromEncoded(url))
|
||||
|
||||
def set_manager(self, manager):
|
||||
self.manager = manager
|
||||
self.scrollbar = manager.horizontal_scrollbar
|
||||
@ -993,6 +1051,11 @@ class DocumentView(QWebView): # {{{
|
||||
self.multiplier -= amount
|
||||
return self.document.scroll_fraction
|
||||
|
||||
def restore_font_size(self):
|
||||
with self.document.page_position:
|
||||
self.multiplier = 1
|
||||
return self.document.scroll_fraction
|
||||
|
||||
def changeEvent(self, event):
|
||||
if event.type() == event.EnabledChange:
|
||||
self.update()
|
||||
|
@ -21,7 +21,7 @@ from calibre.gui2 import (Application, ORG_NAME, APP_UID, choose_files,
|
||||
info_dialog, error_dialog, open_url, available_height)
|
||||
from calibre.ebooks.oeb.iterator.book import EbookIterator
|
||||
from calibre.ebooks import DRMError
|
||||
from calibre.constants import islinux, isbsd, filesystem_encoding
|
||||
from calibre.constants import islinux, filesystem_encoding
|
||||
from calibre.utils.config import Config, StringConfig, JSONConfig
|
||||
from calibre.gui2.search_box import SearchBox2
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
@ -160,7 +160,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
PAGED_MODE_TT = _('Switch to flow mode - where the text is not broken up '
|
||||
'into pages')
|
||||
|
||||
def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None):
|
||||
def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None,
|
||||
start_in_fullscreen=False):
|
||||
MainWindow.__init__(self, None)
|
||||
self.setupUi(self)
|
||||
self.view.initialize_view(debug_javascript)
|
||||
@ -178,6 +179,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
self.pending_restore = False
|
||||
self.existing_bookmarks= []
|
||||
self.selected_text = None
|
||||
self.was_maximized = False
|
||||
self.read_settings()
|
||||
self.dictionary_box.hide()
|
||||
self.close_dictionary_view.clicked.connect(lambda
|
||||
@ -207,7 +209,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
self.view.set_manager(self)
|
||||
self.pi = ProgressIndicator(self)
|
||||
self.toc.setVisible(False)
|
||||
self.action_quit = QAction(self)
|
||||
self.action_quit = QAction(_('&Quit'), self)
|
||||
self.addAction(self.action_quit)
|
||||
self.view_resized_timer = QTimer(self)
|
||||
self.view_resized_timer.timeout.connect(self.viewport_resize_finished)
|
||||
@ -299,6 +301,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
''')
|
||||
self.window_mode_changed = None
|
||||
self.toggle_toolbar_action = QAction(_('Show/hide controls'), self)
|
||||
self.toggle_toolbar_action.setCheckable(True)
|
||||
self.toggle_toolbar_action.triggered.connect(self.toggle_toolbars)
|
||||
self.addAction(self.toggle_toolbar_action)
|
||||
self.full_screen_label_anim = QPropertyAnimation(
|
||||
@ -358,6 +361,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
|
||||
self.restore_state()
|
||||
self.action_toggle_paged_mode.toggled[bool].connect(self.toggle_paged_mode)
|
||||
if (start_in_fullscreen or self.view.document.start_in_fullscreen):
|
||||
self.action_full_screen.trigger()
|
||||
|
||||
def toggle_paged_mode(self, checked, at_start=False):
|
||||
in_paged_mode = not self.action_toggle_paged_mode.isChecked()
|
||||
@ -397,7 +402,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
count += 1
|
||||
|
||||
def shutdown(self):
|
||||
if self.isFullScreen():
|
||||
if self.isFullScreen() and not self.view.document.start_in_fullscreen:
|
||||
self.action_full_screen.trigger()
|
||||
return False
|
||||
self.save_state()
|
||||
@ -421,7 +426,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
def save_state(self):
|
||||
state = bytearray(self.saveState(self.STATE_VERSION))
|
||||
vprefs['viewer_toolbar_state'] = state
|
||||
vprefs.set('viewer_window_geometry', bytearray(self.saveGeometry()))
|
||||
if not self.isFullScreen():
|
||||
vprefs.set('viewer_window_geometry', bytearray(self.saveGeometry()))
|
||||
if self.current_book_has_toc:
|
||||
vprefs.set('viewer_toc_isvisible', bool(self.toc.isVisible()))
|
||||
if self.toc.isVisible():
|
||||
@ -488,6 +494,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
self.window_mode_changed = 'fullscreen'
|
||||
self.tool_bar.setVisible(False)
|
||||
self.tool_bar2.setVisible(False)
|
||||
self.was_maximized = self.isMaximized()
|
||||
if not self.view.document.fullscreen_scrollbar:
|
||||
self.vertical_scrollbar.setVisible(False)
|
||||
self.frame.layout().setSpacing(0)
|
||||
@ -574,14 +581,18 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
om = self._original_frame_margins
|
||||
self.centralwidget.layout().setContentsMargins(om[0])
|
||||
self.frame.layout().setContentsMargins(om[1])
|
||||
super(EbookViewer, self).showNormal()
|
||||
if self.was_maximized:
|
||||
super(EbookViewer, self).showMaximized()
|
||||
else:
|
||||
super(EbookViewer, self).showNormal()
|
||||
|
||||
def handle_window_mode_toggle(self):
|
||||
if self.window_mode_changed:
|
||||
fs = self.window_mode_changed == 'fullscreen'
|
||||
self.window_mode_changed = None
|
||||
if fs:
|
||||
self.show_full_screen_label()
|
||||
if self.view.document.show_fullscreen_help:
|
||||
self.show_full_screen_label()
|
||||
else:
|
||||
self.view.document.switch_to_window_mode()
|
||||
self.view.document.page_position.restore()
|
||||
@ -681,13 +692,9 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
|
||||
def font_size_larger(self):
|
||||
self.view.magnify_fonts()
|
||||
self.action_font_size_larger.setEnabled(self.view.multiplier < 3)
|
||||
self.action_font_size_smaller.setEnabled(self.view.multiplier > 0.2)
|
||||
|
||||
def font_size_smaller(self):
|
||||
self.view.shrink_fonts()
|
||||
self.action_font_size_larger.setEnabled(self.view.multiplier < 3)
|
||||
self.action_font_size_smaller.setEnabled(self.view.multiplier > 0.2)
|
||||
|
||||
def magnification_changed(self, val):
|
||||
tt = _('Make font size %(which)s\nCurrent magnification: %(mag).1f')
|
||||
@ -695,6 +702,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
tt %dict(which=_('larger'), mag=val))
|
||||
self.action_font_size_smaller.setToolTip(
|
||||
tt %dict(which=_('smaller'), mag=val))
|
||||
self.action_font_size_larger.setEnabled(self.view.multiplier < 3)
|
||||
self.action_font_size_smaller.setEnabled(self.view.multiplier > 0.2)
|
||||
|
||||
def find(self, text, repeat=False, backwards=False):
|
||||
if not text:
|
||||
@ -1129,32 +1138,29 @@ def main(args=sys.argv):
|
||||
|
||||
parser = option_parser()
|
||||
opts, args = parser.parse_args(args)
|
||||
pid = os.fork() if False and (islinux or isbsd) else -1
|
||||
try:
|
||||
open_at = float(opts.open_at)
|
||||
except:
|
||||
open_at = None
|
||||
if pid <= 0:
|
||||
override = 'calibre-ebook-viewer' if islinux else None
|
||||
app = Application(args, override_program_name=override)
|
||||
app.load_builtin_fonts()
|
||||
app.setWindowIcon(QIcon(I('viewer.png')))
|
||||
QApplication.setOrganizationName(ORG_NAME)
|
||||
QApplication.setApplicationName(APP_UID)
|
||||
main = EbookViewer(args[1] if len(args) > 1 else None,
|
||||
debug_javascript=opts.debug_javascript, open_at=open_at)
|
||||
# This is needed for paged mode. Without it, the first document that is
|
||||
# loaded will have extra blank space at the bottom, as
|
||||
# turn_off_internal_scrollbars does not take effect for the first
|
||||
# rendered document
|
||||
main.view.load_path(P('viewer/blank.html', allow_user_override=False))
|
||||
override = 'calibre-ebook-viewer' if islinux else None
|
||||
app = Application(args, override_program_name=override)
|
||||
app.load_builtin_fonts()
|
||||
app.setWindowIcon(QIcon(I('viewer.png')))
|
||||
QApplication.setOrganizationName(ORG_NAME)
|
||||
QApplication.setApplicationName(APP_UID)
|
||||
main = EbookViewer(args[1] if len(args) > 1 else None,
|
||||
debug_javascript=opts.debug_javascript, open_at=open_at,
|
||||
start_in_fullscreen=opts.full_screen)
|
||||
# This is needed for paged mode. Without it, the first document that is
|
||||
# loaded will have extra blank space at the bottom, as
|
||||
# turn_off_internal_scrollbars does not take effect for the first
|
||||
# rendered document
|
||||
main.view.load_path(P('viewer/blank.html', allow_user_override=False))
|
||||
|
||||
sys.excepthook = main.unhandled_exception
|
||||
main.show()
|
||||
if opts.raise_window:
|
||||
main.raise_()
|
||||
if opts.full_screen:
|
||||
main.action_full_screen.trigger()
|
||||
sys.excepthook = main.unhandled_exception
|
||||
main.show()
|
||||
if opts.raise_window:
|
||||
main.raise_()
|
||||
with main:
|
||||
return app.exec_()
|
||||
return 0
|
||||
|
@ -283,6 +283,7 @@ class ImageView(QWidget, ImageDropMixin): # {{{
|
||||
self.setMinimumSize(QSize(150, 200))
|
||||
ImageDropMixin.__init__(self)
|
||||
self.draw_border = True
|
||||
self.show_size = False
|
||||
|
||||
def setPixmap(self, pixmap):
|
||||
if not isinstance(pixmap, QPixmap):
|
||||
@ -305,6 +306,7 @@ class ImageView(QWidget, ImageDropMixin): # {{{
|
||||
if pmap.isNull():
|
||||
return
|
||||
w, h = pmap.width(), pmap.height()
|
||||
ow, oh = w, h
|
||||
cw, ch = self.rect().width(), self.rect().height()
|
||||
scaled, nw, nh = fit_image(w, h, cw, ch)
|
||||
if scaled:
|
||||
@ -317,12 +319,22 @@ class ImageView(QWidget, ImageDropMixin): # {{{
|
||||
p = QPainter(self)
|
||||
p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
|
||||
p.drawPixmap(target, pmap)
|
||||
pen = QPen()
|
||||
pen.setWidth(self.BORDER_WIDTH)
|
||||
p.setPen(pen)
|
||||
if self.draw_border:
|
||||
pen = QPen()
|
||||
pen.setWidth(self.BORDER_WIDTH)
|
||||
p.setPen(pen)
|
||||
p.drawRect(target)
|
||||
#p.drawRect(self.rect())
|
||||
if self.show_size:
|
||||
sztgt = target.adjusted(0, 0, 0, -4)
|
||||
f = p.font()
|
||||
f.setBold(True)
|
||||
p.setFont(f)
|
||||
sz = u'\u00a0%d x %d\u00a0'%(ow, oh)
|
||||
flags = Qt.AlignBottom|Qt.AlignRight|Qt.TextSingleLine
|
||||
szrect = p.boundingRect(sztgt, flags, sz)
|
||||
p.fillRect(szrect.adjusted(0, 0, 0, 4), QColor(0, 0, 0, 200))
|
||||
p.setPen(QPen(QColor(255,255,255)))
|
||||
p.drawText(sztgt, flags, sz)
|
||||
p.end()
|
||||
# }}}
|
||||
|
||||
@ -582,6 +594,9 @@ class HistoryLineEdit(QComboBox): # {{{
|
||||
self.setInsertPolicy(self.NoInsert)
|
||||
self.setMaxCount(10)
|
||||
|
||||
def setPlaceholderText(self, txt):
|
||||
return self.lineEdit().setPlaceholderText(txt)
|
||||
|
||||
@property
|
||||
def store_name(self):
|
||||
return 'lineedit_history_'+self._name
|
||||
|
@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
FIELDS = ['all', 'title', 'title_sort', 'author_sort', 'authors', 'comments',
|
||||
'cover', 'formats','id', 'isbn', 'ondevice', 'pubdate', 'publisher',
|
||||
'cover', 'formats','id', 'isbn', 'library_name','ondevice', 'pubdate', 'publisher',
|
||||
'rating', 'series_index', 'series', 'size', 'tags', 'timestamp',
|
||||
'uuid', 'languages', 'identifiers']
|
||||
|
||||
|
@ -48,17 +48,21 @@ class CSV_XML(CatalogPlugin):
|
||||
"Applies to: CSV, XML output formats"))]
|
||||
|
||||
def run(self, path_to_output, opts, db, notification=DummyReporter()):
|
||||
from calibre.library import current_library_name
|
||||
from calibre.utils.date import isoformat
|
||||
from calibre.utils.html2text import html2text
|
||||
from lxml import etree
|
||||
from calibre.utils.logging import default_log as log
|
||||
from lxml import etree
|
||||
|
||||
self.fmt = path_to_output.rpartition('.')[2]
|
||||
self.notification = notification
|
||||
current_library = current_library_name()
|
||||
if getattr(opts, 'library_path', None):
|
||||
current_library = os.path.basename(opts.library_path)
|
||||
|
||||
if opts.verbose:
|
||||
opts_dict = vars(opts)
|
||||
log("%s(): Generating %s" % (self.name,self.fmt.upper()))
|
||||
log("%s('%s'): Generating %s" % (self.name, current_library, self.fmt.upper()))
|
||||
if opts.connected_device['is_device_connected']:
|
||||
log(" connected_device: %s" % opts.connected_device['name'])
|
||||
if opts_dict['search_text']:
|
||||
@ -110,6 +114,8 @@ class CSV_XML(CatalogPlugin):
|
||||
for field in fields:
|
||||
if field.startswith('#'):
|
||||
item = db.get_field(entry['id'],field,index_is_id=True)
|
||||
elif field == 'library_name':
|
||||
item = current_library
|
||||
elif field == 'title_sort':
|
||||
item = entry['sort']
|
||||
else:
|
||||
@ -215,6 +221,9 @@ class CSV_XML(CatalogPlugin):
|
||||
fmt.append(E.format(f.replace(os.sep, '/')))
|
||||
record.append(fmt)
|
||||
|
||||
if 'library_name' in fields:
|
||||
record.append(E.library_name(current_library))
|
||||
|
||||
with open(path_to_output, 'w') as f:
|
||||
f.write(etree.tostring(root, encoding='utf-8',
|
||||
xml_declaration=True, pretty_print=True))
|
||||
|
@ -14,6 +14,7 @@ from calibre import strftime
|
||||
from calibre.customize import CatalogPlugin
|
||||
from calibre.customize.conversion import OptionRecommendation, DummyReporter
|
||||
from calibre.ebooks import calibre_cover
|
||||
from calibre.library import current_library_name
|
||||
from calibre.library.catalogs import AuthorSortMismatchException, EmptyCatalogException
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.utils.localization import get_lang
|
||||
@ -208,8 +209,9 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
|
||||
build_log = []
|
||||
|
||||
build_log.append(u"%s(): Generating %s %sin %s environment, locale: '%s'" %
|
||||
build_log.append(u"%s('%s'): Generating %s %sin %s environment, locale: '%s'" %
|
||||
(self.name,
|
||||
current_library_name(),
|
||||
self.fmt,
|
||||
'for %s ' % opts.output_profile if opts.output_profile else '',
|
||||
'CLI' if opts.cli_environment else 'GUI',
|
||||
|
@ -28,7 +28,8 @@ from calibre.ebooks.metadata.book.base import Metadata
|
||||
from calibre.constants import preferred_encoding, iswindows, filesystem_encoding
|
||||
from calibre.ptempfile import (PersistentTemporaryFile,
|
||||
base_dir, SpooledTemporaryFile)
|
||||
from calibre.customize.ui import run_plugins_on_import
|
||||
from calibre.customize.ui import (run_plugins_on_import,
|
||||
run_plugins_on_postimport)
|
||||
from calibre import isbytestring
|
||||
from calibre.utils.filenames import (ascii_filename, samefile,
|
||||
WindowsAtomicFolderMove, hardlink_file)
|
||||
@ -1495,8 +1496,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
format = os.path.splitext(npath)[-1].lower().replace('.', '').upper()
|
||||
stream = lopen(npath, 'rb')
|
||||
format = check_ebook_format(stream, format)
|
||||
return self.add_format(index, format, stream,
|
||||
retval = self.add_format(index, format, stream,
|
||||
index_is_id=index_is_id, path=path, notify=notify)
|
||||
run_plugins_on_postimport(self, id, format)
|
||||
return retval
|
||||
|
||||
def add_format(self, index, format, stream, index_is_id=False, path=None,
|
||||
notify=True, replace=True, copy_function=None):
|
||||
@ -3475,6 +3478,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
formats, metadata = iter(formats), iter(metadata)
|
||||
duplicates = []
|
||||
ids = []
|
||||
postimport = []
|
||||
for path in paths:
|
||||
mi = metadata.next()
|
||||
self._add_newbook_tag(mi)
|
||||
@ -3506,8 +3510,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
||||
format = check_ebook_format(stream, format)
|
||||
self.add_format(id, format, stream, index_is_id=True)
|
||||
stream.close()
|
||||
postimport.append((id, format))
|
||||
self.conn.commit()
|
||||
self.data.refresh_ids(self, ids) # Needed to update format list and size
|
||||
for book_id, fmt in postimport:
|
||||
run_plugins_on_postimport(self, book_id, fmt)
|
||||
if duplicates:
|
||||
paths = list(duplicate[0] for duplicate in duplicates)
|
||||
formats = list(duplicate[1] for duplicate in duplicates)
|
||||
|