Merge from trunk

This commit is contained in:
Sengian 2010-11-13 08:51:00 +01:00
commit c919e2e0e8
83 changed files with 23397 additions and 14046 deletions

View File

@ -4,6 +4,100 @@
# for important features/bug fixes.
# 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
date: 2010-11-05
@ -44,6 +138,7 @@
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"
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"

View File

@ -81,6 +81,14 @@ p.unread_book {
text-indent: -2em;
}
p.missing_book {
text-align:left;
margin-top:0px;
margin-bottom:0px;
margin-left:2em;
text-indent:-2em;
}
p.date_read {
text-align: left;
margin-top: 0px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View 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/')
]

View File

@ -25,7 +25,7 @@ class Danas(BasicNewsRecipe):
remove_empty_feeds = True
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)}
.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}
.antrfileText{border-left: 2px solid #999999;
margin-left: 0.8em;
@ -66,7 +66,7 @@ class Danas(BasicNewsRecipe):
keep_only_tags = [dict(name='div', attrs={'id':'left'})]
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=['object','link','iframe','meta'])
]

View 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

View 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')
]

View 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

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3'
__author__ = u'Marc T\xf6nsing'
__author__ = u'Marc Toensing'
from calibre.web.feeds.news import BasicNewsRecipe
@ -17,6 +17,7 @@ class GamespotCom(BasicNewsRecipe):
no_javascript = True
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'),
('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'),
@ -37,5 +38,3 @@ class GamespotCom(BasicNewsRecipe):
def get_article_url(self, article):
return article.get('link') + '?print=1'

View 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'

View 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

View File

@ -33,13 +33,14 @@ class IrishTimes(BasicNewsRecipe):
('Letters', 'http://www.irishtimes.com/feeds/rss/newspaper/letters.rss'),
]
def print_version(self, url):
if url.count('rss.feedsportal.com'):
u = url.replace('0Bhtml/story01.htm','_pf0Bhtml/story01.htm')
u = 'http://www.irishtimes.com' + \
(((url[69:].replace('0C','/')).replace('0A','0'))).replace('0Bhtml/story01..htm','_pf.html')
else:
u = url.replace('.html','_pf.html')
return u
def get_article_url(self, article):
return article.link

View File

@ -54,10 +54,7 @@ class LaJornada_mx(BasicNewsRecipe):
preprocess_regexps = [
(re.compile( r'<div class="inicial">(.*)</div><p class="s-s">'
,re.DOTALL|re.IGNORECASE)
,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) + '"')
,lambda match: '<p class="inicial">' + match.group(1) + '</p><p class="s-s">')
]
keep_only_tags = [

View 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'

View 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

View 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' : ''
}

View File

