diff --git a/COPYRIGHT b/COPYRIGHT index a31d1dbcda..8790fb69dd 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -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 diff --git a/resources/images/news/business_insider.png b/resources/images/news/business_insider.png new file mode 100644 index 0000000000..7e86e583e8 Binary files /dev/null and b/resources/images/news/business_insider.png differ diff --git a/resources/recipes/business_insider.recipe b/resources/recipes/business_insider.recipe new file mode 100644 index 0000000000..d7e6a0d59b --- /dev/null +++ b/resources/recipes/business_insider.recipe @@ -0,0 +1,69 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, Darko Miletic ' +''' +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 diff --git a/resources/recipes/el_periodico.recipe b/resources/recipes/el_periodico.recipe new file mode 100644 index 0000000000..2c3ed456fb --- /dev/null +++ b/resources/recipes/el_periodico.recipe @@ -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'

 

', re.DOTALL|re.IGNORECASE), lambda match: ''), + (re.compile(r'

', re.DOTALL|re.IGNORECASE), lambda match: ''), + (re.compile(r'

', re.DOTALL|re.IGNORECASE), lambda match: '

') + ] diff --git a/resources/recipes/elpais_impreso.recipe b/resources/recipes/elpais_impreso.recipe index bba3bda217..130013286c 100644 --- a/resources/recipes/elpais_impreso.recipe +++ b/resources/recipes/elpais_impreso.recipe @@ -1,86 +1,95 @@ -# -*- coding: utf-8 -*- __license__ = 'GPL v3' __copyright__ = '2010, Darko Miletic ' ''' -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 diff --git a/resources/recipes/heraldo.recipe b/resources/recipes/heraldo.recipe index 381e97b9ce..c5669e116b 100644 --- a/resources/recipes/heraldo.recipe +++ b/resources/recipes/heraldo.recipe @@ -1,50 +1,65 @@ #!/usr/bin/env python -__license__ = 'GPL v3' -__author__ = 'Lorenzo Vigentini' -__copyright__ = '2009, Lorenzo Vigentini ' +__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;} + ''' diff --git a/resources/recipes/karlsruhe.recipe b/resources/recipes/karlsruhe.recipe new file mode 100644 index 0000000000..c0bc5369f1 --- /dev/null +++ b/resources/recipes/karlsruhe.recipe @@ -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' + diff --git a/resources/recipes/red_aragon.recipe b/resources/recipes/red_aragon.recipe new file mode 100644 index 0000000000..4681e6660b --- /dev/null +++ b/resources/recipes/red_aragon.recipe @@ -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'}) diff --git a/resources/recipes/salon.recipe b/resources/recipes/salon.recipe index ed7ec98f10..c421ab094d 100644 --- a/resources/recipes/salon.recipe +++ b/resources/recipes/salon.recipe @@ -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') - diff --git a/resources/recipes/the_week_magazine_free.recipe b/resources/recipes/the_week_magazine_free.recipe index 1bac4133e7..6e033eaf82 100644 --- a/resources/recipes/the_week_magazine_free.recipe +++ b/resources/recipes/the_week_magazine_free.recipe @@ -1,17 +1,19 @@ - __license__ = 'GPL v3' -__copyright__ = '2010, Darko Miletic ' +__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'

