Merge from trunk

This commit is contained in:
Charles Haley 2010-12-31 06:13:06 +00:00
commit 81900219a2
50 changed files with 700 additions and 222 deletions

View File

@ -4,6 +4,11 @@ License: GPL-3
The full text of the GPL is distributed as in
/usr/share/common-licenses/GPL-3 on Debian systems.
Files: src/calibre/ebooks/pdf/*.h,*.cpp
License: GPL-2 or later
The full text of the GPL is distributed as in
/usr/share/common-licenses/GPL-2 on Debian systems.
Files: src/calibre/ebooks/BeautifulSoup.py
Copyright: Copyright (c) 2004-2007, Leonard Richardson
License: BSD

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,69 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
'''
www.businessinsider.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
class Business_insider(BasicNewsRecipe):
title = 'Business Insider'
__author__ = 'Darko Miletic'
description = 'Noticias de Argentina y el resto del mundo'
publisher = 'Business Insider, Inc.'
category = 'news, politics, finances, world'
oldest_article = 2
max_articles_per_feed = 200
no_stylesheets = True
encoding = 'utf8'
use_embedded_content = True
language = 'en'
remove_empty_feeds = True
publication_type = 'newsportal'
masthead_url = 'http://static.businessinsider.com/assets/images/logos/tbi_print.jpg'
extra_css = """
body{font-family: Arial,Helvetica,sans-serif }
img{margin-bottom: 0.4em; display:block}
"""
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
remove_tags = [
dict(name=['meta','link'])
,dict(attrs={'class':'feedflare'})
]
remove_attributes=['lang','border']
feeds = [
(u'Latest' , u'http://feeds2.feedburner.com/businessinsider' )
,(u'Markets' , u'http://feeds.feedburner.com/TheMoneyGame' )
,(u'Wall Street' , u'http://feeds.feedburner.com/clusterstock' )
,(u'Tech' , u'http://feeds.feedburner.com/typepad/alleyinsider/silicon_alley_insider')
,(u'The Wire' , u'http://feeds.feedburner.com/businessinsider/thewire' )
,(u'War Room' , u'http://feeds.feedburner.com/businessinsider/warroom' )
,(u'Sports' , u'http://feeds.feedburner.com/businessinsider/sportspage' )
,(u'Tools' , u'http://feeds.feedburner.com/businessinsider/tools' )
,(u'Travel' , u'http://feeds.feedburner.com/businessinsider/travel' )
]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
for item in soup.findAll('a'):
if item['href'].startswith('http://feedads'):
item.extract()
else:
if item.string is not None:
tstr = item.string
item.replaceWith(tstr)
for item in soup.findAll('img'):
if not item.has_key('alt'):
item['alt'] = 'image'
return soup

View File

@ -0,0 +1,109 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__license__ = 'GPL v3'
__copyright__ = '04 December 2010, desUBIKado'
__author__ = 'desUBIKado'
__description__ = 'Daily newspaper from Aragon'
__version__ = 'v0.05'
__date__ = '07, December 2010'
'''
elperiodicodearagon.com
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class elperiodicodearagon(BasicNewsRecipe):
title = u'El Periodico de Aragon'
__author__ = u'desUBIKado'
description = u'Noticias desde Aragon'
publisher = u'elperiodicodearagon.com'
category = u'news, politics, Spain, Aragon'
oldest_article = 2
delay = 0
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
language = 'es'
encoding = 'utf8'
remove_empty_feeds = True
remove_javascript = True
conversion_options = {
'comments' : description
,'tags' : category
,'language' : language
,'publisher' : publisher
}
feeds = [(u'Arag\xf3n', u'http://elperiodicodearagon.com/RSS/2.xml'),
(u'Internacional', u'http://elperiodicodearagon.com/RSS/4.xml'),
(u'Espa\xf1a', u'http://elperiodicodearagon.com/RSS/3.xml'),
(u'Econom\xeda', u'http://elperiodicodearagon.com/RSS/5.xml'),
(u'Deportes', u'http://elperiodicodearagon.com/RSS/7.xml'),
(u'Real Zaragoza', u'http://elperiodicodearagon.com/RSS/10.xml'),
(u'Opini\xf3n', u'http://elperiodicodearagon.com/RSS/103.xml'),
(u'Escenarios', u'http://elperiodicodearagon.com/RSS/105.xml'),
(u'Sociedad', u'http://elperiodicodearagon.com/RSS/104.xml'),
(u'Gente', u'http://elperiodicodearagon.com/RSS/330.xml')]
extra_css = '''
h3{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:xx-large;}
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
dd{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
'''
remove_attributes = ['height','width']
keep_only_tags = [dict(name='div', attrs={'id':'contenidos'})]
# Quitar toda la morralla
remove_tags = [dict(name='ul', attrs={'class':'herramientasDeNoticia'}),
dict(name='span', attrs={'class':'MasInformacion '}),
dict(name='span', attrs={'class':'MasInformacion'}),
dict(name='div', attrs={'class':'Middle'}),
dict(name='div', attrs={'class':'MenuCabeceraRZaragoza'}),
dict(name='div', attrs={'id':'MenuCabeceraRZaragoza'}),
dict(name='div', attrs={'class':'MenuEquipo'}),
dict(name='div', attrs={'class':'TemasRelacionados'}),
dict(name='div', attrs={'class':'GaleriaEnNoticia'}),
dict(name='div', attrs={'class':'Recorte'}),
dict(name='div', attrs={'id':'NoticiasenRecursos'}),
dict(name='div', attrs={'id':'NoticiaEnPapel'}),
dict(name='p', attrs={'class':'RecorteEnNoticias'}),
dict(name='div', attrs={'id':'Comparte'}),
dict(name='div', attrs={'id':'CajaComparte'}),
dict(name='a', attrs={'class':'EscribirComentario'}),
dict(name='a', attrs={'class':'AvisoComentario'}),
dict(name='div', attrs={'class':'CajaAvisoComentario'}),
dict(name='div', attrs={'class':'navegaNoticias'}),
dict(name='div', attrs={'id':'PaginadorDiCom'}),
dict(name='div', attrs={'id':'CajaAccesoCuentaUsuario'}),
dict(name='div', attrs={'id':'CintilloComentario'}),
dict(name='div', attrs={'id':'EscribeComentario'}),
dict(name='div', attrs={'id':'FormularioComentario'}),
dict(name='div', attrs={'id':'FormularioNormas'})]
# Recuperamos la portada de papel (la imagen format=1 tiene mayor resolucion)
def get_cover_url(self):
index = 'http://pdf.elperiodicodearagon.com/'
soup = self.index_to_soup(index)
for image in soup.findAll('img',src=True):
if image['src'].startswith('http://pdf.elperiodicodearagon.com/funciones/portada-preview.php?eid='):
return image['src'].rstrip('format=2') + 'format=1'
return None
# Para quitar espacios entre la noticia y los comentarios (lineas 1 y 2)
# El indice no apuntaba correctamente al empiece de la noticia (linea 3)
preprocess_regexps = [
(re.compile(r'<p>&nbsp;</p>', re.DOTALL|re.IGNORECASE), lambda match: ''),
(re.compile(r'<p> </p>', re.DOTALL|re.IGNORECASE), lambda match: ''),
(re.compile(r'<p id="">', re.DOTALL|re.IGNORECASE), lambda match: '<p>')
]

View File

@ -1,86 +1,95 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
'''
www.elpais.com/diario/
www.elpais.com
'''
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
class ElPaisImpresa(BasicNewsRecipe):
title = u'El Pa\xeds - edicion impresa'
class ElPais_RSS(BasicNewsRecipe):
title = 'El Pais'
__author__ = 'Darko Miletic'
description = u'el periodico global en Espa\xf1ol'
description = 'el periodico global en Castellano'
publisher = 'EDICIONES EL PAIS, S.L.'
category = 'news, politics,Spain,actualidad,noticias,informacion,videos,fotografias,audios,graficos,nacional,internacional,deportes,economia,tecnologia,cultura,gente,television,sociedad,opinion,blogs,foros,chats,encuestas,entrevistas,participacion'
category = 'news, politics, finances, world, spain'
oldest_article = 2
max_articles_per_feed = 200
no_stylesheets = True
encoding = 'latin1'
encoding = 'cp1252'
use_embedded_content = False
language = 'es'
language = 'es_ES'
remove_empty_feeds = True
publication_type = 'newspaper'
masthead_url = 'http://www.elpais.com/im/tit_logo_global.gif'
index = 'http://www.elpais.com/diario/'
extra_css = ' p{text-align: justify} body{ text-align: left; font-family: Georgia,"Times New Roman",Times,serif } h2{font-family: Arial,Helvetica,sans-serif} img{margin-bottom: 0.4em} '
masthead_url = 'http://www.elpais.com/im/tit_logo.gif'
extra_css = """
body{font-family: Georgia,"Times New Roman",Times,serif }
h3{font-family: Arial,Helvetica,sans-serif}
img{margin-bottom: 0.4em; display:block}
"""
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
feeds = [
(u'Internacional' , index + u'internacional/' )
,(u'Espa\xf1a' , index + u'espana/' )
,(u'Economia' , index + u'economia/' )
,(u'Opinion' , index + u'opinion/' )
,(u'Vi\xf1etas' , index + u'vineta/' )
,(u'Sociedad' , index + u'sociedad/' )
,(u'Cultura' , index + u'cultura/' )
,(u'Tendencias' , index + u'tendencias/' )
,(u'Gente' , index + u'gente/' )
,(u'Obituarios' , index + u'obituarios/' )
,(u'Deportes' , index + u'deportes/' )
,(u'Pantallas' , index + u'radioytv/' )
,(u'Ultima' , index + u'ultima/' )
,(u'Educacion' , index + u'educacion/' )
,(u'Saludo' , index + u'salud/' )
,(u'Ciberpais' , index + u'ciberpais/' )
,(u'EP3' , index + u'ep3/' )
,(u'Cine' , index + u'cine/' )
,(u'Babelia' , index + u'babelia/' )
,(u'El viajero' , index + u'viajero/' )
,(u'Negocios' , index + u'negocios/' )
,(u'Domingo' , index + u'domingo/' )
,(u'El Pais semanal' , index + u'eps/' )
,(u'Quadern Catalunya' , index + u'quadern-catalunya/' )
]
keep_only_tags = [dict(attrs={'class':['cabecera_noticia estirar','cabecera_noticia','','contenido_noticia']})]
remove_tags = [
dict(name=['meta','link','base','iframe','embed','object'])
,dict(attrs={'class':['info_complementa','estructura_2col_der','votos estirar','votos']})
,dict(attrs={'id':'utilidades'})
]
remove_tags_after = dict(attrs={'id':'utilidades'})
remove_attributes = ['lang','border','width','height']
keep_only_tags=[dict(attrs={'class':['cabecera_noticia','contenido_noticia']})]
remove_attributes=['width','height']
remove_tags=[dict(name='link')]
def parse_index(self):
totalfeeds = []
lfeeds = self.get_feeds()
for feedobj in lfeeds:
feedtitle, feedurl = feedobj
self.report_progress(0, _('Fetching feed')+' %s...'%(feedtitle if feedtitle else feedurl))
articles = []
soup = self.index_to_soup(feedurl)
for item in soup.findAll('a',attrs={'class':['g19r003','g19i003','g17r003','g17i003']}):
url = 'http://www.elpais.com' + item['href'].rpartition('/')[0]
title = self.tag_to_string(item)
date = strftime(self.timefmt)
articles.append({
'title' :title
,'date' :date
,'url' :url
,'description':''
})
totalfeeds.append((feedtitle, articles))
return totalfeeds
feeds = [
(u'Lo ultimo' , u'http://www.elpais.com/rss/feed.html?feedId=17046')
,(u'America Latina' , u'http://www.elpais.com/rss/feed.html?feedId=17041')
,(u'Mexico' , u'http://www.elpais.com/rss/feed.html?feedId=17042')
,(u'Europa' , u'http://www.elpais.com/rss/feed.html?feedId=17043')
,(u'Estados Unidos' , u'http://www.elpais.com/rss/feed.html?feedId=17044')
,(u'Oriente proximo' , u'http://www.elpais.com/rss/feed.html?feedId=17045')
,(u'Espana' , u'http://www.elpais.com/rss/feed.html?feedId=1002' )
,(u'Andalucia' , u'http://www.elpais.com/rss/feed.html?feedId=17057')
,(u'Catalunia' , u'http://www.elpais.com/rss/feed.html?feedId=17059')
,(u'Comunidad Valenciana' , u'http://www.elpais.com/rss/feed.html?feedId=17061')
,(u'Madrid' , u'http://www.elpais.com/rss/feed.html?feedId=1016' )
,(u'Pais Vasco' , u'http://www.elpais.com/rss/feed.html?feedId=17062')
,(u'Galicia' , u'http://www.elpais.com/rss/feed.html?feedId=17063')
,(u'Opinion' , u'http://www.elpais.com/rss/feed.html?feedId=1003' )
,(u'Sociedad' , u'http://www.elpais.com/rss/feed.html?feedId=1004' )
,(u'Deportes' , u'http://www.elpais.com/rss/feed.html?feedId=1007' )
,(u'Cultura' , u'http://www.elpais.com/rss/feed.html?feedId=1008' )
,(u'Cine' , u'http://www.elpais.com/rss/feed.html?feedId=17052')
,(u'Literatura' , u'http://www.elpais.com/rss/feed.html?feedId=17053')
,(u'Musica' , u'http://www.elpais.com/rss/feed.html?feedId=17051')
,(u'Arte' , u'http://www.elpais.com/rss/feed.html?feedId=17060')
,(u'Tecnologia' , u'http://www.elpais.com/rss/feed.html?feedId=1005' )
,(u'Economia' , u'http://www.elpais.com/rss/feed.html?feedId=1006' )
,(u'Ciencia' , u'http://www.elpais.com/rss/feed.html?feedId=17068')
,(u'Salud' , u'http://www.elpais.com/rss/feed.html?feedId=17074')
,(u'Ocio' , u'http://www.elpais.com/rss/feed.html?feedId=17075')
,(u'Justicia y Leyes' , u'http://www.elpais.com/rss/feed.html?feedId=17069')
,(u'Guerras y conflictos' , u'http://www.elpais.com/rss/feed.html?feedId=17070')
,(u'Politica' , u'http://www.elpais.com/rss/feed.html?feedId=17073')
]
def print_version(self, url):
return url + '?print=1'
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
for item in soup.findAll('a'):
if item.string is not None:
tstr = item.string
item.replaceWith(tstr)
else:
item.name='span'
for atrs in ['href','target','alt','title']:
if item.has_key(atrs):
del item[atrs]
for item in soup.findAll('img',alt=False):
item['alt'] = 'image'
return soup

View File

@ -1,50 +1,65 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__author__ = 'Lorenzo Vigentini'
__copyright__ = '2009, Lorenzo Vigentini <l.vigentini at gmail.com>'
__license__ = 'GPL v3'
__copyright__ = '04 December 2010, desUBIKado'
__author__ = 'desUBIKado'
__description__ = 'Daily newspaper from Aragon'
__version__ = 'v1.01'
__date__ = '30, January 2010'
__version__ = 'v0.03'
__date__ = '11, December 2010'
'''
http://www.heraldo.es/
[url]http://www.heraldo.es/[/url]
'''
import time
from calibre.web.feeds.news import BasicNewsRecipe
class heraldo(BasicNewsRecipe):
author = 'Lorenzo Vigentini'
__author__ = 'desUBIKado'
description = 'Daily newspaper from Aragon'
cover_url = 'http://www.heraldo.es/MODULOS/global/publico/interfaces/img/logo.gif'
title = u'Heraldo de Aragon'
publisher = 'OJD Nielsen'
category = 'News, politics, culture, economy, general interest'
language = 'es'
timefmt = '[%a, %d %b, %Y]'
oldest_article = 1
max_articles_per_feed = 25
max_articles_per_feed = 100
use_embedded_content = False
recursion = 10
remove_javascript = True
no_stylesheets = True
keep_only_tags = [
dict(name='div', attrs={'class':['titularNoticiaNN','textoGrisVerdanaContenidos']})
]
recursion = 10
feeds = [
(u'Portadas ', u'http://www.heraldo.es/index.php/mod.portadas/mem.rss')
]
(u'Portadas', u'http://www.heraldo.es/index.php/mod.portadas/mem.rss')
]
keep_only_tags = [dict(name='div', attrs={'id':['dts','com']})]
remove_tags = [dict(name='a', attrs={'class':['com flo-r','enl-if','enl-df']}),
dict(name='div', attrs={'class':['brb-b-s con marg-btt','cnt-rel con']}),
dict(name='form', attrs={'class':'form'})]
remove_tags_before = dict(name='div' , attrs={'id':'dts'})
remove_tags_after = dict(name='div' , attrs={'id':'com'})
def get_cover_url(self):
cover = None
st = time.localtime()
year = str(st.tm_year)
month = "%.2d" % st.tm_mon
day = "%.2d" % st.tm_mday
#[url]http://oldorigin-www.heraldo.es/20101211/primeras/portada_aragon.pdf[/url]
cover='http://oldorigin-www.heraldo.es/'+ year + month + day +'/primeras/portada_aragon.pdf'
br = BasicNewsRecipe.get_browser()
try:
br.open(cover)
except:
self.log("\nPortada no disponible")
cover ='http://www.heraldo.es/MODULOS/global/publico/interfaces/img/logo-Heraldo.png'
return cover
extra_css = '''
.articledate {color: gray;font-family: monospace;}
.articledescription {display: block;font-family: sans;font-size: 0.7em; text-indent: 0;}
.firma {color: #666;display: block;font-family: verdana, arial, helvetica;font-size: 1em;margin-bottom: 8px;}
.textoGrisVerdanaContenidos {color: #56595c;display: block;font-family: Verdana;font-size: 1.28571em;padding-bottom: 10px}
.titularNoticiaNN {display: block;padding-bottom: 10px;padding-left: 0;padding-right: 0;padding-top: 4px}
.titulo {color: #003066;font-family: Tahoma;font-size: 1.92857em;font-weight: bold;line-height: 1.2em}
'''
h2{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:xx-large;}
'''

View File

@ -0,0 +1,52 @@
import re
from calibre.web.feeds.news import BasicNewsRecipe
class KANewsRecipe(BasicNewsRecipe):
title = u'KA-News.de'
description = u'Nachrichten aus Karlsruhe, Deutschland und der Welt.'
__author__ = 'tfeld'
lang='de'
no_stylesheets = True
oldest_article = 7
max_articles_per_feed = 100
feeds = [
(u'News aus Karlsruhe', 'http://www.ka-news.de/storage/rss/rss/karlsruhe.xml'),
(u'Kulturnachrichten aus Karlsruhe', 'http://www.ka-news.de/storage/rss/rss/kultur.xml'),
(u'Durlach: News aus Durlach', 'http://www.ka-news.de/storage/rss/rss/durlach.xml'),
(u'Stutensee: News aus Stutensee Blankenloch, Büchig, Friedrichstal, Staffort, Spöck', 'http://www.ka-news.de/storage/rss/rss/stutensee.xml'),
(u'Bruchsal: News aus Bruchsal', 'http://www.ka-news.de/storage/rss/rss/bruchsal.xml'),
(u'Wirtschaftsnews aus Karlsruhe', 'http://www.ka-news.de/storage/rss/rss/wirtschaft.xml'),
(u'ka-news.de - Sport', 'http://www.ka-news.de/storage/rss/rss/sport.xml'),
(u'KSC-News - News rund um den KSC', 'http://www.ka-news.de/storage/rss/rss/ksc.xml'),
(u'ka-news.de - BG Karlsruhe', 'http://www.ka-news.de/storage/rss/rss/basketball.xml')
]
preprocess_regexps = [
(re.compile(r'width:[0-9]*?px', re.DOTALL|re.IGNORECASE), lambda match: ''),
]
remove_tags_before = dict(id='artdetail_ueberschrift')
remove_tags_after = dict(id='artdetail_unterzeile')
remove_tags = [dict(name=['div'], attrs={'class': 'lbx_table'}),
dict(name=['div'], attrs={'class': 'lk_zumthema'}),
dict(name=['div'], attrs={'class': 'lk_thumb'}),
dict(name=['div'], attrs={'class': 'lk_trenner'}),
dict(name=['div'], attrs={'class': 'lupen_container'}),
dict(name=['script']),
dict(name=['span'], attrs={'style': 'display:none;'}),
dict(name=['span'], attrs={'class': 'comm_info'}),
dict(name=['h3'], attrs={'id': 'artdetail_unterzeile'})]
# removing style attribute _after_ removing specifig tags above
remove_attributes = ['width','height','style']
extra_css = '''
h1{ font-size:large; font-weight:bold; }
h2{ font-size:medium; font-weight:bold; }
'''
def get_cover_url(self):
return 'http://www.ka-news.de/storage/scl/techkanews/logos/434447_m1t1w250q75s1v29681_ka-news-Logo_mit_Schatten_transparent.png'

View File

@ -0,0 +1,47 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '11 December 2010, desUBIKado'
__author__ = 'desUBIKado'
__description__ = 'Entertainment guide from Aragon'
__version__ = 'v0.01'
__date__ = '11, December 2010'
'''
[url]http://www.redaragon.es/[/url]
'''
from calibre.web.feeds.news import BasicNewsRecipe
class heraldo(BasicNewsRecipe):
__author__ = 'desUBIKado'
description = u'Guia de ocio desde Aragon'
title = u'RedAragon'
publisher = 'Grupo Z'
category = 'Concerts, Movies, Entertainment news'
cover_url = 'http://www.redaragon.com/2008_img/logotipo.gif'
language = 'es'
timefmt = '[%a, %d %b, %Y]'
oldest_article = 15
max_articles_per_feed = 100
encoding = 'iso-8859-1'
use_embedded_content = False
remove_javascript = True
no_stylesheets = True
feeds = [(u'Conciertos', u'http://redaragon.com/rss/agenda.asp?tid=1'),
(u'Exposiciones', u'http://redaragon.com/rss/agenda.asp?tid=5'),
(u'Teatro', u'http://redaragon.com/rss/agenda.asp?tid=10'),
(u'Conferencias', u'http://redaragon.com/rss/agenda.asp?tid=2'),
(u'Ferias', u'http://redaragon.com/rss/agenda.asp?tid=6'),
(u'Filmotecas/Cineclubs', u'http://redaragon.com/rss/agenda.asp?tid=7'),
(u'Presentaciones', u'http://redaragon.com/rss/agenda.asp?tid=9'),
(u'Fiestas', u'http://redaragon.com/rss/agenda.asp?tid=11'),
(u'Infantil', u'http://redaragon.com/rss/agenda.asp?tid=13'),
(u'Otros', u'http://redaragon.com/rss/agenda.asp?tid=8')]
keep_only_tags = [dict(name='div', attrs={'id':'FichaEventoAgenda'})]
remove_tags = [dict(name='div', attrs={'class':['Comparte','CajaAgenda','Caja','Cintillo']})]
remove_tags_before = dict(name='div' , attrs={'id':'FichaEventoAgenda'})
remove_tags_after = dict(name='div' , attrs={'class':'Cintillo'})

View File

@ -25,22 +25,20 @@ class Salon_com(BasicNewsRecipe):
feeds = [
('News & Politics', 'http://feeds.salon.com/salon/news'),
('War Room', 'http://feeds.salon.com/salon/war_room'),
('Arts & Entertainment', 'http://feeds.salon.com/salon/ent'),
('I Like to Watch', 'http://feeds.salon.com/salon/iltw'),
('Beyond Multiplex', 'http://feeds.salon.com/salon/btm'),
('Book Reviews', 'http://feeds.salon.com/salon/books'),
('All Life', 'http://feeds.salon.com/salon/mwt'),
('All Opinion', 'http://feeds.salon.com/salon/opinion'),
('Glenn Greenwald', 'http://feeds.salon.com/salon/greenwald'),
('Garrison Keillor', 'http://dir.salon.com/topics/garrison_keillor/index.rss'),
('Joan Walsh', 'http://www.salon.com/rss/walsh.rss'),
('All Sports', 'http://feeds.salon.com/salon/sports'),
('War Room', 'http://feeds.feedburner.com/salon/war_room'),
('Joan Walsh', 'http://feeds.feedburner.com/Salon_Joan_Walsh'),
('Glenn Greenwald', 'http://feeds.feedburner.com/salon/greenwald'),
('Tech & Business', 'http://feeds.salon.com/salon/tech'),
('How World Works', 'http://feeds.salon.com/salon/htww')
('Ask the Pilot', 'http://feeds.feedburner.com/salon/ask_the_pilot'),
('How World Works', 'http://feeds.feedburner.com/salon/htww'),
('Life', 'http://feeds.feedburner.com/salon/mwt'),
('Broadsheet', 'http://feeds.feedburner.com/salon/broadsheet'),
('Movie Reviews', 'http://feeds.feedburner.com/salon/movie_reviews'),
('Film Salon', 'http://feeds.feedburner.com/Salon/Film_Salon'),
('TV', 'http://feeds.feedburner.com/salon/tv'),
('Books', 'http://feeds.feedburner.com/salon/books')
]
def print_version(self, url):
return url.replace('/index.html', '/print.html')

View File

@ -1,17 +1,19 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
__copyright__ = '2010, JOlo'
'''
www.theweek.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
import re
class TheWeekFree(BasicNewsRecipe):
title = 'The Week Magazine - Free content'
__author__ = 'Darko Miletic'
class TheWeek(BasicNewsRecipe):
title = 'The Week Magazine'
__author__ = 'Jim Olo'
description = "The best of the US and international media. Daily coverage of commentary and analysis of the day's events, as well as arts, entertainment, people and gossip, and political cartoons."
publisher = 'The Week Publications, Inc.'
masthead_url = 'http://test.theweek.com/images/logo_theweek.gif'
cover_url = masthead_url
category = 'news, politics, USA'
oldest_article = 7
max_articles_per_feed = 100
@ -19,31 +21,27 @@ class TheWeekFree(BasicNewsRecipe):
encoding = 'utf-8'
use_embedded_content = False
language = 'en'
preprocess_regexps = [(re.compile(r'<h3><a href=.*</body>', re.DOTALL), lambda match: '</body>')]
remove_tags_before = dict(name='h1')
remove_tags_after = dict(name='div', attrs={'class':'articleSubscribe4free'})
remove_tags = [
dict(name='div', attrs={'class':['floatLeft','imageCaption','slideshowImageAttribution','postDate','utilities','cartoonInfo','left','middle','col300','articleSubscribe4free',' articleFlyout','articleFlyout floatRight','fourFreeBar']})
,dict(name='div', attrs={'id':['cartoonThumbs','rightColumn','header','partners']})
,dict(name='ul', attrs={'class':['slideshowNav','hotTopicsList topicList']})
]
remove_attributes = ['width','height', 'style', 'font', 'color']
extra_css = '''
h1{font-family:Geneva, Arial, Helvetica, sans-serif;color:#154B7A;}
h3{font-size: 14px;color:#999999; font-family:Geneva, Arial, Helvetica, sans-serif;font-weight: bold;}
h2{color:#666666; font-family:Geneva, Arial, Helvetica, sans-serif;font-size:small;}
p {font-family:Arial,Helvetica,sans-serif;}
'''
filter_regexps = [r'www\.palmcoastdata\.com']
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher' : publisher
, 'language' : language
}
keep_only_tags = [
dict(name=['h1','h2'])
, dict(name='div', attrs={'class':'basefont'})
, dict(name='div', attrs={'id':'slideshowLoader'})
]
remove_tags = [
dict(name='div', attrs={'id':['digg_dugg','articleRight','dateHeader']})
,dict(name=['object','embed','iframe'])
]
feeds = [
(u'News & Opinions' , u'http://www.theweek.com/section/index/news_opinion.rss')
,(u'Arts & Leisure' , u'http://www.theweek.com/section/index/arts_leisure.rss')
,(u'Business' , u'http://www.theweek.com/section/index/business.rss' )
,(u'Cartoon & Short takes' , u'http://www.theweek.com/section/index/cartoons_wit.rss')
]
feeds = [
(u'News-Opinion', u'http://theweek.com/section/index/news_opinion.rss'),
(u'Business', u'http://theweek.com/section/index/business.rss'),
(u'Arts-Life', u'http://theweek.com/section/index/arts_life.rss'),
(u'Cartoons', u'http://theweek.com/section/index/cartoon_wit/0/all-cartoons.rss')
]

View File

@ -38,12 +38,12 @@ class Wired(BasicNewsRecipe):
keep_only_tags = [dict(name='div', attrs={'class':'post'})]
remove_tags_after = dict(name='div', attrs={'class':'tweetmeme_button'})
remove_tags = [
dict(name=['object','embed','iframe','link'])
dict(name=['object','embed','iframe','link','meta','base'])
,dict(name='div', attrs={'class':['podcast_storyboard','tweetmeme_button']})
,dict(attrs={'id':'ff_bottom_nav'})
,dict(name='a',attrs={'href':'http://www.wired.com/app'})
]
remove_attributes = ['height','width']
remove_attributes = ['height','width','lang','border','clear']
def parse_index(self):
@ -78,7 +78,9 @@ class Wired(BasicNewsRecipe):
divurl = item.find('div',attrs={'class':'feature-header'})
if divurl:
divdesc = item.find('div',attrs={'class':'feature-text'})
url = 'http://www.wired.com' + divurl.a['href']
url = divurl.a['href']
if not divurl.a['href'].startswith('http://www.wired.com'):
url = 'http://www.wired.com' + divurl.a['href']
title = self.tag_to_string(divurl.a)
description = self.tag_to_string(divdesc)
date = strftime(self.timefmt)
@ -127,5 +129,17 @@ class Wired(BasicNewsRecipe):
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
for item in soup.findAll('a'):
if item.string is not None:
tstr = item.string
item.replaceWith(tstr)
else:
item.name='span'
for atrs in ['href','target','alt','title','name','id']:
if item.has_key(atrs):
del item[atrs]
for item in soup.findAll('img'):
if not item.has_key('alt'):
item['alt'] = 'image'
return soup

View File

@ -478,7 +478,7 @@ from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS, \
from calibre.devices.sne.driver import SNE
from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, \
GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, Q600, LUMIREAD, ALURATEK_COLOR, \
TREKSTOR, EEEREADER
TREKSTOR, EEEREADER, NEXTBOOK
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
from calibre.devices.kobo.driver import KOBO
from calibre.devices.bambook.driver import BAMBOOK
@ -606,6 +606,7 @@ plugins += [
BAMBOOK,
TREKSTOR,
EEEREADER,
NEXTBOOK,
ITUNES,
]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \

View File

@ -439,6 +439,13 @@ class TabletOutput(iPadOutput):
screen_size = (sys.maxint, sys.maxint)
comic_screen_size = (sys.maxint, sys.maxint)
class SamsungGalaxy(TabletOutput):
name = 'Samsung Galaxy'
shortname = 'galaxy'
description = _('Intended for the Samsung Galaxy and similar tablet devices with '
'a resolution of 600x1280')
screen_size = comic_screen_size = (600, 1280)
class SonyReaderOutput(OutputProfile):
name = 'Sony Reader'
@ -707,7 +714,7 @@ class BambookOutput(OutputProfile):
output_profiles = [OutputProfile, SonyReaderOutput, SonyReader300Output,
SonyReader900Output, MSReaderOutput, MobipocketOutput, HanlinV3Output,
HanlinV5Output, CybookG3Output, CybookOpusOutput, KindleOutput,
iPadOutput, KoboReaderOutput, TabletOutput,
iPadOutput, KoboReaderOutput, TabletOutput, SamsungGalaxy,
SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput,
IRexDR1000Output, IRexDR800Output, JetBook5Output, NookOutput,
BambookOutput, NookColorOutput]

View File

@ -64,7 +64,8 @@ class ANDROID(USBMS):
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID',
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000']
'SCH-I500_CARD', 'SPH-D700_CARD', 'MB810', 'GT-P1000', 'DESIRE',
'SGH-T849']
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD']

View File

@ -29,12 +29,16 @@ class BAMBOOK(DeviceConfig, DevicePlugin):
booklist_class = BookList
book_class = Book
ip = None
FORMATS = [ "snb" ]
VENDOR_ID = 0x230b
PRODUCT_ID = 0x0001
BCD = None
CAN_SET_METADATA = False
THUMBNAIL_HEIGHT = 155
EXTRA_CUSTOMIZATION_MESSAGE = \
_("Device IP Address (restart calibre after changing)")
icon = I("devices/bambook.png")
# OPEN_FEEDBACK_MESSAGE = _(
@ -47,6 +51,10 @@ class BAMBOOK(DeviceConfig, DevicePlugin):
METADATA_FILE_GUID = 'calibremetadata.snb'
bambook = None
is_connected = False
def __init__(self, ip):
self.ip = ip
def reset(self, key='-1', log_packets=False, report_progress=None,
detected_device=None) :
@ -60,15 +68,23 @@ class BAMBOOK(DeviceConfig, DevicePlugin):
self.eject()
# Connect
self.bambook = Bambook()
self.bambook.Connect()
self.bambook.Connect(ip = self.ip, timeout = 10000)
if self.bambook.GetState() != CONN_CONNECTED:
self.bambook = None
raise Exception(_("Unable to connect to Bambook."))
raise OpenFeedback(_("Unable to connect to Bambook. \n"
"If you are trying to connect via Wi-Fi, "
"please make sure the IP address of Bambook has been correctly configured."))
self.is_connected = True
return True
def unmount_device(self):
self.eject()
def eject(self):
if self.bambook:
self.bambook.Disconnect()
self.bambook = None
self.is_connected = False
def post_yank_cleanup(self):
self.eject()
@ -475,3 +491,8 @@ class BAMBOOK(DeviceConfig, DevicePlugin):
def get_guid(uuid):
guid = hashlib.md5(uuid).hexdigest()[0:15] + ".snb"
return guid
class BAMBOOKWifi(BAMBOOK):
def is_usb_connected(self, devices_on_system, debug=False,
only_presence=False):
return self.is_connected, self

View File

@ -329,6 +329,8 @@ class Bambook:
self.handle = None
def Connect(self, ip = DEFAULT_BAMBOOK_IP, timeout = 10000):
if ip == None or ip == '':
ip = DEFAULT_BAMBOOK_IP
self.handle = BambookConnect(ip, timeout)
if self.handle and self.handle != 0:
return True

View File

@ -230,7 +230,7 @@ class POCKETBOOK301(USBMS):
class POCKETBOOK602(USBMS):
name = 'PocketBook Pro 602/902 Device Interface'
description = _('Communicate with the PocketBook 602 reader.')
description = _('Communicate with the PocketBook 602/603/902/903 reader.')
author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux']
FORMATS = ['epub', 'fb2', 'prc', 'mobi', 'pdf', 'djvu', 'rtf', 'chm',
@ -244,7 +244,7 @@ class POCKETBOOK602(USBMS):
BCD = [0x0324]
VENDOR_NAME = ''
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['PB602', 'PB902']
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['PB602', 'PB603', 'PB902', 'PB903']
class POCKETBOOK701(USBMS):

View File

@ -264,3 +264,23 @@ class EEEREADER(USBMS):
VENDOR_NAME = 'LINUX'
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'FILE-STOR_GADGET'
class NEXTBOOK(USBMS):
name = 'Nextbook device interface'
gui_name = 'Nextbook'
description = _('Communicate with the Nextbook Reader')
author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux']
# Ordered list of supported formats
FORMATS = ['epub', 'fb2', 'txt', 'pdf']
VENDOR_ID = [0x05e3]
PRODUCT_ID = [0x0726]
BCD = [0x021a]
EBOOK_DIR_MAIN = ''
VENDOR_NAME = 'NEXT2'
WINDOWS_MAIN_MEM = '1.0.14'

View File

@ -41,9 +41,12 @@ class FB2Input(InputFormatPlugin):
from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.ebooks.metadata.meta import get_metadata
from calibre.ebooks.oeb.base import XLINK_NS, XHTML_NS, RECOVER_PARSER
from calibre.ebooks.chardet import xml_to_unicode
NAMESPACES = {'f':FB2NS, 'l':XLINK_NS}
log.debug('Parsing XML...')
raw = stream.read().replace('\0', '')
raw = xml_to_unicode(raw, strip_encoding_pats=True,
assume_utf8=True)[0]
try:
doc = etree.fromstring(raw)
except etree.XMLSyntaxError:

View File

@ -9,6 +9,7 @@ import mimetypes, os
from base64 import b64decode
from lxml import etree
from calibre.ebooks.metadata import MetaInformation
from calibre.ebooks.chardet import xml_to_unicode
XLINK_NS = 'http://www.w3.org/1999/xlink'
def XLINK(name):
@ -23,7 +24,10 @@ def get_metadata(stream):
tostring = lambda x : etree.tostring(x, method='text',
encoding=unicode).strip()
parser = etree.XMLParser(recover=True, no_network=True)
root = etree.fromstring(stream.read(), parser=parser)
raw = stream.read()
raw = xml_to_unicode(raw, strip_encoding_pats=True,
assume_utf8=True)[0]
root = etree.fromstring(raw, parser=parser)
authors, author_sort = [], None
for au in XPath('//fb2:author')(root):
fname = lname = author = None

View File

@ -513,11 +513,14 @@ class MobiReader(object):
mobi_version = self.book_header.mobi_version
for x in root.xpath('//ncx'):
x.getparent().remove(x)
svg_tags = []
for i, tag in enumerate(root.iter(etree.Element)):
tag.attrib.pop('xmlns', '')
for x in tag.attrib:
if ':' in x:
del tag.attrib[x]
if tag.tag and barename(tag.tag) == 'svg':
svg_tags.append(tag)
if tag.tag and barename(tag.tag.lower()) in \
('country-region', 'place', 'placetype', 'placename',
'state', 'city', 'street', 'address', 'content', 'form'):
@ -628,6 +631,11 @@ class MobiReader(object):
cls = cls + (' ' if cls else '') + ncls
attrib['class'] = cls
for tag in svg_tags:
p = tag.getparent()
if hasattr(p, 'remove'):
p.remove(tag)
def create_opf(self, htmlfile, guide=None, root=None):
mi = getattr(self.book_header.exth, 'mi', self.embedded_mi)
if mi is None:

View File

@ -1,6 +1,6 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3
* License: GNU GPL v2+
*/

