mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
d75040c80a
BIN
resources/images/news/praguemonitor.png
Normal file
BIN
resources/images/news/praguemonitor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 921 B |
BIN
resources/images/notify.png
Normal file
BIN
resources/images/notify.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
49
resources/recipes/boston.com.recipe
Normal file
49
resources/recipes/boston.com.recipe
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
www.boston.com
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||||
|
|
||||||
|
class BusinessStandard(BasicNewsRecipe):
|
||||||
|
title = 'Boston'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'News from Boston'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
delay = 1
|
||||||
|
use_embedded_content = False
|
||||||
|
encoding = 'cp1252'
|
||||||
|
publisher = 'Boston'
|
||||||
|
category = 'news, boston, usa, world'
|
||||||
|
language = 'en'
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comments' : description
|
||||||
|
,'tags' : category
|
||||||
|
,'language' : language
|
||||||
|
,'publisher' : publisher
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'class':'story'})]
|
||||||
|
remove_tags = [dict(name=['object','link','script','iframe'])]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Top Stories' , u'http://feeds.boston.com/boston/topstories' )
|
||||||
|
,(u'Patriots news', u'http://feeds.boston.com/boston/sports/football/patriots')
|
||||||
|
,(u'National news', u'http://feeds.boston.com/boston/news/nation' )
|
||||||
|
,(u'World news' , u'http://feeds.boston.com/boston/news/world' )
|
||||||
|
]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url + '?mode=PF'
|
||||||
|
|
||||||
|
def get_article_url(self, article):
|
||||||
|
rawarticle = article.get('pheedo_origlink', None)
|
||||||
|
artls, sep, rsep = rawarticle.rpartition('/?')
|
||||||
|
if artls == '':
|
||||||
|
artls = rawarticle.rpartition('?')[0]
|
||||||
|
return artls
|
||||||
|
|
20
resources/recipes/clarion_ledger.recipe
Normal file
20
resources/recipes/clarion_ledger.recipe
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class ClarionLedger(BasicNewsRecipe):
|
||||||
|
title = u'Clarion Ledger'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
language = 'en'
|
||||||
|
__author__ = 'cr4zyd'
|
||||||
|
|
||||||
|
feeds = [(u'Local News', u'http://www.clarionledger.com/apps/pbcs.dll/oversikt?Category=RSS01'), (u'Breaking News', u'http://www.clarionledger.com/apps/pbcs.dll/section?Category=RSS'), (u'Sports', u'http://www.clarionledger.com/apps/pbcs.dll/oversikt?Category=RSS02'), (u'Business', u'http://www.clarionledger.com/apps/pbcs.dll/oversikt?Category=RSS03')]
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'class':'article-headline'}),
|
||||||
|
dict(name='div', attrs={'class':'article-bodytext'})]
|
||||||
|
remove_tags = [dict(name=['img','script','li']),
|
||||||
|
dict(name='p', attrs={'class':'ratingbyline'}),
|
||||||
|
dict(name='div', attrs={'class':'article-tools'}),
|
||||||
|
dict(name='div', attrs={'class':'article-pagination article-pagination-top'}),
|
||||||
|
dict(name='div', attrs={'class':'article-pagination article-pagination-bottom'}),
|
||||||
|
dict(name='div', attrs={'class':'articleflex-container'})]
|
31
resources/recipes/dosisdiarias.recipe
Normal file
31
resources/recipes/dosisdiarias.recipe
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
http://www.dosisdiarias.com
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||||
|
|
||||||
|
class DosisDiarias(BasicNewsRecipe):
|
||||||
|
title = 'Alberto Montt en dosis diarias'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'Mire sin compromiso y si le gusta vuelva'
|
||||||
|
oldest_article = 5
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = True
|
||||||
|
encoding = 'utf-8'
|
||||||
|
publisher = 'Alberto Montt'
|
||||||
|
category = 'comic, blog, spanish'
|
||||||
|
language = 'es'
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comments' : description
|
||||||
|
,'tags' : category
|
||||||
|
,'language' : language
|
||||||
|
,'publisher' : publisher
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_tags = [dict(name='div',attrs={'class':'feedflare'})]
|
||||||
|
|
||||||
|
feeds = [(u'Dosis diaria', u'http://feeds.feedburner.com/montt' )]
|
@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
@ -19,22 +18,19 @@ class ElMundo(BasicNewsRecipe):
|
|||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
encoding = 'iso8859_15'
|
encoding = 'iso8859_15'
|
||||||
cover_url = 'http://estaticos02.cache.el-mundo.net/papel/imagenes/v2.0/logoverde.gif'
|
language = 'es'
|
||||||
remove_javascript = True
|
|
||||||
|
|
||||||
html2lrf_options = [
|
conversion_options = {
|
||||||
'--comment', description
|
'comments' : description
|
||||||
, '--category', category
|
,'tags' : category
|
||||||
, '--publisher', publisher
|
,'language' : language
|
||||||
]
|
,'publisher' : publisher
|
||||||
|
}
|
||||||
|
|
||||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
keep_only_tags = [dict(name='div', attrs={'class':'noticia'})]
|
||||||
|
remove_tags_before = dict(attrs={'class':['titular','antetitulo'] })
|
||||||
|
remove_tags_after = dict(name='div' , attrs={'id':['desarrollo_noticia','tamano']})
|
||||||
|
|
||||||
|
|
||||||
keep_only_tags = [
|
|
||||||
dict(name='div', attrs={'id':['bloqueprincipal','noticia']})
|
|
||||||
,dict(name='div', attrs={'class':['contenido_noticia_01']})
|
|
||||||
]
|
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name='div', attrs={'class':['herramientas','publicidad_google']})
|
dict(name='div', attrs={'class':['herramientas','publicidad_google']})
|
||||||
,dict(name='div', attrs={'id':'modulo_multimedia' })
|
,dict(name='div', attrs={'id':'modulo_multimedia' })
|
||||||
@ -44,6 +40,8 @@ class ElMundo(BasicNewsRecipe):
|
|||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Portada' , u'http://rss.elmundo.es/rss/descarga.htm?data2=4' )
|
(u'Portada' , u'http://rss.elmundo.es/rss/descarga.htm?data2=4' )
|
||||||
|
,(u'Deportes' , u'http://rss.elmundo.es/rss/descarga.htm?data2=14')
|
||||||
|
,(u'Economia' , u'http://rss.elmundo.es/rss/descarga.htm?data2=7' )
|
||||||
,(u'Espana' , u'http://rss.elmundo.es/rss/descarga.htm?data2=8' )
|
,(u'Espana' , u'http://rss.elmundo.es/rss/descarga.htm?data2=8' )
|
||||||
,(u'Internacional' , u'http://rss.elmundo.es/rss/descarga.htm?data2=9' )
|
,(u'Internacional' , u'http://rss.elmundo.es/rss/descarga.htm?data2=9' )
|
||||||
,(u'Cultura' , u'http://rss.elmundo.es/rss/descarga.htm?data2=6' )
|
,(u'Cultura' , u'http://rss.elmundo.es/rss/descarga.htm?data2=6' )
|
||||||
@ -51,10 +49,3 @@ class ElMundo(BasicNewsRecipe):
|
|||||||
,(u'Comunicacion' , u'http://rss.elmundo.es/rss/descarga.htm?data2=26')
|
,(u'Comunicacion' , u'http://rss.elmundo.es/rss/descarga.htm?data2=26')
|
||||||
,(u'Television' , u'http://rss.elmundo.es/rss/descarga.htm?data2=76')
|
,(u'Television' , u'http://rss.elmundo.es/rss/descarga.htm?data2=76')
|
||||||
]
|
]
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
|
||||||
for item in soup.findAll(style=True):
|
|
||||||
del item['style']
|
|
||||||
return soup
|
|
||||||
|
|
||||||
language = 'es'
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
@ -12,30 +11,29 @@ class LondonReviewOfBooks(BasicNewsRecipe):
|
|||||||
title = u'London Review of Books'
|
title = u'London Review of Books'
|
||||||
__author__ = u'Darko Miletic'
|
__author__ = u'Darko Miletic'
|
||||||
description = u'Literary review publishing essay-length book reviews and topical articles on politics, literature, history, philosophy, science and the arts by leading writers and thinkers'
|
description = u'Literary review publishing essay-length book reviews and topical articles on politics, literature, history, philosophy, science and the arts by leading writers and thinkers'
|
||||||
|
category = 'news, literature, England'
|
||||||
|
publisher = 'London Review of Books'
|
||||||
oldest_article = 7
|
oldest_article = 7
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
language = 'en_GB'
|
language = 'en_GB'
|
||||||
|
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
encoding = 'cp1252'
|
encoding = 'utf-8'
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comments' : description
|
||||||
|
,'tags' : category
|
||||||
|
,'language' : language
|
||||||
|
,'publisher' : publisher
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='div' , attrs={'id' :'main'})]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name='div' , attrs={'id' :'otherarticles'})
|
dict(name='div' , attrs={'class':['pagetools','issue-nav-controls','nocss']})
|
||||||
,dict(name='div' , attrs={'class':'pagetools' })
|
,dict(name='div' , attrs={'id' :['mainmenu','precontent','otherarticles'] })
|
||||||
,dict(name='div' , attrs={'id' :'mainmenu' })
|
,dict(name='span', attrs={'class':['inlineright','article-icons']})
|
||||||
,dict(name='div' , attrs={'id' :'precontent' })
|
,dict(name='ul' , attrs={'class':'article-controls'})
|
||||||
,dict(name='div' , attrs={'class':'nocss' })
|
,dict(name='p' , attrs={'class':'meta-info' })
|
||||||
,dict(name='span', attrs={'class':'inlineright' })
|
|
||||||
]
|
]
|
||||||
|
|
||||||
feeds = [(u'London Review of Books', u'http://www.lrb.co.uk/lrbrss.xml')]
|
feeds = [(u'London Review of Books', u'http://www.lrb.co.uk/lrbrss.xml')]
|
||||||
|
|
||||||
def print_version(self, url):
|
|
||||||
main, split, rest = url.rpartition('/')
|
|
||||||
return main + '/print/' + rest
|
|
||||||
|
|
||||||
def postprocess_html(self, soup, first_fetch):
|
|
||||||
for t in soup.findAll(['table', 'tr', 'td']):
|
|
||||||
t.name = 'div'
|
|
||||||
return soup
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: cp1252 -*-
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
@ -13,7 +14,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
|||||||
class Politico(BasicNewsRecipe):
|
class Politico(BasicNewsRecipe):
|
||||||
|
|
||||||
title = 'Politico'
|
title = 'Politico'
|
||||||
__author__ = 'Darko Miletic'
|
__author__ = 'Darko Miletic and Sujata Raman'
|
||||||
description = 'Political news from USA'
|
description = 'Political news from USA'
|
||||||
publisher = 'Capitol News Company, LLC'
|
publisher = 'Capitol News Company, LLC'
|
||||||
category = 'news, politics, USA'
|
category = 'news, politics, USA'
|
||||||
@ -22,10 +23,9 @@ class Politico(BasicNewsRecipe):
|
|||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
encoding = 'cp1252'
|
encoding = 'UTF-8'
|
||||||
language = 'en'
|
language = 'en'
|
||||||
|
|
||||||
|
|
||||||
html2lrf_options = [
|
html2lrf_options = [
|
||||||
'--comment', description
|
'--comment', description
|
||||||
, '--category', category
|
, '--category', category
|
||||||
@ -35,7 +35,19 @@ class Politico(BasicNewsRecipe):
|
|||||||
|
|
||||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||||
|
|
||||||
remove_tags = [dict(name=['notags','embed','object','link','img'])]
|
remove_tags = [
|
||||||
|
dict(name=['notags','embed','object','link','img']),
|
||||||
|
]
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
body{font-family:Arial,Sans-serif;}
|
||||||
|
element.style{color:#FF0000;font-family:Arial,Sans-serif;}
|
||||||
|
.author{color:#808080;font-size:x-small;}
|
||||||
|
a{ color:#003399;}
|
||||||
|
.byline{color:#696969 ; font-size:x-small;}
|
||||||
|
.story{color:#000000;}
|
||||||
|
td{color:#000000;}
|
||||||
|
'''
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Top Stories' , u'http://www.politico.com/rss/politicopicks.xml' )
|
(u'Top Stories' , u'http://www.politico.com/rss/politicopicks.xml' )
|
||||||
@ -48,17 +60,23 @@ class Politico(BasicNewsRecipe):
|
|||||||
,(u'Roger Simon' , u'http://www.politico.com/rss/rogersimon.xml' )
|
,(u'Roger Simon' , u'http://www.politico.com/rss/rogersimon.xml' )
|
||||||
,(u'Suite Talk' , u'http://www.politico.com/rss/suitetalk.xml' )
|
,(u'Suite Talk' , u'http://www.politico.com/rss/suitetalk.xml' )
|
||||||
,(u'Playbook' , u'http://www.politico.com/rss/playbook.xml' )
|
,(u'Playbook' , u'http://www.politico.com/rss/playbook.xml' )
|
||||||
,(u'The Huddle' , u'http://www.politico.com/rss/huddle.xml' )
|
#(u'The Huddle' , u'http://www.politico.com/rss/huddle.xml' )
|
||||||
]
|
]
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
mtag = '<meta http-equiv="Content-Language" content="en-US"/>'
|
mtag = '<meta http-equiv="Content-Language" content="en"/>'
|
||||||
soup.head.insert(0,mtag)
|
soup.head.insert(0,mtag)
|
||||||
for item in soup.findAll(style=True):
|
for item in soup.findAll(style=True):
|
||||||
del item['style']
|
del item['style']
|
||||||
return soup
|
return soup
|
||||||
|
|
||||||
url_pat = re.compile(r'<a href="([^"]+printstory\.cfm[^"]+)"')
|
url_pat = re.compile(r'<a href="([^"]+print.*\.cfm[^"]+)"')
|
||||||
|
|
||||||
|
def postprocess_html(self, soup, first):
|
||||||
|
|
||||||
|
for tag in soup.findAll(name=['table', 'tr', 'td']):
|
||||||
|
tag.name = 'div'
|
||||||
|
return soup
|
||||||
|
|
||||||
def print_version(self, url):
|
def print_version(self, url):
|
||||||
raw = self.index_to_soup(url, raw=True)
|
raw = self.index_to_soup(url, raw=True)
|
||||||
|
32
resources/recipes/praguemonitor.recipe
Normal file
32
resources/recipes/praguemonitor.recipe
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
praguemonitor.com
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class PragueDailyMonitor(BasicNewsRecipe):
|
||||||
|
title = u'Prague Daily Monitor'
|
||||||
|
__author__ = u'Darko Miletic'
|
||||||
|
description = u'Czech news in English'
|
||||||
|
category = 'news, politics, Czech republic'
|
||||||
|
publisher = 'Prague Daily Monitor'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
language = 'en'
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
encoding = 'utf-8'
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comments' : description
|
||||||
|
,'tags' : category
|
||||||
|
,'language' : language
|
||||||
|
,'publisher' : publisher
|
||||||
|
}
|
||||||
|
|
||||||
|
keep_only_tags = [dict(name='div' , attrs={'id':['content-header','content-area']})]
|
||||||
|
|
||||||
|
feeds = [(u'All Articles', u'http://feeds.feedburner.com/PragueDailyMonitor?format=xml')]
|
@ -26,9 +26,12 @@ class Sueddeutsche(BasicNewsRecipe):
|
|||||||
dict(name='div', attrs={'id':["artikel","contentTable"]}) ,
|
dict(name='div', attrs={'id':["artikel","contentTable"]}) ,
|
||||||
]
|
]
|
||||||
remove_tags = [ dict(name='link'), dict(name='iframe'),
|
remove_tags = [ dict(name='link'), dict(name='iframe'),
|
||||||
dict(name='div', attrs={'id':["themenbox","artikelfoot","CAD_AD","rechteSpalte"]}),
|
dict(name='div', attrs={'id':["themenbox","artikelfoot","CAD_AD","SKY_AD","NT1_AD","rechteSpalte"]}),
|
||||||
dict(name='div', attrs={'class':["similar-article-box","artikelliste","nteaser301bg","pages closed"]}),
|
dict(name='div', attrs={'class':["similar-article-box","artikelliste","nteaser301bg","pages closed"]}),
|
||||||
|
dict(name='div', attrs={'class':["listHeader","listHeader2","hr2","item","videoBigButton"]}),
|
||||||
dict(name='p', attrs={'class':["ressortartikeln",]}),
|
dict(name='p', attrs={'class':["ressortartikeln",]}),
|
||||||
|
dict(name='div', attrs={'style':["position:relative;"]}),
|
||||||
|
dict(name='span', attrs={'class':["nlinkheaderteaserschwarz",]}),
|
||||||
dict(name='table', attrs={'class':["kommentare","footer","pageBoxBot","pageAktiv","bgcontent"]}),
|
dict(name='table', attrs={'class':["kommentare","footer","pageBoxBot","pageAktiv","bgcontent"]}),
|
||||||
dict(name='ul', attrs={'class':["breadcrumb","articles","activities"]}),
|
dict(name='ul', attrs={'class':["breadcrumb","articles","activities"]}),
|
||||||
dict(name='p', text = "ANZEIGE")
|
dict(name='p', text = "ANZEIGE")
|
||||||
@ -66,3 +69,4 @@ class Sueddeutsche(BasicNewsRecipe):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,12 +15,13 @@ class weltDe(BasicNewsRecipe):
|
|||||||
__author__ = 'Oliver Niesner'
|
__author__ = 'Oliver Niesner'
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
timefmt = ' [%d %b %Y]'
|
timefmt = ' [%d %b %Y]'
|
||||||
max_articles_per_feed = 15 # reduced to this value to prevent too many articles (suggested by Gregory Riker
|
max_articles_per_feed = 15
|
||||||
|
linearize_tables = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
remove_stylesheets = True
|
remove_stylesheets = True
|
||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
language = 'de'
|
|
||||||
encoding = 'iso-8859-1'
|
encoding = 'iso-8859-1'
|
||||||
|
BasicNewsRecipe.summary_length = 200
|
||||||
|
|
||||||
|
|
||||||
remove_tags = [dict(id='jumplinks'),
|
remove_tags = [dict(id='jumplinks'),
|
||||||
@ -43,10 +44,14 @@ class weltDe(BasicNewsRecipe):
|
|||||||
dict(id='servicesBox'),
|
dict(id='servicesBox'),
|
||||||
dict(id='toggleAdvancedSearch'),
|
dict(id='toggleAdvancedSearch'),
|
||||||
dict(id='mainNav'),
|
dict(id='mainNav'),
|
||||||
dict(id='ratingBox5136466_1'),
|
|
||||||
dict(id='ratingBox5136466_2'),
|
|
||||||
dict(id='articleInlineMediaBox0'),
|
dict(id='articleInlineMediaBox0'),
|
||||||
dict(id='sectionSponsor'),
|
dict(id='sectionSponsor'),
|
||||||
|
dict(id='sprucharea'),
|
||||||
|
dict(id='xmsg_recommendEmail'),
|
||||||
|
dict(id='xmsg_recommendSms'),
|
||||||
|
dict(id='xmsg_comment'),
|
||||||
|
dict(id='additionalNavWrapper'),
|
||||||
|
dict(id='imagebox'),
|
||||||
#dict(id=''),
|
#dict(id=''),
|
||||||
dict(name='span'),
|
dict(name='span'),
|
||||||
dict(name='div', attrs={'class':'printURL'}),
|
dict(name='div', attrs={'class':'printURL'}),
|
||||||
@ -65,10 +70,21 @@ class weltDe(BasicNewsRecipe):
|
|||||||
dict(name='ul', attrs={'class':'optionsSubNav clear'}),
|
dict(name='ul', attrs={'class':'optionsSubNav clear'}),
|
||||||
dict(name='li', attrs={'class':'next'}),
|
dict(name='li', attrs={'class':'next'}),
|
||||||
dict(name='li', attrs={'class':'prev'}),
|
dict(name='li', attrs={'class':'prev'}),
|
||||||
|
dict(name='li', attrs={'class':'last'}),
|
||||||
|
dict(name='table', attrs={'class':'textGallery'}),
|
||||||
dict(name='li', attrs={'class':'active'})]
|
dict(name='li', attrs={'class':'active'})]
|
||||||
|
|
||||||
remove_tags_after = [dict(id='tw_link_widget')]
|
remove_tags_after = [dict(id='tw_link_widget')]
|
||||||
|
|
||||||
|
extra_css = '''
|
||||||
|
h2{font-family:Arial,Helvetica,sans-serif; font-size: x-small; color: #003399;}
|
||||||
|
a{font-family:Arial,Helvetica,sans-serif; font-size: x-small; font-style:italic;}
|
||||||
|
.dachzeile p{font-family:Arial,Helvetica,sans-serif; font-size: x-small; }
|
||||||
|
h1{ font-family:Arial,Helvetica,sans-serif; font-size:x-large; font-weight:bold;}
|
||||||
|
.artikelTeaser{font-family:Arial,Helvetica,sans-serif; font-size: x-small; font-weight:bold; }
|
||||||
|
body{font-family:Arial,Helvetica,sans-serif; }
|
||||||
|
.photo {font-family:Arial,Helvetica,sans-serif; font-size: x-small; color: #666666;} '''
|
||||||
|
|
||||||
feeds = [ ('Politik', 'http://welt.de/politik/?service=Rss'),
|
feeds = [ ('Politik', 'http://welt.de/politik/?service=Rss'),
|
||||||
('Deutsche Dinge', 'http://www.welt.de/deutsche-dinge/?service=Rss'),
|
('Deutsche Dinge', 'http://www.welt.de/deutsche-dinge/?service=Rss'),
|
||||||
('Wirtschaft', 'http://welt.de/wirtschaft/?service=Rss'),
|
('Wirtschaft', 'http://welt.de/wirtschaft/?service=Rss'),
|
||||||
|
@ -187,7 +187,7 @@
|
|||||||
<!-- section/title -->
|
<!-- section/title -->
|
||||||
<xsl:template match="fb:body/fb:title">
|
<xsl:template match="fb:body/fb:title">
|
||||||
<xsl:element name="h1">
|
<xsl:element name="h1">
|
||||||
<xsl:apply-templates mode="title"/>
|
<xsl:apply-templates />
|
||||||
</xsl:element>
|
</xsl:element>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ class LinuxFreeze(Command):
|
|||||||
'glib', 'gobject']
|
'glib', 'gobject']
|
||||||
|
|
||||||
packages = ['calibre', 'encodings', 'cherrypy', 'cssutils', 'xdg',
|
packages = ['calibre', 'encodings', 'cherrypy', 'cssutils', 'xdg',
|
||||||
'dateutil', 'dns', 'email']
|
'dateutil', 'dns', 'email', 'dbus']
|
||||||
|
|
||||||
includes += ['calibre.gui2.convert.'+x.split('/')[-1].rpartition('.')[0] for x in \
|
includes += ['calibre.gui2.convert.'+x.split('/')[-1].rpartition('.')[0] for x in \
|
||||||
glob.glob('src/calibre/gui2/convert/*.py')]
|
glob.glob('src/calibre/gui2/convert/*.py')]
|
||||||
|
@ -5,7 +5,9 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
Device drivers.
|
Device drivers.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import time
|
import sys, os, time, pprint
|
||||||
|
from functools import partial
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
DAY_MAP = dict(Sun=0, Mon=1, Tue=2, Wed=3, Thu=4, Fri=5, Sat=6)
|
DAY_MAP = dict(Sun=0, Mon=1, Tue=2, Wed=3, Thu=4, Fri=5, Sat=6)
|
||||||
MONTH_MAP = dict(Jan=1, Feb=2, Mar=3, Apr=4, May=5, Jun=6, Jul=7, Aug=8, Sep=9, Oct=10, Nov=11, Dec=12)
|
MONTH_MAP = dict(Jan=1, Feb=2, Mar=3, Apr=4, May=5, Jun=6, Jul=7, Aug=8, Sep=9, Oct=10, Nov=11, Dec=12)
|
||||||
@ -24,3 +26,96 @@ def strftime(epoch, zone=time.gmtime):
|
|||||||
src[0] = INVERSE_DAY_MAP[int(src[0][:-1])]+','
|
src[0] = INVERSE_DAY_MAP[int(src[0][:-1])]+','
|
||||||
src[2] = INVERSE_MONTH_MAP[int(src[2])]
|
src[2] = INVERSE_MONTH_MAP[int(src[2])]
|
||||||
return ' '.join(src)
|
return ' '.join(src)
|
||||||
|
|
||||||
|
def debug():
|
||||||
|
from calibre.customize.ui import device_plugins
|
||||||
|
from calibre.devices.scanner import DeviceScanner
|
||||||
|
from calibre.constants import iswindows, isosx, __version__
|
||||||
|
from calibre import prints
|
||||||
|
oldo, olde = sys.stdout, sys.stderr
|
||||||
|
|
||||||
|
buf = StringIO()
|
||||||
|
sys.stdout = sys.stderr = buf
|
||||||
|
try:
|
||||||
|
out = partial(prints, file=buf)
|
||||||
|
out('Version:', __version__)
|
||||||
|
s = DeviceScanner()
|
||||||
|
s.scan()
|
||||||
|
devices = (s.devices)
|
||||||
|
if not iswindows:
|
||||||
|
devices = [list(x) for x in devices]
|
||||||
|
for d in devices:
|
||||||
|
for i in range(3):
|
||||||
|
d[i] = hex(d[i])
|
||||||
|
out('USB devices on system:')
|
||||||
|
out(pprint.pformat(devices))
|
||||||
|
if iswindows:
|
||||||
|
if iswindows:
|
||||||
|
import pythoncom
|
||||||
|
pythoncom.CoInitialize()
|
||||||
|
try:
|
||||||
|
wmi = __import__('wmi', globals(), locals(), [], -1)
|
||||||
|
drives = []
|
||||||
|
out('Drives detected:')
|
||||||
|
out('\t', '(ID, Partitions, Drive letter)')
|
||||||
|
for drive in wmi.WMI(find_classes=False).Win32_DiskDrive():
|
||||||
|
if drive.Partitions == 0:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
partition = drive.associators("Win32_DiskDriveToDiskPartition")[0]
|
||||||
|
logical_disk = partition.associators('Win32_LogicalDiskToPartition')[0]
|
||||||
|
prefix = logical_disk.DeviceID+os.sep
|
||||||
|
drives.append((str(drive.PNPDeviceID), drive.Index, prefix))
|
||||||
|
except IndexError:
|
||||||
|
drives.append((str(drive.PNPDeviceID), 'No mount points found'))
|
||||||
|
for drive in drives:
|
||||||
|
out('\t', drive)
|
||||||
|
finally:
|
||||||
|
pythoncom.CoUninitialize()
|
||||||
|
|
||||||
|
ioreg = None
|
||||||
|
if isosx:
|
||||||
|
from calibre.devices.usbms.device import Device
|
||||||
|
ioreg = Device.run_ioreg()
|
||||||
|
connected_devices = []
|
||||||
|
for dev in device_plugins():
|
||||||
|
out('Looking for', dev.__class__.__name__)
|
||||||
|
connected = s.is_device_connected(dev, debug=True)
|
||||||
|
if connected:
|
||||||
|
connected_devices.append(dev)
|
||||||
|
|
||||||
|
errors = {}
|
||||||
|
success = False
|
||||||
|
for dev in connected_devices:
|
||||||
|
out('Device possibly connected:', dev.__class__.name)
|
||||||
|
out('Trying to open device...', end=' ')
|
||||||
|
try:
|
||||||
|
dev.open()
|
||||||
|
out('OK')
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
errors[dev] = traceback.format_exc()
|
||||||
|
out('failed')
|
||||||
|
continue
|
||||||
|
success = True
|
||||||
|
if hasattr(dev, '_main_prefix'):
|
||||||
|
out('Main memory:', repr(dev._main_prefix))
|
||||||
|
out('Total space:', dev.total_space())
|
||||||
|
break
|
||||||
|
if not success and errors:
|
||||||
|
out('Opening of the following devices failed')
|
||||||
|
for dev,msg in errors.items():
|
||||||
|
out(dev)
|
||||||
|
out(msg)
|
||||||
|
out(' ')
|
||||||
|
|
||||||
|
if ioreg is not None:
|
||||||
|
out(' ')
|
||||||
|
out('IOREG Output')
|
||||||
|
out(ioreg)
|
||||||
|
|
||||||
|
return buf.getvalue().decode('utf-8')
|
||||||
|
finally:
|
||||||
|
sys.stdout = oldo
|
||||||
|
sys.stderr = olde
|
||||||
|
|
||||||
|
@ -117,9 +117,15 @@ class ITALICA(EB600):
|
|||||||
name = 'Italica Device Interface'
|
name = 'Italica Device Interface'
|
||||||
gui_name = 'Italica'
|
gui_name = 'Italica'
|
||||||
|
|
||||||
FORMATS = ['epub', 'pdf', 'txt']
|
FORMATS = ['epub', 'rtf', 'fb2', 'html', 'prc', 'mobi', 'pdf', 'txt']
|
||||||
|
|
||||||
VENDOR_NAME = 'ITALICA'
|
VENDOR_NAME = 'ITALICA'
|
||||||
WINDOWS_MAIN_MEM = 'EREADER'
|
WINDOWS_MAIN_MEM = 'EREADER'
|
||||||
|
WINDOWS_CARD_A_MEM = WINDOWS_MAIN_MEM
|
||||||
|
|
||||||
OSX_MAIN_MEM = 'Italica eReader Media'
|
OSX_MAIN_MEM = 'Italica eReader Media'
|
||||||
|
OSX_CARD_A_MEM = OSX_MAIN_MEM
|
||||||
|
|
||||||
|
MAIN_MEMORY_VOLUME_LABEL = 'Italica Main Memory'
|
||||||
|
STORAGE_CARD_VOLUME_LABEL = 'Italica Storage Card'
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ class PRS500(DeviceConfig, DevicePlugin):
|
|||||||
try:
|
try:
|
||||||
if not dev.handle:
|
if not dev.handle:
|
||||||
dev.open()
|
dev.open()
|
||||||
if not dev.in_session:
|
if not getattr(dev, 'in_session', False):
|
||||||
dev.send_validated_command(BeginEndSession(end=False))
|
dev.send_validated_command(BeginEndSession(end=False))
|
||||||
dev.in_session = True
|
dev.in_session = True
|
||||||
res = func(*args, **kwargs)
|
res = func(*args, **kwargs)
|
||||||
|
@ -711,7 +711,9 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
candidates = self.get_main_ebook_dir()
|
candidates = self.get_main_ebook_dir()
|
||||||
if isinstance(candidates, basestring):
|
if isinstance(candidates, basestring):
|
||||||
candidates = [candidates]
|
candidates = [candidates]
|
||||||
candidates = [os.path.join(self._main_prefix, *(x.split('/'))) for x
|
candidates = [
|
||||||
|
((os.path.join(self._main_prefix, *(x.split('/')))) if x else
|
||||||
|
self._main_prefix) for x
|
||||||
in candidates]
|
in candidates]
|
||||||
existing = [x for x in candidates if os.path.exists(x)]
|
existing = [x for x in candidates if os.path.exists(x)]
|
||||||
if not existing:
|
if not existing:
|
||||||
|
@ -63,7 +63,7 @@ class USBMS(CLI, Device):
|
|||||||
if isinstance(ebook_dirs, basestring):
|
if isinstance(ebook_dirs, basestring):
|
||||||
ebook_dirs = [ebook_dirs]
|
ebook_dirs = [ebook_dirs]
|
||||||
for ebook_dir in ebook_dirs:
|
for ebook_dir in ebook_dirs:
|
||||||
ebook_dir = os.path.join(prefix, *(ebook_dir.split('/')))
|
ebook_dir = os.path.join(prefix, *(ebook_dir.split('/'))) if ebook_dir else prefix
|
||||||
if not os.path.exists(ebook_dir): continue
|
if not os.path.exists(ebook_dir): continue
|
||||||
# Get all books in the ebook_dir directory
|
# Get all books in the ebook_dir directory
|
||||||
if self.SUPPORTS_SUB_DIRS:
|
if self.SUPPORTS_SUB_DIRS:
|
||||||
|
@ -25,7 +25,7 @@ class DRMError(ValueError):
|
|||||||
BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm',
|
BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm',
|
||||||
'html', 'xhtml', 'pdf', 'pdb', 'prc', 'mobi', 'azw', 'doc',
|
'html', 'xhtml', 'pdf', 'pdb', 'prc', 'mobi', 'azw', 'doc',
|
||||||
'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'oebzip',
|
'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'oebzip',
|
||||||
'rb', 'imp', 'odt', 'chm', 'tpz', 'azw1']
|
'rb', 'imp', 'odt', 'chm', 'tpz', 'azw1', 'pml']
|
||||||
|
|
||||||
class HTMLRenderer(object):
|
class HTMLRenderer(object):
|
||||||
|
|
||||||
|
@ -804,6 +804,11 @@ OptionRecommendation(name='language',
|
|||||||
if line_height < 1e-4:
|
if line_height < 1e-4:
|
||||||
line_height = None
|
line_height = None
|
||||||
|
|
||||||
|
if self.opts.linearize_tables and \
|
||||||
|
self.output_plugin.file_type not in ('mobi', 'lrf'):
|
||||||
|
from calibre.ebooks.oeb.transforms.linearize_tables import LinearizeTables
|
||||||
|
LinearizeTables()(self.oeb, self.opts)
|
||||||
|
|
||||||
flattener = CSSFlattener(fbase=fbase, fkey=fkey,
|
flattener = CSSFlattener(fbase=fbase, fkey=fkey,
|
||||||
lineh=line_height,
|
lineh=line_height,
|
||||||
untable=self.output_plugin.file_type in ('mobi','lit'),
|
untable=self.output_plugin.file_type in ('mobi','lit'),
|
||||||
@ -812,10 +817,6 @@ OptionRecommendation(name='language',
|
|||||||
self.opts.insert_blank_line = oibl
|
self.opts.insert_blank_line = oibl
|
||||||
self.opts.remove_paragraph_spacing = orps
|
self.opts.remove_paragraph_spacing = orps
|
||||||
|
|
||||||
if self.opts.linearize_tables and \
|
|
||||||
self.output_plugin.file_type not in ('mobi', 'lrf'):
|
|
||||||
from calibre.ebooks.oeb.transforms.linearize_tables import LinearizeTables
|
|
||||||
LinearizeTables()(self.oeb, self.opts)
|
|
||||||
pr(0.9)
|
pr(0.9)
|
||||||
self.flush()
|
self.flush()
|
||||||
|
|
||||||
|
@ -16,10 +16,16 @@ class MOBIInput(InputFormatPlugin):
|
|||||||
accelerators):
|
accelerators):
|
||||||
from calibre.ebooks.mobi.reader import MobiReader
|
from calibre.ebooks.mobi.reader import MobiReader
|
||||||
from lxml import html
|
from lxml import html
|
||||||
|
parse_cache = {}
|
||||||
|
try:
|
||||||
mr = MobiReader(stream, log, options.input_encoding,
|
mr = MobiReader(stream, log, options.input_encoding,
|
||||||
options.debug_pipeline)
|
options.debug_pipeline)
|
||||||
parse_cache = {}
|
|
||||||
mr.extract_content('.', parse_cache)
|
mr.extract_content('.', parse_cache)
|
||||||
|
except:
|
||||||
|
mr = MobiReader(stream, log, options.input_encoding,
|
||||||
|
options.debug_pipeline, try_extra_data_fix=True)
|
||||||
|
mr.extract_content('.', parse_cache)
|
||||||
|
|
||||||
raw = parse_cache.pop('calibre_raw_mobi_markup', False)
|
raw = parse_cache.pop('calibre_raw_mobi_markup', False)
|
||||||
if raw:
|
if raw:
|
||||||
if isinstance(raw, unicode):
|
if isinstance(raw, unicode):
|
||||||
|
@ -108,7 +108,7 @@ class EXTHHeader(object):
|
|||||||
|
|
||||||
class BookHeader(object):
|
class BookHeader(object):
|
||||||
|
|
||||||
def __init__(self, raw, ident, user_encoding, log):
|
def __init__(self, raw, ident, user_encoding, log, try_extra_data_fix=False):
|
||||||
self.log = log
|
self.log = log
|
||||||
self.compression_type = raw[:2]
|
self.compression_type = raw[:2]
|
||||||
self.records, self.records_size = struct.unpack('>HH', raw[8:12])
|
self.records, self.records_size = struct.unpack('>HH', raw[8:12])
|
||||||
@ -141,7 +141,8 @@ class BookHeader(object):
|
|||||||
self.codec = 'cp1252' if user_encoding is None else user_encoding
|
self.codec = 'cp1252' if user_encoding is None else user_encoding
|
||||||
log.warn('Unknown codepage %d. Assuming %s' % (self.codepage,
|
log.warn('Unknown codepage %d. Assuming %s' % (self.codepage,
|
||||||
self.codec))
|
self.codec))
|
||||||
if ident == 'TEXTREAD' or self.length < 0xE4 or 0xE8 < self.length:
|
if ident == 'TEXTREAD' or self.length < 0xE4 or 0xE8 < self.length \
|
||||||
|
or (try_extra_data_fix and self.length == 0xE4):
|
||||||
self.extra_flags = 0
|
self.extra_flags = 0
|
||||||
else:
|
else:
|
||||||
self.extra_flags, = struct.unpack('>H', raw[0xF2:0xF4])
|
self.extra_flags, = struct.unpack('>H', raw[0xF2:0xF4])
|
||||||
@ -229,7 +230,8 @@ class MobiReader(object):
|
|||||||
PAGE_BREAK_PAT = re.compile(r'(<[/]{0,1}mbp:pagebreak\s*[/]{0,1}>)+', re.IGNORECASE)
|
PAGE_BREAK_PAT = re.compile(r'(<[/]{0,1}mbp:pagebreak\s*[/]{0,1}>)+', re.IGNORECASE)
|
||||||
IMAGE_ATTRS = ('lowrecindex', 'recindex', 'hirecindex')
|
IMAGE_ATTRS = ('lowrecindex', 'recindex', 'hirecindex')
|
||||||
|
|
||||||
def __init__(self, filename_or_stream, log, user_encoding=None, debug=None):
|
def __init__(self, filename_or_stream, log, user_encoding=None, debug=None,
|
||||||
|
try_extra_data_fix=False):
|
||||||
self.log = log
|
self.log = log
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.embedded_mi = None
|
self.embedded_mi = None
|
||||||
@ -284,7 +286,7 @@ class MobiReader(object):
|
|||||||
|
|
||||||
|
|
||||||
self.book_header = BookHeader(self.sections[0][0], self.ident,
|
self.book_header = BookHeader(self.sections[0][0], self.ident,
|
||||||
user_encoding, self.log)
|
user_encoding, self.log, try_extra_data_fix=try_extra_data_fix)
|
||||||
self.name = self.name.decode(self.book_header.codec, 'replace')
|
self.name = self.name.decode(self.book_header.codec, 'replace')
|
||||||
|
|
||||||
def extract_content(self, output_dir, parse_cache):
|
def extract_content(self, output_dir, parse_cache):
|
||||||
@ -701,7 +703,9 @@ class MobiReader(object):
|
|||||||
if self.book_header.ancient and '<html' not in self.mobi_html[:300].lower():
|
if self.book_header.ancient and '<html' not in self.mobi_html[:300].lower():
|
||||||
self.mobi_html = self.mobi_html.replace('\r ', '\n\n ')
|
self.mobi_html = self.mobi_html.replace('\r ', '\n\n ')
|
||||||
self.mobi_html = self.mobi_html.replace('\0', '')
|
self.mobi_html = self.mobi_html.replace('\0', '')
|
||||||
|
if self.book_header.codec == 'cp1252':
|
||||||
self.mobi_html = self.mobi_html.replace('\x1e', '') # record separator
|
self.mobi_html = self.mobi_html.replace('\x1e', '') # record separator
|
||||||
|
self.mobi_html = self.mobi_html.replace('\x02', '') # start of text
|
||||||
return processed_records
|
return processed_records
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,7 +91,6 @@ class Split(object):
|
|||||||
False))
|
False))
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
page_breaks = set([])
|
page_breaks = set([])
|
||||||
for selector, before in self.page_break_selectors:
|
for selector, before in self.page_break_selectors:
|
||||||
body = item.data.xpath('//h:body', namespaces=NAMESPACES)
|
body = item.data.xpath('//h:body', namespaces=NAMESPACES)
|
||||||
@ -382,6 +381,7 @@ class FlowSplitter(object):
|
|||||||
p[i:i+1] = new_pres
|
p[i:i+1] = new_pres
|
||||||
|
|
||||||
split_point, before = self.find_split_point(root)
|
split_point, before = self.find_split_point(root)
|
||||||
|
self.log.debug('\t\t\tSplit point:', split_point.tag, tree.getpath(split_point))
|
||||||
if split_point is None:
|
if split_point is None:
|
||||||
raise SplitError(self.item.href, root)
|
raise SplitError(self.item.href, root)
|
||||||
|
|
||||||
@ -396,6 +396,9 @@ class FlowSplitter(object):
|
|||||||
'\t\t\tCommitted sub-tree #%d (%d KB)'%(
|
'\t\t\tCommitted sub-tree #%d (%d KB)'%(
|
||||||
len(self.split_trees), size/1024.))
|
len(self.split_trees), size/1024.))
|
||||||
else:
|
else:
|
||||||
|
self.log.debug(
|
||||||
|
'\t\t\tSplit tree still too large: %d KB' % \
|
||||||
|
(size/1024.))
|
||||||
self.split_to_size(t)
|
self.split_to_size(t)
|
||||||
|
|
||||||
def find_split_point(self, root):
|
def find_split_point(self, root):
|
||||||
|
@ -78,7 +78,7 @@ class HorizontalBox(object):
|
|||||||
def append(self, t):
|
def append(self, t):
|
||||||
self.texts.append(t)
|
self.texts.append(t)
|
||||||
|
|
||||||
def sort(self):
|
def sort(self, left_margin, right_margin):
|
||||||
self.texts.sort(cmp=lambda x,y: cmp(x.left, y.left))
|
self.texts.sort(cmp=lambda x,y: cmp(x.left, y.left))
|
||||||
self.top, self.bottom = sys.maxint, 0
|
self.top, self.bottom = sys.maxint, 0
|
||||||
for t in self.texts:
|
for t in self.texts:
|
||||||
@ -86,6 +86,27 @@ class HorizontalBox(object):
|
|||||||
self.bottom = max(self.bottom, t.bottom)
|
self.bottom = max(self.bottom, t.bottom)
|
||||||
self.left = self.texts[0].left
|
self.left = self.texts[0].left
|
||||||
self.right = self.texts[-1].right
|
self.right = self.texts[-1].right
|
||||||
|
self.gaps = []
|
||||||
|
for i, t in enumerate(self.texts[1:]):
|
||||||
|
gap = Interval(self.texts[i].right, t.left)
|
||||||
|
if gap.width > 3:
|
||||||
|
self.gaps.append(gap)
|
||||||
|
left = Interval(left_margin, self.texts[0].left)
|
||||||
|
if left.width > 3:
|
||||||
|
self.gaps.insert(0, left)
|
||||||
|
right = Interval(self.texts[-1].right, right_margin)
|
||||||
|
if right.width > 3:
|
||||||
|
self.gaps.append(right)
|
||||||
|
|
||||||
|
def has_intersection_with(self, gap):
|
||||||
|
for g in self.gaps:
|
||||||
|
if g.intersection(gap):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def identify_columns(self, column_gaps):
|
||||||
|
self.number_of_columns = len(column_gaps) + 1
|
||||||
|
|
||||||
|
|
||||||
class Page(object):
|
class Page(object):
|
||||||
|
|
||||||
@ -138,19 +159,24 @@ class Page(object):
|
|||||||
|
|
||||||
|
|
||||||
for hb in self.horizontal_boxes:
|
for hb in self.horizontal_boxes:
|
||||||
hb.sort()
|
hb.sort(self.left_margin, self.right_margin)
|
||||||
|
|
||||||
self.horizontal_boxes.sort(cmp=lambda x,y: cmp(x.bottom, y.bottom))
|
self.horizontal_boxes.sort(cmp=lambda x,y: cmp(x.bottom, y.bottom))
|
||||||
|
|
||||||
def identify_columns(self):
|
def identify_columns(self):
|
||||||
|
|
||||||
def neighborhood(i):
|
def neighborhood(i):
|
||||||
if i == 0:
|
if i == len(self.horizontal_boxes)-1:
|
||||||
return self.horizontal_boxes[1:3]
|
return self.horizontal_boxes[i-2:i]
|
||||||
|
if i == len(self.horizontal_boxes)-2:
|
||||||
return (self.horizontal_boxes[i-1], self.horizontal_boxes[i+1])
|
return (self.horizontal_boxes[i-1], self.horizontal_boxes[i+1])
|
||||||
|
return self.horizontal_boxes[i+1], self.horizontal_boxes[i+2]
|
||||||
|
|
||||||
for i, hbox in enumerate(self.horizontal_boxes):
|
for i, hbox in enumerate(self.horizontal_boxes):
|
||||||
pass
|
n1, n2 = neighborhood(i)
|
||||||
|
for gap in hbox.gaps:
|
||||||
|
gap.is_column_gap = n1.has_intersection_with(gap) and \
|
||||||
|
n2.has_intersection_with(gap)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ def _config():
|
|||||||
help=_('Columns to be displayed in the book list'))
|
help=_('Columns to be displayed in the book list'))
|
||||||
c.add_opt('autolaunch_server', default=False, help=_('Automatically launch content server on application startup'))
|
c.add_opt('autolaunch_server', default=False, help=_('Automatically launch content server on application startup'))
|
||||||
c.add_opt('oldest_news', default=60, help=_('Oldest news kept in database'))
|
c.add_opt('oldest_news', default=60, help=_('Oldest news kept in database'))
|
||||||
c.add_opt('systray_icon', default=True, help=_('Show system tray icon'))
|
c.add_opt('systray_icon', default=False, help=_('Show system tray icon'))
|
||||||
c.add_opt('upload_news_to_device', default=True,
|
c.add_opt('upload_news_to_device', default=True,
|
||||||
help=_('Upload downloaded news to device'))
|
help=_('Upload downloaded news to device'))
|
||||||
c.add_opt('delete_news_from_library_on_upload', default=False,
|
c.add_opt('delete_news_from_library_on_upload', default=False,
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>400</width>
|
<width>422</width>
|
||||||
<height>64</height>
|
<height>64</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@ -30,7 +30,20 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="edit"/>
|
<widget class="HistoryLineEdit" name="edit">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>100</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>350</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
@ -54,8 +67,28 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>HistoryLineEdit</class>
|
||||||
|
<extends>QComboBox</extends>
|
||||||
|
<header>calibre/gui2/widgets.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../../resources/images.qrc"/>
|
<include location="../../../../resources/images.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -70,6 +70,11 @@ class XPathEdit(QWidget, Ui_Edit):
|
|||||||
if wiz.exec_() == wiz.Accepted:
|
if wiz.exec_() == wiz.Accepted:
|
||||||
self.edit.setText(wiz.xpath)
|
self.edit.setText(wiz.xpath)
|
||||||
|
|
||||||
|
def setObjectName(self, *args):
|
||||||
|
QWidget.setObjectName(self, *args)
|
||||||
|
if hasattr(self, 'edit'):
|
||||||
|
self.edit.initialize('xpath_edit_'+unicode(self.objectName()))
|
||||||
|
|
||||||
|
|
||||||
def set_msg(self, msg):
|
def set_msg(self, msg):
|
||||||
self.msg.setText(msg)
|
self.msg.setText(msg)
|
||||||
@ -95,4 +100,11 @@ class XPathEdit(QWidget, Ui_Edit):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from PyQt4.Qt import QApplication
|
||||||
|
app = QApplication([])
|
||||||
|
w = XPathEdit()
|
||||||
|
w.setObjectName('test')
|
||||||
|
w.show()
|
||||||
|
app.exec_()
|
||||||
|
print w.xpath
|
||||||
|
@ -457,6 +457,12 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
|||||||
self.open_config_dir)
|
self.open_config_dir)
|
||||||
self.opt_get_social_metadata.setChecked(config['get_social_metadata'])
|
self.opt_get_social_metadata.setChecked(config['get_social_metadata'])
|
||||||
self.opt_enforce_cpu_limit.setChecked(config['enforce_cpu_limit'])
|
self.opt_enforce_cpu_limit.setChecked(config['enforce_cpu_limit'])
|
||||||
|
self.device_detection_button.clicked.connect(self.debug_device_detection)
|
||||||
|
|
||||||
|
def debug_device_detection(self):
|
||||||
|
from calibre.gui2.dialogs.config.device_debug import DebugDevice
|
||||||
|
d = DebugDevice(self)
|
||||||
|
d.exec_()
|
||||||
|
|
||||||
def open_config_dir(self):
|
def open_config_dir(self):
|
||||||
from calibre.utils.config import config_dir
|
from calibre.utils.config import config_dir
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<string>Preferences</string>
|
<string>Preferences</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon">
|
<property name="windowIcon">
|
||||||
<iconset resource="../../../../work/calibre/resources/images.qrc">
|
<iconset>
|
||||||
<normaloff>:/images/config.svg</normaloff>:/images/config.svg</iconset>
|
<normaloff>:/images/config.svg</normaloff>:/images/config.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout">
|
<layout class="QGridLayout">
|
||||||
@ -148,7 +148,7 @@
|
|||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../../work/calibre/resources/images.qrc">
|
<iconset>
|
||||||
<normaloff>:/images/mimetypes/dir.svg</normaloff>:/images/mimetypes/dir.svg</iconset>
|
<normaloff>:/images/mimetypes/dir.svg</normaloff>:/images/mimetypes/dir.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -285,7 +285,7 @@
|
|||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../../work/calibre/resources/images.qrc">
|
<iconset>
|
||||||
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -309,7 +309,7 @@
|
|||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../../work/calibre/resources/images.qrc">
|
<iconset>
|
||||||
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -473,7 +473,7 @@
|
|||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../../work/calibre/resources/images.qrc">
|
<iconset>
|
||||||
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
<normaloff>:/images/arrow-up.svg</normaloff>:/images/arrow-up.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -497,7 +497,7 @@
|
|||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../../work/calibre/resources/images.qrc">
|
<iconset>
|
||||||
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
<normaloff>:/images/arrow-down.svg</normaloff>:/images/arrow-down.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
@ -557,7 +557,7 @@
|
|||||||
<string>&Add email</string>
|
<string>&Add email</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../../work/calibre/resources/images.qrc">
|
<iconset>
|
||||||
<normaloff>:/images/plus.svg</normaloff>:/images/plus.svg</iconset>
|
<normaloff>:/images/plus.svg</normaloff>:/images/plus.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="iconSize">
|
<property name="iconSize">
|
||||||
@ -584,7 +584,7 @@
|
|||||||
<string>&Remove email</string>
|
<string>&Remove email</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../../work/calibre/resources/images.qrc">
|
<iconset>
|
||||||
<normaloff>:/images/minus.svg</normaloff>:/images/minus.svg</iconset>
|
<normaloff>:/images/minus.svg</normaloff>:/images/minus.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="iconSize">
|
<property name="iconSize">
|
||||||
@ -649,21 +649,21 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" colspan="2">
|
<item row="3" column="0" colspan="2">
|
||||||
<widget class="QPushButton" name="compact_button">
|
<widget class="QPushButton" name="compact_button">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Check database integrity</string>
|
<string>&Check database integrity</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0" colspan="2">
|
<item row="5" column="0" colspan="2">
|
||||||
<widget class="QPushButton" name="button_osx_symlinks">
|
<widget class="QPushButton" name="button_osx_symlinks">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Install command line tools</string>
|
<string>&Install command line tools</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" colspan="2">
|
<item row="4" column="0" colspan="2">
|
||||||
<widget class="QPushButton" name="button_open_config_dir">
|
<widget class="QPushButton" name="button_open_config_dir">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Open calibre &configuration directory</string>
|
<string>Open calibre &configuration directory</string>
|
||||||
@ -677,6 +677,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QPushButton" name="device_detection_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Debug &device detection</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="page_4">
|
<widget class="QWidget" name="page_4">
|
||||||
@ -966,7 +973,7 @@
|
|||||||
<string>...</string>
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="../../../../work/calibre/resources/images.qrc">
|
<iconset>
|
||||||
<normaloff>:/images/document_open.svg</normaloff>:/images/document_open.svg</iconset>
|
<normaloff>:/images/document_open.svg</normaloff>:/images/document_open.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
52
src/calibre/gui2/dialogs/config/device_debug.py
Normal file
52
src/calibre/gui2/dialogs/config/device_debug.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
|
||||||
|
from PyQt4.Qt import QDialog, QVBoxLayout, QPlainTextEdit, QTimer, \
|
||||||
|
QDialogButtonBox, QPushButton, QApplication, QIcon
|
||||||
|
|
||||||
|
class DebugDevice(QDialog):
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QDialog.__init__(self, parent)
|
||||||
|
self._layout = QVBoxLayout(self)
|
||||||
|
self.setLayout(self._layout)
|
||||||
|
self.log = QPlainTextEdit(self)
|
||||||
|
self._layout.addWidget(self.log)
|
||||||
|
self.log.setPlainText(_('Getting debug information')+'...')
|
||||||
|
self.copy = QPushButton(_('Copy to &clipboard'))
|
||||||
|
self.copy.setDefault(True)
|
||||||
|
self.setWindowTitle(_('Debug device detection'))
|
||||||
|
self.setWindowIcon(QIcon(I('debug.svg')))
|
||||||
|
self.copy.clicked.connect(self.copy_to_clipboard)
|
||||||
|
self.ok = QPushButton('&OK')
|
||||||
|
self.ok.setAutoDefault(False)
|
||||||
|
self.ok.clicked.connect(self.accept)
|
||||||
|
self.bbox = QDialogButtonBox(self)
|
||||||
|
self.bbox.addButton(self.copy, QDialogButtonBox.ActionRole)
|
||||||
|
self.bbox.addButton(self.ok, QDialogButtonBox.AcceptRole)
|
||||||
|
self._layout.addWidget(self.bbox)
|
||||||
|
self.resize(750, 500)
|
||||||
|
self.bbox.setEnabled(False)
|
||||||
|
QTimer.singleShot(1000, self.debug)
|
||||||
|
|
||||||
|
def debug(self):
|
||||||
|
try:
|
||||||
|
from calibre.devices import debug
|
||||||
|
raw = debug()
|
||||||
|
self.log.setPlainText(raw)
|
||||||
|
finally:
|
||||||
|
self.bbox.setEnabled(True)
|
||||||
|
|
||||||
|
def copy_to_clipboard(self):
|
||||||
|
QApplication.clipboard().setText(self.log.toPlainText())
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = QApplication([])
|
||||||
|
d = DebugDevice()
|
||||||
|
d.exec_()
|
111
src/calibre/gui2/notify.py
Normal file
111
src/calibre/gui2/notify.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
|
||||||
|
from calibre.constants import islinux
|
||||||
|
|
||||||
|
class Notifier(object):
|
||||||
|
|
||||||
|
DEFAULT_TIMEOUT = 5000
|
||||||
|
|
||||||
|
def get_msg_parms(self, timeout, body, summary):
|
||||||
|
if summary is None:
|
||||||
|
summary = 'calibre'
|
||||||
|
if timeout == 0:
|
||||||
|
timeout = self.DEFAULT_TIMEOUT
|
||||||
|
return timeout, body, summary
|
||||||
|
|
||||||
|
def __call__(self, body, summary=None, replaces_id=None, timeout=0):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class DBUSNotifier(Notifier):
|
||||||
|
|
||||||
|
ICON = I('notify.png')
|
||||||
|
|
||||||
|
def __init__(self, server, path):
|
||||||
|
self.ok, self.err = True, None
|
||||||
|
try:
|
||||||
|
import dbus
|
||||||
|
self.dbus = dbus
|
||||||
|
self._notify = dbus.SessionBus().get_object(server, path)
|
||||||
|
except Exception, err:
|
||||||
|
self.ok = False
|
||||||
|
self.err = str(err)
|
||||||
|
|
||||||
|
|
||||||
|
class KDENotifier(DBUSNotifier):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
DBUSNotifier.__init__(self, 'org.kde.VisualNotifications',
|
||||||
|
'/VisualNotifications')
|
||||||
|
|
||||||
|
def __call__(self, body, summary=None, replaces_id=None, timeout=0):
|
||||||
|
if replaces_id is None:
|
||||||
|
replaces_id = self.dbus.UInt32()
|
||||||
|
event_id = ''
|
||||||
|
timeout, body, summary = self.get_msg_parms(timeout, body, summary)
|
||||||
|
self._notify.Notify('calibre', replaces_id, event_id, self.ICON, summary, body,
|
||||||
|
self.dbus.Array(signature='s'), self.dbus.Dictionary(signature='sv'),
|
||||||
|
timeout)
|
||||||
|
|
||||||
|
class FDONotifier(DBUSNotifier):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
DBUSNotifier.__init__(self, 'org.freedesktop.Notifications',
|
||||||
|
'/org/freedesktop/Notifications')
|
||||||
|
|
||||||
|
def __call__(self, body, summary=None, replaces_id=None, timeout=0):
|
||||||
|
if replaces_id is None:
|
||||||
|
replaces_id = self.dbus.UInt32()
|
||||||
|
timeout, body, summary = self.get_msg_parms(timeout, body, summary)
|
||||||
|
self._notify.Notify('calibre', replaces_id, self.ICON, summary, body,
|
||||||
|
self.dbus.Array(signature='s'), self.dbus.Dictionary(signature='sv'),
|
||||||
|
timeout)
|
||||||
|
|
||||||
|
class QtNotifier(Notifier):
|
||||||
|
|
||||||
|
def __init__(self, systray=None):
|
||||||
|
self.systray = systray
|
||||||
|
self.ok = self.systray is not None and self.systray.supportsMessages()
|
||||||
|
|
||||||
|
def __call__(self, body, summary=None, replaces_id=None, timeout=0):
|
||||||
|
timeout, body, summary = self.get_msg_parms(timeout, body, summary)
|
||||||
|
if self.systray is not None:
|
||||||
|
self.systray.showMessage(summary, body, self.systray.Information,
|
||||||
|
timeout)
|
||||||
|
|
||||||
|
def get_notifier(systray=None):
|
||||||
|
ans = None
|
||||||
|
if islinux:
|
||||||
|
ans = KDENotifier()
|
||||||
|
if not ans.ok:
|
||||||
|
ans = FDONotifier()
|
||||||
|
if not ans.ok:
|
||||||
|
ans = None
|
||||||
|
if ans is None:
|
||||||
|
ans = QtNotifier(systray)
|
||||||
|
if not ans.ok:
|
||||||
|
ans = None
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
n = KDENotifier()
|
||||||
|
n('hello')
|
||||||
|
n = FDONotifier()
|
||||||
|
n('hello')
|
||||||
|
'''
|
||||||
|
from PyQt4.Qt import QApplication, QSystemTrayIcon, QIcon
|
||||||
|
app = QApplication([])
|
||||||
|
ic = QIcon(I('notify.png'))
|
||||||
|
tray = QSystemTrayIcon(ic)
|
||||||
|
tray.setVisible(True)
|
||||||
|
n = QtNotifier(tray)
|
||||||
|
n('hello')
|
||||||
|
'''
|
@ -9,6 +9,7 @@ from calibre import fit_image, preferred_encoding, isosx
|
|||||||
from calibre.gui2 import qstring_to_unicode, config
|
from calibre.gui2 import qstring_to_unicode, config
|
||||||
from calibre.gui2.widgets import IMAGE_EXTENSIONS
|
from calibre.gui2.widgets import IMAGE_EXTENSIONS
|
||||||
from calibre.gui2.progress_indicator import ProgressIndicator
|
from calibre.gui2.progress_indicator import ProgressIndicator
|
||||||
|
from calibre.gui2.notify import get_notifier
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
|
|
||||||
class BookInfoDisplay(QWidget):
|
class BookInfoDisplay(QWidget):
|
||||||
@ -218,6 +219,7 @@ class StatusBar(QStatusBar):
|
|||||||
def __init__(self, jobs_dialog, systray=None):
|
def __init__(self, jobs_dialog, systray=None):
|
||||||
QStatusBar.__init__(self)
|
QStatusBar.__init__(self)
|
||||||
self.systray = systray
|
self.systray = systray
|
||||||
|
self.notifier = get_notifier(systray)
|
||||||
self.movie_button = MovieButton(jobs_dialog)
|
self.movie_button = MovieButton(jobs_dialog)
|
||||||
self.cover_flow_button = CoverFlowButton()
|
self.cover_flow_button = CoverFlowButton()
|
||||||
self.tag_view_button = TagViewButton()
|
self.tag_view_button = TagViewButton()
|
||||||
@ -247,13 +249,13 @@ class StatusBar(QStatusBar):
|
|||||||
|
|
||||||
def showMessage(self, msg, timeout=0):
|
def showMessage(self, msg, timeout=0):
|
||||||
ret = QStatusBar.showMessage(self, msg, timeout)
|
ret = QStatusBar.showMessage(self, msg, timeout)
|
||||||
if self.systray is not None and not config['disable_tray_notification']:
|
if self.notifier is not None and not config['disable_tray_notification']:
|
||||||
if isosx and isinstance(msg, unicode):
|
if isosx and isinstance(msg, unicode):
|
||||||
try:
|
try:
|
||||||
msg = msg.encode(preferred_encoding)
|
msg = msg.encode(preferred_encoding)
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
self.systray.showMessage('calibre', msg, self.systray.Information, 10000)
|
self.notifier(msg)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def jobs(self):
|
def jobs(self):
|
||||||
|
@ -587,20 +587,32 @@ class DocumentView(QWebView):
|
|||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
self.manager.next_document()
|
self.manager.next_document()
|
||||||
else:
|
else:
|
||||||
|
oopos = self.document.ypos
|
||||||
|
#print '\nOriginal position:', oopos
|
||||||
self.document.set_bottom_padding(0)
|
self.document.set_bottom_padding(0)
|
||||||
opos = self.document.ypos
|
opos = self.document.ypos
|
||||||
|
#print 'After set padding=0:', self.document.ypos
|
||||||
|
if opos < oopos:
|
||||||
|
if self.manager is not None:
|
||||||
|
self.manager.next_document()
|
||||||
|
return
|
||||||
lower_limit = opos + delta_y # Max value of top y co-ord after scrolling
|
lower_limit = opos + delta_y # Max value of top y co-ord after scrolling
|
||||||
max_y = self.document.height - window_height # The maximum possible top y co-ord
|
max_y = self.document.height - window_height # The maximum possible top y co-ord
|
||||||
if max_y < lower_limit:
|
if max_y < lower_limit:
|
||||||
|
#print 'Setting padding to:', lower_limit - max_y
|
||||||
self.document.set_bottom_padding(lower_limit - max_y)
|
self.document.set_bottom_padding(lower_limit - max_y)
|
||||||
max_y = self.document.height - window_height
|
max_y = self.document.height - window_height
|
||||||
lower_limit = min(max_y, lower_limit)
|
lower_limit = min(max_y, lower_limit)
|
||||||
|
#print 'Scroll to:', lower_limit
|
||||||
if lower_limit > opos:
|
if lower_limit > opos:
|
||||||
self.document.scroll_to(self.document.xpos, lower_limit)
|
self.document.scroll_to(self.document.xpos, lower_limit)
|
||||||
actually_scrolled = self.document.ypos - opos
|
actually_scrolled = self.document.ypos - opos
|
||||||
|
#print 'After scroll pos:', self.document.ypos
|
||||||
self.find_next_blank_line(window_height - actually_scrolled)
|
self.find_next_blank_line(window_height - actually_scrolled)
|
||||||
|
#print 'After blank line pos:', self.document.ypos
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
self.manager.scrolled(self.scroll_fraction)
|
self.manager.scrolled(self.scroll_fraction)
|
||||||
|
#print 'After all:', self.document.ypos
|
||||||
|
|
||||||
def scroll_by(self, x=0, y=0, notify=True):
|
def scroll_by(self, x=0, y=0, notify=True):
|
||||||
old_pos = self.document.ypos
|
old_pos = self.document.ypos
|
||||||
|
@ -11,7 +11,7 @@ from PyQt4.Qt import QListView, QIcon, QFont, QLabel, QListWidget, \
|
|||||||
QAbstractListModel, QVariant, Qt, SIGNAL, \
|
QAbstractListModel, QVariant, Qt, SIGNAL, \
|
||||||
QRegExp, QSettings, QSize, QModelIndex, \
|
QRegExp, QSettings, QSize, QModelIndex, \
|
||||||
QAbstractButton, QPainter, QLineEdit, QComboBox, \
|
QAbstractButton, QPainter, QLineEdit, QComboBox, \
|
||||||
QMenu, QStringListModel, QCompleter
|
QMenu, QStringListModel, QCompleter, QStringList
|
||||||
|
|
||||||
from calibre.gui2 import human_readable, NONE, TableView, \
|
from calibre.gui2 import human_readable, NONE, TableView, \
|
||||||
qstring_to_unicode, error_dialog
|
qstring_to_unicode, error_dialog
|
||||||
@ -21,9 +21,11 @@ from calibre import fit_image
|
|||||||
from calibre.utils.fonts import fontconfig
|
from calibre.utils.fonts import fontconfig
|
||||||
from calibre.ebooks import BOOK_EXTENSIONS
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
from calibre.ebooks.metadata.meta import metadata_from_filename
|
from calibre.ebooks.metadata.meta import metadata_from_filename
|
||||||
from calibre.utils.config import prefs
|
from calibre.utils.config import prefs, XMLConfig
|
||||||
from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator
|
from calibre.gui2.progress_indicator import ProgressIndicator as _ProgressIndicator
|
||||||
|
|
||||||
|
history = XMLConfig('history')
|
||||||
|
|
||||||
class ProgressIndicator(QWidget):
|
class ProgressIndicator(QWidget):
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
@ -506,16 +508,16 @@ class LineEditECM(object):
|
|||||||
menu.exec_(event.globalPos())
|
menu.exec_(event.globalPos())
|
||||||
|
|
||||||
def upper_case(self):
|
def upper_case(self):
|
||||||
self.setText(qstring_to_unicode(self.text()).upper())
|
self.setText(unicode(self.text()).upper())
|
||||||
|
|
||||||
def lower_case(self):
|
def lower_case(self):
|
||||||
self.setText(qstring_to_unicode(self.text()).lower())
|
self.setText(unicode(self.text()).lower())
|
||||||
|
|
||||||
def swap_case(self):
|
def swap_case(self):
|
||||||
self.setText(qstring_to_unicode(self.text()).swapcase())
|
self.setText(unicode(self.text()).swapcase())
|
||||||
|
|
||||||
def title_case(self):
|
def title_case(self):
|
||||||
self.setText(qstring_to_unicode(self.text()).title())
|
self.setText(unicode(self.text()).title())
|
||||||
|
|
||||||
|
|
||||||
class EnLineEdit(LineEditECM, QLineEdit):
|
class EnLineEdit(LineEditECM, QLineEdit):
|
||||||
@ -620,7 +622,7 @@ class EnComboBox(QComboBox):
|
|||||||
self.setLineEdit(EnLineEdit(self))
|
self.setLineEdit(EnLineEdit(self))
|
||||||
|
|
||||||
def text(self):
|
def text(self):
|
||||||
return qstring_to_unicode(self.currentText())
|
return unicode(self.currentText())
|
||||||
|
|
||||||
def setText(self, text):
|
def setText(self, text):
|
||||||
idx = self.findText(text, Qt.MatchFixedString)
|
idx = self.findText(text, Qt.MatchFixedString)
|
||||||
@ -629,6 +631,43 @@ class EnComboBox(QComboBox):
|
|||||||
idx = 0
|
idx = 0
|
||||||
self.setCurrentIndex(idx)
|
self.setCurrentIndex(idx)
|
||||||
|
|
||||||
|
class HistoryLineEdit(QComboBox):
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
QComboBox.__init__(self, *args)
|
||||||
|
self.setEditable(True)
|
||||||
|
self.setInsertPolicy(self.NoInsert)
|
||||||
|
self.setMaxCount(10)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def store_name(self):
|
||||||
|
return 'lineedit_history_'+self._name
|
||||||
|
|
||||||
|
def initialize(self, name):
|
||||||
|
self._name = name
|
||||||
|
self.addItems(QStringList(history.get(self.store_name, [])))
|
||||||
|
self.setEditText('')
|
||||||
|
self.lineEdit().editingFinished.connect(self.save_history)
|
||||||
|
|
||||||
|
def save_history(self):
|
||||||
|
items = []
|
||||||
|
ct = unicode(self.currentText())
|
||||||
|
if ct:
|
||||||
|
items.append(ct)
|
||||||
|
for i in range(self.count()):
|
||||||
|
item = unicode(self.itemText(i))
|
||||||
|
if item not in items:
|
||||||
|
items.append(item)
|
||||||
|
|
||||||
|
history.set(self.store_name, items)
|
||||||
|
|
||||||
|
def setText(self, t):
|
||||||
|
self.setEditText(t)
|
||||||
|
self.lineEdit().setCursorPosition(0)
|
||||||
|
|
||||||
|
def text(self):
|
||||||
|
return self.currentText()
|
||||||
|
|
||||||
class PythonHighlighter(QSyntaxHighlighter):
|
class PythonHighlighter(QSyntaxHighlighter):
|
||||||
|
|
||||||
Rules = []
|
Rules = []
|
||||||
|
@ -81,7 +81,7 @@ Device Integration
|
|||||||
|
|
||||||
What devices does |app| support?
|
What devices does |app| support?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
At the moment |app| has full support for the SONY PRS 300/500/505/600/700, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Netronix EB600, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Android phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
|
At the moment |app| has full support for the SONY PRS 300/500/505/600/700, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Netronix EB600, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Italica, various Android phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
|
||||||
|
|
||||||
How can I help get my device supported in |app|?
|
How can I help get my device supported in |app|?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -91,17 +91,14 @@ We just need some information from you:
|
|||||||
|
|
||||||
* What e-book formats does your device support?
|
* What e-book formats does your device support?
|
||||||
* Is there a special directory on the device in which all e-book files should be placed?
|
* Is there a special directory on the device in which all e-book files should be placed?
|
||||||
* We also need the output from running the following command in a terminal, both with the device
|
* We also need information about your device that |app| will collect automatically. First, if your
|
||||||
connected and without::
|
device supports SD cards, insert them. Then connect your device. In calibre go to Preferences->Advanced
|
||||||
|
and click the "Debug device detection" button. This will create some debug output. Copy it to a file
|
||||||
|
and repeat the process, this time with your device disconnected.
|
||||||
|
* Send both the above outputs to us with the other information and we will write a device driver for your
|
||||||
|
device.
|
||||||
|
|
||||||
calibre-debug -d
|
Once you send us the output for a particular operating system, support for the device in that operating system
|
||||||
|
|
||||||
* If your device supports SD cards, run the above command with the cards inserted.
|
|
||||||
|
|
||||||
To run the above command, on Windows you should use the full path to calibre-debug.exe
|
|
||||||
On OSX, you should go to Preferences->Advanced and click "Install command line tools".
|
|
||||||
|
|
||||||
Once you send us the output for a particular operating system, support for the device
|
|
||||||
will appear in the next release of |app|.
|
will appear in the next release of |app|.
|
||||||
|
|
||||||
|
|
||||||
@ -124,6 +121,11 @@ If you do need to reset your metadata due to problems caused by using both
|
|||||||
at the same time, then just delete the media.xml file on the Reader using
|
at the same time, then just delete the media.xml file on the Reader using
|
||||||
your PC's file explorer and it will be recreated after disconnection.
|
your PC's file explorer and it will be recreated after disconnection.
|
||||||
|
|
||||||
|
With recent reader iterations, SONY, in all its wisdom has decided to try to force you to
|
||||||
|
use their software. If you install it, it auto-launches whenever you connect the reader.
|
||||||
|
If you don't want to uninstall it altogether, there are a couple of tricks you can use. The
|
||||||
|
simplest is to simply re-name the executable file that launches the library program. More detail
|
||||||
|
`here http://www.mobileread.com/forums/showthread.php?t=65809`_.
|
||||||
|
|
||||||
Can I use the collections feature of the SONY reader?
|
Can I use the collections feature of the SONY reader?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -194,7 +194,8 @@ class RecursiveFetcher(object):
|
|||||||
purl[i] = quote(purl[i])
|
purl[i] = quote(purl[i])
|
||||||
url = urlparse.urlunparse(purl)
|
url = urlparse.urlunparse(purl)
|
||||||
try:
|
try:
|
||||||
with closing(self.browser.open_novisit(url, timeout=self.timeout)) as f:
|
open_func = getattr(self.browser, 'open_novisit', self.browser.open)
|
||||||
|
with closing(open_func(url, timeout=self.timeout)) as f:
|
||||||
data = response(f.read()+f.read())
|
data = response(f.read()+f.read())
|
||||||
data.newurl = f.geturl()
|
data.newurl = f.geturl()
|
||||||
except urllib2.URLError, err:
|
except urllib2.URLError, err:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user