', re.DOTALL), lambda match: '')] + 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') + ] diff --git a/resources/recipes/wired.recipe b/resources/recipes/wired.recipe index 9599d54de9..bb9a97f5c4 100644 --- a/resources/recipes/wired.recipe +++ b/resources/recipes/wired.recipe @@ -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 diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index aea0e340c4..d0f986209c 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -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 \ diff --git a/src/calibre/customize/profiles.py b/src/calibre/customize/profiles.py index 54c4259678..177f482aa6 100644 --- a/src/calibre/customize/profiles.py +++ b/src/calibre/customize/profiles.py @@ -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] diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 492b00617d..6387b48857 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -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'] diff --git a/src/calibre/devices/bambook/driver.py b/src/calibre/devices/bambook/driver.py index 930c67a159..e7fa66c939 100644 --- a/src/calibre/devices/bambook/driver.py +++ b/src/calibre/devices/bambook/driver.py @@ -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 diff --git a/src/calibre/devices/bambook/libbambookcore.py b/src/calibre/devices/bambook/libbambookcore.py index a11c5e9e87..35d04ba4ac 100644 --- a/src/calibre/devices/bambook/libbambookcore.py +++ b/src/calibre/devices/bambook/libbambookcore.py @@ -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 diff --git a/src/calibre/devices/eb600/driver.py b/src/calibre/devices/eb600/driver.py index 246b753fa8..3201229699 100644 --- a/src/calibre/devices/eb600/driver.py +++ b/src/calibre/devices/eb600/driver.py @@ -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): diff --git a/src/calibre/devices/misc.py b/src/calibre/devices/misc.py index d4776ecca7..2a0fdf6433 100644 --- a/src/calibre/devices/misc.py +++ b/src/calibre/devices/misc.py @@ -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' + diff --git a/src/calibre/ebooks/fb2/input.py b/src/calibre/ebooks/fb2/input.py index 2b08a716cc..1f9a3ffe95 100644 --- a/src/calibre/ebooks/fb2/input.py +++ b/src/calibre/ebooks/fb2/input.py @@ -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: diff --git a/src/calibre/ebooks/metadata/fb2.py b/src/calibre/ebooks/metadata/fb2.py index 3636b89df4..2d6192f949 100644 --- a/src/calibre/ebooks/metadata/fb2.py +++ b/src/calibre/ebooks/metadata/fb2.py @@ -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 diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index 02abc51cd3..14e3ed11c3 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -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: diff --git a/src/calibre/ebooks/pdf/fonts.cpp b/src/calibre/ebooks/pdf/fonts.cpp index 3cd7ef0c5b..99ab7517c1 100644 --- a/src/calibre/ebooks/pdf/fonts.cpp +++ b/src/calibre/ebooks/pdf/fonts.cpp @@ -1,6 +1,6 @@ /** * Copyright 2009 Kovid Goyal - * License: GNU GPL v3 + * License: GNU GPL v2+ */ diff --git a/src/calibre/ebooks/pdf/fonts.h b/src/calibre/ebooks/pdf/fonts.h index 55202c9573..1b380e1b87 100644 --- a/src/calibre/ebooks/pdf/fonts.h +++ b/src/calibre/ebooks/pdf/fonts.h @@ -1,6 +1,6 @@ /** * Copyright 2009 Kovid Goyal - * License: GNU GPL v3 + * License: GNU GPL v2+ */ diff --git a/src/calibre/ebooks/pdf/images.cpp b/src/calibre/ebooks/pdf/images.cpp index b3b062e1f4..4cd1ace776 100644 --- a/src/calibre/ebooks/pdf/images.cpp +++ b/src/calibre/ebooks/pdf/images.cpp @@ -1,3 +1,10 @@ +/** + * Copyright 2009 Kovid Goyal + * License: GNU GPL v2+ + */ + + + #include #include #include diff --git a/src/calibre/ebooks/pdf/images.h b/src/calibre/ebooks/pdf/images.h index 7d6f143147..1b4d9b58bf 100644 --- a/src/calibre/ebooks/pdf/images.h +++ b/src/calibre/ebooks/pdf/images.h @@ -1,3 +1,10 @@ +/** + * Copyright 2009 Kovid Goyal + * License: GNU GPL v2+ + */ + + + #pragma once #include diff --git a/src/calibre/ebooks/pdf/links.cpp b/src/calibre/ebooks/pdf/links.cpp index 414ff5ce24..8d28492bab 100644 --- a/src/calibre/ebooks/pdf/links.cpp +++ b/src/calibre/ebooks/pdf/links.cpp @@ -1,6 +1,6 @@ /** * Copyright 2009 Kovid Goyal - * License: GNU GPL v3 + * License: GNU GPL v2+ */ diff --git a/src/calibre/ebooks/pdf/links.h b/src/calibre/ebooks/pdf/links.h index a8a3127a77..c43911ddca 100644 --- a/src/calibre/ebooks/pdf/links.h +++ b/src/calibre/ebooks/pdf/links.h @@ -1,6 +1,6 @@ /** * Copyright 2009 Kovid Goyal - * License: GNU GPL v3 + * License: GNU GPL v2+ */ diff --git a/src/calibre/ebooks/pdf/main.cpp b/src/calibre/ebooks/pdf/main.cpp index 44257b50f5..4e6ec60388 100644 --- a/src/calibre/ebooks/pdf/main.cpp +++ b/src/calibre/ebooks/pdf/main.cpp @@ -1,3 +1,10 @@ +/** + * Copyright 2009 Kovid Goyal + * License: GNU GPL v2+ + */ + + + #ifndef PDF2XML #define UNICODE #define PY_SSIZE_T_CLEAN diff --git a/src/calibre/ebooks/pdf/reflow.cpp b/src/calibre/ebooks/pdf/reflow.cpp index c08d7e5507..0c569fe0d1 100644 --- a/src/calibre/ebooks/pdf/reflow.cpp +++ b/src/calibre/ebooks/pdf/reflow.cpp @@ -1,6 +1,6 @@ /** * Copyright 2009 Kovid Goyal - * License: GNU GPL v3 + * License: GNU GPL v2+ */ #include diff --git a/src/calibre/ebooks/pdf/reflow.h b/src/calibre/ebooks/pdf/reflow.h index deb1dec326..ad4b79929d 100644 --- a/src/calibre/ebooks/pdf/reflow.h +++ b/src/calibre/ebooks/pdf/reflow.h @@ -1,6 +1,6 @@ /** * Copyright 2009 Kovid Goyal - * License: GNU GPL v3 + * License: GNU GPL v2+ * Based on pdftohtml from the poppler project. */ diff --git a/src/calibre/ebooks/pdf/utils.h b/src/calibre/ebooks/pdf/utils.h index 43f435b1e3..4246239ac7 100644 --- a/src/calibre/ebooks/pdf/utils.h +++ b/src/calibre/ebooks/pdf/utils.h @@ -1,6 +1,6 @@ /** * Copyright 2009 Kovid Goyal - * License: GNU GPL v3 + * License: GNU GPL v2+ */ diff --git a/src/calibre/ebooks/pdf/writer.py b/src/calibre/ebooks/pdf/writer.py index 03519a2cbb..4ff10290c9 100644 --- a/src/calibre/ebooks/pdf/writer.py +++ b/src/calibre/ebooks/pdf/writer.py @@ -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) diff --git a/src/calibre/ebooks/snb/input.py b/src/calibre/ebooks/snb/input.py index 659ca79619..d2acb257aa 100755 --- a/src/calibre/ebooks/snb/input.py +++ b/src/calibre/ebooks/snb/input.py @@ -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') diff --git a/src/calibre/ebooks/txt/output.py b/src/calibre/ebooks/txt/output.py index a6369b6f0b..0e077672d8 100644 --- a/src/calibre/ebooks/txt/output.py +++ b/src/calibre/ebooks/txt/output.py @@ -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.')), diff --git a/src/calibre/gui2/actions/device.py b/src/calibre/gui2/actions/device.py index 744ab20d10..fb3e627789 100644 --- a/src/calibre/gui2/actions/device.py +++ b/src/calibre/gui2/actions/device.py @@ -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' diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index dd12080d7f..8e3e8b10de 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -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): diff --git a/src/calibre/gui2/comments_editor.py b/src/calibre/gui2/comments_editor.py index 97a218a10b..1a6b60284b 100644 --- a/src/calibre/gui2/comments_editor.py +++ b/src/calibre/gui2/comments_editor.py @@ -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 {{{ diff --git a/src/calibre/gui2/convert/pdb_output.py b/src/calibre/gui2/convert/pdb_output.py index 9f88656f2f..51c202cb03 100644 --- a/src/calibre/gui2/convert/pdb_output.py +++ b/src/calibre/gui2/convert/pdb_output.py @@ -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) diff --git a/src/calibre/gui2/convert/pdb_output.ui b/src/calibre/gui2/convert/pdb_output.ui index 772a19b79e..17bdc0a984 100644 --- a/src/calibre/gui2/convert/pdb_output.ui +++ b/src/calibre/gui2/convert/pdb_output.ui @@ -27,7 +27,7 @@ - + Qt::Vertical @@ -40,13 +40,23 @@ - + &Inline TOC + + + + Output Encoding: + + + + + + diff --git a/src/calibre/gui2/convert/pml_output.py b/src/calibre/gui2/convert/pml_output.py index 61207d3de5..f7905194ca 100644 --- a/src/calibre/gui2/convert/pml_output.py +++ b/src/calibre/gui2/convert/pml_output.py @@ -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) diff --git a/src/calibre/gui2/convert/pmlz_output.ui b/src/calibre/gui2/convert/pmlz_output.ui index 3573e14210..9754752c8a 100644 --- a/src/calibre/gui2/convert/pmlz_output.ui +++ b/src/calibre/gui2/convert/pmlz_output.ui @@ -14,7 +14,7 @@ Form - + Qt::Vertical @@ -27,20 +27,30 @@ - + &Inline TOC - + Do not reduce image size and depth + + + + Output Encoding: + + + + + + diff --git a/src/calibre/gui2/convert/txt_output.py b/src/calibre/gui2/convert/txt_output.py index 2fafad4b43..9f30e0d83f 100644 --- a/src/calibre/gui2/convert/txt_output.py +++ b/src/calibre/gui2/convert/txt_output.py @@ -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) diff --git a/src/calibre/gui2/convert/txt_output.ui b/src/calibre/gui2/convert/txt_output.ui index 19e4ec52a1..6290a096c8 100644 --- a/src/calibre/gui2/convert/txt_output.ui +++ b/src/calibre/gui2/convert/txt_output.ui @@ -27,7 +27,7 @@ - + Qt::Vertical @@ -40,7 +40,7 @@ - + &Inline TOC @@ -60,34 +60,44 @@ - + Force maximum line length - + Apply Markdown formatting to text - + Do not remove links (<a> tags) before processing - + Do not remove image references before processing + + + + Output Encoding: + + + + + + diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 3b071aa024..6d289a3e5c 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -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) diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py index 1384c27b8c..eac8461299 100644 --- a/src/calibre/gui2/dialogs/book_info.py +++ b/src/calibre/gui2/dialogs/book_info.py @@ -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 '