View File

@ -1,6 +1,6 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3
* License: GNU GPL v2+
*/

View File

@ -1,3 +1,10 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v2+
*/
#include <stdio.h>
#include <errno.h>
#include <sstream>

View File

@ -1,3 +1,10 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v2+
*/
#pragma once
#include <vector>

View File

@ -1,6 +1,6 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3
* License: GNU GPL v2+
*/

View File

@ -1,6 +1,6 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3
* License: GNU GPL v2+
*/

View File

@ -1,3 +1,10 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v2+
*/
#ifndef PDF2XML
#define UNICODE
#define PY_SSIZE_T_CLEAN

View File

@ -1,6 +1,6 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3
* License: GNU GPL v2+
*/
#include <Outline.h>

View File

@ -1,6 +1,6 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3
* License: GNU GPL v2+
* Based on pdftohtml from the poppler project.
*/

View File

@ -1,6 +1,6 @@
/**
* Copyright 2009 Kovid Goyal <kovid@kovidgoyal.net>
* License: GNU GPL v3
* License: GNU GPL v2+
*/

View File

@ -25,10 +25,6 @@ from PyQt4.QtWebKit import QWebView
from pyPdf import PdfFileWriter, PdfFileReader
def get_pdf_printer():
return QPrinter(QPrinter.HighResolution)
def get_custom_size(opts):
custom_size = None
if opts.custom_size != None:
@ -42,12 +38,12 @@ def get_custom_size(opts):
custom_size = None
return custom_size
def setup_printer(opts, for_comic=False):
def get_pdf_printer(opts, for_comic=False):
from calibre.gui2 import is_ok_to_use_qt
if not is_ok_to_use_qt():
raise Exception('Not OK to use Qt')
printer = get_pdf_printer()
printer = QPrinter(QPrinter.HighResolution)
custom_size = get_custom_size(opts)
if opts.output_profile.short_name == 'default':
@ -61,15 +57,22 @@ def setup_printer(opts, for_comic=False):
h = opts.output_profile.comic_screen_size[1] if for_comic else \
opts.output_profile.height
dpi = opts.output_profile.dpi
printer.setPaperSize(QSizeF(float(w) / dpi, float(h)/dpi), QPrinter.Inch)
printer.setPaperSize(QSizeF(float(w) / dpi, float(h) / dpi), QPrinter.Inch)
printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
if for_comic:
# Comic pages typically have their own margins, or their background
# color is not white, in which case the margin looks bad
printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
else:
printer.setPageMargins(opts.margin_left, opts.margin_top,
opts.margin_right, opts.margin_bottom, QPrinter.Point)
printer.setOrientation(orientation(opts.orientation))
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setFullPage(True)
return printer
def get_printer_page_size(opts, for_comic=False):
printer = setup_printer(opts, for_comic=for_comic)
printer = get_pdf_printer(opts, for_comic=for_comic)
size = printer.paperSize(QPrinter.Millimeter)
return size.width() / 10., size.height() / 10.
@ -154,24 +157,11 @@ class PDFWriter(QObject): # {{{
self.view.load(QUrl.fromLocalFile(item))
def get_printer(self, set_horz_margins=False):
printer = get_pdf_printer()
printer.setPaperSize(QSizeF(self.size[0] * 10, self.size[1] * 10), QPrinter.Millimeter)
if set_horz_margins:
printer.setPageMargins(0., self.opts.margin_top, 0.,
self.opts.margin_bottom, QPrinter.Point)
else:
printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
printer.setOrientation(orientation(self.opts.orientation))
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setFullPage(not set_horz_margins)
return printer
def _render_html(self, ok):
if ok:
item_path = os.path.join(self.tmp_path, '%i.pdf' % len(self.combine_queue))
self.logger.debug('\tRendering item %s as %i' % (os.path.basename(str(self.view.url().toLocalFile())), len(self.combine_queue)))
printer = self.get_printer(set_horz_margins=True)
self.logger.debug('\tRendering item %s as %i.pdf' % (os.path.basename(str(self.view.url().toLocalFile())), len(self.combine_queue)))
printer = get_pdf_printer(self.opts)
printer.setOutputFileName(item_path)
self.view.print_(printer)
self._render_book()
@ -233,16 +223,11 @@ class ImagePDFWriter(object):
os.remove(f.name)
def render_images(self, outpath, mi, items):
printer = get_pdf_printer()
printer.setPaperSize(QSizeF(self.size[0] * 10, self.size[1] * 10), QPrinter.Millimeter)
printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
printer.setOrientation(orientation(self.opts.orientation))
printer.setOutputFormat(QPrinter.PdfFormat)
printer = get_pdf_printer(self.opts, for_comic=True)
printer.setOutputFileName(outpath)
printer.setDocName(mi.title)
printer.setCreator(u'%s [%s]'%(__appname__, __version__))
# Seems to be no way to set author
printer.setFullPage(True)
painter = QPainter(printer)
painter.setRenderHints(QPainter.Antialiasing|QPainter.SmoothPixmapTransform)

