Merge from trunk
@ -4,6 +4,100 @@
|
|||||||
# for important features/bug fixes.
|
# for important features/bug fixes.
|
||||||
# Also, each release can have new and improved recipes.
|
# Also, each release can have new and improved recipes.
|
||||||
|
|
||||||
|
- version: 0.7.28
|
||||||
|
date: 2010-11-12
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "Update the version of the grahical toolkit (Qt 4.7.1) used in the calibre binary builds on windows and linux. This should result in a significant speed up for the calibre ebook viewer"
|
||||||
|
|
||||||
|
- title: "Driver for Nook Color, Eken M001"
|
||||||
|
|
||||||
|
- title: "Add a tweak to turn off double clicking to open viewer"
|
||||||
|
|
||||||
|
- title: "Catalog generation: Add indication when a book has no formats"
|
||||||
|
tickets: [7376]
|
||||||
|
|
||||||
|
- title: "Advanced search dialog: Add a tab to allow searching particular metadata fields easily"
|
||||||
|
|
||||||
|
- title: "Conversion pipeline: When using the Level x Table of Contents expressions, if a tag is empty but has a non-empty title attribute, use that instead of ignoring the tag"
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Comic metadata reader: Sort filenames aplhabetically when choosing an image for the cover"
|
||||||
|
tickets: [7488]
|
||||||
|
|
||||||
|
- title: "Bulk convert dialog: Hide useless restore defaults button."
|
||||||
|
tickets: [7471]
|
||||||
|
|
||||||
|
- title: "Conversion pipeline: Handle input documents that encode null bytes as HTML entities correctly"
|
||||||
|
tickets: [7355]
|
||||||
|
|
||||||
|
- title: "Fix some SONY readers not being detected on windows"
|
||||||
|
tickets: [7413]
|
||||||
|
|
||||||
|
- title: "MOBI Input: Fix images missing when converting MOBI news downloads created with Mobipocket reader"
|
||||||
|
tickets: [7455]
|
||||||
|
|
||||||
|
- title: "ODT Input: Handle hyperlinks to headings that have truncated destination specifiers correctly"
|
||||||
|
tickets: [7506]
|
||||||
|
|
||||||
|
- title: "Sony driver: Ignore invalid strings when updating XML database"
|
||||||
|
|
||||||
|
- title: "Content Server: Add day to displayed date in /mobile book listing"
|
||||||
|
|
||||||
|
- title: "MOBI Input: Do not generate filenames with only extensions if the MOBI file has no internal name"
|
||||||
|
tickets: [7481]
|
||||||
|
|
||||||
|
- title: "MOBI Input: Handle files that has the record sizes set incorrectly to a long integer"
|
||||||
|
tickets: [7472]
|
||||||
|
|
||||||
|
- title: "Fix not enough vertical space for text in the preferences dialog category listing"
|
||||||
|
|
||||||
|
- title: "Remove 'sort' from Search and replace destination fields and add it to source fields. S&R is no longer marked experimental"
|
||||||
|
|
||||||
|
- title: "Edit metadata dialog: Save dialog geometry on reject as well as on accept"
|
||||||
|
|
||||||
|
- title: "E-book viewer: Fix clicking entries in TOC that point to the currently loaded flow not scrolling view to the top of the document"
|
||||||
|
|
||||||
|
- title: "Fix bug in regex used to extract charset from <meta> tags"
|
||||||
|
|
||||||
|
- title: "MOBI Output: Add support for the <q> tag"
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- Zeit Online
|
||||||
|
- Gamespot Review
|
||||||
|
- Ploitika
|
||||||
|
- Pagina12
|
||||||
|
- Irish Times
|
||||||
|
- elektrolese
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: "Handelsblatt and European Voice"
|
||||||
|
author: "malfi"
|
||||||
|
|
||||||
|
- title: "Polityka and Newsweek"
|
||||||
|
author: "Mateusz Kielar"
|
||||||
|
|
||||||
|
- title: "MarcTV"
|
||||||
|
author: "Marc Toensings"
|
||||||
|
|
||||||
|
- title: "Rolling Stone"
|
||||||
|
author: "Darko Miletic"
|
||||||
|
|
||||||
|
- title: "Vedomosti"
|
||||||
|
author: "Nikolai Kotchetkov"
|
||||||
|
|
||||||
|
- title: "Hola.com"
|
||||||
|
author: "bmsleight"
|
||||||
|
|
||||||
|
- title: "Dnevnik, Siol.net, MMC-RTV and Avto-magazon"
|
||||||
|
author: "BlonG"
|
||||||
|
|
||||||
|
- title: "SC Print Magazine"
|
||||||
|
author: "Tony Maro"
|
||||||
|
|
||||||
|
- title: "Diario Sport"
|
||||||
|
author: "Jefferson Frantz"
|
||||||
|
|
||||||
- version: 0.7.27
|
- version: 0.7.27
|
||||||
date: 2010-11-05
|
date: 2010-11-05
|
||||||
|
|
||||||
@ -44,6 +138,7 @@
|
|||||||
tickets: [7356]
|
tickets: [7356]
|
||||||
|
|
||||||
- title: "News download: Workaround lack of thread safety in python mechanize, causing corrupted network packets (degrading network performance) on Ubuntu Maverick 64bit kernels"
|
- title: "News download: Workaround lack of thread safety in python mechanize, causing corrupted network packets (degrading network performance) on Ubuntu Maverick 64bit kernels"
|
||||||
|
tickets: [7321]
|
||||||
|
|
||||||
- title: "Convert comments to HTML for book details panel in separate thread to make scrolling through the book list faster when large comments are present"
|
- title: "Convert comments to HTML for book details panel in separate thread to make scrolling through the book list faster when large comments are present"
|
||||||
|
|
||||||
|
@ -81,6 +81,14 @@ p.unread_book {
|
|||||||
text-indent: -2em;
|
text-indent: -2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.missing_book {
|
||||||
|
text-align:left;
|
||||||
|
margin-top:0px;
|
||||||
|
margin-bottom:0px;
|
||||||
|
margin-left:2em;
|
||||||
|
text-indent:-2em;
|
||||||
|
}
|
||||||
|
|
||||||
p.date_read {
|
p.date_read {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
|
BIN
resources/images/format-text-bold.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
resources/images/format-text-italic.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
resources/images/format-text-strikethrough.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
resources/images/format-text-underline.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
resources/images/news/avto-magazin.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/images/news/dnevnik.png
Normal file
After Width: | Height: | Size: 861 B |
BIN
resources/images/news/rollingstone.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
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'])
|
||||||
]
|
]
|
||||||
|
61
resources/recipes/deredactie.recipe
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import re
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class deredactie(BasicNewsRecipe):
|
||||||
|
title = u'Deredactie.be'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
cover_url = 'http://www.deredactie.be/polopoly_fs/1.510827!image/2710428628.gif'
|
||||||
|
language = 'de'
|
||||||
|
keep_only_tags = []
|
||||||
|
__author__ = 'malfi'
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'id': 'articlehead'}))
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'id': 'articlebody'}))
|
||||||
|
remove_tags = []
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'id': 'story'}))
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'id': 'useractions'}))
|
||||||
|
remove_tags.append(dict(name = 'hr'))
|
||||||
|
|
||||||
|
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 parse_index(self):
|
||||||
|
categories = []
|
||||||
|
catnames = {}
|
||||||
|
soup = self.index_to_soup('http://www.deredactie.be/cm/vrtnieuws.deutsch')
|
||||||
|
for elem in soup.findAll('li', attrs={'id' : re.compile("^navItem[2-9]") }):
|
||||||
|
a = elem.find('a', href=True)
|
||||||
|
m = re.search('(?<=/)[^/]*$', a['href'])
|
||||||
|
cat = str(m.group(0))
|
||||||
|
categories.append(cat)
|
||||||
|
catnames[cat] = a['title']
|
||||||
|
self.log("found cat %s\n" % catnames[cat])
|
||||||
|
|
||||||
|
feeds = []
|
||||||
|
|
||||||
|
for cat in categories:
|
||||||
|
articles = []
|
||||||
|
soup = self.index_to_soup('http://www.deredactie.be/cm/vrtnieuws.deutsch/'+cat)
|
||||||
|
for a in soup.findAll('a',attrs={'href' : re.compile("deutsch.*/[0-9][0-9][0-9][0-9][0-9][0-9]_")}):
|
||||||
|
skip_this_article = False
|
||||||
|
url = a['href'].strip()
|
||||||
|
if url.startswith('/'):
|
||||||
|
url = 'http://www.deredactie.be' + url
|
||||||
|
myarticle=({'title':self.tag_to_string(a), 'url':url, 'description':'', 'date':''})
|
||||||
|
for article in articles :
|
||||||
|
if article['url'] == url :
|
||||||
|
skip_this_article = True
|
||||||
|
self.log("SKIPPING DUP %s" % url)
|
||||||
|
break
|
||||||
|
if skip_this_article :
|
||||||
|
continue;
|
||||||
|
articles.append(myarticle)
|
||||||
|
self.log("Adding URL %s\n" %url)
|
||||||
|
if articles:
|
||||||
|
feeds.append((catnames[cat], articles))
|
||||||
|
return feeds
|
||||||
|
|
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')
|
||||||
|
]
|
51
resources/recipes/european_voice.recipe
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class EuropeanVoice(BasicNewsRecipe):
|
||||||
|
title = u'European Voice'
|
||||||
|
__author__ = 'malfi'
|
||||||
|
oldest_article = 14
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
cover_url = 'http://www.europeanvoice.com/Css/images/logo.gif'
|
||||||
|
language = 'en'
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'id':'articleLeftColumn'})]
|
||||||
|
remove_tags = [dict(name='div', attrs={'id':'BreadCrump'})]
|
||||||
|
feeds = [
|
||||||
|
(u'Whole site ',u'http://www.europeanvoice.com/Rss/2.xml'),
|
||||||
|
(u'News and analysis',u'http://www.europeanvoice.com/Rss/6.xml'),
|
||||||
|
(u'Comment',u'http://www.europeanvoice.com/Rss/7.xml'),
|
||||||
|
(u'Special reports',u'http://www.europeanvoice.com/Rss/5.xml'),
|
||||||
|
(u'People',u'http://www.europeanvoice.com/Rss/8.xml'),
|
||||||
|
(u'Career',u'http://www.europeanvoice.com/Rss/11.xml'),
|
||||||
|
(u'Policies',u'http://www.europeanvoice.com/Rss/4.xml'),
|
||||||
|
(u'EVents',u'http://www.europeanvoice.com/Rss/10.xml'),
|
||||||
|
(u'Policies - Economics',u'http://www.europeanvoice.com/Rss/31.xml'),
|
||||||
|
(u'Policies - Business',u'http://www.europeanvoice.com/Rss/19.xml'),
|
||||||
|
(u'Policies - Trade',u'http://www.europeanvoice.com/Rss/25.xml'),
|
||||||
|
(u'Policies - Information society',u'http://www.europeanvoice.com/Rss/20.xml'),
|
||||||
|
(u'Policies - Energy',u'http://www.europeanvoice.com/Rss/15.xml'),
|
||||||
|
(u'Policies - Transport',u'http://www.europeanvoice.com/Rss/18.xml'),
|
||||||
|
(u'Policies - Climate change',u'http://www.europeanvoice.com/Rss/16.xml'),
|
||||||
|
(u'Policies - Environment',u'http://www.europeanvoice.com/Rss/17.xml'),
|
||||||
|
(u'Policies - Farming & food',u'http://www.europeanvoice.com/Rss/23.xml'),
|
||||||
|
(u'Policies - Health & society',u'http://www.europeanvoice.com/Rss/24.xml'),
|
||||||
|
(u'Policies - Justice',u'http://www.europeanvoice.com/Rss/29.xml'),
|
||||||
|
(u'Policies - Foreign affairs',u'http://www.europeanvoice.com/Rss/27.xml')
|
||||||
|
]
|
||||||
|
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):
|
||||||
|
return url + '?bPrint=1'
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
denied = soup.findAll(True,text='Subscribers')
|
||||||
|
if denied:
|
||||||
|
raise Exception('Article skipped, because content can only be seen with subscription')
|
||||||
|
return soup
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__author__ = u'Marc T\xf6nsing'
|
__author__ = u'Marc Toensing'
|
||||||
|
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
@ -17,6 +17,7 @@ class GamespotCom(BasicNewsRecipe):
|
|||||||
no_javascript = True
|
no_javascript = True
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
|
('All Reviews', 'http://www.gamespot.com/rss/game_updates.php?type=5'),
|
||||||
('PC Reviews', 'http://www.gamespot.com/rss/game_updates.php?type=5&platform=5'),
|
('PC Reviews', 'http://www.gamespot.com/rss/game_updates.php?type=5&platform=5'),
|
||||||
('XBOX 360 Reviews', 'http://www.gamespot.com/rss/game_updates.php?type=5&platform=1029'),
|
('XBOX 360 Reviews', 'http://www.gamespot.com/rss/game_updates.php?type=5&platform=1029'),
|
||||||
('Wii Reviews', 'http://www.gamespot.com/rss/game_updates.php?type=5&platform=1031'),
|
('Wii Reviews', 'http://www.gamespot.com/rss/game_updates.php?type=5&platform=1031'),
|
||||||
@ -37,5 +38,3 @@ class GamespotCom(BasicNewsRecipe):
|
|||||||
|
|
||||||
def get_article_url(self, article):
|
def get_article_url(self, article):
|
||||||
return article.get('link') + '?print=1'
|
return article.get('link') + '?print=1'
|
||||||
|
|
||||||
|
|
||||||
|
41
resources/recipes/handelsblatt.recipe
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Handelsblatt(BasicNewsRecipe):
|
||||||
|
title = u'Handelsblatt'
|
||||||
|
__author__ = 'malfi'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
cover_url = 'http://www.handelsblatt.com/images/logo/logo_handelsblatt.com.png'
|
||||||
|
language = 'de'
|
||||||
|
keep_only_tags = []
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'class': 'structOneCol'}))
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'id': 'fullText'}))
|
||||||
|
remove_tags = [dict(name='img', attrs = {'src': 'http://www.handelsblatt.com/images/icon/loading.gif'})]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Handelsblatt Exklusiv',u'http://www.handelsblatt.com/rss/exklusiv'),
|
||||||
|
(u'Handelsblatt Top-Themen',u'http://www.handelsblatt.com/rss/top-themen'),
|
||||||
|
(u'Handelsblatt Schlagzeilen',u'http://www.handelsblatt.com/rss/ticker/'),
|
||||||
|
(u'Handelsblatt Finanzen',u'http://www.handelsblatt.com/rss/finanzen/'),
|
||||||
|
(u'Handelsblatt Unternehmen',u'http://www.handelsblatt.com/rss/unternehmen/'),
|
||||||
|
(u'Handelsblatt Politik',u'http://www.handelsblatt.com/rss/politik/'),
|
||||||
|
(u'Handelsblatt Technologie',u'http://www.handelsblatt.com/rss/technologie/'),
|
||||||
|
(u'Handelsblatt Meinung',u'http://www.handelsblatt.com/rss/meinung'),
|
||||||
|
(u'Handelsblatt Magazin',u'http://www.handelsblatt.com/rss/magazin/'),
|
||||||
|
(u'Handelsblatt Weblogs',u'http://www.handelsblatt.com/rss/blogs')
|
||||||
|
]
|
||||||
|
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):
|
||||||
|
m = re.search('(?<=;)[0-9]*', url)
|
||||||
|
return u'http://www.handelsblatt.com/_b=' + str(m.group(0)) + ',_p=21,_t=ftprint,doc_page=0;printpage'
|
||||||
|
|
||||||
|
|
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
|
@ -33,13 +33,14 @@ class IrishTimes(BasicNewsRecipe):
|
|||||||
('Letters', 'http://www.irishtimes.com/feeds/rss/newspaper/letters.rss'),
|
('Letters', 'http://www.irishtimes.com/feeds/rss/newspaper/letters.rss'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def print_version(self, url):
|
def print_version(self, url):
|
||||||
if url.count('rss.feedsportal.com'):
|
if url.count('rss.feedsportal.com'):
|
||||||
u = url.replace('0Bhtml/story01.htm','_pf0Bhtml/story01.htm')
|
u = 'http://www.irishtimes.com' + \
|
||||||
else:
|
(((url[69:].replace('0C','/')).replace('0A','0'))).replace('0Bhtml/story01..htm','_pf.html')
|
||||||
u = url.replace('.html','_pf.html')
|
else:
|
||||||
return u
|
u = url.replace('.html','_pf.html')
|
||||||
|
return u
|
||||||
|
|
||||||
|
|
||||||
def get_article_url(self, article):
|
def get_article_url(self, article):
|
||||||
return article.link
|
return article.link
|
||||||
|
@ -54,10 +54,7 @@ class LaJornada_mx(BasicNewsRecipe):
|
|||||||
preprocess_regexps = [
|
preprocess_regexps = [
|
||||||
(re.compile( r'<div class="inicial">(.*)</div><p class="s-s">'
|
(re.compile( r'<div class="inicial">(.*)</div><p class="s-s">'
|
||||||
,re.DOTALL|re.IGNORECASE)
|
,re.DOTALL|re.IGNORECASE)
|
||||||
,lambda match: '<p class="inicial">' + match.group(1) + '</p><p class="s-s">'),
|
,lambda match: '<p class="inicial">' + match.group(1) + '</p><p class="s-s">')
|
||||||
(re.compile( r'<q>(.*?)</q>'
|
|
||||||
,re.DOTALL|re.IGNORECASE)
|
|
||||||
,lambda match: '"' + match.group(1) + '"')
|
|
||||||
]
|
]
|
||||||
|
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
|
35
resources/recipes/marctv.recipe
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
'''
|
||||||
|
Fetch MarcTV.
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class MarcTVde(BasicNewsRecipe):
|
||||||
|
|
||||||
|
title = 'Marc Toensings Visionen'
|
||||||
|
|
||||||
|
description = 'Marc Toensings Visionen'
|
||||||
|
|
||||||
|
language = 'de'
|
||||||
|
|
||||||
|
__author__ = 'Marc Toensing'
|
||||||
|
|
||||||
|
max_articles_per_feed = 40
|
||||||
|
|
||||||
|
oldest_article = 665
|
||||||
|
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
remove_tags = []
|
||||||
|
|
||||||
|
keep_only_tags = dict(name='div', attrs={'class':["content"]})
|
||||||
|
|
||||||
|
feeds = [(u'Spiele', u'http://feeds.feedburner.com/marctv/spiele'), (u'Leben', u'http://feeds.feedburner.com/marctv/leben'), (u'Medien', u'http://feeds.feedburner.com/marctv/medien')]
|
||||||
|
|
||||||
|
extra_css = '.#wrapper .entry p img{width:620px; height: 270px;}'
|
||||||
|
|
||||||
|
def get_cover_url(self):
|
||||||
|
return 'http://marctv.de/marctv.png'
|
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
|
||||||
|
|
68
resources/recipes/newsweek_polska.recipe
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Mateusz Kielar, matek09@gmail.com'
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Newsweek(BasicNewsRecipe):
|
||||||
|
EDITION = 0
|
||||||
|
|
||||||
|
title = u'Newsweek Polska'
|
||||||
|
__author__ = 'Mateusz Kielar'
|
||||||
|
description = 'Weekly magazine'
|
||||||
|
encoding = 'utf-8'
|
||||||
|
no_stylesheets = True
|
||||||
|
language = 'en'
|
||||||
|
remove_javascript = True
|
||||||
|
|
||||||
|
keep_only_tags =[]
|
||||||
|
keep_only_tags.append(dict(name = 'div', attrs = {'class' : 'article'}))
|
||||||
|
|
||||||
|
remove_tags =[]
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'class' : 'copy'}))
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'class' : 'url'}))
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
.body {font-size: small}
|
||||||
|
.author {font-size: x-small}
|
||||||
|
.lead {font-size: x-small}
|
||||||
|
.title{font-size: x-large; font-weight: bold}
|
||||||
|
'''
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url.replace("http://www.newsweek.pl/artykuly/wydanie/" + str(self.EDITION), "http://www.newsweek.pl/artykuly") + '/print'
|
||||||
|
|
||||||
|
def find_last_full_issue(self):
|
||||||
|
page = self.index_to_soup('http://www.newsweek.pl/Frames/IssueCover.aspx')
|
||||||
|
issue = 'http://www.newsweek.pl/Frames/' + page.find(lambda tag: tag.name == 'span' and not tag.attrs).a['href']
|
||||||
|
page = self.index_to_soup(issue)
|
||||||
|
issue = 'http://www.newsweek.pl/Frames/' + page.find(lambda tag: tag.name == 'span' and not tag.attrs).a['href']
|
||||||
|
page = self.index_to_soup(issue)
|
||||||
|
self.EDITION = page.find('a', attrs={'target' : '_parent'})['href'].replace('/wydania/','')
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
self.find_last_full_issue()
|
||||||
|
soup = self.index_to_soup('http://www.newsweek.pl/wydania/' + str(self.EDITION))
|
||||||
|
img = soup.find('img', id="ctl00_C1_PaperIsssueView_IssueImage", src=True)
|
||||||
|
self.cover_url = img['src']
|
||||||
|
feeds = []
|
||||||
|
parent = soup.find(id='content-left-big')
|
||||||
|
for txt in parent.findAll(attrs={'class':'txt_normal_red strong'}):
|
||||||
|
section = self.tag_to_string(txt).capitalize()
|
||||||
|
articles = list(self.find_articles(txt))
|
||||||
|
feeds.append((section, articles))
|
||||||
|
return feeds
|
||||||
|
|
||||||
|
def find_articles(self, txt):
|
||||||
|
for a in txt.findAllNext( attrs={'class':['strong','hr']}):
|
||||||
|
if a.name in "div":
|
||||||
|
break
|
||||||
|
yield {
|
||||||
|
'title' : self.tag_to_string(a),
|
||||||
|
'url' : 'http://www.newsweek.pl'+a['href'],
|
||||||
|
'date' : '',
|
||||||
|
'description' : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -21,8 +21,16 @@ class Pagina12(BasicNewsRecipe):
|
|||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
language = 'es'
|
language = 'es'
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
|
publication_type = 'newspaper'
|
||||||
masthead_url = 'http://www.pagina12.com.ar/commons/imgs/logo-home.gif'
|
masthead_url = 'http://www.pagina12.com.ar/commons/imgs/logo-home.gif'
|
||||||
extra_css = ' body{font-family: Arial,Helvetica,sans-serif } img{margin-bottom: 0.4em} #autor{font-weight: bold} #fecha,#epigrafe{font-size: 0.9em; margin: 5px} #imagen{border: 1px solid black; margin: 0 0 1.25em 1.25em; width: 232px } '
|
extra_css = """
|
||||||
|
body{font-family: Arial,Helvetica,sans-serif }
|
||||||
|
img{margin-bottom: 0.4em; display:block}
|
||||||
|
#autor{font-weight: bold}
|
||||||
|
#fecha,#epigrafe{font-size: 0.9em; margin: 5px}
|
||||||
|
#imagen{border: 1px solid black; margin: 0 0 1.25em 1.25em; width: 232px }
|
||||||
|
.fgprincipal{font-size: large; font-weight: bold}
|
||||||
|
"""
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
'comment' : description
|
'comment' : description
|
||||||
@ -31,7 +39,11 @@ class Pagina12(BasicNewsRecipe):
|
|||||||
, 'language' : language
|
, 'language' : language
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_tags = [dict(name='div', attrs={'id':['volver','logo','logo_suple','fin','permalink']})]
|
remove_tags = [
|
||||||
|
dict(name=['meta','link'])
|
||||||
|
,dict(name='div', attrs={'id':['volver','logo','logo_suple','fin','permalink']})
|
||||||
|
]
|
||||||
|
remove_attributes=['lang']
|
||||||
|
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
@ -65,4 +77,13 @@ class Pagina12(BasicNewsRecipe):
|
|||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
for item in soup.findAll(style=True):
|
for item in soup.findAll(style=True):
|
||||||
del item['style']
|
del item['style']
|
||||||
|
for item in soup.findAll('span', attrs={'id':'seccion'}):
|
||||||
|
it = item.a
|
||||||
|
it.name='span'
|
||||||
|
del it['href']
|
||||||
|
del it['title']
|
||||||
|
for item in soup.findAll('p'):
|
||||||
|
it = item.find('h3')
|
||||||
|
if it:
|
||||||
|
it.name='span'
|
||||||
return soup
|
return soup
|
@ -1,13 +1,10 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2008-2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
'''
|
'''
|
||||||
politika.rs
|
politika.rs
|
||||||
'''
|
'''
|
||||||
import re
|
import re
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
from calibre.ebooks.BeautifulSoup import Tag
|
|
||||||
|
|
||||||
class Politika(BasicNewsRecipe):
|
class Politika(BasicNewsRecipe):
|
||||||
title = 'Politika Online'
|
title = 'Politika Online'
|
||||||
@ -19,53 +16,51 @@ class Politika(BasicNewsRecipe):
|
|||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
remove_javascript = True
|
|
||||||
encoding = 'utf8'
|
encoding = 'utf8'
|
||||||
language = 'sr'
|
delay = 1
|
||||||
|
language = 'sr'
|
||||||
lang = 'sr-Latn-RS'
|
publication_type = 'newspaper'
|
||||||
direction = 'ltr'
|
masthead_url = 'http://static.politika.co.rs/images_new/politika.gif'
|
||||||
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)} body{font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
|
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)}
|
||||||
|
body{font-family: Arial,Helvetica,sans1,sans-serif}
|
||||||
|
h1{font-family: "Times New Roman",Times,serif1,serif}
|
||||||
|
.articledescription{font-family: sans1, sans-serif}
|
||||||
|
"""
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
'comment' : description
|
'comment' : description
|
||||||
, 'tags' : category
|
, 'tags' : category
|
||||||
, 'publisher' : publisher
|
, 'publisher' : publisher
|
||||||
, 'language' : lang
|
, 'language' : language
|
||||||
, 'pretty_print' : True
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||||
|
|
||||||
keep_only_tags = [dict(name='div', attrs={'class':'content_center_border'})]
|
keep_only_tags = [dict(name='div', attrs={'class':'big_article_home item_details'})]
|
||||||
|
remove_tags_after = dict(attrs={'class':'online_date'})
|
||||||
remove_tags = [
|
remove_tags = [dict(name=['link','meta','iframe','embed','object'])]
|
||||||
dict(name='div', attrs={'class':['send_print','txt-komentar']})
|
|
||||||
,dict(name=['object','link','a'])
|
|
||||||
,dict(name='h1', attrs={'class':'box_header-tags'})
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Politika' , u'http://www.politika.rs/rubrike/Politika/index.1.lt.xml' )
|
(u'Politika' , u'http://www.politika.rs/rubrike/Politika/index.1.lt.xml' )
|
||||||
,(u'Svet' , u'http://www.politika.rs/rubrike/Svet/index.1.lt.xml' )
|
,(u'Svet' , u'http://www.politika.rs/rubrike/Svet/index.1.lt.xml' )
|
||||||
,(u'Redakcijski komentari', u'http://www.politika.rs/rubrike/redakcijski-komentari/index.1.lt.xml')
|
,(u'Ostali komentari' , u'http://www.politika.rs/rubrike/ostali-komentari/index.1.lt.xml' )
|
||||||
,(u'Pogledi' , u'http://www.politika.rs/pogledi/index.lt.xml' )
|
,(u'Pogledi' , u'http://www.politika.rs/pogledi/index.lt.xml' )
|
||||||
,(u'Pogledi sa strane' , u'http://www.politika.rs/rubrike/Pogledi-sa-strane/index.1.lt.xml' )
|
,(u'Pogledi sa strane', u'http://www.politika.rs/rubrike/Pogledi-sa-strane/index.1.lt.xml')
|
||||||
,(u'Tema dana' , u'http://www.politika.rs/rubrike/tema-dana/index.1.lt.xml' )
|
,(u'Tema dana' , u'http://www.politika.rs/rubrike/tema-dana/index.1.lt.xml' )
|
||||||
,(u'Kultura' , u'http://www.politika.rs/rubrike/Kultura/index.1.lt.xml' )
|
,(u'Kultura' , u'http://www.politika.rs/rubrike/Kultura/index.1.lt.xml' )
|
||||||
,(u'Zivot i stil' , u'http://www.politika.rs/rubrike/zivot-i-stil/index.1.lt.xml' )
|
,(u'Spektar' , u'http://www.politika.rs/rubrike/zivot-i-stil/index.1.lt.xml' )
|
||||||
]
|
]
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
soup.html['lang'] = self.lang
|
|
||||||
soup.html['dir' ] = self.direction
|
|
||||||
mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
|
|
||||||
soup.head.insert(0,mlang)
|
|
||||||
for item in soup.findAll(style=True):
|
for item in soup.findAll(style=True):
|
||||||
del item['style']
|
del item['style']
|
||||||
ftag = soup.find('div',attrs={'class':'content_center_border'})
|
for item in soup.findAll('a', attrs={'class':'category'}):
|
||||||
if ftag.has_key('align'):
|
item.name='span'
|
||||||
del ftag['align']
|
if item.has_key('href'):
|
||||||
return self.adeify_images(soup)
|
del item['href']
|
||||||
|
if item.has_key('title'):
|
||||||
|
del item['title']
|
||||||
|
return soup
|
||||||
|
68
resources/recipes/polityka.recipe
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Mateusz Kielar, matek09@gmail.com'
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Polityka(BasicNewsRecipe):
|
||||||
|
|
||||||
|
title = u'Polityka'
|
||||||
|
__author__ = 'Mateusz Kielar'
|
||||||
|
description = 'Weekly magazine. Last archive issue'
|
||||||
|
encoding = 'utf-8'
|
||||||
|
no_stylesheets = True
|
||||||
|
language = 'en'
|
||||||
|
remove_javascript = True
|
||||||
|
|
||||||
|
remove_tags_before = dict(dict(name = 'h2', attrs = {'class' : 'box_nag'}))
|
||||||
|
remove_tags_after = dict(dict(name = 'div', attrs = {'class' : 'box_footer'}))
|
||||||
|
|
||||||
|
remove_tags =[]
|
||||||
|
remove_tags.append(dict(name = 'h2', attrs = {'class' : 'box_nag'}))
|
||||||
|
remove_tags.append(dict(name = 'div', attrs = {'class' : 'box_footer'}))
|
||||||
|
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
h1 {font-size: x-large; font-weight: bold}
|
||||||
|
'''
|
||||||
|
|
||||||
|
def parse_index(self):
|
||||||
|
soup = self.index_to_soup('http://archiwum.polityka.pl/')
|
||||||
|
box_img3 = soup.findAll(attrs={'class' : 'box_img3'})
|
||||||
|
feeds = []
|
||||||
|
last = 0
|
||||||
|
self.cover_url = 'http://archiwum.polityka.pl' + box_img3[-1].find('img')['src']
|
||||||
|
last_edition = 'http://archiwum.polityka.pl' + box_img3[-1].find('a')['href']
|
||||||
|
|
||||||
|
while True:
|
||||||
|
index = self.index_to_soup(last_edition)
|
||||||
|
|
||||||
|
|
||||||
|
box_list = index.findAll('div', attrs={'class' : 'box_list'})
|
||||||
|
if len(box_list) == 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
articles = {}
|
||||||
|
for box in box_list:
|
||||||
|
for div in box.findAll('div', attrs={'class': 'list_tresc'}):
|
||||||
|
article_page = self.index_to_soup('http://archiwum.polityka.pl' + div.a['href'],)
|
||||||
|
section = self.tag_to_string(article_page.find('h2', attrs = {'class' : 'box_nag'})).split('/')[0].lstrip().rstrip()
|
||||||
|
print section
|
||||||
|
if not articles.has_key(section):
|
||||||
|
articles[section] = []
|
||||||
|
articles[section].append( {
|
||||||
|
'title' : self.tag_to_string(div.a),
|
||||||
|
'url' : 'http://archiwum.polityka.pl' + div.a['href'],
|
||||||
|
'date' : '',
|
||||||
|
'description' : ''
|
||||||
|
})
|
||||||
|
|
||||||
|
for section in articles:
|
||||||
|
feeds.append((section, articles[section]))
|
||||||
|
|
||||||
|
last_edition = last_edition.replace('http://archiwum.polityka.pl/wydanie/' + str(last), 'http://archiwum.polityka.pl/wydanie/' + str(last + 1))
|
||||||
|
last = last + 1
|
||||||
|
|
||||||
|
return feeds
|
||||||
|
|
69
resources/recipes/rollingstone.recipe
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
rollingstone.com
|
||||||
|
'''
|
||||||
|
|
||||||
|
import re
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class RollingStone(BasicNewsRecipe):
|
||||||
|
title = 'Rolling Stone Magazine - free content'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'Rolling Stone Magazine features music, album and artist news, movie reviews, political, economic and pop culture commentary, videos, photos, and more.'
|
||||||
|
publisher = 'Werner Media inc.'
|
||||||
|
category = 'news, music, USA, world'
|
||||||
|
oldest_article = 15
|
||||||
|
max_articles_per_feed = 200
|
||||||
|
no_stylesheets = True
|
||||||
|
encoding = 'utf8'
|
||||||
|
use_embedded_content = False
|
||||||
|
language = 'en'
|
||||||
|
remove_empty_feeds = True
|
||||||
|
publication_type = 'magazine'
|
||||||
|
masthead_url = 'http://www.rollingstone.com/templates/rolling-stone-templates/theme/rstheme/images/rsLogo.png'
|
||||||
|
extra_css = """
|
||||||
|
body{font-family: Georgia,Times,serif }
|
||||||
|
img{margin-bottom: 0.4em; display:block}
|
||||||
|
"""
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
|
preprocess_regexps = [
|
||||||
|
(re.compile(r'xml:lang="en">.*?<head>', re.DOTALL|re.IGNORECASE),lambda match: 'xml:lang="en">\n<head>\n')
|
||||||
|
,(re.compile(r'</title>.*?</head>' , re.DOTALL|re.IGNORECASE),lambda match: '</title>\n</head>\n' )
|
||||||
|
]
|
||||||
|
|
||||||
|
keep_only_tags=[
|
||||||
|
dict(attrs={'class':['headerImgHolder','headerContent']})
|
||||||
|
,dict(name='div',attrs={'id':['teaser','storyTextContainer']})
|
||||||
|
,dict(name='div',attrs={'class':'blogDetailModule clearfix'})
|
||||||
|
]
|
||||||
|
|
||||||
|
remove_tags = [
|
||||||
|
dict(name=['meta','iframe','object','embed'])
|
||||||
|
,dict(attrs={'id':'mpStoryHeader'})
|
||||||
|
,dict(attrs={'class':'relatedTopics'})
|
||||||
|
]
|
||||||
|
remove_attributes=['lang','onclick','width','height','name']
|
||||||
|
remove_tags_before=dict(attrs={'class':'bloggerInfo'})
|
||||||
|
remove_tags_after=dict(attrs={'class':'relatedTopics'})
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'All News' , u'http://www.rollingstone.com/siteServices/rss/allNews' )
|
||||||
|
,(u'All Blogs' , u'http://www.rollingstone.com/siteServices/rss/allBlogs' )
|
||||||
|
,(u'Movie Reviews' , u'http://www.rollingstone.com/siteServices/rss/movieReviews' )
|
||||||
|
,(u'Album Reviews' , u'http://www.rollingstone.com/siteServices/rss/albumReviews' )
|
||||||
|
,(u'Song Reviews' , u'http://www.rollingstone.com/siteServices/rss/songReviews' )
|
||||||
|
]
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
for item in soup.findAll(style=True):
|
||||||
|
del item['style']
|
||||||
|
return soup
|
@ -1,4 +1,4 @@
|
|||||||
from calibre.web.feeds.recipes import BasicNewsRecipe, LoginFailed
|
from calibre.web.feeds.news import BasicNewsRecipe, LoginFailed
|
||||||
|
|
||||||
class SCPrintMagazine(BasicNewsRecipe):
|
class SCPrintMagazine(BasicNewsRecipe):
|
||||||
title = u'SC Print Magazine'
|
title = u'SC Print Magazine'
|
||||||
|
@ -5,18 +5,17 @@ __copyright__ = '2010, BlonG'
|
|||||||
www.siol.si
|
www.siol.si
|
||||||
'''
|
'''
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class Siol(BasicNewsRecipe):
|
class Siol(BasicNewsRecipe):
|
||||||
title = u'Siol.net'
|
title = u'Siol.net'
|
||||||
__author__ = u'BlonG'
|
__author__ = u'BlonG'
|
||||||
description = "Multimedijski portal z aktualnimi vsebinami, intervjuji, komentarji iz Slovenije in sveta, sportal, trendi, avtomoto, blogos"
|
description = "Multimedijski portal z aktualnimi vsebinami, intervjuji, komentarji iz Slovenije in sveta, sportal, trendi, avtomoto, blogos"
|
||||||
oldest_article = 3
|
oldest_article = 3
|
||||||
|
language = 'sl'
|
||||||
max_articles_per_feed = 20
|
max_articles_per_feed = 20
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
language = 'sl'
|
|
||||||
|
|
||||||
cover_url = 'http://farm4.static.flickr.com/3540/3401820496_c771550fe6.jpg'
|
cover_url = 'https://sites.google.com/site/javno2010/home/siol_cover.jpg'
|
||||||
|
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
@ -25,6 +24,8 @@ class Siol(BasicNewsRecipe):
|
|||||||
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
html2lrf_options = ['--base-font-size', '10']
|
||||||
|
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
dict(name='div', attrs={'id':'idContent'}),
|
dict(name='div', attrs={'id':'idContent'}),
|
||||||
]
|
]
|
||||||
@ -52,4 +53,3 @@ class Siol(BasicNewsRecipe):
|
|||||||
,(u'Tehnologija', u'http://www.siol.net/rss.aspx?path=Tehnologija')
|
,(u'Tehnologija', u'http://www.siol.net/rss.aspx?path=Tehnologija')
|
||||||
,(u'TV / Film', u'http://www.siol.net/rss.aspx?path=TV')
|
,(u'TV / Film', u'http://www.siol.net/rss.aspx?path=TV')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@ -43,7 +43,7 @@ class ZeitDe(BasicNewsRecipe):
|
|||||||
('Sport', 'http://newsfeed.zeit.de/sport/index'),
|
('Sport', 'http://newsfeed.zeit.de/sport/index'),
|
||||||
]
|
]
|
||||||
|
|
||||||
extra_css = '.reaktion,.taglist,.comments,.reponse,.responsetitle,.responsebody,.reponse,.inline,.date{display:none;}li.date{display:block}'
|
extra_css = '.excerpt{font-size:1em}.reaktion,.taglist,.comments,.reponse,.responsetitle,.responsebody,.reponse,.inline,.date{display:none;}li.date{display:block}'
|
||||||
|
|
||||||
#filter_regexps = [r'ad.de.doubleclick.net/']
|
#filter_regexps = [r'ad.de.doubleclick.net/']
|
||||||
|
|
||||||
@ -55,6 +55,16 @@ class ZeitDe(BasicNewsRecipe):
|
|||||||
ans = None
|
ans = None
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
for tag in soup.findAll(name=['ul','li']):
|
||||||
|
tag.name = 'div'
|
||||||
|
|
||||||
|
soup.html['xml:lang'] = self.lang
|
||||||
|
soup.html['lang'] = self.lang
|
||||||
|
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '">'
|
||||||
|
soup.head.insert(0,mtag)
|
||||||
|
return soup
|
||||||
|
|
||||||
def get_cover_url(self):
|
def get_cover_url(self):
|
||||||
try:
|
try:
|
||||||
inhalt = self.index_to_soup('http://www.zeit.de/inhalt')
|
inhalt = self.index_to_soup('http://www.zeit.de/inhalt')
|
||||||
|
@ -348,8 +348,12 @@ class Build(Command):
|
|||||||
VERSION = 1.0.0
|
VERSION = 1.0.0
|
||||||
CONFIG += %s
|
CONFIG += %s
|
||||||
''')%(ext.name, ' '.join(ext.headers), ' '.join(ext.sources), archs)
|
''')%(ext.name, ' '.join(ext.headers), ' '.join(ext.sources), archs)
|
||||||
|
pro = pro.replace('\\', '\\\\')
|
||||||
open(ext.name+'.pro', 'wb').write(pro)
|
open(ext.name+'.pro', 'wb').write(pro)
|
||||||
subprocess.check_call([QMAKE, '-o', 'Makefile', ext.name+'.pro'])
|
qmc = [QMAKE, '-o', 'Makefile']
|
||||||
|
if iswindows:
|
||||||
|
qmc += ['-spec', 'win32-msvc2008']
|
||||||
|
subprocess.check_call(qmc + [ext.name+'.pro'])
|
||||||
subprocess.check_call([make, '-f', 'Makefile'])
|
subprocess.check_call([make, '-f', 'Makefile'])
|
||||||
objects = glob.glob(obj_pat)
|
objects = glob.glob(obj_pat)
|
||||||
return list(map(self.a, objects))
|
return list(map(self.a, objects))
|
||||||
|
@ -13,7 +13,7 @@ from setup import Command, modules, functions, basenames, __version__, \
|
|||||||
from setup.build_environment import msvc, MT, RC
|
from setup.build_environment import msvc, MT, RC
|
||||||
from setup.installer.windows.wix import WixMixIn
|
from setup.installer.windows.wix import WixMixIn
|
||||||
|
|
||||||
QT_DIR = 'C:\\Qt\\4.6.3'
|
QT_DIR = 'Q:\\Qt\\4.7.1'
|
||||||
QT_DLLS = ['Core', 'Gui', 'Network', 'Svg', 'WebKit', 'Xml', 'XmlPatterns']
|
QT_DLLS = ['Core', 'Gui', 'Network', 'Svg', 'WebKit', 'Xml', 'XmlPatterns']
|
||||||
LIBUSB_DIR = 'C:\\libusb'
|
LIBUSB_DIR = 'C:\\libusb'
|
||||||
LIBUNRAR = 'C:\\Program Files\\UnrarDLL\\unrar.dll'
|
LIBUNRAR = 'C:\\Program Files\\UnrarDLL\\unrar.dll'
|
||||||
|
@ -37,8 +37,7 @@ Qt
|
|||||||
|
|
||||||
Extract Qt sourcecode to C:\Qt\4.x.x. Run configure and make::
|
Extract Qt sourcecode to C:\Qt\4.x.x. Run configure and make::
|
||||||
|
|
||||||
configure -opensource -qt-zlib -qt-gif -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -release -platform win32-msvc -no-qt3support -webkit -xmlpatterns -no-phonon
|
configure -opensource -release -qt-zlib -qt-gif -qt-libmng -qt-libpng -qt-libtiff -qt-libjpeg -release -platform win32-msvc2008 -no-qt3support -webkit -xmlpatterns -no-phonon -no-style-plastique -no-style-cleanlooks -no-style-motif -no-style-cde -no-declarative -no-scripttools -no-audio-backend -no-multimedia -no-dbus -no-openvg -no-opengl -no-qt3support -confirm-license && nmake
|
||||||
nmake
|
|
||||||
|
|
||||||
SIP
|
SIP
|
||||||
-----
|
-----
|
||||||
|
@ -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('>', '>')
|
||||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = 'calibre'
|
__appname__ = 'calibre'
|
||||||
__version__ = '0.7.27'
|
__version__ = '0.7.28'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
@ -2,9 +2,7 @@ import os.path
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import textwrap
|
import textwrap, os, glob, functools
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
from calibre.customize import FileTypePlugin, MetadataReaderPlugin, \
|
from calibre.customize import FileTypePlugin, MetadataReaderPlugin, \
|
||||||
MetadataWriterPlugin, PreferencesPlugin, InterfaceActionBase
|
MetadataWriterPlugin, PreferencesPlugin, InterfaceActionBase
|
||||||
from calibre.constants import numeric_version
|
from calibre.constants import numeric_version
|
||||||
@ -95,10 +93,12 @@ class ComicMetadataReader(MetadataReaderPlugin):
|
|||||||
|
|
||||||
def get_metadata(self, stream, ftype):
|
def get_metadata(self, stream, ftype):
|
||||||
if ftype == 'cbr':
|
if ftype == 'cbr':
|
||||||
from calibre.libunrar import extract_member as extract_first
|
from calibre.libunrar import extract_first_alphabetically as extract_first
|
||||||
extract_first
|
extract_first
|
||||||
else:
|
else:
|
||||||
from calibre.libunzip import extract_member as extract_first
|
from calibre.libunzip import extract_member
|
||||||
|
extract_first = functools.partial(extract_member,
|
||||||
|
sort_alphabetically=True)
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
ret = extract_first(stream)
|
ret = extract_first(stream)
|
||||||
mi = MetaInformation(None, None)
|
mi = MetaInformation(None, None)
|
||||||
@ -462,7 +462,7 @@ from calibre.devices.iliad.driver import ILIAD
|
|||||||
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
|
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
|
||||||
from calibre.devices.jetbook.driver import JETBOOK, MIBUK, JETBOOK_MINI
|
from calibre.devices.jetbook.driver import JETBOOK, MIBUK, JETBOOK_MINI
|
||||||
from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX
|
from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX
|
||||||
from calibre.devices.nook.driver import NOOK
|
from calibre.devices.nook.driver import NOOK, NOOK_COLOR
|
||||||
from calibre.devices.prs505.driver import PRS505
|
from calibre.devices.prs505.driver import PRS505
|
||||||
from calibre.devices.android.driver import ANDROID, S60
|
from calibre.devices.android.driver import ANDROID, S60
|
||||||
from calibre.devices.nokia.driver import N770, N810, E71X, E52
|
from calibre.devices.nokia.driver import N770, N810, E71X, E52
|
||||||
@ -548,6 +548,7 @@ plugins += [
|
|||||||
KINDLE2,
|
KINDLE2,
|
||||||
KINDLE_DX,
|
KINDLE_DX,
|
||||||
NOOK,
|
NOOK,
|
||||||
|
NOOK_COLOR,
|
||||||
PRS505,
|
PRS505,
|
||||||
ANDROID,
|
ANDROID,
|
||||||
S60,
|
S60,
|
||||||
|
@ -250,8 +250,11 @@ class OutputProfile(Plugin):
|
|||||||
#: If True, the date is appended to the title of downloaded news
|
#: If True, the date is appended to the title of downloaded news
|
||||||
periodical_date_in_title = True
|
periodical_date_in_title = True
|
||||||
|
|
||||||
#: The character used to represent a star in ratings
|
#: Characters used in jackets and catalogs
|
||||||
|
missing_char = u'x'
|
||||||
ratings_char = u'*'
|
ratings_char = u'*'
|
||||||
|
empty_ratings_char = u' '
|
||||||
|
read_char = u'+'
|
||||||
|
|
||||||
#: Unsupported unicode characters to be replaced during preprocessing
|
#: Unsupported unicode characters to be replaced during preprocessing
|
||||||
unsupported_unicode_chars = []
|
unsupported_unicode_chars = []
|
||||||
@ -287,7 +290,12 @@ class iPadOutput(OutputProfile):
|
|||||||
'macros': {'border-width': '{length}|medium|thick|thin'}
|
'macros': {'border-width': '{length}|medium|thick|thin'}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
ratings_char = u'\u2605'
|
|
||||||
|
missing_char = u'\u2715\u200a' # stylized 'x' plus hair space
|
||||||
|
ratings_char = u'\u2605' # filled star
|
||||||
|
empty_ratings_char = u'\u2606' # hollow star
|
||||||
|
read_char = u'\u2713' # check mark
|
||||||
|
|
||||||
touchscreen = True
|
touchscreen = True
|
||||||
# touchscreen_news_css {{{
|
# touchscreen_news_css {{{
|
||||||
touchscreen_news_css = u'''
|
touchscreen_news_css = u'''
|
||||||
@ -498,7 +506,6 @@ class SonyReaderLandscapeOutput(SonyReaderOutput):
|
|||||||
screen_size = (784, 1012)
|
screen_size = (784, 1012)
|
||||||
comic_screen_size = (784, 1012)
|
comic_screen_size = (784, 1012)
|
||||||
|
|
||||||
|
|
||||||
class MSReaderOutput(OutputProfile):
|
class MSReaderOutput(OutputProfile):
|
||||||
|
|
||||||
name = 'Microsoft Reader'
|
name = 'Microsoft Reader'
|
||||||
@ -582,7 +589,12 @@ class KindleOutput(OutputProfile):
|
|||||||
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
||||||
supports_mobi_indexing = True
|
supports_mobi_indexing = True
|
||||||
periodical_date_in_title = False
|
periodical_date_in_title = False
|
||||||
|
|
||||||
|
missing_char = u'x\u2009'
|
||||||
|
empty_ratings_char = u'\u2606'
|
||||||
ratings_char = u'\u2605'
|
ratings_char = u'\u2605'
|
||||||
|
read_char = u'\u2713'
|
||||||
|
|
||||||
mobi_ems_per_blockquote = 2.0
|
mobi_ems_per_blockquote = 2.0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -603,6 +615,8 @@ class KindleDXOutput(OutputProfile):
|
|||||||
#comic_screen_size = (741, 1022)
|
#comic_screen_size = (741, 1022)
|
||||||
supports_mobi_indexing = True
|
supports_mobi_indexing = True
|
||||||
periodical_date_in_title = False
|
periodical_date_in_title = False
|
||||||
|
ratings_char = u'\u2605'
|
||||||
|
read_char = u'\u2713'
|
||||||
mobi_ems_per_blockquote = 2.0
|
mobi_ems_per_blockquote = 2.0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -23,6 +23,9 @@ class ANDROID(USBMS):
|
|||||||
: [0x0100, 0x0227, 0x0226], 0x0c87: [0x0100, 0x0227, 0x0226],
|
: [0x0100, 0x0227, 0x0226], 0x0c87: [0x0100, 0x0227, 0x0226],
|
||||||
0xc92 : [0x100]},
|
0xc92 : [0x100]},
|
||||||
|
|
||||||
|
# Eken
|
||||||
|
0x040d : { 0x8510 : [0x0001] },
|
||||||
|
|
||||||
# Motorola
|
# Motorola
|
||||||
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216],
|
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216],
|
||||||
0x4285 : [0x216]},
|
0x4285 : [0x216]},
|
||||||
|
@ -19,7 +19,7 @@ class BLACKBERRY(USBMS):
|
|||||||
|
|
||||||
VENDOR_ID = [0x0fca]
|
VENDOR_ID = [0x0fca]
|
||||||
PRODUCT_ID = [0x8004, 0x0004]
|
PRODUCT_ID = [0x8004, 0x0004]
|
||||||
BCD = [0x0200, 0x0107, 0x0210, 0x0201]
|
BCD = [0x0200, 0x0107, 0x0210, 0x0201, 0x0211]
|
||||||
|
|
||||||
VENDOR_NAME = 'RIM'
|
VENDOR_NAME = 'RIM'
|
||||||
WINDOWS_MAIN_MEM = 'BLACKBERRY_SD'
|
WINDOWS_MAIN_MEM = 'BLACKBERRY_SD'
|
||||||
|
@ -74,9 +74,9 @@ class DevicePlugin(Plugin):
|
|||||||
if bcd is None or len(bcd) == 0:
|
if bcd is None or len(bcd) == 0:
|
||||||
return True
|
return True
|
||||||
for c in bcd:
|
for c in bcd:
|
||||||
# Bug in winutil.get_usb_devices converts a to :
|
rev = 'rev_%4.4x'%c
|
||||||
rev = ('rev_%4.4x'%c).replace('a', ':')
|
# Bug in winutil.get_usb_devices sometimes converts a to :
|
||||||
if rev in device_id:
|
if rev in device_id or rev.replace('a', ':') in device_id:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -80,3 +80,14 @@ class NOOK(USBMS):
|
|||||||
|
|
||||||
def sanitize_path_components(self, components):
|
def sanitize_path_components(self, components):
|
||||||
return [x.replace('#', '_') for x in components]
|
return [x.replace('#', '_') for x in components]
|
||||||
|
|
||||||
|
class NOOK_COLOR(NOOK):
|
||||||
|
gui_name = _('Nook Color')
|
||||||
|
description = _('Communicate with the Nook Color eBook reader.')
|
||||||
|
|
||||||
|
PRODUCT_ID = [0x002]
|
||||||
|
BCD = [0x216]
|
||||||
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'EBOOK_DISK'
|
||||||
|
|
||||||
|
EBOOK_DIR_MAIN = 'My Files/Books'
|
||||||
|
|
||||||
|
@ -619,6 +619,13 @@ class XMLCache(object):
|
|||||||
x.replace(u'\0', '')
|
x.replace(u'\0', '')
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
def record_set(k, v):
|
||||||
|
try:
|
||||||
|
record.set(k, clean(v))
|
||||||
|
except:
|
||||||
|
# v is not suitable for XML, ignore
|
||||||
|
pass
|
||||||
|
|
||||||
if not getattr(book, '_new_book', False): # book is not new
|
if not getattr(book, '_new_book', False): # book is not new
|
||||||
if record.get('tz', None) is not None:
|
if record.get('tz', None) is not None:
|
||||||
use_tz_var = True
|
use_tz_var = True
|
||||||
@ -641,20 +648,20 @@ class XMLCache(object):
|
|||||||
record.set('date', clean(date))
|
record.set('date', clean(date))
|
||||||
record.set('size', clean(str(os.stat(path).st_size)))
|
record.set('size', clean(str(os.stat(path).st_size)))
|
||||||
title = book.title if book.title else _('Unknown')
|
title = book.title if book.title else _('Unknown')
|
||||||
record.set('title', clean(title))
|
record_set('title', title)
|
||||||
ts = book.title_sort
|
ts = book.title_sort
|
||||||
if not ts:
|
if not ts:
|
||||||
ts = title_sort(title)
|
ts = title_sort(title)
|
||||||
record.set('titleSorter', clean(ts))
|
record_set('titleSorter', ts)
|
||||||
if self.use_author_sort:
|
if self.use_author_sort:
|
||||||
if book.author_sort:
|
if book.author_sort:
|
||||||
aus = book.author_sort
|
aus = book.author_sort
|
||||||
else:
|
else:
|
||||||
debug_print('Author_sort is None for book', book.lpath)
|
debug_print('Author_sort is None for book', book.lpath)
|
||||||
aus = authors_to_sort_string(book.authors)
|
aus = authors_to_sort_string(book.authors)
|
||||||
record.set('author', clean(aus))
|
record_set('author', aus)
|
||||||
else:
|
else:
|
||||||
record.set('author', clean(authors_to_string(book.authors)))
|
record_set('author', authors_to_string(book.authors))
|
||||||
ext = os.path.splitext(path)[1]
|
ext = os.path.splitext(path)[1]
|
||||||
if ext:
|
if ext:
|
||||||
ext = ext[1:].lower()
|
ext = ext[1:].lower()
|
||||||
|
@ -109,9 +109,11 @@ 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:
|
||||||
self.root = os.getcwdu()
|
name = getattr(stream, 'name', False)
|
||||||
if hasattr(stream, 'name'):
|
if name:
|
||||||
self.root = os.path.abspath(os.path.dirname(stream.name))
|
self.root = os.path.abspath(os.path.dirname(name))
|
||||||
|
else:
|
||||||
|
self.root = os.getcwdu()
|
||||||
super(OCFZipReader, self).__init__()
|
super(OCFZipReader, self).__init__()
|
||||||
|
|
||||||
def open(self, name, mode='r'):
|
def open(self, name, mode='r'):
|
||||||
|
@ -221,7 +221,10 @@ class MetadataHeader(BookHeader):
|
|||||||
else:
|
else:
|
||||||
end = self.section_offset(number + 1)
|
end = self.section_offset(number + 1)
|
||||||
self.stream.seek(start)
|
self.stream.seek(start)
|
||||||
return self.stream.read(end - start)
|
try:
|
||||||
|
return self.stream.read(end - start)
|
||||||
|
except OverflowError:
|
||||||
|
return self.stream.read(os.stat(self.stream.name).st_size - start)
|
||||||
|
|
||||||
|
|
||||||
class MobiReader(object):
|
class MobiReader(object):
|
||||||
@ -398,6 +401,8 @@ class MobiReader(object):
|
|||||||
elem.getparent().remove(elem)
|
elem.getparent().remove(elem)
|
||||||
fname = self.name.encode('ascii', 'replace')
|
fname = self.name.encode('ascii', 'replace')
|
||||||
fname = re.sub(r'[\x08\x15\0]+', '', fname)
|
fname = re.sub(r'[\x08\x15\0]+', '', fname)
|
||||||
|
if not fname:
|
||||||
|
fname = 'dummy'
|
||||||
htmlfile = os.path.join(output_dir,
|
htmlfile = os.path.join(output_dir,
|
||||||
ascii_filename(fname) + '.html')
|
ascii_filename(fname) + '.html')
|
||||||
try:
|
try:
|
||||||
@ -564,6 +569,10 @@ class MobiReader(object):
|
|||||||
for attr in self.IMAGE_ATTRS:
|
for attr in self.IMAGE_ATTRS:
|
||||||
recindex = attrib.pop(attr, None) or recindex
|
recindex = attrib.pop(attr, None) or recindex
|
||||||
if recindex is not None:
|
if recindex is not None:
|
||||||
|
try:
|
||||||
|
recindex = '%05d'%int(recindex)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
attrib['src'] = 'images/%s.jpg' % recindex
|
attrib['src'] = 'images/%s.jpg' % recindex
|
||||||
for attr in ('width', 'height'):
|
for attr in ('width', 'height'):
|
||||||
if attr in attrib:
|
if attr in attrib:
|
||||||
|
@ -787,6 +787,8 @@ class Manifest(object):
|
|||||||
# Convert to Unicode and normalize line endings
|
# Convert to Unicode and normalize line endings
|
||||||
data = self.oeb.decode(data)
|
data = self.oeb.decode(data)
|
||||||
data = self.oeb.html_preprocessor(data)
|
data = self.oeb.html_preprocessor(data)
|
||||||
|
# There could be null bytes in data if it had � entities in it
|
||||||
|
data = data.replace('\0', '')
|
||||||
|
|
||||||
# Remove DOCTYPE declaration as it messes up parsing
|
# Remove DOCTYPE declaration as it messes up parsing
|
||||||
# In particular, it causes tostring to insert xmlns
|
# In particular, it causes tostring to insert xmlns
|
||||||
|
@ -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
|
||||||
|
143
src/calibre/gui2/comments_editor.py
Normal file
@ -47,6 +47,8 @@ class BulkConfig(Config):
|
|||||||
self.show_pane)
|
self.show_pane)
|
||||||
self.connect(self.groups, SIGNAL('entered(QModelIndex)'),
|
self.connect(self.groups, SIGNAL('entered(QModelIndex)'),
|
||||||
self.show_group_help)
|
self.show_group_help)
|
||||||
|
rb = self.buttonBox.button(self.buttonBox.RestoreDefaults)
|
||||||
|
rb.setVisible(False)
|
||||||
self.groups.setMouseTracking(True)
|
self.groups.setMouseTracking(True)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1014,7 +1014,13 @@ class DeviceMixin(object): # {{{
|
|||||||
self.status_bar.show_message(_('Sent by email:') + ', '.join(good),
|
self.status_bar.show_message(_('Sent by email:') + ', '.join(good),
|
||||||
5000)
|
5000)
|
||||||
if remove:
|
if remove:
|
||||||
self.library_view.model().delete_books_by_id(remove)
|
try:
|
||||||
|
self.library_view.model().delete_books_by_id(remove)
|
||||||
|
except:
|
||||||
|
# Probably the user deleted the files, in any case, failing
|
||||||
|
# to delete the book is not catastrophic
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
def cover_to_thumbnail(self, data):
|
def cover_to_thumbnail(self, data):
|
||||||
ht = self.device_manager.device.THUMBNAIL_HEIGHT \
|
ht = self.device_manager.device.THUMBNAIL_HEIGHT \
|
||||||
|
@ -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">
|
||||||
|
@ -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,370 @@
|
|||||||
<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>411</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="QLabel" name="label_5">
|
||||||
<property name="title" >
|
<property name="text">
|
||||||
<string>Find entries that have...</string>
|
<string>What kind of match to use:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>matchkind</cstring>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout" >
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
|
||||||
<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>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="0" column="1">
|
||||||
<widget class="QGroupBox" name="groupBox_2" >
|
<widget class="QComboBox" name="matchkind">
|
||||||
<property name="title" >
|
<item>
|
||||||
<string>But dont show entries that have...</string>
|
<property name="text">
|
||||||
</property>
|
<string>Contains: the word or phrase matches anywhere in the metadata field</string>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2" >
|
</property>
|
||||||
<item>
|
</item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4" >
|
<item>
|
||||||
<item>
|
<property name="text">
|
||||||
<widget class="QLabel" name="label_4" >
|
<string>Equals: the word or phrase must match the entire metadata field</string>
|
||||||
<property name="text" >
|
</property>
|
||||||
<string>Any of these &unwanted words:</string>
|
</item>
|
||||||
</property>
|
<item>
|
||||||
<property name="buddy" >
|
<property name="text">
|
||||||
<cstring>all</cstring>
|
<string>Regular expression: the expression must match anywhere in the metadata field</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</item>
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="none" />
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="2" column="0" colspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox" >
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<property name="maximumSize" >
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>A&dvanced Search</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Find entries that have...</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<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 row="1" column="0">
|
||||||
|
<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>
|
||||||
|
<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_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 row="2" column="0">
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<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="3" column="0">
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_2">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Titl&e/Author/Series ...</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Title:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>title_box</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="title_box">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Enter the title.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<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>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>16777215</width>
|
<width>20</width>
|
||||||
<height>60</height>
|
<height>40</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_5" >
|
</spacer>
|
||||||
<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> </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>
|
|
||||||
<item>
|
|
||||||
<widget class="QDialogButtonBox" name="buttonBox" >
|
|
||||||
<property name="orientation" >
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons" >
|
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<tabstops>
|
||||||
|
<tabstop>all</tabstop>
|
||||||
|
<tabstop>phrase</tabstop>
|
||||||
|
<tabstop>any</tabstop>
|
||||||
|
<tabstop>none</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>
|
||||||
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../../resources/images.qrc" />
|
<include location="../../../../resources/images.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
<connections>
|
<connections>
|
||||||
<connection>
|
<connection>
|
||||||
@ -198,11 +373,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 +389,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"))
|
||||||
|
@ -50,10 +50,11 @@ class BooksView(QTableView): # {{{
|
|||||||
def __init__(self, parent, modelcls=BooksModel):
|
def __init__(self, parent, modelcls=BooksModel):
|
||||||
QTableView.__init__(self, parent)
|
QTableView.__init__(self, parent)
|
||||||
|
|
||||||
self.setEditTriggers(self.SelectedClicked|self.EditKeyPressed)
|
self.setEditTriggers(self.EditKeyPressed)
|
||||||
if tweaks['doubleclick_on_library_view'] == 'edit_cell':
|
if tweaks['doubleclick_on_library_view'] == 'edit_cell':
|
||||||
self.setEditTriggers(self.DoubleClicked|self.editTriggers())
|
self.setEditTriggers(self.DoubleClicked|self.editTriggers())
|
||||||
elif tweaks['doubleclick_on_library_view'] == 'open_viewer':
|
elif tweaks['doubleclick_on_library_view'] == 'open_viewer':
|
||||||
|
self.setEditTriggers(self.SelectedClicked|self.editTriggers())
|
||||||
self.doubleClicked.connect(parent.iactions['View'].view_triggered)
|
self.doubleClicked.connect(parent.iactions['View'].view_triggered)
|
||||||
|
|
||||||
self.drag_allowed = True
|
self.drag_allowed = True
|
||||||
|
@ -101,9 +101,9 @@ class Category(QWidget): # {{{
|
|||||||
ac.setStatusTip(p.description)
|
ac.setStatusTip(p.description)
|
||||||
self.actions.append(ac)
|
self.actions.append(ac)
|
||||||
w = self.bar.widgetForAction(ac)
|
w = self.bar.widgetForAction(ac)
|
||||||
w.setStyleSheet('QToolButton { margin-right: 20px; min-width: 100px }')
|
|
||||||
w.setCursor(Qt.PointingHandCursor)
|
w.setCursor(Qt.PointingHandCursor)
|
||||||
w.setAutoRaise(True)
|
w.setAutoRaise(True)
|
||||||
|
w.setMinimumWidth(100)
|
||||||
|
|
||||||
def triggered(self, plugin, *args):
|
def triggered(self, plugin, *args):
|
||||||
self.plugin_activated.emit(plugin)
|
self.plugin_activated.emit(plugin)
|
||||||
|
@ -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())
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ def generate_catalog(parent, dbspec, ids, device_manager):
|
|||||||
]
|
]
|
||||||
out.close()
|
out.close()
|
||||||
|
|
||||||
# This returns to gui2.ui:generate_catalog()
|
# This returns to gui2.actions.catalog:generate_catalog()
|
||||||
# Which then calls gui2.convert.gui_conversion:gui_catalog() with the args inline
|
# Which then calls gui2.convert.gui_conversion:gui_catalog() with the args inline
|
||||||
return 'gui_catalog', args, _('Generate catalog'), out.name, d.catalog_sync, \
|
return 'gui_catalog', args, _('Generate catalog'), out.name, d.catalog_sync, \
|
||||||
d.catalog_title
|
d.catalog_title
|
||||||
|
@ -625,7 +625,11 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def remove(self, id):
|
def remove(self, id):
|
||||||
self._data[id] = None
|
try:
|
||||||
|
self._data[id] = None
|
||||||
|
except IndexError:
|
||||||
|
# id is out of bounds, no point setting it to None anyway
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
self._map.remove(id)
|
self._map.remove(id)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -832,6 +832,9 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
'[<source>] : Source of content (e.g., Amazon, Project Gutenberg). Do not create genre
|
'[<source>] : Source of content (e.g., Amazon, Project Gutenberg). Do not create genre
|
||||||
|
|
||||||
- Program flow
|
- Program flow
|
||||||
|
gui2.actions.catalog:generate_catalog()
|
||||||
|
gui2.tools:generate_catalog() or library.cli:command_catalog()
|
||||||
|
called from gui2.convert.gui_conversion:gui_catalog()
|
||||||
catalog = Catalog(notification=Reporter())
|
catalog = Catalog(notification=Reporter())
|
||||||
catalog.createDirectoryStructure()
|
catalog.createDirectoryStructure()
|
||||||
catalog.copyResources()
|
catalog.copyResources()
|
||||||
@ -883,6 +886,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
self.__htmlFileList = []
|
self.__htmlFileList = []
|
||||||
self.__markerTags = self.getMarkerTags()
|
self.__markerTags = self.getMarkerTags()
|
||||||
self.__ncxSoup = None
|
self.__ncxSoup = None
|
||||||
|
self.__output_profile = None
|
||||||
self.__playOrder = 1
|
self.__playOrder = 1
|
||||||
self.__plugin = plugin
|
self.__plugin = plugin
|
||||||
self.__progressInt = 0.0
|
self.__progressInt = 0.0
|
||||||
@ -897,6 +901,12 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
self.__useSeriesPrefixInTitlesSection = False
|
self.__useSeriesPrefixInTitlesSection = False
|
||||||
self.__verbose = opts.verbose
|
self.__verbose = opts.verbose
|
||||||
|
|
||||||
|
from calibre.customize.ui import output_profiles
|
||||||
|
for profile in output_profiles():
|
||||||
|
if profile.short_name == self.opts.output_profile:
|
||||||
|
self.__output_profile = profile
|
||||||
|
break
|
||||||
|
|
||||||
# Tweak build steps based on optional sections: 1 call for HTML, 1 for NCX
|
# Tweak build steps based on optional sections: 1 call for HTML, 1 for NCX
|
||||||
if self.opts.generate_titles:
|
if self.opts.generate_titles:
|
||||||
self.__totalSteps += 2
|
self.__totalSteps += 2
|
||||||
@ -1168,10 +1178,14 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
|
def MISSING_SYMBOL(self):
|
||||||
|
def fget(self):
|
||||||
|
return self.__output_profile.missing_char
|
||||||
|
return property(fget=fget)
|
||||||
|
@dynamic_property
|
||||||
def NOT_READ_SYMBOL(self):
|
def NOT_READ_SYMBOL(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
return '<span style="color:white">✓</span>' if self.generateForKindle else \
|
return '<span style="color:white">%s</span>' % self.__output_profile.read_char
|
||||||
'<span style="color:white">%s</span>' % self.opts.read_tag
|
|
||||||
return property(fget=fget)
|
return property(fget=fget)
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def READING_SYMBOL(self):
|
def READING_SYMBOL(self):
|
||||||
@ -1182,18 +1196,17 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
@dynamic_property
|
@dynamic_property
|
||||||
def READ_SYMBOL(self):
|
def READ_SYMBOL(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
return '<span style="color:black">✓</span>' if self.generateForKindle else \
|
return self.__output_profile.read_char
|
||||||
'<span style="color:black">%s</span>' % self.opts.read_tag
|
|
||||||
return property(fget=fget)
|
return property(fget=fget)
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def FULL_RATING_SYMBOL(self):
|
def FULL_RATING_SYMBOL(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
return "★" if self.generateForKindle else "*"
|
return self.__output_profile.ratings_char
|
||||||
return property(fget=fget)
|
return property(fget=fget)
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def EMPTY_RATING_SYMBOL(self):
|
def EMPTY_RATING_SYMBOL(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
return "☆" if self.generateForKindle else ' '
|
return self.__output_profile.empty_ratings_char
|
||||||
return property(fget=fget)
|
return property(fget=fget)
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def READ_PROGRESS_SYMBOL(self):
|
def READ_PROGRESS_SYMBOL(self):
|
||||||
@ -1626,13 +1639,16 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
self.generateAuthorAnchor(title['author']))
|
self.generateAuthorAnchor(title['author']))
|
||||||
aTag.insert(0, title['author'])
|
aTag.insert(0, title['author'])
|
||||||
|
|
||||||
# Prefix author with read/reading/none symbol
|
# Prefix author with read|reading|none symbol or missing symbol
|
||||||
if title['read']:
|
if 'formats' in title and title['formats']:
|
||||||
authorTag.insert(0, NavigableString(self.READ_SYMBOL + "by "))
|
if title['read']:
|
||||||
elif self.opts.connected_kindle and title['id'] in self.bookmarked_books:
|
authorTag.insert(0, NavigableString(self.READ_SYMBOL + " by "))
|
||||||
authorTag.insert(0, NavigableString(self.READING_SYMBOL + " by "))
|
elif self.opts.connected_kindle and title['id'] in self.bookmarked_books:
|
||||||
|
authorTag.insert(0, NavigableString(self.READING_SYMBOL + " by "))
|
||||||
|
else:
|
||||||
|
authorTag.insert(0, NavigableString(self.NOT_READ_SYMBOL + " by "))
|
||||||
else:
|
else:
|
||||||
authorTag.insert(0, NavigableString(self.NOT_READ_SYMBOL + "by "))
|
authorTag.insert(0, NavigableString(self.MISSING_SYMBOL + " by "))
|
||||||
authorTag.insert(1, aTag)
|
authorTag.insert(1, aTag)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@ -1666,7 +1682,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
fontTag['style'] = 'color:white;font-size:large'
|
fontTag['style'] = 'color:white;font-size:large'
|
||||||
if self.opts.fmt == 'epub':
|
if self.opts.fmt == 'epub':
|
||||||
fontTag['style'] += ';opacity: 0.0'
|
fontTag['style'] += ';opacity: 0.0'
|
||||||
fontTag.insert(0, NavigableString("by "))
|
fontTag.insert(0, NavigableString(" by "))
|
||||||
tagsTag.insert(ttc, fontTag)
|
tagsTag.insert(ttc, fontTag)
|
||||||
ttc += 1
|
ttc += 1
|
||||||
|
|
||||||
@ -1819,21 +1835,27 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# book with read/reading/unread symbol
|
# book with read|reading|unread symbol or missing symbol
|
||||||
if book['read']:
|
if 'formats' in book and book['formats']:
|
||||||
# check mark
|
if book['read']:
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
# check mark
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||||
ptc += 1
|
pBookTag['class'] = "read_book"
|
||||||
elif book['id'] in self.bookmarked_books:
|
ptc += 1
|
||||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
elif book['id'] in self.bookmarked_books:
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||||
ptc += 1
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
|
else:
|
||||||
|
# hidden check mark
|
||||||
|
pBookTag['class'] = "unread_book"
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||||
|
ptc += 1
|
||||||
else:
|
else:
|
||||||
# hidden check mark
|
# missing formats
|
||||||
pBookTag['class'] = "unread_book"
|
pBookTag['class'] = "missing_book"
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
# Link to book
|
# Link to book
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
@ -1988,20 +2010,26 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# book with read/reading/unread symbol
|
# book with read|reading|unread symbol or missing symbol
|
||||||
if book['read']:
|
if 'formats' in book and book['formats']:
|
||||||
# check mark
|
if book['read']:
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
# check mark
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||||
ptc += 1
|
pBookTag['class'] = "read_book"
|
||||||
elif book['id'] in self.bookmarked_books:
|
ptc += 1
|
||||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
elif book['id'] in self.bookmarked_books:
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||||
ptc += 1
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
|
else:
|
||||||
|
# hidden check mark
|
||||||
|
pBookTag['class'] = "unread_book"
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||||
|
ptc += 1
|
||||||
else:
|
else:
|
||||||
# hidden check mark
|
# missing book
|
||||||
pBookTag['class'] = "unread_book"
|
pBookTag['class'] = "missing_book"
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
@ -2116,20 +2144,26 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# book with read/reading/unread symbol
|
# book with read|reading|unread symbol or missing symbol
|
||||||
if new_entry['read']:
|
if 'formats' in new_entry and new_entry['formats']:
|
||||||
# check mark
|
if new_entry['read']:
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
# check mark
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||||
ptc += 1
|
pBookTag['class'] = "read_book"
|
||||||
elif new_entry['id'] in self.bookmarked_books:
|
ptc += 1
|
||||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
elif new_entry['id'] in self.bookmarked_books:
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||||
ptc += 1
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
|
else:
|
||||||
|
# hidden check mark
|
||||||
|
pBookTag['class'] = "unread_book"
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||||
|
ptc += 1
|
||||||
else:
|
else:
|
||||||
# hidden check mark
|
# missing book
|
||||||
pBookTag['class'] = "unread_book"
|
pBookTag['class'] = "missing_book"
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
@ -2162,20 +2196,26 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# book with read/reading/unread symbol
|
# book with read|reading|unread symbol or missing symbol
|
||||||
if new_entry['read']:
|
if 'formats' in new_entry and new_entry['formats']:
|
||||||
# check mark
|
if new_entry['read']:
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
# check mark
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||||
ptc += 1
|
pBookTag['class'] = "read_book"
|
||||||
elif new_entry['id'] in self.bookmarked_books:
|
ptc += 1
|
||||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
elif new_entry['id'] in self.bookmarked_books:
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||||
ptc += 1
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
|
else:
|
||||||
|
# hidden check mark
|
||||||
|
pBookTag['class'] = "unread_book"
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||||
|
ptc += 1
|
||||||
else:
|
else:
|
||||||
# hidden check mark
|
# missing book
|
||||||
pBookTag['class'] = "unread_book"
|
pBookTag['class'] = "missing_book"
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
@ -2611,19 +2651,26 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
else:
|
else:
|
||||||
book['read'] = False
|
book['read'] = False
|
||||||
|
|
||||||
if book['read']:
|
# book with read|reading|unread symbol or missing symbol
|
||||||
# check mark
|
if 'formats' in book and book['formats']:
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
if book['read']:
|
||||||
pBookTag['class'] = "read_book"
|
# check mark
|
||||||
ptc += 1
|
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||||
elif book['id'] in self.bookmarked_books:
|
pBookTag['class'] = "read_book"
|
||||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
ptc += 1
|
||||||
pBookTag['class'] = "read_book"
|
elif book['id'] in self.bookmarked_books:
|
||||||
ptc += 1
|
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||||
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
|
else:
|
||||||
|
# hidden check mark
|
||||||
|
pBookTag['class'] = "unread_book"
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||||
|
ptc += 1
|
||||||
else:
|
else:
|
||||||
# hidden check mark
|
# missing book
|
||||||
pBookTag['class'] = "unread_book"
|
pBookTag['class'] = "missing_book"
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
@ -2702,6 +2749,8 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
this_book['title'] = book['title']
|
this_book['title'] = book['title']
|
||||||
this_book['author_sort'] = book['author_sort'].capitalize()
|
this_book['author_sort'] = book['author_sort'].capitalize()
|
||||||
this_book['read'] = book['read']
|
this_book['read'] = book['read']
|
||||||
|
if 'formats' in book:
|
||||||
|
this_book['formats'] = book['formats']
|
||||||
this_book['id'] = book['id']
|
this_book['id'] = book['id']
|
||||||
this_book['series'] = book['series']
|
this_book['series'] = book['series']
|
||||||
normalized_tag = self.genre_tags_dict[friendly_tag]
|
normalized_tag = self.genre_tags_dict[friendly_tag]
|
||||||
@ -4103,20 +4152,26 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
pBookTag = Tag(soup, "p")
|
pBookTag = Tag(soup, "p")
|
||||||
ptc = 0
|
ptc = 0
|
||||||
|
|
||||||
# book with read/reading/unread symbol
|
# book with read|reading|unread symbol or missing symbol
|
||||||
if book['read']:
|
if 'formats' in book and book['formats']:
|
||||||
# check mark
|
if book['read']:
|
||||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
# check mark
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||||
ptc += 1
|
pBookTag['class'] = "read_book"
|
||||||
elif book['id'] in self.bookmarked_books:
|
ptc += 1
|
||||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
elif book['id'] in self.bookmarked_books:
|
||||||
pBookTag['class'] = "read_book"
|
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||||
ptc += 1
|
pBookTag['class'] = "read_book"
|
||||||
|
ptc += 1
|
||||||
|
else:
|
||||||
|
# hidden check mark
|
||||||
|
pBookTag['class'] = "unread_book"
|
||||||
|
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||||
|
ptc += 1
|
||||||
else:
|
else:
|
||||||
# hidden check mark
|
# missing book
|
||||||
pBookTag['class'] = "unread_book"
|
pBookTag['class'] = "missing_book"
|
||||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
||||||
ptc += 1
|
ptc += 1
|
||||||
|
|
||||||
# Add the book title
|
# Add the book title
|
||||||
@ -4575,7 +4630,8 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
|
|
||||||
if opts.connected_device['name'] and 'kindle' in opts.connected_device['name'].lower():
|
if opts.connected_device['name'] and 'kindle' in opts.connected_device['name'].lower():
|
||||||
opts.connected_kindle = True
|
opts.connected_kindle = True
|
||||||
if opts.connected_device['serial'] and opts.connected_device['serial'][:4] in ['B004','B005']:
|
if opts.connected_device['serial'] and \
|
||||||
|
opts.connected_device['serial'][:4] in ['B004','B005']:
|
||||||
op = "kindle_dx"
|
op = "kindle_dx"
|
||||||
else:
|
else:
|
||||||
op = "kindle"
|
op = "kindle"
|
||||||
@ -4638,7 +4694,8 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
build_log.append(" opts:")
|
build_log.append(" opts:")
|
||||||
for key in keys:
|
for key in keys:
|
||||||
if key in ['catalog_title','authorClip','connected_kindle','descriptionClip',
|
if key in ['catalog_title','authorClip','connected_kindle','descriptionClip',
|
||||||
'exclude_genre','exclude_tags','note_tag','numbers_as_text','read_tag',
|
'exclude_genre','exclude_tags','note_tag','numbers_as_text',
|
||||||
|
'output_profile','read_tag',
|
||||||
'search_text','sort_by','sort_descriptions_by_author','sync']:
|
'search_text','sort_by','sort_descriptions_by_author','sync']:
|
||||||
build_log.append(" %s: %s" % (key, opts_dict[key]))
|
build_log.append(" %s: %s" % (key, opts_dict[key]))
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ class MobileServer(object):
|
|||||||
no_tag_count=True)
|
no_tag_count=True)
|
||||||
book['title'] = record[FM['title']]
|
book['title'] = record[FM['title']]
|
||||||
for x in ('timestamp', 'pubdate'):
|
for x in ('timestamp', 'pubdate'):
|
||||||
book[x] = strftime('%b, %Y', record[FM[x]])
|
book[x] = strftime('%d %b, %Y', record[FM[x]])
|
||||||
book['id'] = record[FM['id']]
|
book['id'] = record[FM['id']]
|
||||||
books.append(book)
|
books.append(book)
|
||||||
for key in CKEYS:
|
for key in CKEYS:
|
||||||
|
@ -269,3 +269,16 @@ def extract_member(path, match=re.compile(r'\.(jpg|jpeg|gif|png)\s*$', re.I),
|
|||||||
path = _extract_member(path, match, name)
|
path = _extract_member(path, match, name)
|
||||||
return path, open(path, 'rb').read()
|
return path, open(path, 'rb').read()
|
||||||
|
|
||||||
|
def extract_first_alphabetically(path):
|
||||||
|
if hasattr(path, 'read'):
|
||||||
|
data = path.read()
|
||||||
|
f = NamedTemporaryFile(suffix='.rar')
|
||||||
|
f.write(data)
|
||||||
|
f.flush()
|
||||||
|
path = f.name
|
||||||
|
|
||||||
|
names_ = [x for x in names(path) if os.path.splitext(x)[1][1:].lower() in
|
||||||
|
('png', 'jpg', 'jpeg', 'gif')]
|
||||||
|
names_.sort()
|
||||||
|
return extract_member(path, name=names_[0], match=None)
|
||||||
|
|
||||||
|
@ -43,9 +43,12 @@ def extract(filename, dir):
|
|||||||
zf = zipfile.ZipFile( filename )
|
zf = zipfile.ZipFile( filename )
|
||||||
zf.extractall(dir)
|
zf.extractall(dir)
|
||||||
|
|
||||||
def extract_member(filename, match=re.compile(r'\.(jpg|jpeg|gif|png)\s*$', re.I)):
|
def extract_member(filename, match=re.compile(r'\.(jpg|jpeg|gif|png)\s*$',
|
||||||
|
re.I), sort_alphabetically=False):
|
||||||
zf = zipfile.ZipFile(filename)
|
zf = zipfile.ZipFile(filename)
|
||||||
names = zf.namelist()
|
names = list(zf.namelist())
|
||||||
|
if sort_alphabetically:
|
||||||
|
names.sort()
|
||||||
for name in names:
|
for name in names:
|
||||||
if match.search(name):
|
if match.search(name):
|
||||||
return name, zf.read(name)
|
return name, zf.read(name)
|
||||||
|
@ -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`
|
||||||
|
@ -21,15 +21,14 @@
|
|||||||
#import pdb
|
#import pdb
|
||||||
#pdb.set_trace()
|
#pdb.set_trace()
|
||||||
import zipfile
|
import zipfile
|
||||||
import xml.sax
|
|
||||||
from xml.sax import handler, expatreader
|
from xml.sax import handler, expatreader
|
||||||
from xml.sax.xmlreader import InputSource
|
from xml.sax.xmlreader import InputSource
|
||||||
from xml.sax.saxutils import escape, quoteattr
|
from xml.sax.saxutils import escape, quoteattr
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
|
|
||||||
from namespaces import ANIMNS, CHARTNS, CONFIGNS, DCNS, DR3DNS, DRAWNS, FONS, \
|
from namespaces import DCNS, DRAWNS, FONS, \
|
||||||
FORMNS, MATHNS, METANS, NUMBERNS, OFFICENS, PRESENTATIONNS, SCRIPTNS, \
|
METANS, NUMBERNS, OFFICENS, PRESENTATIONNS, \
|
||||||
SMILNS, STYLENS, SVGNS, TABLENS, TEXTNS, XLINKNS
|
STYLENS, SVGNS, TABLENS, TEXTNS, XLINKNS
|
||||||
|
|
||||||
# Handling of styles
|
# Handling of styles
|
||||||
#
|
#
|
||||||
@ -526,7 +525,8 @@ class ODF2XHTML(handler.ContentHandler):
|
|||||||
|
|
||||||
def get_anchor(self, name):
|
def get_anchor(self, name):
|
||||||
if not self.anchors.has_key(name):
|
if not self.anchors.has_key(name):
|
||||||
self.anchors[name] = "anchor%03d" % (len(self.anchors) + 1)
|
# Changed by Kovid
|
||||||
|
self.anchors[name] = "anchor%d" % (len(self.anchors) + 1)
|
||||||
return self.anchors.get(name)
|
return self.anchors.get(name)
|
||||||
|
|
||||||
|
|
||||||
@ -1025,9 +1025,13 @@ class ODF2XHTML(handler.ContentHandler):
|
|||||||
if level < 1: level = 1
|
if level < 1: level = 1
|
||||||
lev = self.headinglevels[1:level+1]
|
lev = self.headinglevels[1:level+1]
|
||||||
outline = '.'.join(map(str,lev) )
|
outline = '.'.join(map(str,lev) )
|
||||||
anchor = self.get_anchor("%s.%s" % ( outline, ''.join(self.data)))
|
tail = ''.join(self.data)
|
||||||
|
anchor = self.get_anchor("%s.%s" % ( outline, tail))
|
||||||
|
anchor2 = self.get_anchor(tail) # Added by kovid to fix #7506
|
||||||
self.opentag('a', {'id': anchor} )
|
self.opentag('a', {'id': anchor} )
|
||||||
self.closetag('a', False)
|
self.closetag('a', False)
|
||||||
|
self.opentag('a', {'id': anchor2} )
|
||||||
|
self.closetag('a', False)
|
||||||
self.closetag('h%s' % level)
|
self.closetag('h%s' % level)
|
||||||
self.purgedata()
|
self.purgedata()
|
||||||
|
|
||||||
|