mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
60059b0172
@ -19,12 +19,92 @@
|
||||
# new recipes:
|
||||
# - title:
|
||||
|
||||
- version: 0.8.8
|
||||
date: 2011-07-01
|
||||
|
||||
new features:
|
||||
- title: "Make author names in the Book Details panel clickable. Clicking them takes you to the wikipedia page for the author by default. You may have to tell calibre to display author names in the Book details panel first via Preferences->Look & Feel->Book details. You can change the link for individual authors by right clicking on the author's name in the Tag Browser and selecting Manage Authors."
|
||||
|
||||
- title: "Get Books: Add 'Open Books' as an available book source"
|
||||
|
||||
- title: "Get Books: When a free download is available for a search result, for example, for public domain books, allow direct download of the book into your calibre library."
|
||||
|
||||
- title: "Support for detecting and mounting reader devices on FreeBSD."
|
||||
tickets: [802708]
|
||||
|
||||
- title: "When creating a composite custom column, allow the use of HTML to create links and other markup that display in the Book details panel"
|
||||
|
||||
- title: "Add the swap_around_comma function to the template language."
|
||||
|
||||
- title: "Drivers for HTC G2, Advent Vega, iRiver Story HD, Lark FreeMe and Moovyman mp7"
|
||||
|
||||
- title: "Quick View: Survives changing libraries. Also allow sorting by series index as well as name."
|
||||
|
||||
- title: "Connect to iTunes: Add an option to control how the driver works depending on whether you have iTunes setup to copy files to its media directory or not. Set this option by customizing the Apple driver in Preferences->Plugins. Having iTunes copy media to its storage folder is no longer neccessary. See http://www.mobileread.com/forums/showthread.php?t=118559 for details"
|
||||
|
||||
- title: "Remove the delete library functionality from calibre, instead you can now remove a library, so calibre will forget about it, but you have to delete the files manually"
|
||||
|
||||
bug fixes:
|
||||
- title: "Fix a regression introduced in 0.8.7 in the Tag Browser that could cause calibre to crash after performing various actions"
|
||||
|
||||
- title: "Fix an unhandled error when deleting all saved searches"
|
||||
tickets: [804383]
|
||||
|
||||
- title: "Fix row numbers in a previous selection being incorrect after a sort operation."
|
||||
|
||||
- title: "Fix ISBN identifier type not recognized if it is in upper case"
|
||||
tickets: [802288]
|
||||
|
||||
- title: "Fix a regression in 0.8.7 that broke reading metadata from MOBI files in the Edit metadata dialog."
|
||||
tickets: [801981]
|
||||
|
||||
- title: "Fix handling of filenames that have an even number of periods before the file extension."
|
||||
tickets: [801939]
|
||||
|
||||
- title: "Fix lack of thread saefty in template format system, that could lead to incorrect template evaluation in some cases."
|
||||
tickets: [801944]
|
||||
|
||||
- title: "Fix conversion to PDB when the input document has no text"
|
||||
tickets: [801888]
|
||||
|
||||
- title: "Fix clicking on first letter of author names generating incorrect search."
|
||||
|
||||
- title: "Also fix updating bulk metadata in custom column causing unnneccessary Tag Browser refreshes."
|
||||
|
||||
- title: "Fix a regression in 0.8.7 that broke renaming items via the Tag Browser"
|
||||
|
||||
- title: "Fix a regression in 0.8.7 that caused the regex builder wizard to fail with LIT files as the input"
|
||||
|
||||
improved recipes:
|
||||
- Zaman Gazetesi
|
||||
- Infobae
|
||||
- El Cronista
|
||||
- Critica de la Argentina
|
||||
- Buenos Aires Economico
|
||||
- El Universal (Venezuela)
|
||||
- wprost
|
||||
- Financial Times UK
|
||||
|
||||
new recipes:
|
||||
- title: "Today's Zaman by thomass"
|
||||
|
||||
- title: "Athens News by Darko Miletic"
|
||||
|
||||
- title: "Catholic News Agency"
|
||||
author: Jetkey
|
||||
|
||||
- title: "Arizona Republic"
|
||||
author: Jim Olo
|
||||
|
||||
- title: "Add Ming Pao Vancouver and Toronto"
|
||||
author: Eddie Lau
|
||||
|
||||
|
||||
- version: 0.8.7
|
||||
date: 2011-06-24
|
||||
|
||||
new features:
|
||||
- title: "Connect to iTunes: You now need to tell iTunes to keep its own copy of every ebook. Do this in iTunes by going to Preferences->Advanced and setting the 'Copy files to iTunes Media folder when adding to library' option. To learn about why this is necessary, see: http://www.mobileread.com/forums/showthread.php?t=140260"
|
||||
type: major
|
||||
|
||||
- title: "Add a couple of date related functions to the calibre template langauge to get 'todays' date and create text based on the value of a date type field"
|
||||
|
||||
|
68
recipes/arizona_republic.recipe
Normal file
68
recipes/arizona_republic.recipe
Normal file
@ -0,0 +1,68 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, jolo'
|
||||
'''
|
||||
azrepublic.com
|
||||
'''
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1307301031(BasicNewsRecipe):
|
||||
title = u'AZRepublic'
|
||||
__author__ = 'Jim Olo'
|
||||
language = 'en'
|
||||
description = "The Arizona Republic is Arizona's leading provider of news and information, and has published a daily newspaper in Phoenix for more than 110 years"
|
||||
publisher = 'AZRepublic/AZCentral'
|
||||
masthead_url = 'http://freedom2t.com/wp-content/uploads/press_az_republic_v2.gif'
|
||||
cover_url = 'http://www.valleyleadership.org/Common/Img/2line4c_AZRepublic%20with%20azcentral%20logo.jpg'
|
||||
category = 'news, politics, USA, AZ, Arizona'
|
||||
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
remove_empty_feeds = True
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
# extra_css = '.headline {font-size: medium;} \n .fact { padding-top: 10pt }'
|
||||
extra_css = ' body{ font-family: Verdana,Helvetica,Arial,sans-serif } .headline {font-size: medium} .introduction{font-weight: bold} .story-feature{display: block; padding: 0; border: 1px solid; width: 40%; font-size: small} .story-feature h2{text-align: center; text-transform: uppercase} '
|
||||
|
||||
remove_attributes = ['width','height','h2','subHeadline','style']
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'id':['slidingBillboard', 'top728x90', 'subindex-header', 'topSearch']}),
|
||||
dict(name='div', attrs={'id':['simplesearch', 'azcLoginBox', 'azcLoginBoxInner', 'topNav']}),
|
||||
dict(name='div', attrs={'id':['carsDrop', 'homesDrop', 'rentalsDrop', 'classifiedDrop']}),
|
||||
dict(name='div', attrs={'id':['nav', 'mp', 'subnav', 'jobsDrop']}),
|
||||
dict(name='h6', attrs={'class':['section-header']}),
|
||||
dict(name='a', attrs={'href':['#comments']}),
|
||||
dict(name='div', attrs={'class':['articletools clearfix', 'floatRight']}),
|
||||
dict(name='div', attrs={'id':['fbFrame', 'ob', 'storyComments', 'storyGoogleAdBox']}),
|
||||
dict(name='div', attrs={'id':['storyTopHomes', 'openRight', 'footerwrap', 'copyright']}),
|
||||
dict(name='div', attrs={'id':['blogsHed', 'blog_comments', 'blogByline','blogTopics']}),
|
||||
dict(name='div', attrs={'id':['membersRightMain', 'dealsfooter', 'azrTopHed', 'azrRightCol']}),
|
||||
dict(name='div', attrs={'id':['ttdHeader', 'ttdTimeWeather']}),
|
||||
dict(name='div', attrs={'id':['membersRightMain', 'deals-header-wrap']}),
|
||||
dict(name='div', attrs={'id':['todoTopSearchBar', 'byline clearfix', 'subdex-topnav']}),
|
||||
dict(name='h1', attrs={'id':['SEOtext']}),
|
||||
dict(name='table', attrs={'class':['ap-mediabox-table']}),
|
||||
dict(name='p', attrs={'class':['ap_para']}),
|
||||
dict(name='span', attrs={'class':['source-org vcard', 'org fn']}),
|
||||
dict(name='a', attrs={'href':['http://hosted2.ap.org/APDEFAULT/privacy']}),
|
||||
dict(name='a', attrs={'href':['http://hosted2.ap.org/APDEFAULT/terms']}),
|
||||
dict(name='div', attrs={'id':['onespot_nextclick']}),
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'FrontPage', u'http://www.azcentral.com/rss/feeds/republicfront.xml'),
|
||||
(u'TopUS-News', u'http://hosted.ap.org/lineups/USHEADS.rss?SITE=AZPHG&SECTION=HOME'),
|
||||
(u'WorldNews', u'http://hosted.ap.org/lineups/WORLDHEADS.rss?SITE=AZPHG&SECTION=HOME'),
|
||||
(u'TopBusiness', u'http://hosted.ap.org/lineups/BUSINESSHEADS.rss?SITE=AZPHG&SECTION=HOME'),
|
||||
(u'Entertainment', u'http://hosted.ap.org/lineups/ENTERTAINMENT.rss?SITE=AZPHG&SECTION=HOME'),
|
||||
(u'ArizonaNews', u'http://www.azcentral.com/rss/feeds/news.xml'),
|
||||
(u'Gilbert', u'http://www.azcentral.com/rss/feeds/gilbert.xml'),
|
||||
(u'Chandler', u'http://www.azcentral.com/rss/feeds/chandler.xml'),
|
||||
(u'DiningReviews', u'http://www.azcentral.com/rss/feeds/diningreviews.xml'),
|
||||
(u'AZBusiness', u'http://www.azcentral.com/rss/feeds/business.xml'),
|
||||
(u'ArizonaDeals', u'http://www.azcentral.com/members/Blog%7E/RealDealsblog'),
|
||||
(u'GroceryDeals', u'http://www.azcentral.com/members/Blog%7E/RealDealsblog/tag/2646')
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
70
recipes/athens_news.recipe
Normal file
70
recipes/athens_news.recipe
Normal file
@ -0,0 +1,70 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2011, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
www.athensnews.gr
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AthensNews(BasicNewsRecipe):
|
||||
title = 'Athens News'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Greece in English since 1952'
|
||||
publisher = 'NEP Publishing Company SA'
|
||||
category = 'news, politics, Greece, Athens'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 200
|
||||
no_stylesheets = True
|
||||
encoding = 'utf8'
|
||||
use_embedded_content = False
|
||||
language = 'en_GR'
|
||||
remove_empty_feeds = True
|
||||
publication_type = 'newspaper'
|
||||
masthead_url = 'http://www.athensnews.gr/sites/athensnews/themes/athensnewsv3/images/logo.jpg'
|
||||
extra_css = """
|
||||
body{font-family: Arial,Helvetica,sans-serif }
|
||||
img{margin-bottom: 0.4em; display:block}
|
||||
.big{font-size: xx-large; font-family: Georgia,serif}
|
||||
.articlepubdate{font-size: small; color: gray; font-family: Georgia,serif}
|
||||
.lezanta{font-size: x-small; font-weight: bold; text-align: left; margin-bottom: 1em; display: block}
|
||||
"""
|
||||
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
, 'linearize_tables' : True
|
||||
}
|
||||
|
||||
remove_tags = [
|
||||
dict(name=['meta','link'])
|
||||
]
|
||||
keep_only_tags=[
|
||||
dict(name='span',attrs={'class':'big'})
|
||||
,dict(name='td', attrs={'class':['articlepubdate','text']})
|
||||
]
|
||||
remove_attributes=['lang']
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'News' , u'http://www.athensnews.gr/category/1/feed' )
|
||||
,(u'Politics' , u'http://www.athensnews.gr/category/8/feed' )
|
||||
,(u'Business' , u'http://www.athensnews.gr/category/2/feed' )
|
||||
,(u'Economy' , u'http://www.athensnews.gr/category/11/feed')
|
||||
,(u'Community' , u'http://www.athensnews.gr/category/5/feed' )
|
||||
,(u'Arts' , u'http://www.athensnews.gr/category/3/feed' )
|
||||
,(u'Living in Athens', u'http://www.athensnews.gr/category/7/feed' )
|
||||
,(u'Sports' , u'http://www.athensnews.gr/category/4/feed' )
|
||||
,(u'Travel' , u'http://www.athensnews.gr/category/6/feed' )
|
||||
,(u'Letters' , u'http://www.athensnews.gr/category/44/feed')
|
||||
,(u'Media' , u'http://www.athensnews.gr/multimedia/feed' )
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
return url + '?action=print'
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
@ -1,72 +1,59 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
__copyright__ = '2009-2011, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
elargentino.com
|
||||
www.diariobae.com
|
||||
'''
|
||||
|
||||
from calibre import strftime
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import Tag
|
||||
|
||||
class BsAsEconomico(BasicNewsRecipe):
|
||||
title = 'Buenos Aires Economico'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Revista Argentina'
|
||||
publisher = 'ElArgentino.com'
|
||||
description = 'Diario BAE es el diario economico-politico con mas influencia en la Argentina. Fuente de empresarios y politicos del pais y el exterior. El pozo estaria aportando en periodos breves un volumen equivalente a 800m3 diarios. Pero todavia deben efectuarse otras perforaciones adicionales.'
|
||||
publisher = 'Diario BAE'
|
||||
category = 'news, politics, economy, Argentina'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = 'utf-8'
|
||||
language = 'es_AR'
|
||||
language = 'es_AR'
|
||||
cover_url = strftime('http://www.diariobae.com/imgs_portadas/%Y%m%d_portadasBAE.jpg')
|
||||
masthead_url = 'http://www.diariobae.com/img/logo_bae.png'
|
||||
remove_empty_feeds = True
|
||||
publication_type = 'newspaper'
|
||||
extra_css = """
|
||||
body{font-family: Georgia,"Times New Roman",Times,serif}
|
||||
#titulo{font-size: x-large}
|
||||
#epi{font-size: small; font-style: italic; font-weight: bold}
|
||||
img{display: block; margin-top: 1em}
|
||||
"""
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
lang = 'es-AR'
|
||||
direction = 'ltr'
|
||||
INDEX = 'http://www.elargentino.com/medios/121/Buenos-Aires-Economico.html'
|
||||
extra_css = ' .titulo{font-size: x-large; font-weight: bold} .volantaImp{font-size: small; font-weight: bold} '
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , category
|
||||
, '--publisher', publisher
|
||||
remove_tags_before= dict(attrs={'id':'titulo'})
|
||||
remove_tags_after = dict(attrs={'id':'autor' })
|
||||
remove_tags = [
|
||||
dict(name=['meta','base','iframe','link','lang'])
|
||||
,dict(attrs={'id':'barra_tw'})
|
||||
]
|
||||
remove_attributes = ['data-count','data-via']
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0cm; margin-top: 0em; margin-bottom: 0.5em} "'
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'ContainerPop'})]
|
||||
|
||||
remove_tags = [dict(name='link')]
|
||||
|
||||
feeds = [(u'Articulos', u'http://www.elargentino.com/Highlights.aspx?ParentType=Section&ParentId=121&Content-Type=text/xml&ChannelDesc=Buenos%20Aires%20Econ%C3%B3mico')]
|
||||
|
||||
def print_version(self, url):
|
||||
main, sep, article_part = url.partition('/nota-')
|
||||
article_id, rsep, rrest = article_part.partition('-')
|
||||
return u'http://www.elargentino.com/Impresion.aspx?Id=' + article_id
|
||||
feeds = [
|
||||
(u'Argentina' , u'http://www.diariobae.com/rss/argentina.xml' )
|
||||
,(u'Valores' , u'http://www.diariobae.com/rss/valores.xml' )
|
||||
,(u'Finanzas' , u'http://www.diariobae.com/rss/finanzas.xml' )
|
||||
,(u'Negocios' , u'http://www.diariobae.com/rss/negocios.xml' )
|
||||
,(u'Mundo' , u'http://www.diariobae.com/rss/mundo.xml' )
|
||||
,(u'5 dias' , u'http://www.diariobae.com/rss/5dias.xml' )
|
||||
,(u'Espectaculos', u'http://www.diariobae.com/rss/espectaculos.xml')
|
||||
]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
soup.html['lang'] = self.lang
|
||||
soup.html['dir' ] = self.direction
|
||||
mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
|
||||
mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=utf-8")])
|
||||
soup.head.insert(0,mlang)
|
||||
soup.head.insert(1,mcharset)
|
||||
return soup
|
||||
|
||||
def get_cover_url(self):
|
||||
cover_url = None
|
||||
soup = self.index_to_soup(self.INDEX)
|
||||
cover_item = soup.find('div',attrs={'class':'colder'})
|
||||
if cover_item:
|
||||
clean_url = self.image_url_processor(None,cover_item.div.img['src'])
|
||||
cover_url = 'http://www.elargentino.com' + clean_url + '&height=600'
|
||||
return cover_url
|
||||
|
||||
def image_url_processor(self, baseurl, url):
|
||||
base, sep, rest = url.rpartition('?Id=')
|
||||
img, sep2, rrest = rest.partition('&')
|
||||
return base + sep + img
|
||||
|
13
recipes/catholic_news_agency.recipe
Normal file
13
recipes/catholic_news_agency.recipe
Normal file
@ -0,0 +1,13 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1301972345(BasicNewsRecipe):
|
||||
title = u'Catholic News Agency'
|
||||
language = 'en'
|
||||
__author__ = 'Jetkey'
|
||||
oldest_article = 5
|
||||
max_articles_per_feed = 20
|
||||
|
||||
feeds = [(u'U.S. News', u'http://feeds.feedburner.com/catholicnewsagency/dailynews-us'),
|
||||
(u'Vatican', u'http://feeds.feedburner.com/catholicnewsagency/dailynews-vatican'),
|
||||
(u'Bishops Corner', u'http://feeds.feedburner.com/catholicnewsagency/columns/bishopscorner'),
|
||||
(u'Saint of the Day', u'http://feeds.feedburner.com/catholicnewsagency/saintoftheday')]
|
@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
criticadigital.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class CriticaDigital(BasicNewsRecipe):
|
||||
title = 'Critica de la Argentina'
|
||||
__author__ = 'Darko Miletic and Sujata Raman'
|
||||
description = 'Noticias de Argentina'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
language = 'es_AR'
|
||||
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = 'cp1252'
|
||||
|
||||
extra_css = '''
|
||||
h1{font-family:"Trebuchet MS";}
|
||||
h3{color:#9A0000; font-family:Tahoma; font-size:x-small;}
|
||||
h2{color:#504E53; font-family:Arial,Helvetica,sans-serif ;font-size:small;}
|
||||
#epigrafe{font-family:Arial,Helvetica,sans-serif ;color:#666666 ; font-size:x-small;}
|
||||
p {font-family:Arial,Helvetica,sans-serif;}
|
||||
#fecha{color:#858585; font-family:Tahoma; font-size:x-small;}
|
||||
#autor{color:#858585; font-family:Tahoma; font-size:x-small;}
|
||||
#hora{color:#F00000;font-family:Tahoma; font-size:x-small;}
|
||||
'''
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'class':['bloqueTitulosNoticia','cfotonota']})
|
||||
,dict(name='div', attrs={'id':'boxautor'})
|
||||
,dict(name='p', attrs={'id':'textoNota'})
|
||||
]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'class':'box300' })
|
||||
,dict(name='div', style=True )
|
||||
,dict(name='div', attrs={'class':'titcomentario'})
|
||||
,dict(name='div', attrs={'class':'comentario' })
|
||||
,dict(name='div', attrs={'class':'paginador' })
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'Politica', u'http://www.criticadigital.com/herramientas/rss.php?ch=politica' )
|
||||
,(u'Economia', u'http://www.criticadigital.com/herramientas/rss.php?ch=economia' )
|
||||
,(u'Deportes', u'http://www.criticadigital.com/herramientas/rss.php?ch=deportes' )
|
||||
,(u'Espectaculos', u'http://www.criticadigital.com/herramientas/rss.php?ch=espectaculos')
|
||||
,(u'Mundo', u'http://www.criticadigital.com/herramientas/rss.php?ch=mundo' )
|
||||
,(u'Policiales', u'http://www.criticadigital.com/herramientas/rss.php?ch=policiales' )
|
||||
,(u'Sociedad', u'http://www.criticadigital.com/herramientas/rss.php?ch=sociedad' )
|
||||
,(u'Salud', u'http://www.criticadigital.com/herramientas/rss.php?ch=salud' )
|
||||
,(u'Tecnologia', u'http://www.criticadigital.com/herramientas/rss.php?ch=tecnologia' )
|
||||
,(u'Santa Fe', u'http://www.criticadigital.com/herramientas/rss.php?ch=santa_fe' )
|
||||
]
|
||||
|
||||
def get_cover_url(self):
|
||||
cover_url = None
|
||||
index = 'http://www.criticadigital.com/impresa/'
|
||||
soup = self.index_to_soup(index)
|
||||
link_item = soup.find('div',attrs={'class':'tapa'})
|
||||
if link_item:
|
||||
cover_url = index + link_item.img['src']
|
||||
return cover_url
|
||||
|
||||
|
@ -1,72 +1,59 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
||||
__copyright__ = '2008-2011, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
cronista.com
|
||||
www.cronista.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class ElCronista(BasicNewsRecipe):
|
||||
title = 'El Cronista'
|
||||
class Pagina12(BasicNewsRecipe):
|
||||
title = 'El Cronista Comercial'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Noticias de Argentina'
|
||||
description = 'El Cronista Comercial es el Diario economico-politico mas valorado. Es la fuente mas confiable de informacion en temas de economia, finanzas y negocios enmarcados politicamente.'
|
||||
publisher = 'Cronista.com'
|
||||
category = 'news, politics, economy, finances, Argentina'
|
||||
oldest_article = 2
|
||||
language = 'es_AR'
|
||||
|
||||
max_articles_per_feed = 100
|
||||
max_articles_per_feed = 200
|
||||
no_stylesheets = True
|
||||
encoding = 'utf8'
|
||||
use_embedded_content = False
|
||||
encoding = 'cp1252'
|
||||
language = 'es_AR'
|
||||
remove_empty_feeds = True
|
||||
publication_type = 'newspaper'
|
||||
masthead_url = 'http://www.cronista.com/export/sites/diarioelcronista/arte/header-logo.gif'
|
||||
extra_css = """
|
||||
body{font-family: Arial,Helvetica,sans-serif }
|
||||
h2{font-family: Georgia,"Times New Roman",Times,serif }
|
||||
img{margin-bottom: 0.4em; display:block}
|
||||
.nom{font-weight: bold; vertical-align: baseline}
|
||||
.autor-cfoto{border-bottom: 1px solid #D2D2D2;
|
||||
border-top: 1px solid #D2D2D2;
|
||||
display: inline-block;
|
||||
margin: 0 10px 10px 0;
|
||||
padding: 10px;
|
||||
width: 210px}
|
||||
.under{font-weight: bold}
|
||||
.time{font-size: small}
|
||||
"""
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , 'news, Argentina'
|
||||
, '--publisher' , title
|
||||
]
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='table', attrs={'width':'100%' })
|
||||
,dict(name='h1' , attrs={'class':'Arialgris16normal'})
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name=['meta','link','base','iframe','object','embed'])
|
||||
,dict(attrs={'class':['user-tools','tabsmedia']})
|
||||
]
|
||||
remove_attributes = ['lang']
|
||||
remove_tags_before = dict(attrs={'class':'top'})
|
||||
remove_tags_after = dict(attrs={'class':'content-nota'})
|
||||
feeds = [(u'Ultimas noticias', u'http://www.cronista.com/rss.html')]
|
||||
|
||||
remove_tags = [dict(name='a', attrs={'class':'Arialazul12'})]
|
||||
|
||||
feeds = [
|
||||
(u'Economia' , u'http://www.cronista.com/adjuntos/8/rss/Economia_EI.xml' )
|
||||
,(u'Negocios' , u'http://www.cronista.com/adjuntos/8/rss/negocios_EI.xml' )
|
||||
,(u'Ultimo momento' , u'http://www.cronista.com/adjuntos/8/rss/ultimo_momento.xml' )
|
||||
,(u'Finanzas y Mercados' , u'http://www.cronista.com/adjuntos/8/rss/Finanzas_Mercados_EI.xml' )
|
||||
,(u'Financial Times' , u'http://www.cronista.com/adjuntos/8/rss/FT_EI.xml' )
|
||||
,(u'Opinion edicion impresa' , u'http://www.cronista.com/adjuntos/8/rss/opinion_edicion_impresa.xml' )
|
||||
,(u'Socialmente Responsables', u'http://www.cronista.com/adjuntos/8/rss/Socialmente_Responsables.xml')
|
||||
,(u'Asuntos Legales' , u'http://www.cronista.com/adjuntos/8/rss/asuntoslegales.xml' )
|
||||
,(u'IT Business' , u'http://www.cronista.com/adjuntos/8/rss/itbusiness.xml' )
|
||||
,(u'Management y RR.HH.' , u'http://www.cronista.com/adjuntos/8/rss/management.xml' )
|
||||
,(u'Inversiones Personales' , u'http://www.cronista.com/adjuntos/8/rss/inversionespersonales.xml' )
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
main, sep, rest = url.partition('.com/notas/')
|
||||
article_id, lsep, rrest = rest.partition('-')
|
||||
return 'http://www.cronista.com/interior/index.php?p=imprimir_nota&idNota=' + article_id
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
mtag = '<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'
|
||||
soup.head.insert(0,mtag)
|
||||
soup.head.base.extract()
|
||||
htext = soup.find('h1',attrs={'class':'Arialgris16normal'})
|
||||
htext.name = 'p'
|
||||
soup.prettify()
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
|
||||
def get_cover_url(self):
|
||||
cover_url = None
|
||||
index = 'http://www.cronista.com/contenidos/'
|
||||
soup = self.index_to_soup(index + 'ee.html')
|
||||
link_item = soup.find('a',attrs={'href':"javascript:Close()"})
|
||||
if link_item:
|
||||
cover_url = index + link_item.img['src']
|
||||
return cover_url
|
||||
|
||||
|
BIN
recipes/icons/athens_news.png
Normal file
BIN
recipes/icons/athens_news.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 514 B |
BIN
recipes/icons/buenosaireseconomico.png
Normal file
BIN
recipes/icons/buenosaireseconomico.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 400 B |
Binary file not shown.
Before Width: | Height: | Size: 770 B After Width: | Height: | Size: 1.1 KiB |
@ -1,5 +1,5 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008-2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
__copyright__ = '2008-2011, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
infobae.com
|
||||
'''
|
||||
@ -9,7 +9,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
class Infobae(BasicNewsRecipe):
|
||||
title = 'Infobae.com'
|
||||
__author__ = 'Darko Miletic and Sujata Raman'
|
||||
description = 'Informacion Libre las 24 horas'
|
||||
description = 'Infobae.com es el sitio de noticias con mayor actualizacion de Latinoamérica. Noticias actualizadas las 24 horas, los 365 días del año.'
|
||||
publisher = 'Infobae.com'
|
||||
category = 'news, politics, Argentina'
|
||||
oldest_article = 1
|
||||
@ -17,13 +17,13 @@ class Infobae(BasicNewsRecipe):
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
language = 'es_AR'
|
||||
encoding = 'cp1252'
|
||||
masthead_url = 'http://www.infobae.com/imgs/header/header.gif'
|
||||
remove_javascript = True
|
||||
encoding = 'utf8'
|
||||
masthead_url = 'http://www.infobae.com/media/img/static/logo-infobae.gif'
|
||||
remove_empty_feeds = True
|
||||
extra_css = '''
|
||||
body{font-family:Arial,Helvetica,sans-serif;}
|
||||
.popUpTitulo{color:#0D4261; font-size: xx-large}
|
||||
body{font-family: Arial,Helvetica,sans-serif}
|
||||
img{display: block}
|
||||
.categoria{font-size: small; text-transform: uppercase}
|
||||
'''
|
||||
|
||||
conversion_options = {
|
||||
@ -31,26 +31,44 @@ class Infobae(BasicNewsRecipe):
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
, 'linearize_tables' : True
|
||||
}
|
||||
|
||||
|
||||
|
||||
keep_only_tags = [dict(attrs={'class':['titularnota','nota','post-title','post-entry','entry-title','entry-info','entry-content']})]
|
||||
remove_tags_after = dict(attrs={'class':['interior-noticia','nota-desc','tags']})
|
||||
remove_tags = [
|
||||
dict(name=['base','meta','link','iframe','object','embed','ins'])
|
||||
,dict(attrs={'class':['barranota','tags']})
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'Noticias' , u'http://www.infobae.com/adjuntos/html/RSS/hoy.xml' )
|
||||
,(u'Salud' , u'http://www.infobae.com/adjuntos/html/RSS/salud.xml' )
|
||||
,(u'Tecnologia', u'http://www.infobae.com/adjuntos/html/RSS/tecnologia.xml')
|
||||
,(u'Deportes' , u'http://www.infobae.com/adjuntos/html/RSS/deportes.xml' )
|
||||
(u'Saludable' , u'http://www.infobae.com/rss/saludable.xml')
|
||||
,(u'Economia' , u'http://www.infobae.com/rss/economia.xml' )
|
||||
,(u'En Numeros', u'http://www.infobae.com/rss/rating.xml' )
|
||||
,(u'Finanzas' , u'http://www.infobae.com/rss/finanzas.xml' )
|
||||
,(u'Mundo' , u'http://www.infobae.com/rss/mundo.xml' )
|
||||
,(u'Sociedad' , u'http://www.infobae.com/rss/sociedad.xml' )
|
||||
,(u'Politica' , u'http://www.infobae.com/rss/politica.xml' )
|
||||
,(u'Deportes' , u'http://www.infobae.com/rss/deportes.xml' )
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
article_part = url.rpartition('/')[2]
|
||||
article_id= article_part.partition('-')[0]
|
||||
return 'http://www.infobae.com/notas/nota_imprimir.php?Idx=' + article_id
|
||||
|
||||
def postprocess_html(self, soup, first):
|
||||
for tag in soup.findAll(name='strong'):
|
||||
tag.name = 'b'
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
for item in soup.findAll('a'):
|
||||
limg = item.find('img')
|
||||
if item.string is not None:
|
||||
str = item.string
|
||||
item.replaceWith(str)
|
||||
else:
|
||||
if limg:
|
||||
item.name = 'div'
|
||||
item.attrs = []
|
||||
else:
|
||||
str = self.tag_to_string(item)
|
||||
item.replaceWith(str)
|
||||
for item in soup.findAll('img'):
|
||||
if not item.has_key('alt'):
|
||||
item['alt'] = 'image'
|
||||
return soup
|
||||
|
||||
|
||||
|
||||
|
@ -99,7 +99,7 @@ class LeMonde(BasicNewsRecipe):
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'class':['contenu']})
|
||||
]
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'class':['LM_atome']})]
|
||||
remove_tags_after = [dict(id='appel_temoignage')]
|
||||
|
||||
def get_article_url(self, article):
|
||||
|
53
recipes/todays_zaman.recipe
Normal file
53
recipes/todays_zaman.recipe
Normal file
@ -0,0 +1,53 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class TodaysZaman_en(BasicNewsRecipe):
|
||||
title = u'Todays Zaman'
|
||||
__author__ = u'thomass'
|
||||
description = 'a Turkey based daily for national and international news in the fields of business, diplomacy, politics, culture, arts, sports and economics, in addition to commentaries, specials and features'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed =100
|
||||
no_stylesheets = True
|
||||
#delay = 1
|
||||
#use_embedded_content = False
|
||||
encoding = 'utf-8'
|
||||
#publisher = ' '
|
||||
category = 'news, haberler,TR,gazete'
|
||||
language = 'en_TR'
|
||||
publication_type = 'newspaper'
|
||||
#extra_css = ' body{ font-family: Verdana,Helvetica,Arial,sans-serif } .introduction{font-weight: bold} .story-feature{display: block; padding: 0; border: 1px solid; width: 40%; font-size: small} .story-feature h2{text-align: center; text-transform: uppercase} '
|
||||
#keep_only_tags = [dict(name='font', attrs={'class':['newsDetail','agenda2NewsSpot']}),dict(name='span', attrs={'class':['agenda2Title']}),dict(name='div', attrs={'id':['gallery']})]
|
||||
keep_only_tags = [dict(name='h1', attrs={'class':['georgia_30']}),dict(name='span', attrs={'class':['left-date','detailDate','detailCName']}),dict(name='td', attrs={'id':['newsSpot','newsText']})] #resim ekleme: ,dict(name='div', attrs={'id':['gallery','detailDate',]})
|
||||
|
||||
remove_attributes = ['aria-describedby']
|
||||
remove_tags = [dict(name='img', attrs={'src':['/images/icon_print.gif','http://gmodules.com/ig/images/plus_google.gif','/images/template/jazz/agenda/i1.jpg', 'http://medya.todayszaman.com/todayszaman/images/logo/logo.bmp']}),dict(name='hr', attrs={'class':[ 'interactive-hr']}),dict(name='div', attrs={'class':[ 'empty_height_18','empty_height_9']}) ,dict(name='td', attrs={'id':[ 'superTitle']}),dict(name='span', attrs={'class':[ 't-count enabled t-count-focus']}),dict(name='a', attrs={'id':[ 'count']}),dict(name='td', attrs={'class':[ 'left-date']}) ]
|
||||
cover_img_url = 'http://medya.todayszaman.com/todayszaman/images/logo/logo.bmp'
|
||||
masthead_url = 'http://medya.todayszaman.com/todayszaman/images/logo/logo.bmp'
|
||||
remove_empty_feeds= True
|
||||
# remove_attributes = ['width','height']
|
||||
|
||||
feeds = [
|
||||
( u'Home', u'http://www.todayszaman.com/rss?sectionId=0'),
|
||||
( u'News', u'http://www.todayszaman.com/rss?sectionId=100'),
|
||||
( u'Business', u'http://www.todayszaman.com/rss?sectionId=105'),
|
||||
( u'Interviews', u'http://www.todayszaman.com/rss?sectionId=8'),
|
||||
( u'Columnists', u'http://www.todayszaman.com/rss?sectionId=6'),
|
||||
( u'Op-Ed', u'http://www.todayszaman.com/rss?sectionId=109'),
|
||||
( u'Arts & Culture', u'http://www.todayszaman.com/rss?sectionId=110'),
|
||||
( u'Expat Zone', u'http://www.todayszaman.com/rss?sectionId=132'),
|
||||
( u'Sports', u'http://www.todayszaman.com/rss?sectionId=5'),
|
||||
( u'Features', u'http://www.todayszaman.com/rss?sectionId=116'),
|
||||
( u'Travel', u'http://www.todayszaman.com/rss?sectionId=117'),
|
||||
( u'Leisure', u'http://www.todayszaman.com/rss?sectionId=118'),
|
||||
( u'Weird But True', u'http://www.todayszaman.com/rss?sectionId=134'),
|
||||
( u'Life', u'http://www.todayszaman.com/rss?sectionId=133'),
|
||||
( u'Health', u'http://www.todayszaman.com/rss?sectionId=126'),
|
||||
( u'Press Review', u'http://www.todayszaman.com/rss?sectionId=130'),
|
||||
( u'Todays think tanks', u'http://www.todayszaman.com/rss?sectionId=159'),
|
||||
|
||||
]
|
||||
|
||||
#def preprocess_html(self, soup):
|
||||
# return self.adeify_images(soup)
|
||||
#def print_version(self, url): #there is a probem caused by table format
|
||||
#return url.replace('http://www.todayszaman.com/newsDetail_getNewsById.action?load=detay&', 'http://www.todayszaman.com/newsDetail_openPrintPage.action?')
|
||||
|
@ -1,20 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class ZamanRecipe(BasicNewsRecipe):
|
||||
title = u'Zaman'
|
||||
__author__ = u'Deniz Og\xfcz'
|
||||
language = 'tr'
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 10
|
||||
class Zaman (BasicNewsRecipe):
|
||||
|
||||
cover_url = 'http://medya.zaman.com.tr/zamantryeni/pics/zamanonline.gif'
|
||||
feeds = [(u'Gundem', u'http://www.zaman.com.tr/gundem.rss'),
|
||||
(u'Son Dakika', u'http://www.zaman.com.tr/sondakika.rss'),
|
||||
(u'Spor', u'http://www.zaman.com.tr/spor.rss'),
|
||||
(u'Ekonomi', u'http://www.zaman.com.tr/ekonomi.rss'),
|
||||
(u'Politika', u'http://www.zaman.com.tr/politika.rss'),
|
||||
(u'D\u0131\u015f Haberler', u'http://www.zaman.com.tr/dishaberler.rss'),
|
||||
(u'Yazarlar', u'http://www.zaman.com.tr/yazarlar.rss'),]
|
||||
title = u'ZAMAN Gazetesi'
|
||||
__author__ = u'thomass'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed =100
|
||||
# no_stylesheets = True
|
||||
#delay = 1
|
||||
#use_embedded_content = False
|
||||
encoding = 'ISO 8859-9'
|
||||
publisher = 'Zaman'
|
||||
category = 'news, haberler,TR,gazete'
|
||||
language = 'tr'
|
||||
publication_type = 'newspaper '
|
||||
extra_css = ' body{ font-family: Verdana,Helvetica,Arial,sans-serif } .introduction{font-weight: bold} .story-feature{display: block; padding: 0; border: 1px solid; width: 40%; font-size: small} .story-feature h2{text-align: center; text-transform: uppercase} '
|
||||
conversion_options = {
|
||||
'tags' : category
|
||||
,'language' : language
|
||||
,'publisher' : publisher
|
||||
,'linearize_tables': False
|
||||
}
|
||||
cover_img_url = 'https://fbcdn-profile-a.akamaihd.net/hprofile-ak-snc4/188140_81722291869_2111820_n.jpg'
|
||||
masthead_url = 'http://medya.zaman.com.tr/extentions/zaman.com.tr/img/section/logo-section.png'
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace('www.zaman.com.tr/haber.do?', 'www.zaman.com.tr/yazdir.do?')
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':[ 'news-detail-content']}), dict(name='td', attrs={'class':['columnist-detail','columnist_head']}) ]
|
||||
remove_tags = [ dict(name='div', attrs={'id':['news-detail-news-text-font-size','news-detail-gallery','news-detail-news-bottom-social']}),dict(name='div', attrs={'class':['radioEmbedBg','radyoProgramAdi']}),dict(name='a', attrs={'class':['webkit-html-attribute-value webkit-html-external-link']}),dict(name='table', attrs={'id':['yaziYorumTablosu']}),dict(name='img', attrs={'src':['http://medya.zaman.com.tr/pics/paylas.gif','http://medya.zaman.com.tr/extentions/zaman.com.tr/img/columnist/ma-16.png']})]
|
||||
|
||||
|
||||
#remove_attributes = ['width','height']
|
||||
remove_empty_feeds= True
|
||||
|
||||
feeds = [
|
||||
( u'Anasayfa', u'http://www.zaman.com.tr/anasayfa.rss'),
|
||||
( u'Son Dakika', u'http://www.zaman.com.tr/sondakika.rss'),
|
||||
( u'En çok Okunanlar', u'http://www.zaman.com.tr/max_all.rss'),
|
||||
( u'Gündem', u'http://www.zaman.com.tr/gundem.rss'),
|
||||
( u'Yazarlar', u'http://www.zaman.com.tr/yazarlar.rss'),
|
||||
( u'Politika', u'http://www.zaman.com.tr/politika.rss'),
|
||||
( u'Ekonomi', u'http://www.zaman.com.tr/ekonomi.rss'),
|
||||
( u'Dış Haberler', u'http://www.zaman.com.tr/dishaberler.rss'),
|
||||
( u'Yorumlar', u'http://www.zaman.com.tr/yorumlar.rss'),
|
||||
( u'Röportaj', u'http://www.zaman.com.tr/roportaj.rss'),
|
||||
( u'Spor', u'http://www.zaman.com.tr/spor.rss'),
|
||||
( u'Kürsü', u'http://www.zaman.com.tr/kursu.rss'),
|
||||
( u'Kültür Sanat', u'http://www.zaman.com.tr/kultursanat.rss'),
|
||||
( u'Televizyon', u'http://www.zaman.com.tr/televizyon.rss'),
|
||||
( u'Manşet', u'http://www.zaman.com.tr/manset.rss'),
|
||||
|
||||
|
||||
]
|
||||
|
@ -292,13 +292,17 @@ maximum_resort_levels = 5
|
||||
generate_cover_title_font = None
|
||||
generate_cover_foot_font = None
|
||||
|
||||
#: Control behavior of double clicks on the book list
|
||||
# Behavior of doubleclick on the books list. Choices: open_viewer, do_nothing,
|
||||
#: Control behavior of the book list
|
||||
# You can control the behavior of doubleclicks on the books list.
|
||||
# Choices: open_viewer, do_nothing,
|
||||
# edit_cell, edit_metadata. Selecting edit_metadata has the side effect of
|
||||
# disabling editing a field using a single click.
|
||||
# Default: open_viewer.
|
||||
# Example: doubleclick_on_library_view = 'do_nothing'
|
||||
# You can also control whether the book list scrolls horizontal per column or
|
||||
# per pixel. Default is per column.
|
||||
doubleclick_on_library_view = 'open_viewer'
|
||||
horizontal_scrolling_per_column = True
|
||||
|
||||
#: Language to use when sorting.
|
||||
# Setting this tweak will force sorting to use the
|
||||
|
@ -53,6 +53,13 @@ SQLite
|
||||
|
||||
Put sqlite3*.h from the sqlite windows amlgamation in ~/sw/include
|
||||
|
||||
APSW
|
||||
-----
|
||||
|
||||
Download source from http://code.google.com/p/apsw/downloads/list and run in visual studio prompt
|
||||
|
||||
python setup.py fetch --all build --missing-checksum-ok --enable-all-extensions install test
|
||||
|
||||
OpenSSL
|
||||
--------
|
||||
|
||||
|
@ -4,7 +4,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = u'calibre'
|
||||
numeric_version = (0, 8, 7)
|
||||
numeric_version = (0, 8, 8)
|
||||
__version__ = u'.'.join(map(unicode, numeric_version))
|
||||
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
|
@ -611,7 +611,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,
|
||||
GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, LUMIREAD, ALURATEK_COLOR,
|
||||
TREKSTOR, EEEREADER, NEXTBOOK, ADAM)
|
||||
TREKSTOR, EEEREADER, NEXTBOOK, ADAM, MOOVYBOOK)
|
||||
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
|
||||
@ -746,6 +746,7 @@ plugins += [
|
||||
EEEREADER,
|
||||
NEXTBOOK,
|
||||
ADAM,
|
||||
MOOVYBOOK,
|
||||
ITUNES,
|
||||
BOEYE_BEX,
|
||||
BOEYE_BDX,
|
||||
@ -1382,7 +1383,7 @@ class StoreOpenBooksStore(StoreBase):
|
||||
name = 'Open Books'
|
||||
description = u'Comprehensive listing of DRM free ebooks from a variety of sources provided by users of calibre.'
|
||||
actual_plugin = 'calibre.gui2.store.stores.open_books_plugin:OpenBooksStore'
|
||||
|
||||
|
||||
drm_free_only = True
|
||||
headquarters = 'US'
|
||||
|
||||
|
@ -48,6 +48,12 @@ class Table(object):
|
||||
|
||||
class OneToOneTable(Table):
|
||||
|
||||
'''
|
||||
Represents data that is unique per book (it may not actually be unique) but
|
||||
each item is assigned to a book in a one-to-one mapping. For example: uuid,
|
||||
timestamp, size, etc.
|
||||
'''
|
||||
|
||||
def read(self, db):
|
||||
self.book_col_map = {}
|
||||
idcol = 'id' if self.metadata['table'] == 'books' else 'book'
|
||||
@ -66,6 +72,13 @@ class SizeTable(OneToOneTable):
|
||||
|
||||
class ManyToOneTable(Table):
|
||||
|
||||
'''
|
||||
Represents data where one data item can map to many books, for example:
|
||||
series or publisher.
|
||||
|
||||
Each book however has only one value for data of this type.
|
||||
'''
|
||||
|
||||
def read(self, db):
|
||||
self.id_map = {}
|
||||
self.extra_map = {}
|
||||
@ -91,6 +104,12 @@ class ManyToOneTable(Table):
|
||||
|
||||
class ManyToManyTable(ManyToOneTable):
|
||||
|
||||
'''
|
||||
Represents data that has a many-to-many mapping with books. i.e. each book
|
||||
can have more than one value and each value can be mapped to more than one
|
||||
book. For example: tags or authors.
|
||||
'''
|
||||
|
||||
def read_maps(self, db):
|
||||
for row in db.conn.execute(
|
||||
'SELECT book, {0} FROM books_{1}_link'.format(
|
||||
|
@ -101,6 +101,9 @@ class ANDROID(USBMS):
|
||||
# ZTE
|
||||
0x19d2 : { 0x1353 : [0x226] },
|
||||
|
||||
# Advent
|
||||
0x0955 : { 0x7100 : [0x9999] }, # This is the same as the Notion Ink Adam
|
||||
|
||||
}
|
||||
EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books']
|
||||
EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to '
|
||||
|
@ -120,7 +120,7 @@ class DriverBase(DeviceConfig, DevicePlugin):
|
||||
_('Cache covers from iTunes/iBooks') +
|
||||
':::' +
|
||||
_('Enable to cache and display covers from iTunes/iBooks'),
|
||||
_("'Copy files to iTunes Media folder" u"\u2026" "' is enabled in iTunes Preferences|Advanced") +
|
||||
_(u'"Copy files to iTunes Media folder %s" is enabled in iTunes Preferences|Advanced')%u'\u2026' +
|
||||
':::' +
|
||||
_("<p>This setting should match your iTunes <i>Preferences</i>|<i>Advanced</i> setting.</p>"
|
||||
"<p>Disabling will store copies of books transferred to iTunes in your calibre configuration directory.</p>"
|
||||
|
@ -329,3 +329,25 @@ class NEXTBOOK(USBMS):
|
||||
f.write(metadata.thumbnail[-1])
|
||||
'''
|
||||
|
||||
class MOOVYBOOK(USBMS):
|
||||
|
||||
name = 'Moovybook device interface'
|
||||
gui_name = 'Moovybook'
|
||||
description = _('Communicate with the Moovybook Reader')
|
||||
author = 'Kovid Goyal'
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
|
||||
# Ordered list of supported formats
|
||||
FORMATS = ['epub', 'txt', 'pdf']
|
||||
|
||||
VENDOR_ID = [0x1cae]
|
||||
PRODUCT_ID = [0x9b08]
|
||||
BCD = [0x02]
|
||||
|
||||
EBOOK_DIR_MAIN = ''
|
||||
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
def get_main_ebook_dir(self, for_upload=False):
|
||||
return 'Books' if for_upload else self.EBOOK_DIR_MAIN
|
||||
|
||||
|
@ -260,7 +260,8 @@ class ChooseLibraryAction(InterfaceAction):
|
||||
'The files remain on your computer, if you want '
|
||||
'to delete them, you will have to do so manually.') % loc,
|
||||
show=True)
|
||||
open_local_file(loc)
|
||||
if os.path.exists(loc):
|
||||
open_local_file(loc)
|
||||
|
||||
def backup_status(self, location):
|
||||
dirty_text = 'no'
|
||||
|
@ -5,8 +5,6 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import urllib2
|
||||
|
||||
from PyQt4.Qt import (QPixmap, QSize, QWidget, Qt, pyqtSignal, QUrl,
|
||||
QPropertyAnimation, QEasingCurve, QApplication, QFontInfo,
|
||||
QSizePolicy, QPainter, QRect, pyqtProperty, QLayout, QPalette, QMenu)
|
||||
@ -100,6 +98,14 @@ def render_data(mi, use_roman_numbers=True, all_fields=False):
|
||||
val = force_unicode(val)
|
||||
ans.append((field,
|
||||
u'<td class="comments" colspan="2">%s</td>'%comments_to_html(val)))
|
||||
elif metadata['datatype'] == 'composite' and \
|
||||
metadata['display'].get('contains_html', False):
|
||||
val = getattr(mi, field)
|
||||
if val:
|
||||
val = force_unicode(val)
|
||||
ans.append((field,
|
||||
u'<td class="title">%s</td><td>%s</td>'%
|
||||
(name, comments_to_html(val))))
|
||||
elif field == 'path':
|
||||
if mi.path:
|
||||
path = force_unicode(mi.path, filesystem_encoding)
|
||||
|
@ -73,6 +73,7 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
self.last_search = None
|
||||
self.current_column = None
|
||||
self.current_item = None
|
||||
self.no_valid_items = False
|
||||
|
||||
self.items.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.items.currentTextChanged.connect(self.item_selected)
|
||||
@ -119,6 +120,8 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
|
||||
# search button
|
||||
def do_search(self):
|
||||
if self.no_valid_items:
|
||||
return
|
||||
if self.last_search is not None:
|
||||
self.gui.search.set_search_string(self.last_search)
|
||||
|
||||
@ -132,6 +135,8 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
|
||||
# clicks on the items listWidget
|
||||
def item_selected(self, txt):
|
||||
if self.no_valid_items:
|
||||
return
|
||||
self.fill_in_books_box(unicode(txt))
|
||||
|
||||
# Given a cell in the library view, display the information
|
||||
@ -144,6 +149,7 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
# Only show items for categories
|
||||
if not self.db.field_metadata[key]['is_category']:
|
||||
if self.current_key is None:
|
||||
self.indicate_no_items()
|
||||
return
|
||||
key = self.current_key
|
||||
self.items_label.setText('{0} ({1})'.format(
|
||||
@ -157,6 +163,7 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
vals = mi.get(key, None)
|
||||
|
||||
if vals:
|
||||
self.no_valid_items = False
|
||||
if not isinstance(vals, list):
|
||||
vals = [vals]
|
||||
vals.sort(key=sort_key)
|
||||
@ -170,8 +177,19 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
self.current_key = key
|
||||
|
||||
self.fill_in_books_box(vals[0])
|
||||
else:
|
||||
self.indicate_no_items()
|
||||
|
||||
self.items.blockSignals(False)
|
||||
|
||||
def indicate_no_items(self):
|
||||
print 'no items'
|
||||
self.no_valid_items = True
|
||||
self.items.clear()
|
||||
self.items.addItem(QListWidgetItem(_('**No items found**')))
|
||||
self.books_label.setText(_('Click in a column in the library view '
|
||||
'to see the information for that book'))
|
||||
|
||||
def fill_in_books_box(self, selected_item):
|
||||
self.current_item = selected_item
|
||||
# Do a bit of fix-up on the items so that the search works.
|
||||
@ -185,7 +203,8 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
self.db.data.search_restriction)
|
||||
|
||||
self.books_table.setRowCount(len(books))
|
||||
self.books_label.setText(_('Books with selected item: {0}').format(len(books)))
|
||||
self.books_label.setText(_('Books with selected item "{0}": {1}').
|
||||
format(selected_item, len(books)))
|
||||
|
||||
select_item = None
|
||||
self.books_table.setSortingEnabled(False)
|
||||
@ -235,6 +254,8 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
self.save_state()
|
||||
|
||||
def book_doubleclicked(self, row, column):
|
||||
if self.no_valid_items:
|
||||
return
|
||||
book_id = self.books_table.item(row, 0).data(Qt.UserRole).toInt()[0]
|
||||
self.view.select_rows([book_id])
|
||||
modifiers = int(QApplication.keyboardModifiers())
|
||||
|
@ -51,6 +51,9 @@ class BooksView(QTableView): # {{{
|
||||
def __init__(self, parent, modelcls=BooksModel, use_edit_metadata_dialog=True):
|
||||
QTableView.__init__(self, parent)
|
||||
|
||||
if not tweaks['horizontal_scrolling_per_column']:
|
||||
self.setHorizontalScrollMode(self.ScrollPerPixel)
|
||||
|
||||
self.setEditTriggers(self.EditKeyPressed)
|
||||
if tweaks['doubleclick_on_library_view'] == 'edit_cell':
|
||||
self.setEditTriggers(self.DoubleClicked|self.editTriggers())
|
||||
@ -110,6 +113,7 @@ class BooksView(QTableView): # {{{
|
||||
self.column_header.sectionMoved.connect(self.save_state)
|
||||
self.column_header.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
self.column_header.customContextMenuRequested.connect(self.show_column_header_context_menu)
|
||||
self.column_header.sectionResized.connect(self.column_resized, Qt.QueuedConnection)
|
||||
# }}}
|
||||
|
||||
self._model.database_changed.connect(self.database_changed)
|
||||
@ -214,6 +218,9 @@ class BooksView(QTableView): # {{{
|
||||
|
||||
|
||||
self.column_header_context_menu.addSeparator()
|
||||
self.column_header_context_menu.addAction(
|
||||
_('Shrink column if it is too wide to fit'),
|
||||
partial(self.resize_column_to_fit, column=self.column_map[idx]))
|
||||
self.column_header_context_menu.addAction(
|
||||
_('Restore default layout'),
|
||||
partial(self.column_header_context_handler,
|
||||
@ -235,13 +242,8 @@ class BooksView(QTableView): # {{{
|
||||
self.selected_ids = [idc(r) for r in selected_rows]
|
||||
|
||||
def sorting_done(self, indexc):
|
||||
if self.selected_ids:
|
||||
indices = [self.model().index(indexc(i), 0) for i in
|
||||
self.selected_ids]
|
||||
sm = self.selectionModel()
|
||||
for idx in indices:
|
||||
sm.select(idx, sm.Select|sm.Rows)
|
||||
self.scroll_to_row(indices[0].row())
|
||||
self.select_rows(self.selected_ids, using_ids=True, change_current=True,
|
||||
scroll=True)
|
||||
self.selected_ids = []
|
||||
|
||||
def sort_by_named_field(self, field, order, reset=True):
|
||||
@ -456,7 +458,9 @@ class BooksView(QTableView): # {{{
|
||||
traceback.print_exc()
|
||||
old_state['sort_history'] = sh
|
||||
|
||||
self.column_header.blockSignals(True)
|
||||
self.apply_state(old_state)
|
||||
self.column_header.blockSignals(False)
|
||||
|
||||
# Resize all rows to have the correct height
|
||||
if self.model().rowCount(QModelIndex()) > 0:
|
||||
@ -465,6 +469,19 @@ class BooksView(QTableView): # {{{
|
||||
|
||||
self.was_restored = True
|
||||
|
||||
def resize_column_to_fit(self, column):
|
||||
col = self.column_map.index(column)
|
||||
self.column_resized(col, self.columnWidth(col), self.columnWidth(col))
|
||||
|
||||
def column_resized(self, col, old_size, new_size):
|
||||
# arbitrary: scroll bar + header + some
|
||||
max_width = self.width() - (self.verticalScrollBar().width() +
|
||||
self.verticalHeader().width() + 10)
|
||||
if new_size > max_width:
|
||||
self.column_header.blockSignals(True)
|
||||
self.setColumnWidth(col, max_width)
|
||||
self.column_header.blockSignals(False)
|
||||
|
||||
# }}}
|
||||
|
||||
# Initialization/Delegate Setup {{{
|
||||
|
@ -127,6 +127,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
||||
self.composite_sort_by.setCurrentIndex(sb)
|
||||
self.composite_make_category.setChecked(
|
||||
c['display'].get('make_category', False))
|
||||
self.composite_make_category.setChecked(
|
||||
c['display'].get('contains_html', False))
|
||||
elif ct == 'enumeration':
|
||||
self.enum_box.setText(','.join(c['display'].get('enum_values', [])))
|
||||
self.enum_colors.setText(','.join(c['display'].get('enum_colors', [])))
|
||||
@ -141,6 +143,21 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
||||
|
||||
all_colors = [unicode(s) for s in list(QColor.colorNames())]
|
||||
self.enum_colors_label.setToolTip('<p>' + ', '.join(all_colors) + '</p>')
|
||||
|
||||
self.composite_contains_html.setToolTip('<p>' +
|
||||
_('If checked, this column will be displayed as HTML in '
|
||||
'book details and the content server. This can be used to '
|
||||
'construct links with the template language. For example, '
|
||||
'the template '
|
||||
'<pre><big><b>{title}</b></big>'
|
||||
'{series:| [|}{series_index:| [|]]}</pre>'
|
||||
'will create a field displaying the title in bold large '
|
||||
'characters, along with the series, for example <br>"<big><b>'
|
||||
'An Oblique Approach</b></big> [Belisarius [1]]". The template '
|
||||
'<pre><a href="http://www.beam-ebooks.de/ebook/{identifiers'
|
||||
':select(beam)}">Beam book</a></pre> '
|
||||
'will generate a link to the book on the Beam ebooks site.')
|
||||
+ '</p>')
|
||||
self.exec_()
|
||||
|
||||
def shortcut_activated(self, url):
|
||||
@ -179,7 +196,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
||||
getattr(self, 'date_format_'+x).setVisible(col_type == 'datetime')
|
||||
getattr(self, 'number_format_'+x).setVisible(col_type in ['int', 'float'])
|
||||
for x in ('box', 'default_label', 'label', 'sort_by', 'sort_by_label',
|
||||
'make_category'):
|
||||
'make_category', 'contains_html'):
|
||||
getattr(self, 'composite_'+x).setVisible(col_type in ['composite', '*composite'])
|
||||
for x in ('box', 'default_label', 'label', 'colors', 'colors_label'):
|
||||
getattr(self, 'enum_'+x).setVisible(col_type == 'enumeration')
|
||||
@ -257,6 +274,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
||||
'composite_sort': ['text', 'number', 'date', 'bool']
|
||||
[self.composite_sort_by.currentIndex()],
|
||||
'make_category': self.composite_make_category.isChecked(),
|
||||
'contains_html': self.composite_contains_html.isChecked(),
|
||||
}
|
||||
elif col_type == 'enumeration':
|
||||
if not unicode(self.enum_box.text()).strip():
|
||||
|
@ -294,6 +294,13 @@ and end with <code>}</code> You can have text before and after the f
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="composite_contains_html">
|
||||
<property name="text">
|
||||
<string>Show as HTML in book details</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_24">
|
||||
<property name="sizePolicy">
|
||||
|
@ -31,7 +31,7 @@ class SaveTemplate(QWidget, Ui_Form):
|
||||
(var, FORMAT_ARG_DESCS[var]))
|
||||
rows.append(u'<tr><td>%s </td><td> </td><td>%s</td></tr>'%(
|
||||
_('Any custom field'),
|
||||
_('The lookup name of any custom field. These names begin with "#")')))
|
||||
_('The lookup name of any custom field (these names begin with "#").')))
|
||||
table = u'<table>%s</table>'%(u'\n'.join(rows))
|
||||
self.template_variables.setText(table)
|
||||
|
||||
|
@ -30,6 +30,9 @@ class ArchiveOrgStore(BasicStoreConfig, OpenSearchStore):
|
||||
The opensearch feed only returns a subset of formats that are available.
|
||||
We want to get a list of all formats that the user can get.
|
||||
'''
|
||||
from calibre import browser
|
||||
from contextlib import closing
|
||||
from lxml import html
|
||||
br = browser()
|
||||
with closing(br.open(search_result.detail_item, timeout=timeout)) as nf:
|
||||
idata = html.fromstring(nf.read())
|
||||
|
@ -7,7 +7,6 @@ __copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import random
|
||||
import re
|
||||
from contextlib import closing
|
||||
|
||||
from lxml import html
|
||||
@ -22,13 +21,13 @@ from calibre.gui2.store.search_result import SearchResult
|
||||
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||
|
||||
class BNStore(BasicStoreConfig, StorePlugin):
|
||||
|
||||
|
||||
def open(self, parent=None, detail_item=None, external=False):
|
||||
pub_id = 'sHa5EXvYOwA'
|
||||
# Use Kovid's affiliate id 30% of the time.
|
||||
if random.randint(1, 10) in (1, 2, 3):
|
||||
pub_id = '0dsO3kDu/AU'
|
||||
|
||||
|
||||
base_url = 'http://click.linksynergy.com/fs-bin/click?id=%s&subid=&offerid=229293.1&type=10&tmpid=8433&RD_PARM1=' % pub_id
|
||||
url = base_url + 'http%253A%252F%252Fwww.barnesandnoble.com%252F'
|
||||
|
||||
@ -46,27 +45,27 @@ class BNStore(BasicStoreConfig, StorePlugin):
|
||||
def search(self, query, max_results=10, timeout=60):
|
||||
query = query.replace(' ', '-')
|
||||
url = 'http://www.barnesandnoble.com/s/%s?store=ebook&sze=%s' % (query, max_results)
|
||||
|
||||
|
||||
br = browser()
|
||||
|
||||
|
||||
counter = max_results
|
||||
with closing(br.open(url, timeout=timeout)) as f:
|
||||
doc = html.fromstring(f.read())
|
||||
for data in doc.xpath('//ul[contains(@class, "result-set")]/li[contains(@class, "result")]'):
|
||||
if counter <= 0:
|
||||
break
|
||||
|
||||
|
||||
id = ''.join(data.xpath('.//div[contains(@class, "image")]/a/@href'))
|
||||
if not id:
|
||||
continue
|
||||
cover_url = ''.join(data.xpath('.//div[contains(@class, "image")]//img/@src'))
|
||||
|
||||
|
||||
title = ''.join(data.xpath('.//p[@class="title"]//span[@class="name"]/text()'))
|
||||
author = ', '.join(data.xpath('.//ul[@class="contributors"]//li[position()>1]//a/text()'))
|
||||
price = ''.join(data.xpath('.//table[@class="displayed-formats"]//a[@class="subtle"]/text()'))
|
||||
|
||||
|
||||
counter -= 1
|
||||
|
||||
|
||||
s = SearchResult()
|
||||
s.cover_url = cover_url
|
||||
s.title = title.strip()
|
||||
|
@ -575,7 +575,9 @@ class TagsView(QTreeView): # {{{
|
||||
expanded_categories, state_map = self.get_state()
|
||||
self._model.rebuild_node_tree(state_map=state_map)
|
||||
for category in expanded_categories:
|
||||
self.expand(self._model.index_for_category(category))
|
||||
idx = self._model.index_for_category(category)
|
||||
if idx is not None and idx.isValid():
|
||||
self.expand(idx)
|
||||
self.show_item_at_path(path)
|
||||
|
||||
def show_item_at_path(self, path, box=False,
|
||||
|
@ -795,7 +795,9 @@ class BrowseServer(object):
|
||||
list(mi.get_all_user_metadata(False).items()):
|
||||
if m['is_custom'] and field not in displayed_custom_fields:
|
||||
continue
|
||||
if m['datatype'] == 'comments' or field == 'comments':
|
||||
if m['datatype'] == 'comments' or field == 'comments' or (
|
||||
m['datatype'] == 'composite' and \
|
||||
m['display'].get('contains_html', False)):
|
||||
val = mi.get(field, '')
|
||||
if val and val.strip():
|
||||
comments.append((m['name'], comments_to_html(val)))
|
||||
|
@ -186,7 +186,8 @@ def ACQUISITION_ENTRY(item, version, db, updated, CFM, CKEYS, prefix):
|
||||
CFM[key]['is_multiple']['ui_to_list'],
|
||||
ignore_max=True, no_tag_count=True,
|
||||
joinval=CFM[key]['is_multiple']['list_to_ui']))))
|
||||
elif datatype == 'comments':
|
||||
elif datatype == 'comments' or (CFM[key]['datatype'] == 'composite' and
|
||||
CFM[key]['display'].get('contains_html', False)):
|
||||
extra.append('%s: %s<br />'%(xml(name), comments_to_html(unicode(val))))
|
||||
else:
|
||||
extra.append('%s: %s<br />'%(xml(name), xml(unicode(val))))
|
||||
|
@ -56,7 +56,7 @@ You should not change the files in this resources folder, as your changes will g
|
||||
|app| will automatically use your custom file in preference to the builtin one the next time it is started.
|
||||
|
||||
For example, if you wanted to change the icon for the :guilabel:`Remove books` action, you would first look in the builtin resources folder and see that the relevant file is
|
||||
:file:`resources/images/trash.svg`. Assuming you have an alternate icon in svg format called :file:`mytrash.svg` you would save it in the configuration directory as :file:`resources/images/trash.svg`. All the icons used by the calibre user interface are in :file:`resources/images` and its sub-folders.
|
||||
:file:`resources/images/trash.png`. Assuming you have an alternate icon in PNG format called :file:`mytrash.png` you would save it in the configuration directory as :file:`resources/images/trash.png`. All the icons used by the calibre user interface are in :file:`resources/images` and its sub-folders.
|
||||
|
||||
Customizing |app| with plugins
|
||||
--------------------------------
|
||||
|
@ -187,6 +187,26 @@ in your favorite editor and add the line::
|
||||
|
||||
near the top of the file. Now run the command :command:`calibredb`. The very first line of output should be ``Hello, world!``.
|
||||
|
||||
Having separate "normal" and "development" |app| installs on the same computer
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
The calibre source tree is very stable, it rarely breaks, but if you feel the need to run from source on a separate
|
||||
test library and run the released calibre version with your everyday library, you can achieve this easily using
|
||||
.bat files or shell scripts to launch |app|. The example below shows how to do this on windows using .bat files (the
|
||||
instructions for other platforms are the same, just use a BASh script instead of a .bat file)
|
||||
|
||||
To launch the relase version of |app| with your everyday library:
|
||||
|
||||
calibre-normal.bat::
|
||||
|
||||
calibre.exe "--with-library=C:\path\to\everyday\library folder"
|
||||
|
||||
calibre-dev.bat::
|
||||
|
||||
set CALIBRE_DEVELOP_FROM=C:\path\to\calibre\checkout\src
|
||||
calibre.exe "--with-library=C:\path\to\test\library folder"
|
||||
|
||||
|
||||
Debugging tips
|
||||
----------------
|
||||
|
||||
|
@ -164,13 +164,16 @@ Library
|
||||
.. |lii| image:: images/library.png
|
||||
:class: float-right-img
|
||||
|
||||
|lii| The :guilabel: `Library` action allows you to create, switch between, rename or delete a Library. |app| allows you to create as many libraries as you wish. You could for instance create a fiction library, a non fiction library, a foreign language library a project library, basically any structure that suits your needs. Libraries are the highest organizational structure within |app|, each library has its own set of books, tags, categories and base storage location.
|
||||
|lii| The :guilabel:`Library` action allows you to create, switch between, rename or delete a Library. |app| allows you to create as many libraries as you wish. You could for instance create a fiction library, a non fiction library, a foreign language library, a project library, basically any structure that suits your needs. Libraries are the highest organizational structure within |app|, each library has its own set of books, tags, categories and base storage location.
|
||||
|
||||
1. **Switch\Create library..**: This action allows you to; a) connect to a pre-existing |app| library at another location from your currently open library, b) Create and empty library at a nw location or, c) Move the current Library to a newly specified location.
|
||||
2. **Quick Switch>**: This action allows you to switch between libraries that have been registered or created within |app|.
|
||||
3. **Rename Library>**: This action allows you to rename a Library.
|
||||
4. **Delete Library>**: This action allows you to **permanenetly delete** a Library.
|
||||
5. **<calibre library>**: Actions 5, 6 etc .. give you immediate switch access between multiple Libraries that you have created or attached to.
|
||||
1. **Switch/Create library**: This action allows you to; a) connect to a pre-existing |app| library at another location from your currently open library, b) Create and empty library at a new location or, c) Move the current Library to a newly specified location.
|
||||
2. **Quick Switch**: This action allows you to switch between libraries that have been registered or created within |app|.
|
||||
3. **Rename Library**: This action allows you to rename a Library.
|
||||
4. **Remove Library**: This action allows you to unregister a library from |app|.
|
||||
5. **<library name>**: Actions 5, 6 etc .. give you immediate switch access between multiple Libraries that you have created or attached to. This list contains only the 5 most frequently used libraries. For the complete list, use the Quick Switch menu.
|
||||
6. **Library Maintenance**: This action allows you to check the current library for data consistency issues and restore the current libraries' database from backups.
|
||||
|
||||
.. note:: Metadata about your ebooks like title/author/tags/etc. is stored in a single file in your |app| library folder called metadata.db. If this file gets corrupted (a very rare event), you can lose the metadata. Fortunately, |app| automatically backs up the metadata for every individual book in the book's folder as an .opf file. By using the Restore Library action under Library Maintenance described above, you can have |app| rebuild the metadata.db file from the individual .opf files for you.
|
||||
|
||||
.. _device:
|
||||
|
||||
|
@ -116,7 +116,7 @@ If you have programming experience, please note that the syntax in this mode (si
|
||||
|
||||
Many functions use regular expressions. In all cases, regular expression matching is case-insensitive.
|
||||
|
||||
The functions available are:
|
||||
The functions available are listed below. Note that the definitive documentation for functions is available in the section :ref:`Function classification <template_functions_reference>`:
|
||||
|
||||
* ``lowercase()`` -- return value of the field in lower case.
|
||||
* ``uppercase()`` -- return the value of the field in upper case.
|
||||
@ -129,6 +129,7 @@ The functions available are:
|
||||
* ``list_item(index, separator)`` -- interpret the field as a list of items separated by `separator`, returning the `index`th item. The first item is number zero. The last item can be returned using `list_item(-1,separator)`. If the item is not in the list, then the empty value is returned. The separator has the same meaning as in the `count` function.
|
||||
* ``re(pattern, replacement)`` -- return the field after applying the regular expression. All instances of `pattern` are replaced with `replacement`. As in all of |app|, these are python-compatible regular expressions.
|
||||
* ``shorten(left chars, middle text, right chars)`` -- Return a shortened version of the field, consisting of `left chars` characters from the beginning of the field, followed by `middle text`, followed by `right chars` characters from the end of the string. `Left chars` and `right chars` must be integers. For example, assume the title of the book is `Ancient English Laws in the Times of Ivanhoe`, and you want it to fit in a space of at most 15 characters. If you use ``{title:shorten(9,-,5)}``, the result will be `Ancient E-nhoe`. If the field's length is less than ``left chars`` + ``right chars`` + the length of ``middle text``, then the field will be used intact. For example, the title `The Dome` would not be changed.
|
||||
* ``swap_around_comma(val) `` -- given a value of the form ``B, A``, return ``A B``. This is most useful for converting names in LN, FN format to FN LN. If there is no comma, the function returns val unchanged.
|
||||
* ``switch(pattern, value, pattern, value, ..., else_value)`` -- for each ``pattern, value`` pair, checks if the field matches the regular expression ``pattern`` and if so, returns that ``value``. If no ``pattern`` matches, then ``else_value`` is returned. You can have as many ``pattern, value`` pairs as you want.
|
||||
* ``lookup(pattern, field, pattern, field, ..., else_field)`` -- like switch, except the arguments are field (metadata) names, not text. The value of the appropriate field will be fetched and used. Note that because composite columns are fields, you can use this function in one composite field to use the value of some other composite field. This is extremely useful when constructing variable save paths (more later).
|
||||
* ``select(key)`` -- interpret the field as a comma-separated list of items, with the items being of the form "id:value". Find the pair with the id equal to key, and return the corresponding value. This function is particularly useful for extracting a value such as an isbn from the set of identifiers for a book.
|
||||
@ -230,13 +231,14 @@ For various values of series_index, the program returns:
|
||||
|
||||
**All the functions listed under single-function mode can be used in program mode**. To do so, you must supply the value that the function is to act upon as the first parameter, in addition to the parameters documented above. For example, in program mode the parameters of the `test` function are ``test(x, text_if_not_empty, text_if_empty)``. The `x` parameter, which is the value to be tested, will almost always be a variable or a function call, often `field()`.
|
||||
|
||||
The following functions are available in addition to those described in single-function mode. Remember from the example above that the single-function mode functions require an additional first parameter specifying the field to operate on. With the exception of the ``id`` parameter of assign, all parameters can be statements (sequences of expressions):
|
||||
The following functions are available in addition to those described in single-function mode. Remember from the example above that the single-function mode functions require an additional first parameter specifying the field to operate on. With the exception of the ``id`` parameter of assign, all parameters can be statements (sequences of expressions). Note that the definitive documentation for functions is available in the section :ref:`Function classification <template_functions_reference>`:
|
||||
|
||||
* ``and(value, value, ...)`` -- returns the string "1" if all values are not empty, otherwise returns the empty string. This function works well with test or first_non_empty. You can have as many values as you want.
|
||||
* ``add(x, y)`` -- returns x + y. Throws an exception if either x or y are not numbers.
|
||||
* ``assign(id, val)`` -- assigns val to id, then returns val. id must be an identifier, not an expression
|
||||
* ``booksize()`` -- returns the value of the |app| 'size' field. Returns '' if there are no formats.
|
||||
* ``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``.
|
||||
* ``days_between(date1, date2)`` -- return the number of days between ``date1`` and ``date2``. The number is positive if ``date1`` is greater than ``date2``, otherwise negative. If either ``date1`` or ``date2`` are not dates, the function returns the empty string.
|
||||
* ``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``.
|
||||
* ``first_non_empty(value, value, ...)`` -- returns the first value that is not empty. If all values are empty, then the empty value is returned. You can have as many values as you want.
|
||||
@ -266,7 +268,10 @@ The following functions are available in addition to those described in single-f
|
||||
* ``strcmp(x, y, lt, eq, gt)`` -- does a case-insensitive comparison x and y as strings. Returns ``lt`` if x < y. Returns ``eq`` if x == y. Otherwise returns ``gt``.
|
||||
* ``substr(str, start, end)`` -- returns the ``start``'th through the ``end``'th characters of ``str``. The first character in ``str`` is the zero'th character. If end is negative, then it indicates that many characters counting from the right. If end is zero, then it indicates the last character. For example, ``substr('12345', 1, 0)`` returns ``'2345'``, and ``substr('12345', 1, -1)`` returns ``'234'``.
|
||||
* ``subtract(x, y)`` -- returns x - y. Throws an exception if either x or y are not numbers.
|
||||
* ``today()`` -- return a date string for today. This value is designed for use in format_date or days_between, but can be manipulated like any other string. The date is in ISO format.
|
||||
* ``template(x)`` -- evaluates x as a template. The evaluation is done in its own context, meaning that variables are not shared between the caller and the template evaluation. Because the `{` and `}` characters are special, you must use `[[` for the `{` character and `]]` for the '}' character; they are converted automatically. For example, ``template('[[title_sort]]') will evaluate the template ``{title_sort}`` and return its value.
|
||||
|
||||
.. _template_functions_reference:
|
||||
|
||||
Function classification
|
||||
---------------------------
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user