View File

@ -46,14 +46,27 @@ class SNBInput(InputFormatPlugin):
meta = snbFile.GetFileStream('snbf/book.snbf')
if meta != None:
meta = etree.fromstring(meta)
oeb.metadata.add('title', meta.find('.//head/name').text)
oeb.metadata.add('creator', meta.find('.//head/author').text, attrib={'role':'aut'})
oeb.metadata.add('language', meta.find('.//head/language').text.lower().replace('_', '-'))
oeb.metadata.add('creator', meta.find('.//head/generator').text)
oeb.metadata.add('publisher', meta.find('.//head/publisher').text)
cover = meta.find('.//head/cover')
if cover != None and cover.text != None:
oeb.guide.add('cover', 'Cover', cover.text)
l = { 'title' : './/head/name',
'creator' : './/head/author',
'language' : './/head/language',
'generator': './/head/generator',
'publisher': './/head/publisher',
'cover' : './/head/cover', }
d = {}
for item in l:
node = meta.find(l[item])
if node != None:
d[item] = node.text if node.text != None else ''
else:
d[item] = ''
oeb.metadata.add('title', d['title'])
oeb.metadata.add('creator', d['creator'], attrib={'role':'aut'})
oeb.metadata.add('language', d['language'].lower().replace('_', '-'))
oeb.metadata.add('generator', d['generator'])
oeb.metadata.add('publisher', d['publisher'])
if d['cover'] != '':
oeb.guide.add('cover', 'Cover', d['cover'])
bookid = str(uuid.uuid4())
oeb.metadata.add('identifier', bookid, id='uuid_id', scheme='uuid')