' for x in lines] comments = '\n'.join(lines) self.comments.setHtml('
%s
' % comments) + self.comments.page().setLinkDelegationPolicy(self.comments.page().DelegateAllLinks) cdata = info.pop('cover', '') self.cover_pixmap = QPixmap.fromImage(cdata) self.resize_cover() diff --git a/src/calibre/gui2/library/__init__.py b/src/calibre/gui2/library/__init__.py index e1344101ec..a40195e72e 100644 --- a/src/calibre/gui2/library/__init__.py +++ b/src/calibre/gui2/library/__init__.py @@ -5,6 +5,4 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from PyQt4.Qt import Qt - DEFAULT_SORT = ('timestamp', False) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index fe9726d8a9..a9ba22f768 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -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) diff --git a/src/calibre/manual/customize.rst b/src/calibre/manual/customize.rst index d7b4e931d9..6218bf8112 100644 --- a/src/calibre/manual/customize.rst +++ b/src/calibre/manual/customize.rst @@ -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) diff --git a/src/calibre/manual/template_lang.rst b/src/calibre/manual/template_lang.rst index b859a84340..8a3bd854b1 100644 --- a/src/calibre/manual/template_lang.rst +++ b/src/calibre/manual/template_lang.rst @@ -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. - \ No newline at end of file + diff --git a/src/calibre/ptempfile.py b/src/calibre/ptempfile.py index 71ae9b0789..ac7df1c4e3 100644 --- a/src/calibre/ptempfile.py +++ b/src/calibre/ptempfile.py @@ -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