mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
KG updates
This commit is contained in:
commit
9aa768b23f
@ -4,7 +4,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
<title>..:: calibre library ::.. {title}</title>
|
<title>..:: calibre {library} ::.. {title}</title>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=100" />
|
<meta http-equiv="X-UA-Compatible" content="IE=100" />
|
||||||
<link rel="icon" type="image/x-icon" href="http://calibre-ebook.com/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="http://calibre-ebook.com/favicon.ico" />
|
||||||
|
|
||||||
@ -41,7 +41,7 @@
|
|||||||
<div class="area">
|
<div class="area">
|
||||||
<div class="bubble">
|
<div class="bubble">
|
||||||
<p><a href="{prefix}/browse" title="Return to top level"
|
<p><a href="{prefix}/browse" title="Return to top level"
|
||||||
>→ home ←</a></p>
|
>→ {home} ←</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="nav-container">
|
<div id="nav-container">
|
||||||
@ -80,7 +80,7 @@
|
|||||||
<form name="search_form" action="{prefix}/browse/search" method="get" accept-charset="UTF-8">
|
<form name="search_form" action="{prefix}/browse/search" method="get" accept-charset="UTF-8">
|
||||||
<input value="{initial_search}" type="text" title="Search" name="query"
|
<input value="{initial_search}" type="text" title="Search" name="query"
|
||||||
class="search_input" />
|
class="search_input" />
|
||||||
<input type="submit" value="Search" title="Search" alt="Search" />
|
<input type="submit" value="{Search}" title="{Search}" alt="{Search}" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div> </div>
|
<div> </div>
|
||||||
|
@ -211,3 +211,9 @@ generate_cover_title_font = None
|
|||||||
# Absolute path to a TTF font file to use as the font for the footer in the
|
# Absolute path to a TTF font file to use as the font for the footer in the
|
||||||
# default cover
|
# default cover
|
||||||
generate_cover_foot_font = None
|
generate_cover_foot_font = None
|
||||||
|
|
||||||
|
|
||||||
|
# Behavior of doubleclick on the books list. Choices:
|
||||||
|
# open_viewer, do_nothing, edit_cell. Default: open_viewer.
|
||||||
|
# Example: doubleclick_on_library_view = 'do_nothing'
|
||||||
|
doubleclick_on_library_view = 'open_viewer'
|
||||||
|
BIN
resources/images/news/avto-magazin.png
Normal file
BIN
resources/images/news/avto-magazin.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/images/news/dnevnik.png
Normal file
BIN
resources/images/news/dnevnik.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 861 B |
BIN
resources/images/news/siol.png
Normal file
BIN
resources/images/news/siol.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 423 B |
46
resources/recipes/avto-magazin.recipe
Normal file
46
resources/recipes/avto-magazin.recipe
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, BlonG'
|
||||||
|
'''
|
||||||
|
avto-magazin.si
|
||||||
|
'''
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
class Dnevnik(BasicNewsRecipe):
|
||||||
|
title = u'Avto Magazin'
|
||||||
|
__author__ = u'BlonG'
|
||||||
|
description = u'Za avtomobilisti\xc4\x8dne frike, poznavalce in nedeljske \xc5\xa1oferje.'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 20
|
||||||
|
labguage = 'sl'
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
conversion_options = {'linearize_tables' : True}
|
||||||
|
|
||||||
|
|
||||||
|
cover_url = 'https://sites.google.com/site/javno2010/home/avto_magazin_cover.jpg'
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
|
h2{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
|
'''
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='div', attrs={'id':'_iprom_inStream'}),
|
||||||
|
# dict(name='div', attrs={'class':'entry-content'}),
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='div', attrs={'id':'voteConfirmation'}),
|
||||||
|
dict(name='div', attrs={'id':'InsideVote'}),
|
||||||
|
dict(name='div', attrs={'class':'Zone234'}),
|
||||||
|
dict(name='div', attrs={'class':'Comments'}),
|
||||||
|
dict(name='div', attrs={'class':'sorodneNovice'}),
|
||||||
|
dict(name='div', attrs={'id':'footer'}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Novice', u'http://www.avto-magazin.si/rss/')
|
||||||
|
]
|
@ -25,7 +25,7 @@ class Danas(BasicNewsRecipe):
|
|||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
extra_css = """ @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)}
|
extra_css = """ @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)}
|
||||||
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
|
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
|
||||||
.article_description,body,.lokacija{font-family: Tahoma,Arial,Helvetica,sans1,sans-serif}
|
.article,.articledescription,body,.lokacija,.feed{font-family: Tahoma,Arial,Helvetica,sans1,sans-serif}
|
||||||
.nadNaslov,h1,.preamble{font-family: Georgia,"Times New Roman",Times,serif1,serif}
|
.nadNaslov,h1,.preamble{font-family: Georgia,"Times New Roman",Times,serif1,serif}
|
||||||
.antrfileText{border-left: 2px solid #999999;
|
.antrfileText{border-left: 2px solid #999999;
|
||||||
margin-left: 0.8em;
|
margin-left: 0.8em;
|
||||||
@ -66,7 +66,7 @@ class Danas(BasicNewsRecipe):
|
|||||||
|
|
||||||
keep_only_tags = [dict(name='div', attrs={'id':'left'})]
|
keep_only_tags = [dict(name='div', attrs={'id':'left'})]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name='div', attrs={'class':['width_1_4','metaClanka','baner']})
|
dict(name='div', attrs={'class':['width_1_4','metaClanka','baner','listaVesti','article_nav']})
|
||||||
,dict(name='div', attrs={'id':'comments'})
|
,dict(name='div', attrs={'id':'comments'})
|
||||||
,dict(name=['object','link','iframe','meta'])
|
,dict(name=['object','link','iframe','meta'])
|
||||||
]
|
]
|
||||||
|
42
resources/recipes/diario_sport.recipe
Normal file
42
resources/recipes/diario_sport.recipe
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class DiarioSport(BasicNewsRecipe):
|
||||||
|
title = u'Diario Sport'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 75
|
||||||
|
__author__ = 'Jefferson Frantz'
|
||||||
|
description = 'Todas las noticias del Barça y del mundo del deporte en general'
|
||||||
|
timefmt = ' [%d %b, %Y]'
|
||||||
|
language = 'es'
|
||||||
|
no_stylesheets = True
|
||||||
|
|
||||||
|
feeds = [(u'Sport', u'http://feeds.feedburner.com/sport/ultimahora')]
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
h2{font-family: serif; font-size: small; font-weight: bold; color: #000000; text-align: justify}
|
||||||
|
'''
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'id':['noticiasMedio']})]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name=['object','link','script','ul'])
|
||||||
|
,dict(name='div', attrs={'id':['scrAdSense','herramientas2','participacion','participacion2','bloque1resultados','bloque2resultados','cont_vinyetesAnt','tinta','noticiasSuperior','cintillopublicidad2']})
|
||||||
|
,dict(name='p', attrs={'class':['masinformacion','hora']})
|
||||||
|
,dict(name='a', attrs={'class':["'link'"]})
|
||||||
|
,dict(name='div', attrs={'class':['addthis_toolbox addthis_default_style','firma','pretitularnoticia']})
|
||||||
|
,dict(name='form', attrs={'id':['formularioDeBusquedaAvanzada']})
|
||||||
|
]
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
for item in soup.findAll(style=True):
|
||||||
|
del item['style']
|
||||||
|
return soup
|
||||||
|
|
||||||
|
|
||||||
|
def postprocess_html(self, soup, first_fetch):
|
||||||
|
img = soup.find('img',src='/img/videos/mascaravideo.png')
|
||||||
|
if not img is None:
|
||||||
|
img.extract()
|
||||||
|
|
||||||
|
return soup
|
||||||
|
|
63
resources/recipes/dnevnik.recipe
Normal file
63
resources/recipes/dnevnik.recipe
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, BlonG'
|
||||||
|
'''
|
||||||
|
dnevnik.si
|
||||||
|
'''
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
class Dnevnik(BasicNewsRecipe):
|
||||||
|
title = u'Dnevnik.si'
|
||||||
|
__author__ = u'BlonG'
|
||||||
|
description = u'''Dnevnik je \u010dasnik z ve\u010d kot polstoletno zgodovino.
|
||||||
|
Pod sloganom \xbb\u017divljenje ima besedo\xab na svojih straneh prina\u0161a
|
||||||
|
bralcem bogastvo informacij, komentarjev in kolumen in raznovrstnost
|
||||||
|
pogledov, zaznamovanih z odgovornostjo do posameznika in \u0161ir\u0161e
|
||||||
|
dru\u017ebe.'''
|
||||||
|
oldest_article = 3
|
||||||
|
max_articles_per_feed = 20
|
||||||
|
language = 'sl'
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
cover_url = 'https://sites.google.com/site/javno2010/home/dnevnik_cover.jpg'
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
|
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||||
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
|
'''
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='div', attrs={'id':'_iprom_inStream'}),
|
||||||
|
dict(name='div', attrs={'class':'entry-content'}),
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='div', attrs={'class':'fb_article_top'}),
|
||||||
|
dict(name='div', attrs={'class':'related'}),
|
||||||
|
dict(name='div', attrs={'class':'fb_article_foot'}),
|
||||||
|
dict(name='div', attrs={'class':'spreading'}),
|
||||||
|
dict(name='dl', attrs={'class':'ad'}),
|
||||||
|
dict(name='p', attrs={'class':'report'}),
|
||||||
|
dict(name='div', attrs={'class':'hfeed comments'}),
|
||||||
|
dict(name='dl', attrs={'id':'entryPanel'}),
|
||||||
|
dict(name='dl', attrs={'class':'infopush ip_wide'}),
|
||||||
|
dict(name='div', attrs={'class':'sidebar'}),
|
||||||
|
dict(name='dl', attrs={'class':'bottom'}),
|
||||||
|
dict(name='div', attrs={'id':'footer'}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Slovenija', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=13')
|
||||||
|
,(u'Svet', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=14')
|
||||||
|
,(u'EU', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=116')
|
||||||
|
,(u'Poslovni dnevnik', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=5')
|
||||||
|
,(u'Kronika', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=15')
|
||||||
|
,(u'Kultura', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=17')
|
||||||
|
,(u'Zdravje', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=18')
|
||||||
|
,(u'Znanost in IT', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=19')
|
||||||
|
,(u'(Ne)verjetno', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=20')
|
||||||
|
,(u'E-strada', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=21')
|
||||||
|
,(u'Svet vozil', u'http://www.dnevnik.si/rss/?articleType=1&articleSection=22')
|
||||||
|
]
|
38
resources/recipes/hola.recipe
Normal file
38
resources/recipes/hola.recipe
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Brendan Sleight <bms.calibre at barwap.com>'
|
||||||
|
'''
|
||||||
|
hola.com
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Hackaday(BasicNewsRecipe):
|
||||||
|
title = u'Hola'
|
||||||
|
__author__ = 'bmsleight'
|
||||||
|
description = 'diario de actualidad, moda y belleza.'
|
||||||
|
oldest_article = 10
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
language = 'es'
|
||||||
|
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='div', attrs={'id':'cuerpo'})
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Famosos' , u'http://www.hola.com/famosos/rss.xml' ),
|
||||||
|
(u'Realeza' , u'http://www.hola.com/realeza/rss.xml' ),
|
||||||
|
(u'Cine' , u'http://www.hola.com/cine/rss.xml' ),
|
||||||
|
(u'Música' , u'http://www.hola.com/musica/rss.xml' ),
|
||||||
|
(u'Moda y modelos' , u'http://www.hola.com/moda/portada/rss.xml' ),
|
||||||
|
(u'Belleza y salud', u'http://www.hola.com/belleza/portada/rss.xml' ),
|
||||||
|
(u'Niños' , u'http://www.hola.com/ninos/rss.xml' ),
|
||||||
|
(u'Todas las noticias', u'http://int2.hola.com/app/feeds/rss_hola.php'),
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_article_url(self, article):
|
||||||
|
url = article.get('guid', None)
|
||||||
|
return url
|
57
resources/recipes/mmc_rtv.recipe
Normal file
57
resources/recipes/mmc_rtv.recipe
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, BlonG'
|
||||||
|
'''
|
||||||
|
www.rtvslo.si
|
||||||
|
'''
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class MMCRTV(BasicNewsRecipe):
|
||||||
|
title = u'MMC RTV Slovenija'
|
||||||
|
__author__ = u'BlonG'
|
||||||
|
description = u"Prvi interaktivni multimedijski portal, MMC RTV Slovenija"
|
||||||
|
oldest_article = 3
|
||||||
|
max_articles_per_feed = 20
|
||||||
|
language = 'sl'
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
cover_url = 'https://sites.google.com/site/javno2010/home/rtv_slo_cover.jpg'
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
|
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||||
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
|
'''
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
split_url = url.split("/")
|
||||||
|
print_url = 'http://www.rtvslo.si/index.php?c_mod=news&op=print&id=' + split_url[-1]
|
||||||
|
return print_url
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='div', attrs={'class':'title'}),
|
||||||
|
dict(name='div', attrs={'id':'newsbody'}),
|
||||||
|
dict(name='div', attrs={'id':'newsblocks'}),
|
||||||
|
]
|
||||||
|
# remove_tags=[
|
||||||
|
# 40 dict(name='div', attrs={'id':'newsblocks'}),
|
||||||
|
# ]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Slovenija', u'http://www.rtvslo.si/feeds/01.xml'),
|
||||||
|
(u'Svet', u'http://www.rtvslo.si/feeds/02.xml'),
|
||||||
|
(u'Evropska unija', u'http://www.rtvslo.si/feeds/16.xml'),
|
||||||
|
(u'Gospodarstvo', u'http://www.rtvslo.si/feeds/04.xml'),
|
||||||
|
(u'\u010crna kronika', u'http://www.rtvslo.si/feeds/08.xml'),
|
||||||
|
(u'Okolje', u'http://www.rtvslo.si/feeds/12.xml'),
|
||||||
|
(u'Znanost in tehnologija', u'http://www.rtvslo.si/feeds/09.xml'),
|
||||||
|
(u'Zabava', u'http://www.rtvslo.si/feeds/06.xml'),
|
||||||
|
(u'Ture avanture', u'http://www.rtvslo.si/feeds/28.xml'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# def preprocess_html(self, soup):
|
||||||
|
# newsblocks = soup.find('div',attrs = ['id':'newsblocks'])
|
||||||
|
# soup.find('div', attrs = {'id':'newsbody'}).insert(-1, newsblocks)
|
||||||
|
# return soup
|
||||||
|
|
73
resources/recipes/scprint.recipe
Normal file
73
resources/recipes/scprint.recipe
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe, LoginFailed
|
||||||
|
|
||||||
|
class SCPrintMagazine(BasicNewsRecipe):
|
||||||
|
title = u'SC Print Magazine'
|
||||||
|
__author__ = u'Tony Maro'
|
||||||
|
description = u'Last print version of the data security magazine'
|
||||||
|
INDEX = "http://www.scmagazineus.com/issuearchive/"
|
||||||
|
no_stylesheets = True
|
||||||
|
language = 'en'
|
||||||
|
keep_only_tags = [dict(id=['article','review'])]
|
||||||
|
remove_tags = [dict(id=['articlePrintTools','reviewBodyColumn'])]
|
||||||
|
LOG_IN = 'http://www.scmagazineus.com/login/'
|
||||||
|
tags = 'News,SC Magazine'
|
||||||
|
needs_subscription = True
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
articles = []
|
||||||
|
issuelink = printsections = None
|
||||||
|
|
||||||
|
soup = self.index_to_soup(self.INDEX)
|
||||||
|
sectit = soup.find('div', attrs={'class':'issueArchiveItem'})
|
||||||
|
if sectit is not None:
|
||||||
|
linkt = sectit.find('a')
|
||||||
|
issuelink = linkt['href']
|
||||||
|
imgt = sectit.find('img')
|
||||||
|
self.cover_url = imgt['src']
|
||||||
|
|
||||||
|
if issuelink is not None:
|
||||||
|
issue = self.index_to_soup(issuelink)
|
||||||
|
if issue is not None:
|
||||||
|
printsections = issue.findAll('div',attrs={'class':'PrintSection'})
|
||||||
|
if printsections is not None:
|
||||||
|
for printsection in printsections:
|
||||||
|
onesection = []
|
||||||
|
sectiontitle = printsection.find('h3').contents[0]
|
||||||
|
articlesec = printsection.findAll('div',attrs={'class':'IssueArchiveFormat'})
|
||||||
|
if articlesec is not None:
|
||||||
|
''' got articles '''
|
||||||
|
for onearticle in articlesec:
|
||||||
|
''' process one article '''
|
||||||
|
arttitlet = onearticle.find('h3')
|
||||||
|
if arttitlet is not None:
|
||||||
|
mylink = arttitlet.find('a')
|
||||||
|
if mylink is not None:
|
||||||
|
if mylink.has_key('title'):
|
||||||
|
arttitle = mylink['title']
|
||||||
|
else:
|
||||||
|
arttitle = 'unknown'
|
||||||
|
if mylink.has_key('href'):
|
||||||
|
artlink = mylink['href']
|
||||||
|
artlink = artlink.replace("/article","/printarticle")
|
||||||
|
artlink = artlink.replace("/review","/printreview")
|
||||||
|
deck = onearticle.find('div',attrs={'class':'deck'})
|
||||||
|
if deck is not None:
|
||||||
|
deck = deck.contents[0]
|
||||||
|
onesection.append({'title':arttitle, 'url':artlink, 'description':deck,'date':''})
|
||||||
|
articles.append((sectiontitle, onesection))
|
||||||
|
|
||||||
|
return articles
|
||||||
|
|
||||||
|
def get_browser(self):
|
||||||
|
br = BasicNewsRecipe.get_browser(self)
|
||||||
|
br.open(self.LOG_IN)
|
||||||
|
br.select_form(name='aspnetForm')
|
||||||
|
br['ctl00$ctl00$cphAllPageContent$cphMainContent$SubscriberEasyLoginView1$txtEmail'] = self.username
|
||||||
|
br['ctl00$ctl00$cphAllPageContent$cphMainContent$SubscriberEasyLoginView1$txtPassword'] = self.password
|
||||||
|
raw = br.submit("ctl00$ctl00$cphAllPageContent$cphMainContent$SubscriberEasyLoginView1$btnLogin").read()
|
||||||
|
if 'Logout</a>' not in raw:
|
||||||
|
raise LoginFailed(
|
||||||
|
_('Failed to log in, check your username and password for'
|
||||||
|
' the calibre Periodicals service.'))
|
||||||
|
return br
|
||||||
|
|
55
resources/recipes/siol.recipe
Normal file
55
resources/recipes/siol.recipe
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, BlonG'
|
||||||
|
'''
|
||||||
|
www.siol.si
|
||||||
|
'''
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
class Siol(BasicNewsRecipe):
|
||||||
|
title = u'Siol.net'
|
||||||
|
__author__ = u'BlonG'
|
||||||
|
description = "Multimedijski portal z aktualnimi vsebinami, intervjuji, komentarji iz Slovenije in sveta, sportal, trendi, avtomoto, blogos"
|
||||||
|
oldest_article = 3
|
||||||
|
language = 'sl'
|
||||||
|
max_articles_per_feed = 20
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
cover_url = 'https://sites.google.com/site/javno2010/home/siol_cover.jpg'
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
|
h2{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
|
'''
|
||||||
|
|
||||||
|
html2lrf_options = ['--base-font-size', '10']
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='div', attrs={'id':'idContent'}),
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='span', attrs={'class':'com1'}),
|
||||||
|
dict(name='div', attrs={'class':'relation'}),
|
||||||
|
dict(name='p', attrs={'class':'path'}),
|
||||||
|
dict(name='div', attrs={'class':'clear_r'}),
|
||||||
|
dict(name='div', attrs={'id':'appendix'}),
|
||||||
|
dict(name='div', attrs={'id':'rail'}),
|
||||||
|
dict(name='div', attrs={'id':'div_comments'}),
|
||||||
|
dict(name='div', attrs={'class':'thumbs'}),
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Slovenija', u'http://www.siol.net/rss.aspx?path=Slovenija')
|
||||||
|
,(u'Lokalne novice', u'http://www.siol.net/rss.aspx?path=Slovenija/Lokalne_novice')
|
||||||
|
,(u'EU', u'http://www.siol.net/rss.aspx?path=EU')
|
||||||
|
,(u'Svet', u'http://www.siol.net/rss.aspx?path=Svet')
|
||||||
|
,(u'Gospodarstvo', u'http://www.siol.net/rss.aspx?path=Gospodarstvo')
|
||||||
|
,(u'Sportal', u'http://www.siol.net/rss.aspx?path=Sportal')
|
||||||
|
,(u'Trendi', u'http://www.siol.net/rss.aspx?path=Trendi')
|
||||||
|
,(u'Avtomoto', u'http://www.siol.net/rss.aspx?path=Avtomoto')
|
||||||
|
,(u'Tehnologija', u'http://www.siol.net/rss.aspx?path=Tehnologija')
|
||||||
|
,(u'TV / Film', u'http://www.siol.net/rss.aspx?path=TV')
|
||||||
|
]
|
195
resources/recipes/vedomosti.recipe
Normal file
195
resources/recipes/vedomosti.recipe
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
u'''
|
||||||
|
Ведомости
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.feedparser import parse
|
||||||
|
from calibre.ebooks.BeautifulSoup import Tag
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class VedomostiRecipe(BasicNewsRecipe):
|
||||||
|
title = u'Ведомости'
|
||||||
|
__author__ = 'Nikolai Kotchetkov'
|
||||||
|
publisher = 'vedomosti.ru'
|
||||||
|
category = 'press, Russia'
|
||||||
|
description = u'Ежедневная деловая газета'
|
||||||
|
oldest_article = 3
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
|
||||||
|
masthead_url = u'http://motorro.com/imgdir/logos/ved_logo_black2_cropped.gif'
|
||||||
|
cover_url = u'http://motorro.com/imgdir/logos/ved_logo_black2_cropped.gif'
|
||||||
|
|
||||||
|
#Add feed names if you want them to be sorted (feeds of this list appear first)
|
||||||
|
sortOrder = [u'_default', u'Первая полоса', u'Власть и деньги']
|
||||||
|
|
||||||
|
encoding = 'cp1251'
|
||||||
|
language = 'ru'
|
||||||
|
no_stylesheets = True
|
||||||
|
remove_javascript = True
|
||||||
|
recursions = 0
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='td', attrs={'class' : ['second_content']})]
|
||||||
|
|
||||||
|
remove_tags_after = [dict(name='div', attrs={'class' : 'article_text'})]
|
||||||
|
|
||||||
|
remove_tags = [dict(name='div', attrs={'class' : ['sep', 'choice', 'articleRightTbl']})]
|
||||||
|
|
||||||
|
feeds = [u'http://www.vedomosti.ru/newspaper/out/rss.xml']
|
||||||
|
|
||||||
|
#base URL for relative links
|
||||||
|
base_url = u'http://www.vedomosti.ru'
|
||||||
|
|
||||||
|
extra_css = 'h1 {font-size: 1.5em; margin: 0em 0em 0em 0em; text-align: center;}'\
|
||||||
|
'h2 {font-size: 1.0em; margin: 0em 0em 0em 0em;}'\
|
||||||
|
'h3 {font-size: 0.8em; margin: 0em 0em 0em 0em;}'\
|
||||||
|
'.article_date {font-size: 0.5em; color: gray; font-family: monospace; text-align:right;}'\
|
||||||
|
'.article_authors {font-size: 0.5em; color: gray; font-family: monospace; text-align:right;}'\
|
||||||
|
'.article_img {width:100%; text-align: center; padding: 3px 3px 3px 3px;}'\
|
||||||
|
'.article_img_desc {width:100%; text-align: center; font-size: 0.5em; color: gray; font-family: monospace;}'\
|
||||||
|
'.article_desc {font-size: 1em; font-style:italic;}'
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
try:
|
||||||
|
feedData = parse(self.feeds[0])
|
||||||
|
if not feedData:
|
||||||
|
raise NotImplementedError
|
||||||
|
self.log("parse_index: Feed loaded successfully.")
|
||||||
|
if feedData.feed.has_key('title'):
|
||||||
|
self.title = feedData.feed.title
|
||||||
|
self.log("parse_index: Title updated to: ", self.title)
|
||||||
|
if feedData.feed.has_key('description'):
|
||||||
|
self.description = feedData.feed.description
|
||||||
|
self.log("parse_index: Description updated to: ", self.description)
|
||||||
|
|
||||||
|
def get_virtual_feed_articles(feed):
|
||||||
|
if feeds.has_key(feed):
|
||||||
|
return feeds[feed][1]
|
||||||
|
self.log("Adding new feed: ", feed)
|
||||||
|
articles = []
|
||||||
|
feeds[feed] = (feed, articles)
|
||||||
|
return articles
|
||||||
|
|
||||||
|
feeds = {}
|
||||||
|
|
||||||
|
#Iterate feed items and distribute articles using tags
|
||||||
|
for item in feedData.entries:
|
||||||
|
link = item.get('link', '');
|
||||||
|
title = item.get('title', '');
|
||||||
|
if '' == link or '' == title:
|
||||||
|
continue
|
||||||
|
article = {'title':title, 'url':link, 'description':item.get('description', ''), 'date':item.get('date', ''), 'content':''};
|
||||||
|
if not item.has_key('tags'):
|
||||||
|
get_virtual_feed_articles('_default').append(article)
|
||||||
|
continue
|
||||||
|
for tag in item.tags:
|
||||||
|
addedToDefault = False
|
||||||
|
term = tag.get('term', '')
|
||||||
|
if '' == term:
|
||||||
|
if (not addedToDefault):
|
||||||
|
get_virtual_feed_articles('_default').append(article)
|
||||||
|
continue
|
||||||
|
get_virtual_feed_articles(term).append(article)
|
||||||
|
|
||||||
|
#Get feed list
|
||||||
|
#Select sorted feeds first of all
|
||||||
|
result = []
|
||||||
|
for feedName in self.sortOrder:
|
||||||
|
if (not feeds.has_key(feedName)): continue
|
||||||
|
result.append(feeds[feedName])
|
||||||
|
del feeds[feedName]
|
||||||
|
result = result + feeds.values()
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception, err:
|
||||||
|
self.log(err)
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
return self.adeify_images(soup)
|
||||||
|
|
||||||
|
def postprocess_html(self, soup, first_fetch):
|
||||||
|
#self.log('Original: ', soup.prettify())
|
||||||
|
|
||||||
|
#Find article
|
||||||
|
contents = soup.find('div', {'class':['article_text']})
|
||||||
|
if not contents:
|
||||||
|
self.log('postprocess_html: article div not found!')
|
||||||
|
return soup
|
||||||
|
contents.extract()
|
||||||
|
|
||||||
|
#Find title
|
||||||
|
title = soup.find('h1')
|
||||||
|
if title:
|
||||||
|
contents.insert(0, title)
|
||||||
|
|
||||||
|
#Find article image
|
||||||
|
newstop = soup.find('div', {'class':['newstop']})
|
||||||
|
if newstop:
|
||||||
|
img = newstop.find('img')
|
||||||
|
if img:
|
||||||
|
imgDiv = Tag(soup, 'div')
|
||||||
|
imgDiv['class'] = 'article_img'
|
||||||
|
|
||||||
|
if img.has_key('width'):
|
||||||
|
del(img['width'])
|
||||||
|
if img.has_key('height'):
|
||||||
|
del(img['height'])
|
||||||
|
|
||||||
|
#find description
|
||||||
|
element = img.parent.nextSibling
|
||||||
|
|
||||||
|
img.extract()
|
||||||
|
imgDiv.insert(0, img)
|
||||||
|
|
||||||
|
while element:
|
||||||
|
if not isinstance(element, Tag):
|
||||||
|
continue
|
||||||
|
nextElement = element.nextSibling
|
||||||
|
if 'p' == element.name:
|
||||||
|
element.extract()
|
||||||
|
element['class'] = 'article_img_desc'
|
||||||
|
imgDiv.insert(len(imgDiv.contents), element)
|
||||||
|
element = nextElement
|
||||||
|
|
||||||
|
contents.insert(1, imgDiv)
|
||||||
|
|
||||||
|
#find article abstract
|
||||||
|
abstract = soup.find('p', {'class':['subhead']})
|
||||||
|
if abstract:
|
||||||
|
abstract['class'] = 'article_desc'
|
||||||
|
contents.insert(2, abstract)
|
||||||
|
|
||||||
|
#Find article authors
|
||||||
|
authorsDiv = soup.find('div', {'class':['autors']})
|
||||||
|
if authorsDiv:
|
||||||
|
authorsP = authorsDiv.find('p')
|
||||||
|
if authorsP:
|
||||||
|
authorsP['class'] = 'article_authors'
|
||||||
|
contents.insert(len(contents.contents), authorsP)
|
||||||
|
|
||||||
|
#Fix urls that use relative path
|
||||||
|
urls = contents.findAll('a');
|
||||||
|
if urls:
|
||||||
|
for url in urls:
|
||||||
|
if not url.has_key('href'):
|
||||||
|
continue
|
||||||
|
if '/' == url['href'][0]:
|
||||||
|
url['href'] = self.base_url + url['href']
|
||||||
|
|
||||||
|
body = soup.find('td', {'class':['second_content']})
|
||||||
|
if body:
|
||||||
|
body.replaceWith(contents)
|
||||||
|
|
||||||
|
self.log('Result: ', soup.prettify())
|
||||||
|
return soup
|
||||||
|
|
@ -49,7 +49,6 @@ class Push(Command):
|
|||||||
print '\n\nPushing to:', host, '\n'
|
print '\n\nPushing to:', host, '\n'
|
||||||
threads.append(Thread(target=subprocess.check_call, args=(rcmd,)))
|
threads.append(Thread(target=subprocess.check_call, args=(rcmd,)))
|
||||||
threads[-1].start()
|
threads[-1].start()
|
||||||
subprocess.check_call(rcmd)
|
|
||||||
for thread in threads:
|
for thread in threads:
|
||||||
thread.join()
|
thread.join()
|
||||||
|
|
||||||
|
@ -444,6 +444,9 @@ xml_entity_to_unicode = partial(entity_to_unicode, result_exceptions = {
|
|||||||
def replace_entities(raw):
|
def replace_entities(raw):
|
||||||
return _ent_pat.sub(entity_to_unicode, raw)
|
return _ent_pat.sub(entity_to_unicode, raw)
|
||||||
|
|
||||||
|
def xml_replace_entities(raw):
|
||||||
|
return _ent_pat.sub(xml_entity_to_unicode, raw)
|
||||||
|
|
||||||
def prepare_string_for_xml(raw, attribute=False):
|
def prepare_string_for_xml(raw, attribute=False):
|
||||||
raw = _ent_pat.sub(entity_to_unicode, raw)
|
raw = _ent_pat.sub(entity_to_unicode, raw)
|
||||||
raw = raw.replace('&', '&').replace('<', '<').replace('>', '>')
|
raw = raw.replace('&', '&').replace('<', '<').replace('>', '>')
|
||||||
|
@ -109,8 +109,9 @@ class OCFZipReader(OCFReader):
|
|||||||
raise EPubException("not a ZIP .epub OCF container")
|
raise EPubException("not a ZIP .epub OCF container")
|
||||||
self.root = root
|
self.root = root
|
||||||
if self.root is None:
|
if self.root is None:
|
||||||
if hasattr(stream, 'name'):
|
name = getattr(stream, 'name', False)
|
||||||
self.root = os.path.abspath(os.path.dirname(stream.name))
|
if name:
|
||||||
|
self.root = os.path.abspath(os.path.dirname(name))
|
||||||
else:
|
else:
|
||||||
self.root = os.getcwdu()
|
self.root = os.getcwdu()
|
||||||
super(OCFZipReader, self).__init__()
|
super(OCFZipReader, self).__init__()
|
||||||
|
@ -133,7 +133,11 @@ class DetectStructure(object):
|
|||||||
|
|
||||||
|
|
||||||
def elem_to_link(self, item, elem, counter):
|
def elem_to_link(self, item, elem, counter):
|
||||||
text = xml2text(elem)
|
text = xml2text(elem).strip()
|
||||||
|
if not text:
|
||||||
|
text = elem.get('title', '')
|
||||||
|
if not text:
|
||||||
|
text = elem.get('alt', '')
|
||||||
text = text[:100].strip()
|
text = text[:100].strip()
|
||||||
id = elem.get('id', 'calibre_toc_%d'%counter)
|
id = elem.get('id', 'calibre_toc_%d'%counter)
|
||||||
elem.set('id', id)
|
elem.set('id', id)
|
||||||
|
@ -213,11 +213,13 @@ class BookInfo(QWebView):
|
|||||||
f = QFontInfo(QApplication.font(self.parent())).pixelSize()
|
f = QFontInfo(QApplication.font(self.parent())).pixelSize()
|
||||||
p = unicode(QApplication.palette().color(QPalette.Normal,
|
p = unicode(QApplication.palette().color(QPalette.Normal,
|
||||||
QPalette.Base).name())
|
QPalette.Base).name())
|
||||||
|
c = unicode(QApplication.palette().color(QPalette.Normal,
|
||||||
|
QPalette.Text).name())
|
||||||
templ = u'''\
|
templ = u'''\
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body, td {background-color: %s; font-size: %dpx}
|
body, td {background-color: %s; font-size: %dpx; color: %s }
|
||||||
a { text-decoration: none; color: blue }
|
a { text-decoration: none; color: blue }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
@ -225,7 +227,7 @@ class BookInfo(QWebView):
|
|||||||
%%s
|
%%s
|
||||||
</body>
|
</body>
|
||||||
<html>
|
<html>
|
||||||
'''%(p, f)
|
'''%(p, f, c)
|
||||||
if self.vertical:
|
if self.vertical:
|
||||||
if comments:
|
if comments:
|
||||||
rows += u'<tr><td colspan="2">%s</td></tr>'%comments
|
rows += u'<tr><td colspan="2">%s</td></tr>'%comments
|
||||||
|
104
src/calibre/gui2/comments_editor.py
Normal file
104
src/calibre/gui2/comments_editor.py
Normal file
File diff suppressed because one or more lines are too long
@ -5,8 +5,8 @@ __docformat__ = 'restructuredtext en'
|
|||||||
|
|
||||||
import textwrap, os, re
|
import textwrap, os, re
|
||||||
|
|
||||||
from PyQt4.QtCore import QCoreApplication, SIGNAL, QModelIndex, QTimer, Qt
|
from PyQt4.Qt import QCoreApplication, SIGNAL, QModelIndex, QTimer, Qt, \
|
||||||
from PyQt4.QtGui import QDialog, QPixmap, QGraphicsScene, QIcon
|
QDialog, QPixmap, QGraphicsScene, QIcon, QSize
|
||||||
|
|
||||||
from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo
|
from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo
|
||||||
from calibre.gui2 import dynamic, open_local_file
|
from calibre.gui2 import dynamic, open_local_file
|
||||||
@ -20,6 +20,8 @@ class BookInfo(QDialog, Ui_BookInfo):
|
|||||||
Ui_BookInfo.__init__(self)
|
Ui_BookInfo.__init__(self)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.cover_pixmap = None
|
self.cover_pixmap = None
|
||||||
|
self.comments.sizeHint = self.comments_size_hint
|
||||||
|
|
||||||
desktop = QCoreApplication.instance().desktop()
|
desktop = QCoreApplication.instance().desktop()
|
||||||
screen_height = desktop.availableGeometry().height() - 100
|
screen_height = desktop.availableGeometry().height() - 100
|
||||||
self.resize(self.size().width(), screen_height)
|
self.resize(self.size().width(), screen_height)
|
||||||
@ -37,12 +39,16 @@ class BookInfo(QDialog, Ui_BookInfo):
|
|||||||
self.fit_cover.stateChanged.connect(self.toggle_cover_fit)
|
self.fit_cover.stateChanged.connect(self.toggle_cover_fit)
|
||||||
self.cover.resizeEvent = self.cover_view_resized
|
self.cover.resizeEvent = self.cover_view_resized
|
||||||
|
|
||||||
|
def comments_size_hint(self):
|
||||||
|
return QSize(350, 350)
|
||||||
|
|
||||||
def toggle_cover_fit(self, state):
|
def toggle_cover_fit(self, state):
|
||||||
dynamic.set('book_info_dialog_fit_cover', self.fit_cover.isChecked())
|
dynamic.set('book_info_dialog_fit_cover', self.fit_cover.isChecked())
|
||||||
self.resize_cover()
|
self.resize_cover()
|
||||||
|
|
||||||
def cover_view_resized(self, event):
|
def cover_view_resized(self, event):
|
||||||
QTimer.singleShot(1, self.resize_cover)
|
QTimer.singleShot(1, self.resize_cover)
|
||||||
|
|
||||||
def slave(self, current, previous):
|
def slave(self, current, previous):
|
||||||
row = current.row()
|
row = current.row()
|
||||||
self.refresh(row)
|
self.refresh(row)
|
||||||
|
@ -47,9 +47,15 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Comments</string>
|
<string>Comments</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item row="0" column="0">
|
<item>
|
||||||
<widget class="QWebView" name="comments">
|
<widget class="QWebView" name="comments">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>350</width>
|
<width>350</width>
|
||||||
|
@ -240,13 +240,13 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
self.writable_fields = ['']
|
self.writable_fields = ['']
|
||||||
fm = self.db.field_metadata
|
fm = self.db.field_metadata
|
||||||
for f in fm:
|
for f in fm:
|
||||||
if (f in ['author_sort'] or (
|
if (f in ['author_sort'] or
|
||||||
fm[f]['datatype'] in ['text', 'series'])
|
(fm[f]['datatype'] in ['text', 'series']
|
||||||
and fm[f].get('search_terms', None)
|
and fm[f].get('search_terms', None)
|
||||||
and f not in ['formats', 'ondevice']):
|
and f not in ['formats', 'ondevice', 'sort'])):
|
||||||
self.all_fields.append(f)
|
self.all_fields.append(f)
|
||||||
self.writable_fields.append(f)
|
self.writable_fields.append(f)
|
||||||
if fm[f]['datatype'] == 'composite':
|
if f in ['sort'] or fm[f]['datatype'] == 'composite':
|
||||||
self.all_fields.append(f)
|
self.all_fields.append(f)
|
||||||
self.all_fields.sort()
|
self.all_fields.sort()
|
||||||
self.writable_fields.sort()
|
self.writable_fields.sort()
|
||||||
@ -274,7 +274,6 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
self.main_heading = _(
|
self.main_heading = _(
|
||||||
'<b>You can destroy your library using this feature.</b> '
|
'<b>You can destroy your library using this feature.</b> '
|
||||||
'Changes are permanent. There is no undo function. '
|
'Changes are permanent. There is no undo function. '
|
||||||
' This feature is experimental, and there may be bugs. '
|
|
||||||
'You are strongly encouraged to back up your library '
|
'You are strongly encouraged to back up your library '
|
||||||
'before proceeding.<p>'
|
'before proceeding.<p>'
|
||||||
'Search and replace in text fields using character matching '
|
'Search and replace in text fields using character matching '
|
||||||
@ -338,7 +337,10 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
def s_r_get_field(self, mi, field):
|
def s_r_get_field(self, mi, field):
|
||||||
if field:
|
if field:
|
||||||
fm = self.db.metadata_for_field(field)
|
fm = self.db.metadata_for_field(field)
|
||||||
val = mi.get(field, None)
|
if field == 'sort':
|
||||||
|
val = mi.get('title_sort', None)
|
||||||
|
else:
|
||||||
|
val = mi.get(field, None)
|
||||||
if val is None:
|
if val is None:
|
||||||
val = []
|
val = []
|
||||||
elif not fm['is_multiple']:
|
elif not fm['is_multiple']:
|
||||||
|
@ -400,7 +400,7 @@ Future conversion of these books will use the default settings.</string>
|
|||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tabWidgetPage3">
|
<widget class="QWidget" name="tabWidgetPage3">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>&Search and replace (experimental)</string>
|
<string>&Search and replace</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QGridLayout" name="vargrid">
|
<layout class="QGridLayout" name="vargrid">
|
||||||
<property name="sizeConstraint">
|
<property name="sizeConstraint">
|
||||||
|
@ -845,7 +845,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
|||||||
if cf is not None and hasattr(cf, 'terminate'):
|
if cf is not None and hasattr(cf, 'terminate'):
|
||||||
cf.terminate()
|
cf.terminate()
|
||||||
cf.wait()
|
cf.wait()
|
||||||
|
self.save_state()
|
||||||
QDialog.reject(self, *args)
|
QDialog.reject(self, *args)
|
||||||
|
|
||||||
def read_state(self):
|
def read_state(self):
|
||||||
|
@ -1,17 +1,75 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
import re
|
|
||||||
from PyQt4.QtGui import QDialog
|
import re, copy
|
||||||
|
|
||||||
|
from PyQt4.QtGui import QDialog, QDialogButtonBox
|
||||||
|
|
||||||
from calibre.gui2.dialogs.search_ui import Ui_Dialog
|
from calibre.gui2.dialogs.search_ui import Ui_Dialog
|
||||||
from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH
|
from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH
|
||||||
|
from calibre.gui2 import gprefs
|
||||||
|
|
||||||
|
box_values = {}
|
||||||
|
|
||||||
class SearchDialog(QDialog, Ui_Dialog):
|
class SearchDialog(QDialog, Ui_Dialog):
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, parent, db):
|
||||||
QDialog.__init__(self, *args)
|
QDialog.__init__(self, parent)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.mc = ''
|
self.mc = ''
|
||||||
|
searchables = sorted(db.field_metadata.searchable_fields(),
|
||||||
|
lambda x, y: cmp(x if x[0] != '#' else x[1:],
|
||||||
|
y if y[0] != '#' else y[1:]))
|
||||||
|
self.general_combo.addItems(searchables)
|
||||||
|
|
||||||
|
self.box_last_values = copy.deepcopy(box_values)
|
||||||
|
if self.box_last_values:
|
||||||
|
for k,v in self.box_last_values.items():
|
||||||
|
if k == 'general_index':
|
||||||
|
continue
|
||||||
|
getattr(self, k).setText(v)
|
||||||
|
self.general_combo.setCurrentIndex(
|
||||||
|
self.general_combo.findText(self.box_last_values['general_index']))
|
||||||
|
|
||||||
|
self.buttonBox.accepted.connect(self.advanced_search_button_pushed)
|
||||||
|
self.tab_2_button_box.accepted.connect(self.accept)
|
||||||
|
self.tab_2_button_box.rejected.connect(self.reject)
|
||||||
|
self.clear_button.clicked.connect(self.clear_button_pushed)
|
||||||
|
self.adv_search_used = False
|
||||||
|
|
||||||
|
current_tab = gprefs.get('advanced search dialog current tab', 0)
|
||||||
|
self.tabWidget.setCurrentIndex(current_tab)
|
||||||
|
self.tabWidget.currentChanged[int].connect(self.tab_changed)
|
||||||
|
self.tab_changed(current_tab)
|
||||||
|
|
||||||
|
def save_state(self):
|
||||||
|
gprefs['advanced search dialog current tab'] = \
|
||||||
|
self.tabWidget.currentIndex()
|
||||||
|
|
||||||
|
def accept(self):
|
||||||
|
self.save_state()
|
||||||
|
return QDialog.accept(self)
|
||||||
|
|
||||||
|
def reject(self):
|
||||||
|
self.save_state()
|
||||||
|
return QDialog.reject(self)
|
||||||
|
|
||||||
|
def tab_changed(self, idx):
|
||||||
|
if idx == 1:
|
||||||
|
self.tab_2_button_box.button(QDialogButtonBox.Ok).setDefault(True)
|
||||||
|
else:
|
||||||
|
self.buttonBox.button(QDialogButtonBox.Ok).setDefault(True)
|
||||||
|
|
||||||
|
def advanced_search_button_pushed(self):
|
||||||
|
self.adv_search_used = True
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
def clear_button_pushed(self):
|
||||||
|
self.title_box.setText('')
|
||||||
|
self.authors_box.setText('')
|
||||||
|
self.series_box.setText('')
|
||||||
|
self.tags_box.setText('')
|
||||||
|
self.general_box.setText('')
|
||||||
|
|
||||||
def tokens(self, raw):
|
def tokens(self, raw):
|
||||||
phrases = re.findall(r'\s*".*?"\s*', raw)
|
phrases = re.findall(r'\s*".*?"\s*', raw)
|
||||||
@ -21,6 +79,12 @@ class SearchDialog(QDialog, Ui_Dialog):
|
|||||||
return ['"' + self.mc + t + '"' for t in phrases + [r.strip() for r in raw.split()]]
|
return ['"' + self.mc + t + '"' for t in phrases + [r.strip() for r in raw.split()]]
|
||||||
|
|
||||||
def search_string(self):
|
def search_string(self):
|
||||||
|
if self.adv_search_used:
|
||||||
|
return self.adv_search_string()
|
||||||
|
else:
|
||||||
|
return self.box_search_string()
|
||||||
|
|
||||||
|
def adv_search_string(self):
|
||||||
mk = self.matchkind.currentIndex()
|
mk = self.matchkind.currentIndex()
|
||||||
if mk == CONTAINS_MATCH:
|
if mk == CONTAINS_MATCH:
|
||||||
self.mc = ''
|
self.mc = ''
|
||||||
@ -56,3 +120,36 @@ class SearchDialog(QDialog, Ui_Dialog):
|
|||||||
tok = '"%s"'%tok
|
tok = '"%s"'%tok
|
||||||
return tok
|
return tok
|
||||||
|
|
||||||
|
def box_search_string(self):
|
||||||
|
ans = []
|
||||||
|
self.box_last_values = {}
|
||||||
|
title = unicode(self.title_box.text()).strip()
|
||||||
|
self.box_last_values['title_box'] = title
|
||||||
|
if title:
|
||||||
|
ans.append('title:"' + title + '"')
|
||||||
|
author = unicode(self.authors_box.text()).strip()
|
||||||
|
self.box_last_values['authors_box'] = author
|
||||||
|
if author:
|
||||||
|
ans.append('author:"' + author + '"')
|
||||||
|
series = unicode(self.series_box.text()).strip()
|
||||||
|
self.box_last_values['series_box'] = series
|
||||||
|
if series:
|
||||||
|
ans.append('series:"' + series + '"')
|
||||||
|
self.mc = '='
|
||||||
|
tags = unicode(self.tags_box.text())
|
||||||
|
self.box_last_values['tags_box'] = tags
|
||||||
|
tags = self.tokens(tags)
|
||||||
|
if tags:
|
||||||
|
tags = ['tags:' + t for t in tags]
|
||||||
|
ans.append('(' + ' or '.join(tags) + ')')
|
||||||
|
general = unicode(self.general_box.text())
|
||||||
|
self.box_last_values['general_box'] = general
|
||||||
|
general_index = unicode(self.general_combo.currentText())
|
||||||
|
self.box_last_values['general_index'] = general_index
|
||||||
|
global box_values
|
||||||
|
box_values = copy.deepcopy(self.box_last_values)
|
||||||
|
if general:
|
||||||
|
ans.append(unicode(self.general_combo.currentText()) + ':"' + general + '"')
|
||||||
|
if ans:
|
||||||
|
return ' and '.join(ans)
|
||||||
|
return ''
|
||||||
|
@ -1,195 +1,359 @@
|
|||||||
<ui version="4.0" >
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
<class>Dialog</class>
|
<class>Dialog</class>
|
||||||
<widget class="QDialog" name="Dialog" >
|
<widget class="QDialog" name="Dialog">
|
||||||
<property name="geometry" >
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>667</width>
|
<width>731</width>
|
||||||
<height>391</height>
|
<height>384</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle" >
|
<property name="windowTitle">
|
||||||
<string>Advanced Search</string>
|
<string>Advanced Search</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon" >
|
<property name="windowIcon">
|
||||||
<iconset resource="../../../../resources/images.qrc" >
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
<normaloff>:/images/search.png</normaloff>:/images/search.png</iconset>
|
<normaloff>:/images/search.png</normaloff>:/images/search.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<item>
|
<item row="0" column="0">
|
||||||
<widget class="QGroupBox" name="groupBox" >
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<property name="title" >
|
<property name="currentIndex">
|
||||||
<string>Find entries that have...</string>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout" >
|
<widget class="QWidget" name="tab">
|
||||||
<item>
|
<attribute name="title">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
<string>A&dvanced Search</string>
|
||||||
<item>
|
</attribute>
|
||||||
<widget class="QLabel" name="label" >
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
<property name="text" >
|
<item>
|
||||||
<string>&All these words:</string>
|
<widget class="QGroupBox" name="groupBox">
|
||||||
</property>
|
<property name="title">
|
||||||
<property name="buddy" >
|
<string>Find entries that have...</string>
|
||||||
<cstring>all</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="all" />
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2" >
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_2" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>This exact &phrase:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy" >
|
|
||||||
<cstring>all</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="phrase" />
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3" >
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_3" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>&One or more of these words:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy" >
|
|
||||||
<cstring>all</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="any" />
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox_2" >
|
|
||||||
<property name="title" >
|
|
||||||
<string>But dont show entries that have...</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2" >
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4" >
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_4" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>Any of these &unwanted words:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy" >
|
|
||||||
<cstring>all</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="none" />
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox" >
|
|
||||||
<property name="maximumSize" >
|
|
||||||
<size>
|
|
||||||
<width>16777215</width>
|
|
||||||
<height>60</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_5" >
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_5" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>What kind of match to use:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy" >
|
|
||||||
<cstring>matchkind</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="matchkind">
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Contains: the word or phrase matches anywhere in the metadata</string>
|
|
||||||
</property>
|
</property>
|
||||||
</item>
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<string>Equals: the word or phrase must match an entire metadata field</string>
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>&All these words:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>all</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="all"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>This exact &phrase:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>all</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="phrase"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>&One or more of these words:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>all</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="any"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
|
<property name="title">
|
||||||
|
<string>But dont show entries that have...</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
<string>Regular expression: the expression must match anywhere in the metadata</string>
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Any of these &unwanted words:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>all</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="none"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>60</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>What kind of match to use:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>matchkind</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="matchkind">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Contains: the word or phrase matches anywhere in the metadata</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Equals: the word or phrase must match an entire metadata field</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Regular expression: the expression must match anywhere in the metadata</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_51">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>40</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>matchkind</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>30</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>See the <a href="http://calibre-ebook.com/user_manual/gui.html#the-search-interface">User Manual</a> for more help</string>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
<property name="sizeHint" stdset="0">
|
||||||
</widget>
|
<size>
|
||||||
</item>
|
<width>20</width>
|
||||||
<item>
|
<height>40</height>
|
||||||
<widget class="QLabel" name="label_51" >
|
</size>
|
||||||
<property name="sizePolicy">
|
</property>
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
</spacer>
|
||||||
<horstretch>40</horstretch>
|
</item>
|
||||||
<verstretch>0</verstretch>
|
<item>
|
||||||
</sizepolicy>
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
</property>
|
<property name="orientation">
|
||||||
<property name="text" >
|
<enum>Qt::Horizontal</enum>
|
||||||
<string> </string>
|
</property>
|
||||||
</property>
|
<property name="standardButtons">
|
||||||
<property name="buddy" >
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
<cstring>matchkind</cstring>
|
</property>
|
||||||
</property>
|
</widget>
|
||||||
</widget>
|
</item>
|
||||||
</item>
|
</layout>
|
||||||
</layout>
|
</widget>
|
||||||
</widget>
|
<widget class="QWidget" name="tab_2">
|
||||||
</item>
|
<attribute name="title">
|
||||||
<item>
|
<string>Titl&e/Author/Series ...</string>
|
||||||
<widget class="QLabel" name="label_6" >
|
</attribute>
|
||||||
<property name="maximumSize" >
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<size>
|
<item row="1" column="0">
|
||||||
<width>16777215</width>
|
<widget class="QLabel" name="label_7">
|
||||||
<height>30</height>
|
<property name="text">
|
||||||
</size>
|
<string>&Title:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text" >
|
<property name="buddy">
|
||||||
<string>See the <a href="http://calibre-ebook.com/user_manual/gui.html#the-search-interface">User Manual</a> for more help</string>
|
<cstring>title_box</cstring>
|
||||||
</property>
|
</property>
|
||||||
<property name="openExternalLinks" >
|
</widget>
|
||||||
<bool>true</bool>
|
</item>
|
||||||
</property>
|
<item row="1" column="1">
|
||||||
</widget>
|
<widget class="QLineEdit" name="title_box">
|
||||||
</item>
|
<property name="toolTip">
|
||||||
<item>
|
<string>Enter the title.</string>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
</property>
|
||||||
<property name="orientation" >
|
</widget>
|
||||||
<enum>Qt::Horizontal</enum>
|
</item>
|
||||||
</property>
|
<item row="2" column="0">
|
||||||
<property name="standardButtons" >
|
<widget class="QLabel" name="label_8">
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
<property name="text">
|
||||||
</property>
|
<string>&Author:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>authors_box</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Series:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>series_box</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="text">
|
||||||
|
<string>Ta&gs:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>tags_box</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="authors_box">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Enter an author's name. Only one author can be used.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="series_box">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Enter a series name, without an index. Only one series name can be used.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="tags_box">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Enter tags separated by spaces</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QLineEdit" name="general_box"/>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QComboBox" name="general_combo"/>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0" colspan="2">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="clear_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Clear</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="tab_2_button_box">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="text">
|
||||||
|
<string>Search only in specific fields:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>all</tabstop>
|
||||||
|
<tabstop>phrase</tabstop>
|
||||||
|
<tabstop>any</tabstop>
|
||||||
|
<tabstop>none</tabstop>
|
||||||
|
<tabstop>matchkind</tabstop>
|
||||||
|
<tabstop>buttonBox</tabstop>
|
||||||
|
<tabstop>title_box</tabstop>
|
||||||
|
<tabstop>authors_box</tabstop>
|
||||||
|
<tabstop>series_box</tabstop>
|
||||||
|
<tabstop>tags_box</tabstop>
|
||||||
|
<tabstop>general_combo</tabstop>
|
||||||
|
<tabstop>general_box</tabstop>
|
||||||
|
<tabstop>clear_button</tabstop>
|
||||||
|
<tabstop>tab_2_button_box</tabstop>
|
||||||
|
<tabstop>tabWidget</tabstop>
|
||||||
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../../resources/images.qrc" />
|
<include location="../../../../resources/images.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
@ -198,11 +362,11 @@
|
|||||||
<receiver>Dialog</receiver>
|
<receiver>Dialog</receiver>
|
||||||
<slot>accept()</slot>
|
<slot>accept()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel" >
|
<hint type="sourcelabel">
|
||||||
<x>248</x>
|
<x>248</x>
|
||||||
<y>254</y>
|
<y>254</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel" >
|
<hint type="destinationlabel">
|
||||||
<x>157</x>
|
<x>157</x>
|
||||||
<y>274</y>
|
<y>274</y>
|
||||||
</hint>
|
</hint>
|
||||||
@ -214,11 +378,11 @@
|
|||||||
<receiver>Dialog</receiver>
|
<receiver>Dialog</receiver>
|
||||||
<slot>reject()</slot>
|
<slot>reject()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel" >
|
<hint type="sourcelabel">
|
||||||
<x>316</x>
|
<x>316</x>
|
||||||
<y>260</y>
|
<y>260</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel" >
|
<hint type="destinationlabel">
|
||||||
<x>286</x>
|
<x>286</x>
|
||||||
<y>274</y>
|
<y>274</y>
|
||||||
</hint>
|
</hint>
|
||||||
|
@ -167,6 +167,7 @@ class SearchBar(QWidget): # {{{
|
|||||||
x.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
x.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||||
|
|
||||||
parent.advanced_search_button = x = QToolButton(self)
|
parent.advanced_search_button = x = QToolButton(self)
|
||||||
|
parent.advanced_search_button.setShortcut(_("Shift+Ctrl+F"))
|
||||||
x.setIcon(QIcon(I('search.png')))
|
x.setIcon(QIcon(I('search.png')))
|
||||||
l.addWidget(x)
|
l.addWidget(x)
|
||||||
x.setToolTip(_("Advanced search"))
|
x.setToolTip(_("Advanced search"))
|
||||||
|
@ -51,6 +51,10 @@ class BooksView(QTableView): # {{{
|
|||||||
QTableView.__init__(self, parent)
|
QTableView.__init__(self, parent)
|
||||||
|
|
||||||
self.setEditTriggers(self.SelectedClicked|self.EditKeyPressed)
|
self.setEditTriggers(self.SelectedClicked|self.EditKeyPressed)
|
||||||
|
if tweaks['doubleclick_on_library_view'] == 'edit_cell':
|
||||||
|
self.setEditTriggers(self.DoubleClicked|self.editTriggers())
|
||||||
|
elif tweaks['doubleclick_on_library_view'] == 'open_viewer':
|
||||||
|
self.doubleClicked.connect(parent.iactions['View'].view_triggered)
|
||||||
|
|
||||||
self.drag_allowed = True
|
self.drag_allowed = True
|
||||||
self.setDragEnabled(True)
|
self.setDragEnabled(True)
|
||||||
@ -100,8 +104,6 @@ class BooksView(QTableView): # {{{
|
|||||||
self._model.about_to_be_sorted.connect(self.about_to_be_sorted)
|
self._model.about_to_be_sorted.connect(self.about_to_be_sorted)
|
||||||
self._model.sorting_done.connect(self.sorting_done)
|
self._model.sorting_done.connect(self.sorting_done)
|
||||||
|
|
||||||
self.doubleClicked.connect(parent.iactions['View'].view_triggered)
|
|
||||||
|
|
||||||
# Column Header Context Menu {{{
|
# Column Header Context Menu {{{
|
||||||
def column_header_context_handler(self, action=None, column=None):
|
def column_header_context_handler(self, action=None, column=None):
|
||||||
if not action or not column:
|
if not action or not column:
|
||||||
|
@ -392,7 +392,7 @@ class SearchBoxMixin(object):
|
|||||||
self.tags_view.clear()
|
self.tags_view.clear()
|
||||||
|
|
||||||
def do_advanced_search(self, *args):
|
def do_advanced_search(self, *args):
|
||||||
d = SearchDialog(self)
|
d = SearchDialog(self, self.library_view.model().db)
|
||||||
if d.exec_() == QDialog.Accepted:
|
if d.exec_() == QDialog.Accepted:
|
||||||
self.search.set_search_string(d.search_string())
|
self.search.set_search_string(d.search_string())
|
||||||
|
|
||||||
|
@ -472,8 +472,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
if path != self.current_page:
|
if path != self.current_page:
|
||||||
self.pending_anchor = frag
|
self.pending_anchor = frag
|
||||||
self.load_path(path)
|
self.load_path(path)
|
||||||
elif frag:
|
else:
|
||||||
self.view.scroll_to(frag)
|
if frag:
|
||||||
|
self.view.scroll_to(frag)
|
||||||
|
else:
|
||||||
|
# Scroll to top
|
||||||
|
self.view.scroll_to('#')
|
||||||
else:
|
else:
|
||||||
open_url(url)
|
open_url(url)
|
||||||
|
|
||||||
|
@ -245,6 +245,9 @@ class BrowseServer(object):
|
|||||||
ans = ans.replace('{sort_select_label}', xml(_('Sort by')+':'))
|
ans = ans.replace('{sort_select_label}', xml(_('Sort by')+':'))
|
||||||
ans = ans.replace('{sort_cookie_name}', scn)
|
ans = ans.replace('{sort_cookie_name}', scn)
|
||||||
ans = ans.replace('{prefix}', self.opts.url_prefix)
|
ans = ans.replace('{prefix}', self.opts.url_prefix)
|
||||||
|
ans = ans.replace('{library}', _('library'))
|
||||||
|
ans = ans.replace('{home}', _('home'))
|
||||||
|
ans = ans.replace('{Search}', _('Search'))
|
||||||
opts = ['<option %svalue="%s">%s</option>' % (
|
opts = ['<option %svalue="%s">%s</option>' % (
|
||||||
'selected="selected" ' if k==sort else '',
|
'selected="selected" ' if k==sort else '',
|
||||||
xml(k), xml(n), ) for k, n in
|
xml(k), xml(n), ) for k, n in
|
||||||
|
@ -377,7 +377,7 @@ They are XPath expressions that match tags in the intermediate XHTML produced by
|
|||||||
how to construct XPath expressions. Next to each option is a button that launches a wizard to help with the creation
|
how to construct XPath expressions. Next to each option is a button that launches a wizard to help with the creation
|
||||||
of basic XPath expressions. The following simple example illustrates how to use these options.
|
of basic XPath expressions. The following simple example illustrates how to use these options.
|
||||||
|
|
||||||
Suppose you have an input document taht results in XHTML that look like this:
|
Suppose you have an input document that results in XHTML that look like this:
|
||||||
|
|
||||||
.. code-block:: html
|
.. code-block:: html
|
||||||
|
|
||||||
@ -418,6 +418,25 @@ This will result in an automatically generated two level Table of Contents that
|
|||||||
Not all output formats support a multi level Table of Contents. You should first try with EPUB Output. If that
|
Not all output formats support a multi level Table of Contents. You should first try with EPUB Output. If that
|
||||||
works, then try your format of choice.
|
works, then try your format of choice.
|
||||||
|
|
||||||
|
Using images as chapter titles when converting HTML input documents
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
Suppose you want to use an image as your chapter title, but still want |app| to be able to automatically generate a Table of Contents for you from the chapter titles.
|
||||||
|
Use the following HTML markup to achieve this
|
||||||
|
|
||||||
|
.. code-block:: html
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h2>Chapter 1</h2>
|
||||||
|
<p>chapter 1 text...</p>
|
||||||
|
<h2 title="Chapter 2"><img src="chapter2.jpg" /></h2>
|
||||||
|
<p>chapter 2 text...</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
Set the :guilabel:`Level 1 TOC` setting to ``//h:h2``. Then, for chapter two, |app| will take the title from the value of the ``title`` attribute on the ``<h2>`` tag, since the tag has no text.
|
||||||
|
|
||||||
How options are set/saved for Conversion
|
How options are set/saved for Conversion
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
|
@ -380,6 +380,8 @@ Calibre has several keyboard shortcuts to save you time and mouse movement. Thes
|
|||||||
- Show books in the same series as current book
|
- Show books in the same series as current book
|
||||||
* - :kbd:`/, Ctrl+F`
|
* - :kbd:`/, Ctrl+F`
|
||||||
- Focus the search bar
|
- Focus the search bar
|
||||||
|
* - :kbd:`Shift+Ctrl+F`
|
||||||
|
- Open the advanced search dialog
|
||||||
* - :kbd:`Ctrl+D`
|
* - :kbd:`Ctrl+D`
|
||||||
- Download metadata and shortcuts
|
- Download metadata and shortcuts
|
||||||
* - :kbd:`Ctrl+R`
|
* - :kbd:`Ctrl+R`
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: calibre 0.7.27\n"
|
"Project-Id-Version: calibre 0.7.27\n"
|
||||||
"POT-Creation-Date: 2010-11-05 15:17+MDT\n"
|
"POT-Creation-Date: 2010-11-06 09:35+MDT\n"
|
||||||
"PO-Revision-Date: 2010-11-05 15:17+MDT\n"
|
"PO-Revision-Date: 2010-11-06 09:35+MDT\n"
|
||||||
"Last-Translator: Automatically generated\n"
|
"Last-Translator: Automatically generated\n"
|
||||||
"Language-Team: LANGUAGE\n"
|
"Language-Team: LANGUAGE\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -1932,7 +1932,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:313
|
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:313
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1127
|
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1127
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:160
|
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:160
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:620
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:623
|
||||||
msgid "Tags"
|
msgid "Tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -2299,7 +2299,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:159
|
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:159
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:71
|
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:71
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:618
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:621
|
||||||
msgid "Rating"
|
msgid "Rating"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3570,7 +3570,7 @@ msgid "Click the show details button to see which ones."
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/actions/show_book_details.py:16
|
#: /home/kovid/work/calibre/src/calibre/gui2/actions/show_book_details.py:16
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:625
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:628
|
||||||
msgid "Show book details"
|
msgid "Show book details"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -3878,7 +3878,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:25
|
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:25
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:49
|
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:49
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:58
|
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:58
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:402
|
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:403
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:119
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:119
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:120
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:120
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:121
|
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:121
|
||||||
@ -3925,7 +3925,7 @@ msgstr ""
|
|||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:401
|
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:402
|
||||||
msgid "Double-click to open Book Details window"
|
msgid "Double-click to open Book Details window"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -7601,7 +7601,7 @@ msgid "Successfully downloaded metadata for %d out of %d books"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/metadata.py:287
|
#: /home/kovid/work/calibre/src/calibre/gui2/metadata.py:287
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:624
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:627
|
||||||
msgid "Details"
|
msgid "Details"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -8581,6 +8581,7 @@ msgstr ""
|
|||||||
#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:97
|
#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:97
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:270
|
#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:270
|
||||||
#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:574
|
#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:574
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:250
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -10477,7 +10478,7 @@ msgid "Password to access your calibre library. Username is "
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:51
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:51
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:402
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:405
|
||||||
msgid "Loading, please wait"
|
msgid "Loading, please wait"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -10522,70 +10523,78 @@ msgstr ""
|
|||||||
msgid "Sort by"
|
msgid "Sort by"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:307
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:248
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:513
|
msgid "library"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:249
|
||||||
|
msgid "home"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:310
|
||||||
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:516
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:569
|
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:569
|
||||||
msgid "Newest"
|
msgid "Newest"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:308
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:311
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:514
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:517
|
||||||
msgid "All books"
|
msgid "All books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:341
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:344
|
||||||
msgid "Browse books by"
|
msgid "Browse books by"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:346
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:349
|
||||||
msgid "Choose a category to browse by:"
|
msgid "Choose a category to browse by:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:422
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:425
|
||||||
msgid "Browsing by"
|
msgid "Browsing by"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:423
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:426
|
||||||
msgid "Up"
|
msgid "Up"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:544
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:547
|
||||||
msgid "in"
|
msgid "in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:547
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:550
|
||||||
msgid "Books in"
|
msgid "Books in"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:599
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:602
|
||||||
msgid "Other formats"
|
msgid "Other formats"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:606
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:609
|
||||||
msgid "Read %s in the %s format"
|
msgid "Read %s in the %s format"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:611
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:614
|
||||||
msgid "Get"
|
msgid "Get"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:626
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:629
|
||||||
msgid "Permalink"
|
msgid "Permalink"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:627
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:630
|
||||||
msgid "A permanent link to this book"
|
msgid "A permanent link to this book"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:638
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:641
|
||||||
msgid "This book has been deleted"
|
msgid "This book has been deleted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:722
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:725
|
||||||
msgid "in search"
|
msgid "in search"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:724
|
#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:727
|
||||||
msgid "Matching books"
|
msgid "Matching books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user