mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync with trunk.
This commit is contained in:
commit
df9c4da9bb
69
recipes/aktualne.cz.recipe
Normal file
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')
|
||||
|
55
recipes/blesk.recipe
Normal file
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
|
||||
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@ class Buchreport(BasicNewsRecipe) :
|
||||
title = u'Buchreport'
|
||||
timefmt = ' [%d.%m.%Y]'
|
||||
encoding = 'cp1252'
|
||||
language = 'de_DE'
|
||||
language = 'de'
|
||||
|
||||
|
||||
extra_css = 'body { margin-left: 0.00em; margin-right: 0.00em; } \
|
||||
|
68
recipes/ceska_pozice.recipe
Normal file
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
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
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
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
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
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
|
||||
|
@ -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'
|
||||
|
36
recipes/ihned.cz.recipe
Normal file
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
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
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
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
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']})]
|
30
recipes/nadacni_fond_proti_korupci.recipe
Normal file
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
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
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
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
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
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
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
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
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}
|
||||
|
||||
|
44
recipes/tyden.cz.recipe
Normal file
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): # {{{
|
||||
|
@ -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 {{{
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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">
|
||||
|
@ -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
|
||||
@ -77,7 +77,9 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
|
||||
# Create and add the progress indicator
|
||||
self.pi = ProgressIndicator(self, 24)
|
||||
self.top_layout.addWidget(self.pi, 1, 3, Qt.AlignHCenter)
|
||||
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')))
|
||||
@ -173,6 +175,9 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
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.
|
||||
|
@ -14,78 +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="QGridLayout" name="top_layout">
|
||||
<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>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="HistoryLineEdit" name="search_author"/>
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QPushButton" name="search">
|
||||
<property name="text">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="HistoryLineEdit" name="search_title"/>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<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>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QToolButton" name="adv_search_button">
|
||||
<property name="text">
|
||||
<string>...</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>
|
||||
@ -111,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>
|
||||
@ -231,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">
|
||||
@ -263,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>
|
||||
|
@ -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
|
||||
|
@ -328,7 +328,6 @@ class ImageView(QWidget, ImageDropMixin): # {{{
|
||||
sztgt = target.adjusted(0, 0, 0, -4)
|
||||
f = p.font()
|
||||
f.setBold(True)
|
||||
f.setPointSize(12)
|
||||
p.setFont(f)
|
||||
sz = u'\u00a0%d x %d\u00a0'%(ow, oh)
|
||||
flags = Qt.AlignBottom|Qt.AlignRight|Qt.TextSingleLine
|
||||
@ -595,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
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user