@ -21,8 +21,16 @@ class Pagina12(BasicNewsRecipe):
use_embedded_content = False
language = 'es'
remove_empty_feeds = True
publication_type = 'newspaper'
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 = {
'comment' : description
@ -31,7 +39,11 @@ class Pagina12(BasicNewsRecipe):
, '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 = [
@ -65,4 +77,13 @@ class Pagina12(BasicNewsRecipe):
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
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

View File

@ -1,13 +1,10 @@
#!/usr/bin/env python
__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
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import Tag
class Politika(BasicNewsRecipe):
title = 'Politika Online'
@ -19,53 +16,51 @@ class Politika(BasicNewsRecipe):
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
remove_javascript = True
encoding = 'utf8'
delay = 1
language = 'sr'
lang = 'sr-Latn-RS'
direction = 'ltr'
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}'
publication_type = 'newspaper'
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: Arial,Helvetica,sans1,sans-serif}
h1{font-family: "Times New Roman",Times,serif1,serif}
.articledescription{font-family: sans1, sans-serif}
"""
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : lang
, 'pretty_print' : True
, 'language' : language
}
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
keep_only_tags = [dict(name='div', attrs={'class':'content_center_border'})]
remove_tags = [
dict(name='div', attrs={'class':['send_print','txt-komentar']})
,dict(name=['object','link','a'])
,dict(name='h1', attrs={'class':'box_header-tags'})
]
keep_only_tags = [dict(name='div', attrs={'class':'big_article_home item_details'})]
remove_tags_after = dict(attrs={'class':'online_date'})
remove_tags = [dict(name=['link','meta','iframe','embed','object'])]
feeds = [
(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'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 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'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):
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):
del item['style']
ftag = soup.find('div',attrs={'class':'content_center_border'})
if ftag.has_key('align'):
del ftag['align']
return self.adeify_images(soup)
for item in soup.findAll('a', attrs={'class':'category'}):
item.name='span'
if item.has_key('href'):
del item['href']
if item.has_key('title'):
del item['title']
return soup

View 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

View 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

View File

@ -1,4 +1,4 @@
from calibre.web.feeds.recipes import BasicNewsRecipe, LoginFailed
from calibre.web.feeds.news import BasicNewsRecipe, LoginFailed
class SCPrintMagazine(BasicNewsRecipe):
title = u'SC Print Magazine'

View File

@ -5,18 +5,17 @@ __copyright__ = '2010, BlonG'
www.siol.si
'''
from calibre.web.feeds.news import BasicNewsRecipe
class Siol(BasicNewsRecipe):
title = u'Siol.net'
__author__ = u'BlonG'
description = "Multimedijski portal z aktualnimi vsebinami, intervjuji, komentarji iz Slovenije in sveta, sportal, trendi, avtomoto, blogos"
oldest_article = 3
language = 'sl'
max_articles_per_feed = 20
no_stylesheets = True
use_embedded_content = False
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 = '''
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;}
'''
html2lrf_options = ['--base-font-size', '10']
keep_only_tags = [
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'TV / Film', u'http://www.siol.net/rss.aspx?path=TV')
]

View 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

View File

@ -43,7 +43,7 @@ class ZeitDe(BasicNewsRecipe):
('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/']
@ -55,6 +55,16 @@ class ZeitDe(BasicNewsRecipe):
ans = None
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):
try:
inhalt = self.index_to_soup('http://www.zeit.de/inhalt')

View File

@ -348,8 +348,12 @@ class Build(Command):
VERSION = 1.0.0
CONFIG += %s
''')%(ext.name, ' '.join(ext.headers), ' '.join(ext.sources), archs)
pro = pro.replace('\\', '\\\\')
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'])
objects = glob.glob(obj_pat)
return list(map(self.a, objects))

View File

@ -13,7 +13,7 @@ from setup import Command, modules, functions, basenames, __version__, \
from setup.build_environment import msvc, MT, RC
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']
LIBUSB_DIR = 'C:\\libusb'
LIBUNRAR = 'C:\\Program Files\\UnrarDLL\\unrar.dll'

View File

@ -37,8 +37,7 @@ Qt
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
nmake
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
SIP
-----

View File

@ -444,6 +444,9 @@ xml_entity_to_unicode = partial(entity_to_unicode, result_exceptions = {
def replace_entities(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):
raw = _ent_pat.sub(entity_to_unicode, raw)
raw = raw.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = 'calibre'
__version__ = '0.7.27'
__version__ = '0.7.28'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re

View File

@ -2,9 +2,7 @@ import os.path
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import textwrap
import os
import glob
import textwrap, os, glob, functools
from calibre.customize import FileTypePlugin, MetadataReaderPlugin, \
MetadataWriterPlugin, PreferencesPlugin, InterfaceActionBase
from calibre.constants import numeric_version
@ -95,10 +93,12 @@ class ComicMetadataReader(MetadataReaderPlugin):
def get_metadata(self, stream, ftype):
if ftype == 'cbr':
from calibre.libunrar import extract_member as extract_first
from calibre.libunrar import extract_first_alphabetically as extract_first
extract_first
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
ret = extract_first(stream)
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.jetbook.driver import JETBOOK, MIBUK, JETBOOK_MINI
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.android.driver import ANDROID, S60
from calibre.devices.nokia.driver import N770, N810, E71X, E52
@ -548,6 +548,7 @@ plugins += [
KINDLE2,
KINDLE_DX,
NOOK,
NOOK_COLOR,
PRS505,
ANDROID,
S60,

View File

@ -250,8 +250,11 @@ class OutputProfile(Plugin):
#: If True, the date is appended to the title of downloaded news
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'*'
empty_ratings_char = u' '
read_char = u'+'
#: Unsupported unicode characters to be replaced during preprocessing
unsupported_unicode_chars = []
@ -287,7 +290,12 @@ class iPadOutput(OutputProfile):
'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_news_css {{{
touchscreen_news_css = u'''
@ -498,7 +506,6 @@ class SonyReaderLandscapeOutput(SonyReaderOutput):
screen_size = (784, 1012)
comic_screen_size = (784, 1012)
class MSReaderOutput(OutputProfile):
name = 'Microsoft Reader'
@ -582,7 +589,12 @@ class KindleOutput(OutputProfile):
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
supports_mobi_indexing = True
periodical_date_in_title = False
missing_char = u'x\u2009'
empty_ratings_char = u'\u2606'
ratings_char = u'\u2605'
read_char = u'\u2713'
mobi_ems_per_blockquote = 2.0
@classmethod
@ -603,6 +615,8 @@ class KindleDXOutput(OutputProfile):
#comic_screen_size = (741, 1022)
supports_mobi_indexing = True
periodical_date_in_title = False
ratings_char = u'\u2605'
read_char = u'\u2713'
mobi_ems_per_blockquote = 2.0
@classmethod

View File

@ -23,6 +23,9 @@ class ANDROID(USBMS):
: [0x0100, 0x0227, 0x0226], 0x0c87: [0x0100, 0x0227, 0x0226],
0xc92 : [0x100]},
# Eken
0x040d : { 0x8510 : [0x0001] },
# Motorola
0x22b8 : { 0x41d9 : [0x216], 0x2d67 : [0x100], 0x41db : [0x216],
0x4285 : [0x216]},

View File

@ -19,7 +19,7 @@ class BLACKBERRY(USBMS):
VENDOR_ID = [0x0fca]
PRODUCT_ID = [0x8004, 0x0004]
BCD = [0x0200, 0x0107, 0x0210, 0x0201]
BCD = [0x0200, 0x0107, 0x0210, 0x0201, 0x0211]
VENDOR_NAME = 'RIM'
WINDOWS_MAIN_MEM = 'BLACKBERRY_SD'

View File

@ -74,9 +74,9 @@ class DevicePlugin(Plugin):
if bcd is None or len(bcd) == 0:
return True
for c in bcd:
# Bug in winutil.get_usb_devices converts a to :
rev = ('rev_%4.4x'%c).replace('a', ':')
if rev in device_id:
rev = 'rev_%4.4x'%c
# Bug in winutil.get_usb_devices sometimes converts a to :
if rev in device_id or rev.replace('a', ':') in device_id:
return True
return False

View File

@ -80,3 +80,14 @@ class NOOK(USBMS):
def sanitize_path_components(self, 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'

View File

@ -619,6 +619,13 @@ class XMLCache(object):
x.replace(u'\0', '')
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 record.get('tz', None) is not None:
use_tz_var = True
@ -641,20 +648,20 @@ class XMLCache(object):
record.set('date', clean(date))
record.set('size', clean(str(os.stat(path).st_size)))
title = book.title if book.title else _('Unknown')
record.set('title', clean(title))
record_set('title', title)
ts = book.title_sort
if not ts:
ts = title_sort(title)
record.set('titleSorter', clean(ts))
record_set('titleSorter', ts)
if self.use_author_sort:
if book.author_sort:
aus = book.author_sort
else:
debug_print('Author_sort is None for book', book.lpath)
aus = authors_to_sort_string(book.authors)
record.set('author', clean(aus))
record_set('author', aus)
else:
record.set('author', clean(authors_to_string(book.authors)))
record_set('author', authors_to_string(book.authors))
ext = os.path.splitext(path)[1]
if ext:
ext = ext[1:].lower()

View File

@ -109,9 +109,11 @@ class OCFZipReader(OCFReader):
raise EPubException("not a ZIP .epub OCF container")
self.root = root
if self.root is None:
name = getattr(stream, 'name', False)
if name:
self.root = os.path.abspath(os.path.dirname(name))
else:
self.root = os.getcwdu()
if hasattr(stream, 'name'):
self.root = os.path.abspath(os.path.dirname(stream.name))
super(OCFZipReader, self).__init__()
def open(self, name, mode='r'):

View File

@ -221,7 +221,10 @@ class MetadataHeader(BookHeader):
else:
end = self.section_offset(number + 1)
self.stream.seek(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):
@ -398,6 +401,8 @@ class MobiReader(object):
elem.getparent().remove(elem)
fname = self.name.encode('ascii', 'replace')
fname = re.sub(r'[\x08\x15\0]+', '', fname)
if not fname:
fname = 'dummy'
htmlfile = os.path.join(output_dir,
ascii_filename(fname) + '.html')
try:
@ -564,6 +569,10 @@ class MobiReader(object):
for attr in self.IMAGE_ATTRS:
recindex = attrib.pop(attr, None) or recindex
if recindex is not None:
try:
recindex = '%05d'%int(recindex)
except:
pass
attrib['src'] = 'images/%s.jpg' % recindex
for attr in ('width', 'height'):
if attr in attrib:

View File

@ -787,6 +787,8 @@ class Manifest(object):
# Convert to Unicode and normalize line endings
data = self.oeb.decode(data)
data = self.oeb.html_preprocessor(data)
# There could be null bytes in data if it had &#0; entities in it
data = data.replace('\0', '')
# Remove DOCTYPE declaration as it messes up parsing
# In particular, it causes tostring to insert xmlns

View File

@ -213,11 +213,13 @@ class BookInfo(QWebView):
f = QFontInfo(QApplication.font(self.parent())).pixelSize()
p = unicode(QApplication.palette().color(QPalette.Normal,
QPalette.Base).name())
c = unicode(QApplication.palette().color(QPalette.Normal,
QPalette.Text).name())
templ = u'''\
<html>
<head>
<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 }
</style>
</head>
@ -225,7 +227,7 @@ class BookInfo(QWebView):
%%s
</body>
<html>
'''%(p, f)
'''%(p, f, c)
if self.vertical:
if comments:
rows += u'<tr><td colspan="2">%s</td></tr>'%comments

File diff suppressed because one or more lines are too long

View File

@ -47,6 +47,8 @@ class BulkConfig(Config):
self.show_pane)
self.connect(self.groups, SIGNAL('entered(QModelIndex)'),
self.show_group_help)
rb = self.buttonBox.button(self.buttonBox.RestoreDefaults)
rb.setVisible(False)
self.groups.setMouseTracking(True)

View File

@ -1014,7 +1014,13 @@ class DeviceMixin(object): # {{{
self.status_bar.show_message(_('Sent by email:') + ', '.join(good),
5000)
if 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):
ht = self.device_manager.device.THUMBNAIL_HEIGHT \

View File

@ -240,13 +240,13 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
self.writable_fields = ['']
fm = self.db.field_metadata
for f in fm:
if (f in ['author_sort'] or (
fm[f]['datatype'] in ['text', 'series'])
if (f in ['author_sort'] or
(fm[f]['datatype'] in ['text', 'series']
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.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.sort()
self.writable_fields.sort()
@ -274,7 +274,6 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
self.main_heading = _(
'<b>You can destroy your library using this feature.</b> '
'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 '
'before proceeding.<p>'
'Search and replace in text fields using character matching '
@ -338,6 +337,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
def s_r_get_field(self, mi, field):
if field:
fm = self.db.metadata_for_field(field)
if field == 'sort':
val = mi.get('title_sort', None)
else:
val = mi.get(field, None)
if val is None:
val = []

View File

@ -400,7 +400,7 @@ Future conversion of these books will use the default settings.</string>
</widget>
<widget class="QWidget" name="tabWidgetPage3">
<attribute name="title">
<string>&amp;Search and replace (experimental)</string>
<string>&amp;Search and replace</string>
</attribute>
<layout class="QGridLayout" name="vargrid">
<property name="sizeConstraint">

View File

@ -1,17 +1,75 @@
__license__ = 'GPL v3'
__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.library.caches import CONTAINS_MATCH, EQUALS_MATCH
from calibre.gui2 import gprefs
box_values = {}
class SearchDialog(QDialog, Ui_Dialog):
def __init__(self, *args):
QDialog.__init__(self, *args)
def __init__(self, parent, db):
QDialog.__init__(self, parent)
self.setupUi(self)
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):
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()]]
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()
if mk == CONTAINS_MATCH:
self.mc = ''
@ -56,3 +120,36 @@ class SearchDialog(QDialog, Ui_Dialog):
tok = '"%s"'%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 ''

View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
@ -5,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>667</width>
<height>391</height>
<width>731</width>
<height>411</height>
</rect>
</property>
<property name="windowTitle">
@ -16,8 +17,47 @@
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/search.png</normaloff>:/images/search.png</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3" >
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<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 row="0" column="1">
<widget class="QComboBox" name="matchkind">
<item>
<property name="text">
<string>Contains: the word or phrase matches anywhere in the metadata field</string>
</property>
</item>
<item>
<property name="text">
<string>Equals: the word or phrase must match the entire metadata field</string>
</property>
</item>
<item>
<property name="text">
<string>Regular expression: the expression must match anywhere in the metadata field</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>A&amp;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>
@ -77,7 +117,7 @@
</layout>
</widget>
</item>
<item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>But dont show entries that have...</string>
@ -100,9 +140,6 @@
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="maximumSize">
@ -112,35 +149,6 @@
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_5" >
<property name="text" >
<string>What kind of match to use:</string>
</property>
<property name="buddy" >
<cstring>matchkind</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="matchkind">
<item>
<property name="text">
<string>Contains: the word or phrase matches anywhere in the metadata</string>
</property>
</item>
<item>
<property name="text">
<string>Equals: the word or phrase must match an entire metadata field</string>
</property>
</item>
<item>
<property name="text">
<string>Regular expression: the expression must match anywhere in the metadata</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_51">
<property name="sizePolicy">
@ -150,7 +158,7 @@
</sizepolicy>
</property>
<property name="text">
<string> </string>
<string/>
</property>
<property name="buddy">
<cstring>matchkind</cstring>
@ -169,14 +177,30 @@
</size>
</property>
<property name="text">
<string>See the &lt;a href="http://calibre-ebook.com/user_manual/gui.html#the-search-interface">User Manual&lt;/a> for more help</string>
<string>See the &lt;a href=&quot;http://calibre-ebook.com/user_manual/gui.html#the-search-interface&quot;&gt;User Manual&lt;/a&gt; for more help</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<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>
@ -188,6 +212,157 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Titl&amp;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>&amp;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>&amp;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>&amp;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&amp;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>&amp;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>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</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>
<include location="../../../../resources/images.qrc"/>
</resources>

View File

@ -167,6 +167,7 @@ class SearchBar(QWidget): # {{{
x.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
parent.advanced_search_button = x = QToolButton(self)
parent.advanced_search_button.setShortcut(_("Shift+Ctrl+F"))
x.setIcon(QIcon(I('search.png')))
l.addWidget(x)
x.setToolTip(_("Advanced search"))

View File

@ -50,10 +50,11 @@ class BooksView(QTableView): # {{{
def __init__(self, parent, modelcls=BooksModel):
QTableView.__init__(self, parent)
self.setEditTriggers(self.SelectedClicked|self.EditKeyPressed)
self.setEditTriggers(self.EditKeyPressed)
if tweaks['doubleclick_on_library_view'] == 'edit_cell':
self.setEditTriggers(self.DoubleClicked|self.editTriggers())
elif tweaks['doubleclick_on_library_view'] == 'open_viewer':
self.setEditTriggers(self.SelectedClicked|self.editTriggers())
self.doubleClicked.connect(parent.iactions['View'].view_triggered)
self.drag_allowed = True

View File

@ -101,9 +101,9 @@ class Category(QWidget): # {{{
ac.setStatusTip(p.description)
self.actions.append(ac)
w = self.bar.widgetForAction(ac)
w.setStyleSheet('QToolButton { margin-right: 20px; min-width: 100px }')
w.setCursor(Qt.PointingHandCursor)
w.setAutoRaise(True)
w.setMinimumWidth(100)
def triggered(self, plugin, *args):
self.plugin_activated.emit(plugin)

View File

@ -392,7 +392,7 @@ class SearchBoxMixin(object):
self.tags_view.clear()
def do_advanced_search(self, *args):
d = SearchDialog(self)
d = SearchDialog(self, self.library_view.model().db)
if d.exec_() == QDialog.Accepted:
self.search.set_search_string(d.search_string())

View File

@ -299,7 +299,7 @@ def generate_catalog(parent, dbspec, ids, device_manager):
]
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
return 'gui_catalog', args, _('Generate catalog'), out.name, d.catalog_sync, \
d.catalog_title

View File

@ -625,7 +625,11 @@ class ResultCache(SearchQueryParser): # {{{
# }}}
def remove(self, id):
try:
self._data[id] = None
except IndexError:
# id is out of bounds, no point setting it to None anyway
pass
try:
self._map.remove(id)
except ValueError:

View File

@ -832,6 +832,9 @@ class EPUB_MOBI(CatalogPlugin):
'[<source>] : Source of content (e.g., Amazon, Project Gutenberg). Do not create genre
- 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.createDirectoryStructure()
catalog.copyResources()
@ -883,6 +886,7 @@ class EPUB_MOBI(CatalogPlugin):
self.__htmlFileList = []
self.__markerTags = self.getMarkerTags()
self.__ncxSoup = None
self.__output_profile = None
self.__playOrder = 1
self.__plugin = plugin
self.__progressInt = 0.0
@ -897,6 +901,12 @@ class EPUB_MOBI(CatalogPlugin):
self.__useSeriesPrefixInTitlesSection = False
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
if self.opts.generate_titles:
self.__totalSteps += 2
@ -1168,10 +1178,14 @@ class EPUB_MOBI(CatalogPlugin):
return property(fget=fget, fset=fset)
@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 fget(self):
return '<span style="color:white">&#x2713;</span>' if self.generateForKindle else \
'<span style="color:white">%s</span>' % self.opts.read_tag
return '<span style="color:white">%s</span>' % self.__output_profile.read_char
return property(fget=fget)
@dynamic_property
def READING_SYMBOL(self):
@ -1182,18 +1196,17 @@ class EPUB_MOBI(CatalogPlugin):
@dynamic_property
def READ_SYMBOL(self):
def fget(self):
return '<span style="color:black">&#x2713;</span>' if self.generateForKindle else \
'<span style="color:black">%s</span>' % self.opts.read_tag
return self.__output_profile.read_char
return property(fget=fget)
@dynamic_property
def FULL_RATING_SYMBOL(self):
def fget(self):
return "&#9733;" if self.generateForKindle else "*"
return self.__output_profile.ratings_char
return property(fget=fget)
@dynamic_property
def EMPTY_RATING_SYMBOL(self):
def fget(self):
return "&#9734;" if self.generateForKindle else ' '
return self.__output_profile.empty_ratings_char
return property(fget=fget)
@dynamic_property
def READ_PROGRESS_SYMBOL(self):
@ -1626,13 +1639,16 @@ class EPUB_MOBI(CatalogPlugin):
self.generateAuthorAnchor(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 'formats' in title and title['formats']:
if title['read']:
authorTag.insert(0, NavigableString(self.READ_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:
authorTag.insert(0, NavigableString(self.MISSING_SYMBOL + " by "))
authorTag.insert(1, aTag)
'''
@ -1819,7 +1835,8 @@ class EPUB_MOBI(CatalogPlugin):
pBookTag = Tag(soup, "p")
ptc = 0
# book with read/reading/unread symbol
# book with read|reading|unread symbol or missing symbol
if 'formats' in book and book['formats']:
if book['read']:
# check mark
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
@ -1834,6 +1851,11 @@ class EPUB_MOBI(CatalogPlugin):
pBookTag['class'] = "unread_book"
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
ptc += 1
else:
# missing formats
pBookTag['class'] = "missing_book"
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
ptc += 1
# Link to book
aTag = Tag(soup, "a")
@ -1988,7 +2010,8 @@ class EPUB_MOBI(CatalogPlugin):
pBookTag = Tag(soup, "p")
ptc = 0
# book with read/reading/unread symbol
# book with read|reading|unread symbol or missing symbol
if 'formats' in book and book['formats']:
if book['read']:
# check mark
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
@ -2003,6 +2026,11 @@ class EPUB_MOBI(CatalogPlugin):
pBookTag['class'] = "unread_book"
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
ptc += 1
else:
# missing book
pBookTag['class'] = "missing_book"
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
ptc += 1
aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(book['id'])))
@ -2116,7 +2144,8 @@ class EPUB_MOBI(CatalogPlugin):
pBookTag = Tag(soup, "p")
ptc = 0
# book with read/reading/unread symbol
# book with read|reading|unread symbol or missing symbol
if 'formats' in new_entry and new_entry['formats']:
if new_entry['read']:
# check mark
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
@ -2131,6 +2160,11 @@ class EPUB_MOBI(CatalogPlugin):
pBookTag['class'] = "unread_book"
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
ptc += 1
else:
# missing book
pBookTag['class'] = "missing_book"
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
ptc += 1
aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(new_entry['id'])))
@ -2162,7 +2196,8 @@ class EPUB_MOBI(CatalogPlugin):
pBookTag = Tag(soup, "p")
ptc = 0
# book with read/reading/unread symbol
# book with read|reading|unread symbol or missing symbol
if 'formats' in new_entry and new_entry['formats']:
if new_entry['read']:
# check mark
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
@ -2177,6 +2212,11 @@ class EPUB_MOBI(CatalogPlugin):
pBookTag['class'] = "unread_book"
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
ptc += 1
else:
# missing book
pBookTag['class'] = "missing_book"
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
ptc += 1
aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(new_entry['id'])))
@ -2611,6 +2651,8 @@ class EPUB_MOBI(CatalogPlugin):
else:
book['read'] = False
# book with read|reading|unread symbol or missing symbol
if 'formats' in book and book['formats']:
if book['read']:
# check mark
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
@ -2625,6 +2667,11 @@ class EPUB_MOBI(CatalogPlugin):
pBookTag['class'] = "unread_book"
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
ptc += 1
else:
# missing book
pBookTag['class'] = "missing_book"
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
ptc += 1
aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(book['id'])))
@ -2702,6 +2749,8 @@ class EPUB_MOBI(CatalogPlugin):
this_book['title'] = book['title']
this_book['author_sort'] = book['author_sort'].capitalize()
this_book['read'] = book['read']
if 'formats' in book:
this_book['formats'] = book['formats']
this_book['id'] = book['id']
this_book['series'] = book['series']
normalized_tag = self.genre_tags_dict[friendly_tag]
@ -4103,7 +4152,8 @@ class EPUB_MOBI(CatalogPlugin):
pBookTag = Tag(soup, "p")
ptc = 0
# book with read/reading/unread symbol
# book with read|reading|unread symbol or missing symbol
if 'formats' in book and book['formats']:
if book['read']:
# check mark
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
@ -4118,6 +4168,11 @@ class EPUB_MOBI(CatalogPlugin):
pBookTag['class'] = "unread_book"
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
ptc += 1
else:
# missing book
pBookTag['class'] = "missing_book"
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
ptc += 1
# Add the book title
aTag = Tag(soup, "a")
@ -4575,7 +4630,8 @@ class EPUB_MOBI(CatalogPlugin):
if opts.connected_device['name'] and 'kindle' in opts.connected_device['name'].lower():
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"
else:
op = "kindle"
@ -4638,7 +4694,8 @@ class EPUB_MOBI(CatalogPlugin):
build_log.append(" opts:")
for key in keys:
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']:
build_log.append(" %s: %s" % (key, opts_dict[key]))

View File

@ -235,7 +235,7 @@ class MobileServer(object):
no_tag_count=True)
book['title'] = record[FM['title']]
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']]
books.append(book)
for key in CKEYS:

View File

@ -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)
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)

View File

@ -43,9 +43,12 @@ def extract(filename, dir):
zf = zipfile.ZipFile( filename )
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)
names = zf.namelist()
names = list(zf.namelist())
if sort_alphabetically:
names.sort()
for name in names:
if match.search(name):
return name, zf.read(name)

View File

@ -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
* - :kbd:`/, Ctrl+F`
- Focus the search bar
* - :kbd:`Shift+Ctrl+F`
- Open the advanced search dialog
* - :kbd:`Ctrl+D`
- Download metadata and shortcuts
* - :kbd:`Ctrl+R`

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -21,15 +21,14 @@
#import pdb
#pdb.set_trace()
import zipfile
import xml.sax
from xml.sax import handler, expatreader
from xml.sax.xmlreader import InputSource
from xml.sax.saxutils import escape, quoteattr
from cStringIO import StringIO
from namespaces import ANIMNS, CHARTNS, CONFIGNS, DCNS, DR3DNS, DRAWNS, FONS, \
FORMNS, MATHNS, METANS, NUMBERNS, OFFICENS, PRESENTATIONNS, SCRIPTNS, \
SMILNS, STYLENS, SVGNS, TABLENS, TEXTNS, XLINKNS
from namespaces import DCNS, DRAWNS, FONS, \
METANS, NUMBERNS, OFFICENS, PRESENTATIONNS, \
STYLENS, SVGNS, TABLENS, TEXTNS, XLINKNS
# Handling of styles
#
@ -526,7 +525,8 @@ class ODF2XHTML(handler.ContentHandler):
def get_anchor(self, 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)
@ -1025,9 +1025,13 @@ class ODF2XHTML(handler.ContentHandler):
if level < 1: level = 1
lev = self.headinglevels[1:level+1]
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.closetag('a', False)
self.opentag('a', {'id': anchor2} )
self.closetag('a', False)
self.closetag('h%s' % level)
self.purgedata()