View File

@ -29,8 +29,7 @@ class TXTOutput(OutputFormatPlugin):
OptionRecommendation(name='output_encoding', recommended_value='utf-8',
level=OptionRecommendation.LOW,
help=_('Specify the character encoding of the output document. ' \
'The default is utf-8. Note: This option is not honored by all ' \
'formats.')),
'The default is utf-8.')),
OptionRecommendation(name='inline_toc',
recommended_value=False, level=OptionRecommendation.LOW,
help=_('Add Table of Contents to beginning of the book.')),

View File

@ -12,11 +12,15 @@ from PyQt4.Qt import QToolButton, QMenu, pyqtSignal, QIcon
from calibre.gui2.actions import InterfaceAction
from calibre.utils.smtp import config as email_config
from calibre.constants import iswindows, isosx
from calibre.customize.ui import is_disabled
from calibre.devices.bambook.driver import BAMBOOK
class ShareConnMenu(QMenu): # {{{
connect_to_folder = pyqtSignal()
connect_to_itunes = pyqtSignal()
connect_to_bambook = pyqtSignal()
config_email = pyqtSignal()
toggle_server = pyqtSignal()
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
@ -34,6 +38,17 @@ class ShareConnMenu(QMenu): # {{{
self.connect_to_itunes_action = mitem
if not (iswindows or isosx):
mitem.setVisible(False)
mitem = self.addAction(QIcon(I('devices/bambook.png')), _('Connect to Bambook'))
mitem.setEnabled(True)
mitem.triggered.connect(lambda x : self.connect_to_bambook.emit())
self.connect_to_bambook_action = mitem
bambook_visible = False
if not is_disabled(BAMBOOK):
device_ip = BAMBOOK.settings().extra_customization
if device_ip:
bambook_visible = True
self.connect_to_bambook_action.setVisible(bambook_visible)
self.addSeparator()
self.toggle_server_action = \
self.addAction(QIcon(I('network-server.png')),
@ -88,6 +103,7 @@ class ShareConnMenu(QMenu): # {{{
def set_state(self, device_connected):
self.connect_to_folder_action.setEnabled(not device_connected)
self.connect_to_itunes_action.setEnabled(not device_connected)
self.connect_to_bambook_action.setEnabled(not device_connected)
# }}}
@ -126,6 +142,7 @@ class ConnectShareAction(InterfaceAction):
self.qaction.setMenu(self.share_conn_menu)
self.share_conn_menu.connect_to_folder.connect(self.gui.connect_to_folder)
self.share_conn_menu.connect_to_itunes.connect(self.gui.connect_to_itunes)
self.share_conn_menu.connect_to_bambook.connect(self.gui.connect_to_bambook)
def location_selected(self, loc):
enabled = loc == 'library'

View File

@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
import os, collections, sys
from Queue import Queue
from PyQt4.Qt import QPixmap, QSize, QWidget, Qt, pyqtSignal, \
from PyQt4.Qt import QPixmap, QSize, QWidget, Qt, pyqtSignal, QUrl, \
QPropertyAnimation, QEasingCurve, QThread, QApplication, QFontInfo, \
QSizePolicy, QPainter, QRect, pyqtProperty, QLayout, QPalette
from PyQt4.QtWebKit import QWebView
@ -18,7 +18,7 @@ from calibre.gui2.widgets import IMAGE_EXTENSIONS
from calibre.ebooks import BOOK_EXTENSIONS
from calibre.constants import preferred_encoding
from calibre.library.comments import comments_to_html
from calibre.gui2 import config, open_local_file
from calibre.gui2 import config, open_local_file, open_url
from calibre.utils.icu import sort_key
# render_rows(data) {{{
@ -412,6 +412,12 @@ class BookDetails(QWidget): # {{{
self.view_specific_format.emit(int(id_), fmt)
elif typ == 'devpath':
open_local_file(val)
else:
try:
open_url(QUrl(link, QUrl.TolerantMode))
except:
import traceback
traceback.print_exc()
def mouseDoubleClickEvent(self, ev):

View File

@ -259,6 +259,19 @@ class EditorWidget(QWebView): # {{{
return property(fget=fget, fset=fset)
def keyPressEvent(self, ev):
if ev.key() in (Qt.Key_Tab, Qt.Key_Escape, Qt.Key_Backtab):
ev.ignore()
else:
return QWebView.keyPressed(self, ev)
def keyReleaseEvent(self, ev):
if ev.key() in (Qt.Key_Tab, Qt.Key_Escape, Qt.Key_Backtab):
ev.ignore()
else:
return QWebView.keyReleased(self, ev)
# }}}
# Highlighter {{{

View File

@ -19,7 +19,7 @@ class PluginWidget(Widget, Ui_Form):
ICON = I('mimetypes/unknown.png')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, ['format', 'inline_toc'])
Widget.__init__(self, parent, ['format', 'inline_toc', 'output_encoding'])
self.db, self.book_id = db, book_id
self.initialize_options(get_option, get_help, db, book_id)

