Merge from trunk
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
|
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
|
||||
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'])
|
||||
]
|
||||
|
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'
|
||||
__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'
|
||||
|
||||
|
||||
|
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'),
|
||||
]
|
||||
|
||||
|
||||
def print_version(self, url):
|
||||
if url.count('rss.feedsportal.com'):
|
||||
u = url.replace('0Bhtml/story01.htm','_pf0Bhtml/story01.htm')
|
||||
else:
|
||||
u = url.replace('.html','_pf.html')
|
||||
return u
|
||||
if url.count('rss.feedsportal.com'):
|
||||
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
|
||||
|
@ -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 = [
|
||||
|
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
|
||||
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
|
@ -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'
|
||||
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}'
|
||||
delay = 1
|
||||
language = 'sr'
|
||||
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
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, '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'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'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'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'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
|
||||
|
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):
|
||||
title = u'SC Print Magazine'
|
||||
|
@ -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')
|
||||
]
|
||||
|
||||
|
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'),
|
||||
]
|
||||
|
||||
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')
|
||||
|
@ -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))
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
-----
|
||||
|
@ -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('&', '&').replace('<', '<').replace('>', '>')
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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]},
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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()
|
||||
|
@ -109,9 +109,11 @@ class OCFZipReader(OCFReader):
|
||||
raise EPubException("not a ZIP .epub OCF container")
|
||||
self.root = root
|
||||
if self.root is None:
|
||||
self.root = os.getcwdu()
|
||||
if hasattr(stream, 'name'):
|
||||
self.root = os.path.abspath(os.path.dirname(stream.name))
|
||||
name = getattr(stream, 'name', False)
|
||||
if name:
|
||||
self.root = os.path.abspath(os.path.dirname(name))
|
||||
else:
|
||||
self.root = os.getcwdu()
|
||||
super(OCFZipReader, self).__init__()
|
||||
|
||||
def open(self, name, mode='r'):
|
||||
|
@ -221,7 +221,10 @@ class MetadataHeader(BookHeader):
|
||||
else:
|
||||
end = self.section_offset(number + 1)
|
||||
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):
|
||||
@ -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:
|
||||
|
@ -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 � entities in it
|
||||
data = data.replace('\0', '')
|
||||
|
||||
# Remove DOCTYPE declaration as it messes up parsing
|
||||
# In particular, it causes tostring to insert xmlns
|
||||
|
@ -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
|
||||
|
143
src/calibre/gui2/comments_editor.py
Normal 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)
|
||||
|
||||
|
||||
|
@ -1014,7 +1014,13 @@ class DeviceMixin(object): # {{{
|
||||
self.status_bar.show_message(_('Sent by email:') + ', '.join(good),
|
||||
5000)
|
||||
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):
|
||||
ht = self.device_manager.device.THUMBNAIL_HEIGHT \
|
||||
|
@ -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'])
|
||||
and fm[f].get('search_terms', None)
|
||||
and f not in ['formats', 'ondevice']):
|
||||
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', '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,7 +337,10 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
def s_r_get_field(self, mi, field):
|
||||
if 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:
|
||||
val = []
|
||||
elif not fm['is_multiple']:
|
||||
|
@ -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>&Search and replace (experimental)</string>
|
||||
<string>&Search and replace</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="vargrid">
|
||||
<property name="sizeConstraint">
|
||||
|
@ -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 ''
|
||||
|
@ -1,195 +1,370 @@
|
||||
<ui version="4.0" >
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog" >
|
||||
<property name="geometry" >
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>667</width>
|
||||
<height>391</height>
|
||||
<width>731</width>
|
||||
<height>411</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<property name="windowTitle">
|
||||
<string>Advanced Search</string>
|
||||
</property>
|
||||
<property name="windowIcon" >
|
||||
<iconset resource="../../../../resources/images.qrc" >
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/search.png</normaloff>:/images/search.png</iconset>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="title" >
|
||||
<string>Find entries that have...</string>
|
||||
<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>
|
||||
<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>
|
||||
<widget class="QGroupBox" name="groupBox_2" >
|
||||
<property name="title" >
|
||||
<string>But dont show entries that have...</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2" >
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4" >
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4" >
|
||||
<property name="text" >
|
||||
<string>Any of these &unwanted words:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>all</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="none" />
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
<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>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="maximumSize" >
|
||||
<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&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>
|
||||
<width>16777215</width>
|
||||
<height>60</height>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5" >
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5" >
|
||||
<property name="text" >
|
||||
<string>What kind of match to use:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>matchkind</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="matchkind">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Contains: the word or phrase matches anywhere in the metadata</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Equals: the word or phrase must match an entire metadata field</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Regular expression: the expression must match anywhere in the metadata</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_51" >
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>40</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string> </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>
|
||||
</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" />
|
||||
<include location="../../../../resources/images.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
@ -198,11 +373,11 @@
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
@ -214,11 +389,11 @@
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel" >
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel" >
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
|
@ -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"))
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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())
|
||||
|
||||
|
@ -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
|
||||
|
@ -625,7 +625,11 @@ class ResultCache(SearchQueryParser): # {{{
|
||||
# }}}
|
||||
|
||||
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:
|
||||
self._map.remove(id)
|
||||
except ValueError:
|
||||
|
@ -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">✓</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">✓</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 "★" 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 "☆" 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
|
||||
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 "))
|
||||
# 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.NOT_READ_SYMBOL + "by "))
|
||||
authorTag.insert(0, NavigableString(self.MISSING_SYMBOL + " by "))
|
||||
authorTag.insert(1, aTag)
|
||||
|
||||
'''
|
||||
@ -1666,7 +1682,7 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
fontTag['style'] = 'color:white;font-size:large'
|
||||
if self.opts.fmt == 'epub':
|
||||
fontTag['style'] += ';opacity: 0.0'
|
||||
fontTag.insert(0, NavigableString("by "))
|
||||
fontTag.insert(0, NavigableString(" by "))
|
||||
tagsTag.insert(ttc, fontTag)
|
||||
ttc += 1
|
||||
|
||||
@ -1819,21 +1835,27 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
pBookTag = Tag(soup, "p")
|
||||
ptc = 0
|
||||
|
||||
# book with read/reading/unread symbol
|
||||
if book['read']:
|
||||
# check mark
|
||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
elif book['id'] in self.bookmarked_books:
|
||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
# 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))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
elif book['id'] in self.bookmarked_books:
|
||||
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:
|
||||
# hidden check mark
|
||||
pBookTag['class'] = "unread_book"
|
||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||
ptc += 1
|
||||
# missing formats
|
||||
pBookTag['class'] = "missing_book"
|
||||
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
||||
ptc += 1
|
||||
|
||||
# Link to book
|
||||
aTag = Tag(soup, "a")
|
||||
@ -1988,20 +2010,26 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
pBookTag = Tag(soup, "p")
|
||||
ptc = 0
|
||||
|
||||
# book with read/reading/unread symbol
|
||||
if book['read']:
|
||||
# check mark
|
||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
elif book['id'] in self.bookmarked_books:
|
||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
# 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))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
elif book['id'] in self.bookmarked_books:
|
||||
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:
|
||||
# hidden check mark
|
||||
pBookTag['class'] = "unread_book"
|
||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||
# missing book
|
||||
pBookTag['class'] = "missing_book"
|
||||
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
||||
ptc += 1
|
||||
|
||||
aTag = Tag(soup, "a")
|
||||
@ -2116,20 +2144,26 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
pBookTag = Tag(soup, "p")
|
||||
ptc = 0
|
||||
|
||||
# book with read/reading/unread symbol
|
||||
if new_entry['read']:
|
||||
# check mark
|
||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
elif new_entry['id'] in self.bookmarked_books:
|
||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
# 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))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
elif new_entry['id'] in self.bookmarked_books:
|
||||
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:
|
||||
# hidden check mark
|
||||
pBookTag['class'] = "unread_book"
|
||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||
# missing book
|
||||
pBookTag['class'] = "missing_book"
|
||||
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
||||
ptc += 1
|
||||
|
||||
aTag = Tag(soup, "a")
|
||||
@ -2162,20 +2196,26 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
pBookTag = Tag(soup, "p")
|
||||
ptc = 0
|
||||
|
||||
# book with read/reading/unread symbol
|
||||
if new_entry['read']:
|
||||
# check mark
|
||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
elif new_entry['id'] in self.bookmarked_books:
|
||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
# 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))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
elif new_entry['id'] in self.bookmarked_books:
|
||||
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:
|
||||
# hidden check mark
|
||||
pBookTag['class'] = "unread_book"
|
||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||
# missing book
|
||||
pBookTag['class'] = "missing_book"
|
||||
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
||||
ptc += 1
|
||||
|
||||
aTag = Tag(soup, "a")
|
||||
@ -2611,19 +2651,26 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
else:
|
||||
book['read'] = False
|
||||
|
||||
if book['read']:
|
||||
# check mark
|
||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
elif book['id'] in self.bookmarked_books:
|
||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
# 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))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
elif book['id'] in self.bookmarked_books:
|
||||
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:
|
||||
# hidden check mark
|
||||
pBookTag['class'] = "unread_book"
|
||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||
# missing book
|
||||
pBookTag['class'] = "missing_book"
|
||||
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
||||
ptc += 1
|
||||
|
||||
aTag = Tag(soup, "a")
|
||||
@ -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,20 +4152,26 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
pBookTag = Tag(soup, "p")
|
||||
ptc = 0
|
||||
|
||||
# book with read/reading/unread symbol
|
||||
if book['read']:
|
||||
# check mark
|
||||
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
elif book['id'] in self.bookmarked_books:
|
||||
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
# 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))
|
||||
pBookTag['class'] = "read_book"
|
||||
ptc += 1
|
||||
elif book['id'] in self.bookmarked_books:
|
||||
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:
|
||||
# hidden check mark
|
||||
pBookTag['class'] = "unread_book"
|
||||
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
|
||||
# missing book
|
||||
pBookTag['class'] = "missing_book"
|
||||
pBookTag.insert(ptc,NavigableString(self.MISSING_SYMBOL))
|
||||
ptc += 1
|
||||
|
||||
# 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():
|
||||
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]))
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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`
|
||||
|
@ -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()
|
||||
|
||||
|