View File

@ -27,7 +27,7 @@
<item row="0" column="1">
<widget class="QComboBox" name="opt_format"/>
</item>
<item row="2" column="0">
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -40,13 +40,23 @@
</property>
</spacer>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QCheckBox" name="opt_inline_toc">
<property name="text">
<string>&amp;Inline TOC</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Output Encoding:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="opt_output_encoding"/>
</item>
</layout>
</widget>
<resources/>

View File

@ -17,6 +17,7 @@ class PluginWidget(Widget, Ui_Form):
ICON = I('mimetypes/unknown.png')
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, ['inline_toc', 'full_image_depth'])
Widget.__init__(self, parent, ['inline_toc', 'full_image_depth',
'output_encoding'])
self.db, self.book_id = db, book_id
self.initialize_options(get_option, get_help, db, book_id)

View File

@ -14,7 +14,7 @@
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -27,20 +27,30 @@
</property>
</spacer>
</item>
<item row="0" column="0">
<item row="1" column="0">
<widget class="QCheckBox" name="opt_inline_toc">
<property name="text">
<string>&amp;Inline TOC</string>
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QCheckBox" name="opt_full_image_depth">
<property name="text">
<string>Do not reduce image size and depth</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Output Encoding:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="opt_output_encoding"/>
</item>
</layout>
</widget>
<resources/>

View File

@ -21,7 +21,8 @@ class PluginWidget(Widget, Ui_Form):
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent,
['newline', 'max_line_length', 'force_max_line_length',
'inline_toc', 'markdown_format', 'keep_links', 'keep_image_references'])
'inline_toc', 'markdown_format', 'keep_links', 'keep_image_references',
'output_encoding'])
self.db, self.book_id = db, book_id
self.initialize_options(get_option, get_help, db, book_id)

View File

@ -27,7 +27,7 @@
<item row="0" column="1">
<widget class="QComboBox" name="opt_newline"/>
</item>
<item row="7" column="0">
<item row="8" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -40,7 +40,7 @@
</property>
</spacer>
</item>
<item row="3" column="0" colspan="2">
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="opt_inline_toc">
<property name="text">
<string>&amp;Inline TOC</string>
@ -60,34 +60,44 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="opt_force_max_line_length">
<property name="text">
<string>Force maximum line length</string>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QCheckBox" name="opt_markdown_format">
<property name="text">
<string>Apply Markdown formatting to text</string>
</property>
</widget>
</item>
<item row="5" column="0">
<item row="6" column="0">
<widget class="QCheckBox" name="opt_keep_links">
<property name="text">
<string>Do not remove links (&lt;a&gt; tags) before processing</string>
</property>
</widget>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QCheckBox" name="opt_keep_image_references">
<property name="text">
<string>Do not remove image references before processing</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Output Encoding:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="opt_output_encoding"/>
</item>
</layout>
</widget>
<resources/>

View File

@ -24,6 +24,7 @@ from calibre.utils.filenames import ascii_filename
from calibre.devices.errors import FreeSpaceError
from calibre.devices.apple.driver import ITUNES_ASYNC
from calibre.devices.folder_device.driver import FOLDER_DEVICE
from calibre.devices.bambook.driver import BAMBOOK, BAMBOOKWifi
from calibre.ebooks.metadata.meta import set_metadata
from calibre.constants import DEBUG
from calibre.utils.config import prefs, tweaks
@ -635,6 +636,10 @@ class DeviceMixin(object): # {{{
if dir is not None:
self.device_manager.mount_device(kls=FOLDER_DEVICE, kind='folder', path=dir)
def connect_to_bambook(self):
self.device_manager.mount_device(kls=BAMBOOKWifi, kind='bambook',
path=BAMBOOK.settings().extra_customization)
def connect_to_itunes(self):
self.device_manager.mount_device(kls=ITUNES_ASYNC, kind='itunes', path=None)

View File

@ -9,7 +9,7 @@ from PyQt4.Qt import QCoreApplication, SIGNAL, QModelIndex, QTimer, Qt, \
QDialog, QPixmap, QGraphicsScene, QIcon, QSize
from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo
from calibre.gui2 import dynamic, open_local_file
from calibre.gui2 import dynamic, open_local_file, open_url
from calibre import fit_image
from calibre.library.comments import comments_to_html
from calibre.utils.icu import sort_key
@ -22,6 +22,8 @@ class BookInfo(QDialog, Ui_BookInfo):
self.setupUi(self)
self.cover_pixmap = None
self.comments.sizeHint = self.comments_size_hint
self.comments.page().setLinkDelegationPolicy(self.comments.page().DelegateAllLinks)
self.comments.linkClicked(self.link_clicked)
self.view_func = view_func
@ -41,6 +43,8 @@ class BookInfo(QDialog, Ui_BookInfo):
screen_height = desktop.availableGeometry().height() - 100
self.resize(self.size().width(), screen_height)
def link_clicked(self, url):
open_url(url)
def comments_size_hint(self):
return QSize(350, 250)
@ -115,6 +119,7 @@ class BookInfo(QDialog, Ui_BookInfo):
lines = [x if x.strip() else '<br><br>' for x in lines]
comments = '\n'.join(lines)
self.comments.setHtml('<div>%s</div>' % comments)
self.comments.page().setLinkDelegationPolicy(self.comments.page().DelegateAllLinks)
cdata = info.pop('cover', '')
self.cover_pixmap = QPixmap.fromImage(cdata)
self.resize_cover()

View File

@ -5,6 +5,4 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import Qt
DEFAULT_SORT = ('timestamp', False)

View File

@ -336,7 +336,7 @@ class TagsView(QTreeView): # {{{
# If the number of user categories changed, if custom columns have come or
# gone, or if columns have been hidden or restored, we must rebuild the
# model. Reason: it is much easier than reconstructing the browser tree.
def set_new_model(self, filter_categories_by = None):
def set_new_model(self, filter_categories_by=None):
try:
self._model = TagsModel(self.db, parent=self,
hidden_categories=self.hidden_categories,
@ -1165,7 +1165,7 @@ class TagBrowserWidget(QWidget): # {{{
'containing the text "foo"'))
search_layout.addWidget(self.item_search)
self.search_button = QPushButton()
self.search_button.setText(_('Find!'))
self.search_button.setText(_('&Find'))
self.search_button.setToolTip(_('Find the first/next matching item'))
self.search_button.setFixedWidth(40)
search_layout.addWidget(self.search_button)

View File

@ -21,6 +21,7 @@ Environment variables
-----------------------
* ``CALIBRE_CONFIG_DIRECTORY`` - sets the directory where configuration files are stored/read.
* ``CALIBRE_TEMP_DIR`` - sets the temporary directory used by calibre
* ``CALIBRE_OVERRIDE_DATABASE_PATH`` - allows you to specify the full path to metadata.db. Using this variable you can have metadata.db be in a location other than the library folder. Useful if your library folder is on a networked drive that does not support file locking.
* ``CALIBRE_DEVELOP_FROM`` - Used to run from a calibre development environment. See :ref:`develop`.
* ``CALIBRE_OVERRIDE_LANG`` - Used to force the language used by the interface (ISO 639 language code)

View File

@ -211,7 +211,7 @@ The following functions are available in addition to those described in single-f
* ``cmp(x, y, lt, eq, gt)`` -- compares x and y after converting both to numbers. Returns ``lt`` if x < y. Returns ``eq`` if x == y. Otherwise returns ``gt``.
* ``divide(x, y)`` -- returns x / y. Throws an exception if either x or y are not numbers.
* ``field(name)`` -- returns the metadata field named by ``name``.
* ``eval(string)`` -- evaluates the string as a program, passing the local variables (those `assign`ed to). This permits using the template processor to construct complex results from local variables.
* ``eval(string)`` -- evaluates the string as a program, passing the local variables (those ``assign`` ed to). This permits using the template processor to construct complex results from local variables.
* ``multiply(x, y)`` -- returns x * y. Throws an exception if either x or y are not numbers.
* ``print(a, b, ...)`` -- prints the arguments to standard output. Unless you start calibre from the command line (``calibre-debug -g``), the output will go to a black hole.
* ``strcat(a, b, ...)`` -- can take any number of arguments. Returns a string formed by concatenating all the arguments.
@ -268,7 +268,7 @@ The solution requires creating three composite columns. The first column is used
Title: Revenge of the Wrought-Iron Flamingos
Output: MLM [03] Revenge of the Wrought-Iron Flamingos
The following program produces the same results as the original recipe, using only one custom column to hold the results of a program that computes the special title value.
The following program produces the same results as the original recipe, using only one custom column to hold the results of a program that computes the special title value::
Custom column:
Name: #special_title
@ -349,4 +349,4 @@ You might find the following tips useful.
* Templates can use other templates by referencing a composite custom column.
* In a plugboard, you can set a field to empty (or whatever is equivalent to empty) by using the special template ``{null}``. This template will always evaluate to an empty string.
* The technique described above to show numbers even if they have a zero value works with the standard field series_index.

View File

@ -40,7 +40,7 @@ def base_dir():
_base_dir = td
else:
_base_dir = tempfile.mkdtemp(prefix='%s_%s_tmp_'%(__appname__,
__version__))
__version__), dir=os.environ.get('CALIBRE_TEMP_DIR', None))
atexit.register(remove_dir, _base_dir)
return _base_dir