Sync to trunk.

This commit is contained in:
John Schember 2011-09-24 15:06:05 -04:00
commit 1fa27dcc2a
140 changed files with 30950 additions and 24676 deletions

View File

@ -19,6 +19,76 @@
# new recipes:
# - title:
- version: 0.8.20
date: 2011-09-23
new features:
- title: "MOBI Output: Map a larger set of font names to sans-serif/monospace font in the MOBI file"
- title: "Get Books: Allow searching on the DRM column in the results."
tickets: [852514]
- title: "Manage tags/series/etc dialog: Add a was column to show the old value when changing values."
tickets: [846538]
- title: "Template language: Add new functions to manipulate language codes"
tickets: [832084]
bug fixes:
- title: "MOBI Output: Don't set cdetype when option to enable sharing instead of syncing is specified. This fixes the option."
- title: "Conversion pipeline: Fix crash caused by empty <style> elements."
tickets: [775277]
- title: "Get Books: Fix Woblink store"
- title: "MOBI Input: Correctly handle MOBI files that have been passed through a DRM removal tool that leaves the DRM fields in the header."
tickets: [855732]
- title: "Fix typo preventing the updating of metadata in MOBI files serverd by the content server"
- title: "Get Books: Handle non ASCII filenames for downloaded books"
tickets: [855109]
- title: "When generating the title sort string and stripping a leading article, strip leading punctuation that remains after removing the article"
tickets: [855070]
- title: "Fix downloading metadata in the Edit metadata dialog could result in off by one day published dates, in timezones behind GMT"
tickets: [855143]
- title: "Fix handing of title_sort and custom columns when creating a BiBTeX catalog."
tickets: [853249]
- title: "TXT Markdown Input: Change handling of _ to work mid word."
- title: "Fix Check library reporting unknown files ad both missing an unknown"
tickets: [846926]
- title: "Search/Replace: Permit .* to match empty tag like columns."
tickets: [840517]
improved recipes:
- Cicero (DE)
- Taz.de
- Ming Pao - HK
- Macleans Magazine
- IDG.se
- PC World (eng)
- LA Times
new recipes:
- title: Ekantipur (Nepal)
author: fab4.ilam
- title: Various Polish news sources
author: fenuks
- title: Taipei Times and China Post
author: Krittika Goyal
- title: Berliner Zeitung
author: ape
- version: 0.8.19
date: 2011-09-16

View File

@ -1,15 +1,52 @@
from calibre.web.feeds.news import BasicNewsRecipe
class Bash_org_pl(BasicNewsRecipe):
title = u'Bash.org.pl'
__author__ = 'fenuks'
description = 'Bash.org.pl - funny quotations from IRC discussions'
category = 'funny quotations, humour'
language = 'pl'
oldest_article = 15
cover_url = u'http://userlogos.org/files/logos/dzikiosiol/none_0.png'
max_articles_per_feed = 100
max_articles_per_feed = 50
no_stylesheets= True
keep_only_tags= [dict(name='div', attrs={'class':'quote post-content post-body'})]
feeds = [(u'Cytaty', u'http://bash.org.pl/rss')]
keep_only_tags= [dict(name='a', attrs={'class':'qid click'}),
dict(name='div', attrs={'class':'quote post-content post-body'})]
def latest_articles(self):
articles = []
soup=self.index_to_soup(u'http://bash.org.pl/latest/')
#date=soup.find('div', attrs={'class':'right'}).string
tags=soup.findAll('a', attrs={'class':'qid click'})
for a in tags:
title=a.string
url='http://bash.org.pl' +a['href']
articles.append({'title' : title,
'url' : url,
'date' : '',
'description' : ''
})
return articles
def random_articles(self):
articles = []
for i in range(self.max_articles_per_feed):
soup=self.index_to_soup(u'http://bash.org.pl/random/')
#date=soup.find('div', attrs={'class':'right'}).string
url=soup.find('a', attrs={'class':'qid click'})
title=url.string
url='http://bash.org.pl' +url['href']
articles.append({'title' : title,
'url' : url,
'date' : '',
'description' : ''
})
return articles
def parse_index(self):
feeds = []
feeds.append((u"Najnowsze", self.latest_articles()))
feeds.append((u"Losowe", self.random_articles()))
return feeds

View File

@ -0,0 +1,70 @@
from calibre.web.feeds.news import BasicNewsRecipe
import re
class Benchmark_pl(BasicNewsRecipe):
title = u'Benchmark.pl'
__author__ = 'fenuks'
description = u'benchmark.pl -IT site'
cover_url = 'http://www.ieaddons.pl/benchmark/logo_benchmark_new.gif'
category = 'IT'
language = 'pl'
oldest_article = 8
max_articles_per_feed = 100
no_stylesheets=True
preprocess_regexps = [(re.compile(ur'\bWięcej o .*</body>', re.DOTALL|re.IGNORECASE), lambda match: '</body>')]
keep_only_tags=[dict(name='div', attrs={'class':['m_zwykly', 'gallery']})]
remove_tags_after=dict(name='div', attrs={'class':'body'})
remove_tags=[dict(name='div', attrs={'class':['kategoria', 'socialize', 'thumb', 'panelOcenaObserwowane', 'categoryNextToSocializeGallery']})]
INDEX= 'http://www.benchmark.pl'
feeds = [(u'Aktualności', u'http://www.benchmark.pl/rss/aktualnosci-pliki.xml'),
(u'Testy i recenzje', u'http://www.benchmark.pl/rss/testy-recenzje-minirecenzje.xml')]
def append_page(self, soup, appendtag):
nexturl = soup.find('span', attrs={'class':'next'})
while nexturl is not None:
nexturl= self.INDEX + nexturl.parent['href']
soup2 = self.index_to_soup(nexturl)
nexturl=soup2.find('span', attrs={'class':'next'})
pagetext = soup2.find(name='div', attrs={'class':'body'})
appendtag.find('div', attrs={'class':'k_ster'}).extract()
pos = len(appendtag.contents)
appendtag.insert(pos, pagetext)
if appendtag.find('div', attrs={'class':'k_ster'}) is not None:
appendtag.find('div', attrs={'class':'k_ster'}).extract()
def image_article(self, soup, appendtag):
nexturl=soup.find('div', attrs={'class':'preview'})
if nexturl is not None:
nexturl=nexturl.find('a', attrs={'class':'move_next'})
image=appendtag.find('div', attrs={'class':'preview'}).div['style'][16:]
image=self.INDEX + image[:image.find("')")]
appendtag.find(attrs={'class':'preview'}).name='img'
appendtag.find(attrs={'class':'preview'})['src']=image
appendtag.find('a', attrs={'class':'move_next'}).extract()
while nexturl is not None:
nexturl= self.INDEX + nexturl['href']
soup2 = self.index_to_soup(nexturl)
nexturl=soup2.find('a', attrs={'class':'move_next'})
image=soup2.find('div', attrs={'class':'preview'}).div['style'][16:]
image=self.INDEX + image[:image.find("')")]
soup2.find(attrs={'class':'preview'}).name='img'
soup2.find(attrs={'class':'preview'})['src']=image
pagetext=soup2.find('div', attrs={'class':'gallery'})
pagetext.find('div', attrs={'class':'title'}).extract()
pagetext.find('div', attrs={'class':'thumb'}).extract()
pagetext.find('div', attrs={'class':'panelOcenaObserwowane'}).extract()
if nexturl is not None:
pagetext.find('a', attrs={'class':'move_next'}).extract()
pagetext.find('a', attrs={'class':'move_back'}).extract()
pos = len(appendtag.contents)
appendtag.insert(pos, pagetext)
def preprocess_html(self, soup):
if soup.find('div', attrs={'class':'preview'}) is not None:
self.image_article(soup, soup.body)
else:
self.append_page(soup, soup.body)
return soup

40
recipes/cgm_pl.recipe Normal file
View File

@ -0,0 +1,40 @@
from calibre.web.feeds.news import BasicNewsRecipe
class CGM(BasicNewsRecipe):
title = u'CGM'
oldest_article = 7
__author__ = 'fenuks'
description = u'Codzienna Gazeta Muzyczna'
cover_url = 'http://www.krafcy.com/foto/tinymce/Image/cgm%281%29.jpg'
category = 'music'
language = 'pl'
use_embedded_content = False
max_articles_per_feed = 100
no_stylesheers=True
extra_css = 'div {color:black;} strong {color:black;} span {color:black;} p {color:black;}'
remove_tags_before=dict(id='mainContent')
remove_tags_after=dict(name='div', attrs={'class':'fbContainer'})
remove_tags=[dict(name='div', attrs={'class':'fbContainer'}),
dict(name='p', attrs={'class':['tagCloud', 'galleryAuthor']}),
dict(id=['movieShare', 'container'])]
feeds = [(u'Informacje', u'http://www.cgm.pl/rss.xml'), (u'Polecamy', u'http://www.cgm.pl/rss,4,news.xml'),
(u'Recenzje', u'http://www.cgm.pl/rss,1,news.xml')]
def preprocess_html(self, soup):
ad=soup.findAll('img')
for r in ad:
if '/_vault/_article_photos/5841.jpg' in r['src'] or '_vault/_article_photos/5807.jpg' in r['src'] or 'article_photos/5841.jpg' in r['src'] or 'article_photos/5825.jpg' in r['src'] or '_article_photos/5920.jpg' in r['src'] or '_article_photos/5919.jpg' in r['src'] or '_article_photos/5918.jpg' in r['src'] or '_article_photos/5914.jpg' in r['src'] or '_article_photos/5911.jpg' in r['src'] or '_article_photos/5923.jpg' in r['src'] or '_article_photos/5921.jpg' in r['src']:
ad[ad.index(r)].extract()
gallery=soup.find('div', attrs={'class':'galleryFlash'})
if gallery:
img=gallery.find('embed')
if img:
img=img['src'][35:]
img='http://www.cgm.pl/_vault/_gallery/_photo/'+img
param=gallery.findAll(name='param')
for i in param:
i.extract()
gallery.contents[1].name='img'
gallery.contents[1]['src']=img
return soup

View File

@ -1,13 +1,14 @@
from calibre.web.feeds.news import BasicNewsRecipe
class BasicUserRecipe1316245412(BasicNewsRecipe):
#from calibre.utils.magick import Image, PixelWand
title = u'Cicero Online'
description = u'Magazin f\xfcr politische Kultur'
description = u'Magazin f\xfcr politische Kultur (RSS Version)'
publisher = 'Ringier Publishing GmbH'
category = 'news, politics, Germany'
language = 'de'
encoding = 'UTF-8'
__author__ = 'Armin Geller' # Upd. 2011-09-19
__author__ = 'Armin Geller' # Upd. 2011-09-23
oldest_article = 7
max_articles_per_feed = 100
@ -18,16 +19,20 @@ class BasicUserRecipe1316245412(BasicNewsRecipe):
remove_tags = [
dict(name='div', attrs={'id':["header", "navigation", "skip-link", "header-print", "header-print-url", "meta-toolbar", "footer"]}),
dict(name='div', attrs={'class':["region region-sidebar-first column sidebar", "breadcrumb", "breadcrumb-title", "meta", "comment-wrapper",
"field field-name-field-show-teaser-right field-type-list-boolean field-label-above"]}),
dict(name='div', attrs={'class':["region region-sidebar-first column sidebar", "breadcrumb",
"breadcrumb-title", "meta", "comment-wrapper",
"field field-name-field-show-teaser-right field-type-list-boolean field-label-above",
"page-header",
"view view-alle-karikaturen view-id-alle_karikaturen view-display-id-default view-dom-id-1",
"pagination",
"view view-letzte-videos view-id-letzte_videos view-display-id-default view-dom-id-1",
"view view-letzte-videos view-id-letzte_videos view-display-id-default view-dom-id-2", # 2011-09-23
"view view-alle-karikaturen view-id-alle_karikaturen view-display-id-default view-dom-id-2", # 2011-09-23
]}),
dict(name='div', attrs={'title':["Dossier Auswahl"]}),
dict(name='h2', attrs={'class':["title comment-form"]}),
dict(name='form', attrs={'class':["comment-form user-info-from-cookie"]}),
# 2011-09-19 clean-up on first feed historical caricature- and video preview pictures and social icons
dict(name='table', attrs={'class':["mcx-social-horizontal", "page-header"]}), # 2011-09-19
dict(name='div', attrs={'class':["page-header", "view view-alle-karikaturen view-id-alle_karikaturen view-display-id-default view-dom-id-1",
"pagination",
"view view-letzte-videos view-id-letzte_videos view-display-id-default view-dom-id-1"]}), # 2011-09-19
dict(name='table', attrs={'class':["mcx-social-horizontal", "page-header"]}),
]
feeds = [

View File

@ -2,6 +2,11 @@
__license__ = 'GPL v3'
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
'''
Changelog:
2011-09-24
Changed cover (drMerry)
'''
'''
news.cnet.com
'''
@ -9,7 +14,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
class CnetNews(BasicNewsRecipe):
title = 'CNET News'
__author__ = 'Darko Miletic'
__author__ = 'Darko Miletic updated by DrMerry.'
description = 'Tech news and business reports by CNET News. Focused on information technology, core topics include computers, hardware, software, networking, and Internet media.'
publisher = 'CNET'
category = 'news, IT, USA'

View File

@ -1,6 +1,3 @@
'''
dnaindia.com
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
@ -12,6 +9,10 @@ class DNAIndia(BasicNewsRecipe):
language = 'en_IN'
encoding = 'cp1252'
use_embedded_content = False
no_stylesheets = True
auto_cleanup = True
feeds = [
('Top News', 'http://www.dnaindia.com/syndication/rss_topnews.xml'),
@ -22,15 +23,10 @@ class DNAIndia(BasicNewsRecipe):
('World', 'http://www.dnaindia.com/syndication/rss,catid-9.xml'),
('Money', 'http://www.dnaindia.com/syndication/rss,catid-4.xml'),
('Sports', 'http://www.dnaindia.com/syndication/rss,catid-6.xml'),
('After Hours', 'http://www.dnaindia.com/syndication/rss,catid-7.xml'),
('Digital Life', 'http://www.dnaindia.com/syndication/rss,catid-1089741.xml'),
('After Hours', 'http://www.dnaindia.com/syndication/rss,catid-7.xml')
]
remove_tags = [{'id':['footer', 'lhs-col']}, {'class':['bottom', 'categoryHead',
'article_tools']}]
keep_only_tags = dict(id='middle-col')
remove_tags_after=[dict(attrs={'id':'story'})]
remove_attributes=['style']
no_stylesheets = True
def print_version(self, url):
match = re.search(r'newsid=(\d+)', url)

View File

@ -1,5 +1,5 @@
from calibre.web.feeds.news import BasicNewsRecipe
import re
class Dobreprogramy_pl(BasicNewsRecipe):
title = 'Dobreprogramy.pl'
@ -15,6 +15,7 @@ class Dobreprogramy_pl(BasicNewsRecipe):
extra_css = '.title {font-size:22px;}'
oldest_article = 8
max_articles_per_feed = 100
preprocess_regexps = [(re.compile(ur'<div id="\S+360pmp4">Twoja przeglądarka nie obsługuje Flasha i HTML5 lub wyłączono obsługę JavaScript...</div>'), lambda match: '') ]
remove_tags = [dict(name='div', attrs={'class':['komentarze', 'block', 'portalInfo', 'menuBar', 'topBar']})]
keep_only_tags = [dict(name='div', attrs={'class':['mainBar', 'newsContent', 'postTitle title', 'postInfo', 'contentText', 'content']})]
feeds = [(u'Aktualności', 'http://feeds.feedburner.com/dobreprogramy/Aktualnosci'),

17
recipes/dzieje_pl.recipe Normal file
View File

@ -0,0 +1,17 @@
from calibre.web.feeds.news import BasicNewsRecipe
class Dzieje(BasicNewsRecipe):
title = u'dzieje.pl'
__author__ = 'fenuks'
description = 'Dzieje - history of Poland'
cover_url = 'http://www.dzieje.pl/sites/default/files/dzieje_logo.png'
category = 'history'
language = 'pl'
oldest_article = 8
max_articles_per_feed = 100
remove_javascript=True
no_stylesheets= True
remove_tags_before= dict(name='h1', attrs={'class':'title'})
remove_tags_after= dict(id='dogory')
remove_tags=[dict(id='dogory')]
feeds = [(u'Dzieje', u'http://dzieje.pl/rss.xml')]

21
recipes/ekantipur.recipe Normal file
View File

@ -0,0 +1,21 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1314326622(BasicNewsRecipe):
title = u'Ekantipur'
__author__ = 'Manish Bhattarai'
description = 'News from the No.1 News Portal In Nepal'
language = 'en_NP'
oldest_article = 7
max_articles_per_feed = 25
masthead_url = 'http://kantipur.com.np/images/ekantipur_01.jpg'
remove_empty_feeds = True
remove_tags_before = dict(id='main-content')
remove_tags_after = dict(id='view-comments')
remove_tags = [dict(attrs={'class':['ratings', 'news-tool', 'comment', 'post-ur-comment','asideBox','commentsbox','related-sidebar-row related-news']}),
dict(id=['sidebar','news-detail-img', 'footer-wrapper']),
dict(name=['script'])]
feeds = [(u'Top Stories', u'http://www.ekantipur.com/en/rss/top-stories/'), (u'National', u'http://www.ekantipur.com/en/rss/national/1'), (u'Capital', u'http://www.ekantipur.com/en/rss/capital/7'), (u'Business', u'http://www.ekantipur.com/en/rss/business/3'), (u'World', u'http://www.ekantipur.com/en/rss/world/5'), (u'Sports', u'http://www.ekantipur.com/en/rss/sports/4'), (u'Mixed Bag', u'http://www.ekantipur.com/en/rss/mixed-bag/14'), (u'Health & Living', u'http://www.ekantipur.com/en/rss/health-and-living/19'), (u'Entertainment', u'http://www.ekantipur.com/en/rss/entertainment/6')]

View File

@ -16,13 +16,13 @@ class Fleshbot(BasicNewsRecipe):
max_articles_per_feed = 100
no_stylesheets = True
encoding = 'utf-8'
use_embedded_content = False
use_embedded_content = True
language = 'en'
masthead_url = 'http://cache.fleshbot.com/assets/base/img/thumbs140x140/fleshbot.com.png'
masthead_url = 'http://cache.gawkerassets.com/assets/kotaku.com/img/logo.png'
extra_css = '''
body{font-family: "Lucida Grande",Helvetica,Arial,sans-serif}
img{margin-bottom: 1em}
h1{font-family :Arial,Helvetica,sans-serif; font-size:x-large}
h1{font-family :Arial,Helvetica,sans-serif; font-size:large}
'''
conversion_options = {
'comment' : description
@ -31,13 +31,12 @@ class Fleshbot(BasicNewsRecipe):
, 'language' : language
}
remove_attributes = ['width','height']
keep_only_tags = [dict(attrs={'class':'content permalink'})]
remove_tags_before = dict(name='h1')
remove_tags = [dict(attrs={'class':'contactinfo'})]
remove_tags_after = dict(attrs={'class':'contactinfo'})
feeds = [(u'Articles', u'http://feeds.gawker.com/fleshbot/vip?format=xml')]
remove_tags = [
{'class': 'feedflare'},
]
feeds = [(u'Articles', u'http://feeds.gawker.com/fleshbot/full')]
def preprocess_html(self, soup):
return self.adeify_images(soup)

View File

@ -0,0 +1,13 @@
from calibre.web.feeds.news import BasicNewsRecipe
class GreenLinux(BasicNewsRecipe):
title = u'GreenLinux.pl'
__author__ = 'fenuks'
category = 'IT'
language = 'pl'
cover_url = 'http://lh5.ggpht.com/_xd_6Y9kXhEc/S8tjyqlfhfI/AAAAAAAAAYU/zFNTp07ZQko/top.png'
oldest_article = 15
max_articles_per_feed = 100
auto_cleanup = True
feeds = [(u'Newsy', u'http://feeds.feedburner.com/greenlinux')]

View File

@ -0,0 +1,13 @@
from calibre.web.feeds.news import BasicNewsRecipe
class Historia_org_pl(BasicNewsRecipe):
title = u'Historia.org.pl'
__author__ = 'fenuks'
description = u'history site'
cover_url = 'http://lh3.googleusercontent.com/_QeRQus12wGg/TOvHsZ2GN7I/AAAAAAAAD_o/LY1JZDnq7ro/logo5.jpg'
category = 'history'
language = 'pl'
oldest_article = 8
max_articles_per_feed = 100
feeds = [(u'Artykuły', u'http://www.historia.org.pl/index.php?format=feed&type=rss')]

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

BIN
recipes/icons/cgm_pl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 B

BIN
recipes/icons/dzieje_pl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 806 B

BIN
recipes/icons/lomza.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

28
recipes/ksiazka_pl.recipe Normal file
View File

@ -0,0 +1,28 @@
from calibre.web.feeds.news import BasicNewsRecipe
import re
class Ksiazka_net_pl(BasicNewsRecipe):
title = u'ksiazka.net.pl'
__author__ = 'fenuks'
description = u'Ksiazka.net.pl - book vortal'
cover_url = 'http://www.ksiazka.net.pl/fileadmin/templates/ksiazka.net.pl/images/1PortalKsiegarski-logo.jpg'
category = 'books'
language = 'pl'
oldest_article = 8
max_articles_per_feed = 100
no_stylesheets= True
#extra_css = 'img {float: right;}'
preprocess_regexps = [(re.compile(ur'Podoba mi się, kupuję:'), lambda match: '<br />')]
remove_tags_before= dict(name='div', attrs={'class':'m-body'})
remove_tags_after= dict(name='div', attrs={'class':'m-body-link'})
remove_tags=[dict(attrs={'class':['mk_library-icon', 'm-body-link', 'tagi']})]
feeds = [(u'Wiadomości', u'http://www.ksiazka.net.pl/?id=wiadomosci&type=100'),
(u'Książki', u'http://www.ksiazka.net.pl/?id=ksiazki&type=100'),
(u'Rynek', u'http://www.ksiazka.net.pl/?id=rynek&type=100')]
def image_url_processor(self, baseurl, url):
if (('file://' in url) and ('www.ksiazka.net.pl/' not in url)):
return 'http://www.ksiazka.net.pl/' + url[8:]
elif 'http://' not in url:
return 'http://www.ksiazka.net.pl/' + url
else:
return url

View File

@ -29,7 +29,7 @@ class Kurier(BasicNewsRecipe):
, 'language' : language
}
remove_tags = [dict(attrs={'class':['contenttabs','drucken','versenden','leserbrief','kommentieren','addthis_button']})]
remove_tags = [dict(attrs={'class':['functionsleiste','functions','social_positionierung','contenttabs','drucken','versenden','leserbrief','kommentieren','addthis_button']})]
keep_only_tags = [dict(attrs={'id':'content'})]
remove_tags_after = dict(attrs={'id':'author'})
remove_attributes = ['width','height']

14
recipes/lomza.recipe Normal file
View File

@ -0,0 +1,14 @@
from calibre.web.feeds.news import BasicNewsRecipe
class Lomza(BasicNewsRecipe):
title = u'4Lomza'
__author__ = 'fenuks'
description = u'4Łomża - regional site'
cover_url = 'http://www.4lomza.pl/i/logo4lomza_m.jpg'
language = 'pl'
oldest_article = 15
no_styleseets=True
max_articles_per_feed = 100
remove_tags=[dict(name='div', attrs={'class':['bxbanner', 'drukuj', 'wyslijznajomemu']})]
keep_only_tags=[dict(name='div', attrs={'class':'wiadomosc'})]
feeds = [(u'Łomża', u'http://feeds.feedburner.com/4lomza.pl')]

View File

@ -95,7 +95,7 @@ class WeeklyLWN(BasicNewsRecipe):
break
article = dict(
title=tag_title.string,
title=self.tag_to_string(tag_title),
url= 'http://lwn.net' + tag_url['href'].split('#')[0] + '?format=printable',
description='', content='', date='')
articles[section].append(article)

View File

@ -12,11 +12,14 @@ __UseChineseTitle__ = False
__KeepImages__ = True
# (HK only) Turn below to true if you wish to use life.mingpao.com as the main article source
__UseLife__ = True
# (HK only) if __UseLife__ is true, turn this on if you want to include the column section
__InclCols__ = False
'''
Change Log:
2011/09/18: parse "column" section stuff from source text files directly.
2011/09/21: fetching "column" section is made optional. Default is False
2011/09/18: parse "column" section stuff from source text file directly.
2011/09/07: disable "column" section as it is no longer offered free.
2011/06/26: add fetching Vancouver and Toronto versions of the paper, also provide captions for images using life.mingpao fetch source
provide options to remove all images in the file
@ -241,10 +244,11 @@ class MPRecipe(BasicNewsRecipe):
if articles:
feeds.append((title, articles))
if __InclCols__ == True:
# parse column section articles directly from .txt files
for title, url, keystr in [(u'\u5c08\u6b04 Columns', 'http://life.mingpao.com/cfm/dailynews2.cfm?Issue=' + dateStr +'&Category=ncolumn', 'ncl')
]:
articles = self.parse_section2_col(url, keystr)
articles = self.parse_section2_txt(url, keystr)
if articles:
feeds.append((title, articles))
@ -368,8 +372,8 @@ class MPRecipe(BasicNewsRecipe):
current_articles.reverse()
return current_articles
# parse from life.mingpao.com
def parse_section2_col(self, url, keystr):
# parse from text file of life.mingpao.com
def parse_section2_txt(self, url, keystr):
self.get_fetchdate()
soup = self.index_to_soup(url)
a = soup.findAll('a', href=True)
@ -654,4 +658,3 @@ class MPRecipe(BasicNewsRecipe):
with nested(open(opf_path, 'wb'), open(ncx_path, 'wb')) as (opf_file, ncx_file):
opf.render(opf_file, ncx_file)

14
recipes/republica.recipe Normal file
View File

@ -0,0 +1,14 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1316862613(BasicNewsRecipe):
title = u'Republica'
__author__ = 'Manish Bhattarai'
description = 'News from the Republica'
language = 'en_NP'
masthead_url = 'http://blog.nyayahealth.org/wp-content/uploads/2011/03/myrepublica1.gif'
oldest_article = 1
max_articles_per_feed = 100
auto_cleanup = True
cover_url = 'http://www.myrepublica.com/repub_front.jpg'
feeds = [(u'Political Affairs', u'http://www.myrepublica.com/portal/news_rss.php?news_category_id=14'), (u'Business & Economy', u'http://www.myrepublica.com/portal/news_rss.php?news_category_id=15'), (u'International', u'http://www.myrepublica.com/portal/news_rss.php?news_category_id=21'), (u'Social Issues', u'http://www.myrepublica.com/portal/news_rss.php?news_category_id=16'), (u'Sports', u'http://www.myrepublica.com/portal/news_rss.php?news_category_id=18'), (u'Lifestyle', u'http://www.myrepublica.com/portal/news_rss.php?news_category_id=17')]

53
recipes/sign_on_sd.recipe Normal file
View File

@ -0,0 +1,53 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1315899507(BasicNewsRecipe):
title = u'Sign On San Diego'
__author__ = 'Jay Kindle'
description = 'Local news stories from The San Diego Union-Tribune; breaking news, business and technology, local and national sports coverage, entertainment news and reviews.'
publisher = 'Tribune Company'
category = 'news, politics, USA, San Diego, California, world'
oldest_article = 2
max_articles_per_feed = 200
timefmt = ' [%b %d, %Y]'
no_stylesheets = True
encoding = 'utf8'
use_embedded_content = False
language = 'en'
auto_cleanup = True
remove_empty_feeds = True
publication_type = 'newspaper'
masthead_url = 'http://media.signonsandiego.com/e2/sosd/images/sosd_logo.png'
feeds = [
(u'Latest News', u'http://www.signonsandiego.com/rss/headlines/'),
(u'Local News', u'http://www.signonsandiego.com/rss/headlines/metro/'),
(u'Business', u'http://www.signonsandiego.com/rss/headlines/business/'),
(u'Politics', u'http://www.signonsandiego.com/rss/headlines/local/politics/'),
(u'Border & Immigration', u'http://www.signonsandiego.com/rss/headlines/border/'),
(u'Courts', u'http://www.signonsandiego.com/rss/headlines/courts/'),
(u'Education', u'http://www.signonsandiego.com/news/education/'),
(u'Sports', u'http://www.signonsandiego.com/rss/headlines/sports/'),
(u'Chargers', u'http://www.signonsandiego.com/rss/headlines/sports/chargers/'),
(u'Padres', u'http://www.signonsandiego.com/rss/headlines/sports/padres/'),
(u'NFL', u'http://www.signonsandiego.com/rss/headlines/sports/nfl/'),
(u'NBA', u'http://www.signonsandiego.com/rss/headlines/sports/nba/'),
(u'Nick Canepa', u'http://www.signonsandiego.com/rss/authors/nick-canepa/'),
(u'Tim Sullivan', u'http://www.signonsandiego.com/rss/authors/tim-sullivan/'),
(u'Ruben Navarrette', u'http://www.signonsandiego.com/rss/authors/ruben-navarrette/'),
(u'Diane Bell', u'http://www.signonsandiego.com/rss/authors/diane-bell/'),
(u'Smart Living', u'http://www.signonsandiego.com/rss/headlines/smart-living/'),
(u'Photos', u'http://www.signonsandiego.com/rss/photos/'),
(u'Arts', u'http://www.signonsandiego.com/rss/headlines/night-and-day/theater-arts/'),
(u'Books', u'http://www.signonsandiego.com/rss/headlines/lifestyle/books/'),
(u'Currents-Passages', u'http://www.signonsandiego.com/rss/headlines/lifestyle/currents/passages/'),
(u'Currents-Weekend', u'http://www.signonsandiego.com/news/rss2/daily/currentsweekend.xml'),
(u'Dialog', u'http://www.signonsandiego.com/news/rss2/daily/dialog.xml'),
(u'Home', u'http://www.signonsandiego.com/rss/headlines/home/'),
(u'Homescape', u'http://www.signonsandiego.com/rss/headlines/lifestyle/homescape/'),
(u'Night & Day', u'http://www.signonsandiego.com/news/rss2/daily/nightday.xml'),
(u'Opinion', u'http://www.signonsandiego.com/rss/headlines/opinion/'),
(u'Quest', u'http://www.signonsandiego.com/news/rss2/daily/quest.xml'),
(u'Travel', u'http://www.signonsandiego.com/news/rss2/daily/travel.xml'),
(u'Wheels', u'http://www.signonsandiego.com/news/rss2/daily/wheels.xml')
]

12
recipes/tablety_pl.recipe Normal file
View File

@ -0,0 +1,12 @@
from calibre.web.feeds.news import BasicNewsRecipe
class Tablety_pl(BasicNewsRecipe):
title = u'Tablety.pl'
__author__ = 'fenuks'
description = u'tablety.pl - latest tablet news'
cover_url = 'http://www.tablety.pl/wp-content/themes/kolektyw/img/logo.png'
category = 'IT'
language = 'pl'
oldest_article = 8
max_articles_per_feed = 100
feeds = [(u'Najnowsze posty', u'http://www.tablety.pl/feed/')]

View File

@ -19,6 +19,6 @@ class TazRSSRecipe(BasicNewsRecipe):
feeds = [(u'TAZ main feed', u'http://www.taz.de/rss.xml')]
keep_only_tags = [dict(name='div', attrs={'class': 'sect sect_article'})]
remove_tags_after = dict(name='div', attrs={'class': 'rack'})
remove_tags = [dict(name=['div'], attrs={'class': 'rack'}),
remove_tags = [
dict(name=['div'], attrs={'class': 'artikelwerbung'}),
dict(name=['ul'], attrs={'class': 'toolbar'}),]

View File

@ -9,10 +9,15 @@ class TimesOfIndia(BasicNewsRecipe):
max_articles_per_feed = 25
no_stylesheets = True
keep_only_tags = [{'class':['maintable12', 'prttabl']}]
remove_attributes = ['style']
keep_only_tags = [
{'class':re.compile(r'maintable12|prttabl')},
{'id':['mod-article-header',
'mod-a-body-after-first-para', 'mod-a-body-first-para']},
]
remove_tags = [
dict(style=lambda x: x and 'float' in x),
{'class':['prvnxtbg', 'footbdrin', 'bcclftr']},
{'class':re.compile('tabsintbgshow|prvnxtbg')},
{'id':['fbrecommend', 'relmaindiv']}
]
feeds = [
@ -41,6 +46,8 @@ class TimesOfIndia(BasicNewsRecipe):
]
def get_article_url(self, article):
# Times of India sometimes serves an ad page instead of the article,
# this code, detects and circumvents that
url = BasicNewsRecipe.get_article_url(self, article)
if '/0Ltimesofindia' in url:
url = url.partition('/0L')[-1]
@ -61,6 +68,3 @@ class TimesOfIndia(BasicNewsRecipe):
return url
def preprocess_html(self, soup):
return soup

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python
__author__ = 'Darko Spasovski'
__license__ = 'GPL v3'
__copyright__ = '2011, Darko Spasovski <darko.spasovski at gmail.com>'
'''
@ -9,10 +10,11 @@ utrinski.com.mk
import re
import datetime
from calibre.web.feeds.news import BasicNewsRecipe
from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre import browser
class UtrinskiVesnik(BasicNewsRecipe):
__author__ = 'Darko Spasovski'
INDEX = 'http://www.utrinski.com.mk/'
title = 'Utrinski Vesnik'
description = 'Daily Macedonian newspaper'
@ -21,7 +23,6 @@ class UtrinskiVesnik(BasicNewsRecipe):
remove_javascript = True
publication_type = 'newspaper'
category = 'news, Macedonia'
oldest_article = 2
max_articles_per_feed = 100
no_stylesheets = True
use_embedded_content = False
@ -47,25 +48,29 @@ class UtrinskiVesnik(BasicNewsRecipe):
}
def parse_index(self):
soup = self.index_to_soup(self.INDEX)
feeds = []
for section in soup.findAll('a', attrs={'class':'WB_UTRINSKIVESNIK_TOCTitleBig'}):
# open main page
soup = self.index_to_soup(self.INDEX)
# find all anchors with class attribute equal to 'WB_UTRINSKIVESNIK_MainMenu'
for section in soup.findAll('a', attrs={'class':'WB_UTRINSKIVESNIK_MainMenu'}):
sectionTitle = section.contents[0].string
tocItemTable = section.findAllPrevious('table')[1]
if tocItemTable is None: continue
sectionUrl = self.INDEX + section['href'].strip()
# open the anchor link
raw = browser().open_novisit(sectionUrl).read()
sectionSoup = BeautifulSoup(raw)
# find all anchors with class attribute equal to 'WB_UTRINSKIVESNIK_ONLINEArticleTitle'
sectionArticles = sectionSoup.findAll('a', attrs={'class':'WB_UTRINSKIVESNIK_ONLINEArticleTitle'})
articles = []
while True:
tocItemTable = tocItemTable.nextSibling
if tocItemTable is None: break
article = tocItemTable.findAll('a', attrs={'class': 'WB_UTRINSKIVESNIK_TocItem'})
if len(article)==0: break
title = self.tag_to_string(article[0], use_alt=True).strip()
articles.append({'title': title, 'url':'http://www.utrinski.com.mk/' + article[0]['href'], 'description':'', 'date':''})
for sectionArticle in sectionArticles:
# article title = anchor's contents, article url = anchor's href
articleTitle = sectionArticle.contents[0].string.strip()
articleUrl = self.INDEX + sectionArticle['href'].strip()
articleDate = datetime.datetime.today().strftime('%d.%m.%Y')
articles.append({'title': articleTitle, 'url':articleUrl, 'description':'', 'date': articleDate})
if articles:
feeds.append((sectionTitle, articles))
return feeds
def get_cover_url(self):
datum = datetime.datetime.today().strftime('%d_%m_%Y')
return 'http://www.utrinski.com.mk/WBStorage/Files/' + datum + '.jpg'

View File

@ -1,6 +1,10 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
'''
Changelog:
2011-09-24
Changed cover (drMerry)
'''
'''
Fetch xkcd.
'''
@ -9,9 +13,10 @@ import time, re
from calibre.web.feeds.news import BasicNewsRecipe
class XkcdCom(BasicNewsRecipe):
cover_url = 'http://imgs.xkcd.com/s/9be30a7.png'
title = 'xkcd'
description = 'A webcomic of romance and math humor.'
__author__ = 'Martin Pitt'
__author__ = 'Martin Pitt updated by DrMerry.'
language = 'en'
use_embedded_content = False

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = u'calibre'
numeric_version = (0, 8, 19)
numeric_version = (0, 8, 20)
__version__ = u'.'.join(map(unicode, numeric_version))
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"

View File

@ -653,6 +653,7 @@ class KindleDXOutput(OutputProfile):
return u'%s <br/><span style="color: white">%s</span>' % (', '.join(tags),
'ttt '.join(tags)+'ttt ')
class IlliadOutput(OutputProfile):
name = 'Illiad'
@ -731,12 +732,23 @@ class BambookOutput(OutputProfile):
fbase = 12
fsizes = [10, 12, 14, 16]
class PocketBook900Output(OutputProfile):
author = 'Chris Lockfort'
name = 'PocketBook Pro 900'
short_name = 'pocketbook_900'
description = _('This profile is intended for the PocketBook Pro 900 series of devices.')
screen_size = (810, 1180)
dpi = 150.0
comic_screen_size = screen_size
output_profiles = [OutputProfile, SonyReaderOutput, SonyReader300Output,
SonyReader900Output, MSReaderOutput, MobipocketOutput, HanlinV3Output,
HanlinV5Output, CybookG3Output, CybookOpusOutput, KindleOutput,
iPadOutput, KoboReaderOutput, TabletOutput, SamsungGalaxy,
SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput,
IRexDR1000Output, IRexDR800Output, JetBook5Output, NookOutput,
BambookOutput, NookColorOutput, GenericEink, GenericEinkLarge]
BambookOutput, NookColorOutput, PocketBook900Output, GenericEink, GenericEinkLarge]
output_profiles.sort(cmp=lambda x,y:cmp(x.name.lower(), y.name.lower()))

View File

@ -4,6 +4,10 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import os
import cStringIO
from calibre.devices.usbms.driver import USBMS
class ANDROID(USBMS):
@ -209,4 +213,63 @@ class WEBOS(USBMS):
VENDOR_NAME = 'HP'
WINDOWS_MAIN_MEM = 'WEBOS-DEVICE'
THUMBNAIL_HEIGHT = 160
THUMBNAIL_WIDTH = 120
def upload_cover(self, path, filename, metadata, filepath):
try:
from PIL import Image, ImageDraw
Image, ImageDraw
except ImportError:
import Image, ImageDraw
coverdata = getattr(metadata, 'thumbnail', None)
if coverdata and coverdata[2]:
cover = Image.open(cStringIO.StringIO(coverdata[2]))
else:
coverdata = open(I('library.png'), 'rb').read()
cover = Image.new('RGB', (120,160), 'black')
im = Image.open(cStringIO.StringIO(coverdata))
im.thumbnail((120, 160), Image.ANTIALIAS)
x, y = im.size
cover.paste(im, ((120-x)/2, (160-y)/2))
draw = ImageDraw.Draw(cover)
draw.text((1, 10), metadata.get('title', _('Unknown')).encode('ascii', 'ignore'))
draw.text((1, 140), metadata.get('authors', _('Unknown'))[0].encode('ascii', 'ignore'))
data = cStringIO.StringIO()
cover.save(data, 'JPEG')
coverdata = data.getvalue()
with open(os.path.join(path, 'coverCache', filename + '-medium.jpg'), 'wb') as coverfile:
coverfile.write(coverdata)
coverdata = getattr(metadata, 'thumbnail', None)
if coverdata and coverdata[2]:
cover = Image.open(cStringIO.StringIO(coverdata[2]))
else:
coverdata = open(I('library.png'), 'rb').read()
cover = Image.new('RGB', (52,69), 'black')
im = Image.open(cStringIO.StringIO(coverdata))
im.thumbnail((52, 69), Image.ANTIALIAS)
x, y = im.size
cover.paste(im, ((52-x)/2, (69-y)/2))
cover2 = cover.resize((52, 69), Image.ANTIALIAS).convert('RGB')
data = cStringIO.StringIO()
cover2.save(data, 'JPEG')
coverdata = data.getvalue()
with open(os.path.join(path, 'coverCache', filename +
'-small.jpg'), 'wb') as coverfile:
coverfile.write(coverdata)

View File

@ -116,6 +116,8 @@ def title_sort(title, order=None):
if match:
prep = match.group(1)
title = title[len(prep):] + ', ' + prep
if title[0] in _ignore_starts:
title = title[1:]
return title.strip()
coding = zip(

View File

@ -78,7 +78,7 @@ class StreamSlicer(object):
stream = self._stream
base = self.start
stream.seek(base)
self._stream.truncate(base)
stream.truncate()
for block in data_blocks:
stream.write(block)

View File

@ -367,9 +367,11 @@ class MobiMLizer(object):
istate.fgcolor = style['color']
istate.strikethrough = style['text-decoration'] == 'line-through'
istate.underline = style['text-decoration'] == 'underline'
if 'monospace' in style['font-family']:
ff = style['font-family'].lower() if style['font-family'] else ''
if 'monospace' in ff or 'courier' in ff or ff.endswith(' mono'):
istate.family = 'monospace'
elif 'sans-serif' in style['font-family']:
elif ('sans-serif' in ff or 'sansserif' in ff or 'verdana' in ff or
'arial' in ff or 'helvetica' in ff):
istate.family = 'sans-serif'
else:
istate.family = 'serif'

View File

@ -135,7 +135,6 @@ class BookHeader(object):
self.length, self.type, self.codepage, self.unique_id, \
self.version = struct.unpack('>LLLLL', raw[20:40])
try:
self.codec = {
1252: 'cp1252',
@ -145,8 +144,16 @@ class BookHeader(object):
self.codec = 'cp1252' if not user_encoding else user_encoding
log.warn('Unknown codepage %d. Assuming %s' % (self.codepage,
self.codec))
if ident == 'TEXTREAD' or self.length < 0xE4 or 0xE8 < self.length \
or (try_extra_data_fix and self.length == 0xE4):
# There exists some broken DRM removal tool that removes DRM but
# leaves the DRM fields in the header yielding a header size of
# 0xF8. The actual value of max_header_length should be 0xE8 but
# it's changed to accommodate this silly tool. Hopefully that will
# not break anything else.
max_header_length = 0xF8
if (ident == 'TEXTREAD' or self.length < 0xE4 or
self.length > max_header_length or
(try_extra_data_fix and self.length == 0xE4)):
self.extra_flags = 0
else:
self.extra_flags, = struct.unpack('>H', raw[0xF2:0xF4])

View File

@ -216,7 +216,7 @@ class Stylizer(object):
if t:
text += u'\n\n' + force_unicode(t, u'utf-8')
if text:
text = XHTML_CSS_NAMESPACE + elem.text
text = XHTML_CSS_NAMESPACE + text
text = oeb.css_preprocessor(text)
stylesheet = parser.parseString(text, href=cssname)
stylesheet.namespaces['h'] = XHTML_NS

View File

@ -28,7 +28,7 @@ class Tokenize:
self.__bug_handler = bug_handler
self.__copy = copy
self.__write_to = tempfile.mktemp()
# self.__out_file = out_file
# self.__write_to = out_file
self.__compile_expressions()
#variables
self.__uc_char = 0
@ -41,14 +41,11 @@ class Tokenize:
def __remove_uc_chars(self, startchar, token):
for i in xrange(startchar, len(token)):
#handle the case of an uc char with a terminating blank before ansi char
if token[i] == " " and self.__uc_char:
continue
elif self.__uc_char:
if self.__uc_char:
self.__uc_char -= 1
else:
return token[i:]
#if only " " and char to skip
#if only char to skip
return ''
def __unicode_process(self, token):
@ -90,7 +87,7 @@ class Tokenize:
self.__reini_utf8_counters()
#get value and handle negative case
uni_char = int(match_obj.group(1))
uni_len = len(match_obj.group(1)) + 2
uni_len = len(match_obj.group(0))
if uni_char < 0:
uni_char += 65536
uni_char = unichr(uni_char).encode('ascii', 'xmlcharrefreplace')
@ -199,7 +196,7 @@ class Tokenize:
# import sys
# def main(args=sys.argv):
# if len(args) < 1:
# if len(args) < 2:
# print 'No file'
# return
# file = 'data_tokens.txt'
@ -211,3 +208,5 @@ class Tokenize:
# if __name__ == '__main__':
# sys.exit(main())
# calibre-debug -e src/calibre/ebooks/rtf2xml/tokenize.py

View File

@ -18,7 +18,6 @@ class ToolBar(QToolBar): # {{{
def __init__(self, donate, location_manager, parent):
QToolBar.__init__(self, parent)
self.setContextMenuPolicy(Qt.PreventContextMenu)
self.setMovable(False)
self.setFloatable(False)
self.setOrientation(Qt.Horizontal)
@ -53,8 +52,14 @@ class ToolBar(QToolBar): # {{{
style = Qt.ToolButtonIconOnly
return style
def contextMenuEvent(self, *args):
pass
def contextMenuEvent(self, ev):
ac = self.actionAt(ev.pos())
if ac is None: return
ch = self.widgetForAction(ac)
sm = getattr(ch, 'showMenu', None)
if callable(sm):
ev.accept()
sm()
def update_lm_actions(self):
for ac in self.added_actions:

View File

@ -207,10 +207,15 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
cols = sorted([k for k in displayable_columns(fm)])
self.colored_field.addItems(cols)
self.colored_field.setCurrentIndex(self.colored_field.findText(color_field))
colors = QColor.colorNames()
colors.sort()
self.color_name.addItems(colors)
else:
self.colored_field.setVisible(False)
self.colored_field_label.setVisible(False)
self.color_chooser_label.setVisible(False)
self.color_name.setVisible(False)
self.color_copy_button.setVisible(False)
if mi:
self.mi = mi
else:
@ -235,6 +240,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
self.textbox.setPlainText(text)
self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK'))
self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel'))
self.color_copy_button.clicked.connect(self.color_to_clipboard)
try:
with open(P('template-functions.json'), 'rb') as f:
@ -263,6 +269,11 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
'<a href="http://manual.calibre-ebook.com/template_ref.html">'
'%s</a>'%tt)
def color_to_clipboard(self):
app = QApplication.instance()
c = app.clipboard()
c.setText(unicode(self.color_name.currentText()))
def textbox_changed(self):
cur_text = unicode(self.textbox.toPlainText())
if self.last_text != cur_text:

View File

@ -21,8 +21,8 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout">
<item>
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QLabel" name="colored_field_label">
<property name="text">
<string>Set the color of the column:</string>
@ -32,10 +32,35 @@
</property>
</widget>
</item>
<item>
<item row="0" column="1">
<widget class="QComboBox" name="colored_field">
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="color_chooser_label">
<property name="text">
<string>Copy a color name to the clipboard:</string>
</property>
<property name="buddy">
<cstring>color_name</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="color_name">
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="color_copy_button">
<property name="icon">
<iconset resource="../../../../resources/images.qrc">
<normaloff>:/images/edit-copy.png</normaloff>:/images/edit-copy.png</iconset>
</property>
<property name="toolTip">
<string>Copy the selected color name to the clipboard</string>
</property>
</widget>
</item>
</layout>
</item>
<item>

View File

@ -223,6 +223,7 @@ class LayoutMixin(object): # {{{
self.bd_splitter.addWidget(self.book_details)
self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False)
self.centralwidget.layout().addWidget(self.bd_splitter)
button_order = ('tb', 'bd', 'cb')
# }}}
else: # wide {{{
self.bd_splitter = Splitter('book_details_splitter',
@ -237,10 +238,11 @@ class LayoutMixin(object): # {{{
self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,
QSizePolicy.Expanding))
self.centralwidget.layout().addWidget(self.bd_splitter)
button_order = ('tb', 'cb', 'bd')
# }}}
self.status_bar = StatusBar(self)
for x in ('cb', 'tb', 'bd'):
for x in button_order:
button = getattr(self, x+'_splitter').button
button.setIconSize(QSize(24, 24))
if isosx:

View File

@ -170,6 +170,10 @@ class GuiRunner(QObject):
sys.excepthook = main.unhandled_exception
if len(self.args) > 1:
p = os.path.abspath(self.args[1])
if os.path.isdir(p):
prints('Ignoring directory passed as command line argument:',
self.args[1])
else:
add_filesystem_book(p)
self.app.file_event_hook = add_filesystem_book
self.main = main

View File

@ -9,6 +9,7 @@ __docformat__ = 'restructuredtext en'
import os, errno
from functools import partial
from datetime import datetime
from PyQt4.Qt import (Qt, QVBoxLayout, QHBoxLayout, QWidget, QPushButton,
QGridLayout, pyqtSignal, QDialogButtonBox, QScrollArea, QFont,
@ -26,6 +27,7 @@ from calibre.gui2.custom_column_widgets import populate_metadata_page
from calibre.utils.config import tweaks
from calibre.ebooks.metadata.book.base import Metadata
from calibre.utils.localization import canonicalize_lang
from calibre.utils.date import local_tz
BASE_TITLE = _('Edit Metadata')
@ -396,6 +398,14 @@ class MetadataSingleDialogBase(ResizableDialog):
if ':' not in f:
setattr(mi, f, getattr(dummy, f))
if mi is not None:
pd = mi.pubdate
if pd is not None:
# Put the downloaded published date into the local timezone
# as we discard time info and the date is timezone
# invariant. This prevents the as_local_timezone() call in
# update_from_mi from changing the pubdate
mi.pubdate = datetime(pd.year, pd.month, pd.day,
tzinfo=local_tz)
self.update_from_mi(mi)
if d.cover_pixmap is not None:
self.cover.current_val = pixmap_to_data(d.cover_pixmap)

View File

@ -234,7 +234,7 @@
<widget class="QLabel" name="label_13">
<property name="text">
<string>&lt;p&gt;Remember to leave calibre running as the server only runs as long as calibre is running.
&lt;p&gt;Stanza should see your calibre collection automatically. If not, try adding the URL http://myhostname:8080 as a new catalog in the Stanza reader on your iPhone. Here myhostname should be the fully qualified hostname or the IP address of the computer calibre is running on.</string>
&lt;p&gt;To connect to the calibre server from your device you should use a URL of the form &lt;b&gt;http://myhostname:8080&lt;/b&gt; as a new catalog in the Stanza reader on your iPhone. Here myhostname should be either the fully qualified hostname or the IP address of the computer calibre is running on.</string>
</property>
<property name="wordWrap">
<bool>true</bool>

View File

@ -34,7 +34,7 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<item row="2" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>A&amp;vailable actions</string>
@ -62,7 +62,7 @@
</layout>
</widget>
</item>
<item row="1" column="2">
<item row="2" column="2">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QToolButton" name="add_action_button">
@ -122,7 +122,7 @@
</item>
</layout>
</item>
<item row="1" column="3" colspan="2">
<item row="2" column="3" colspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>&amp;Current actions</string>
@ -210,6 +210,16 @@
</layout>
</widget>
</item>
<item row="1" column="0" colspan="5">
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;p&gt;The toolbar in calibre is different depending on whether a device is connected or not. To customize the toolbar when a device is connected as well as customizing right click menus, &lt;b&gt;click the dropdown above&lt;/b&gt; and select which toolbar/menu you want to customize.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources>

View File

@ -56,7 +56,7 @@ class WoblinkStore(BasicStoreConfig, StorePlugin):
continue
cover_url = ''.join(data.xpath('.//td[@class="w10 va-t"]/a[1]/img/@src'))
title = ''.join(data.xpath('.//h3[@class="title"]/a[1]/text()'))
title = ''.join(data.xpath('.//h2[@class="title"]/a[1]/text()'))
author = ', '.join(data.xpath('.//p[@class="author"]/a/text()'))
price = ''.join(data.xpath('.//div[@class="prices"]/p[1]/span/text()'))
price = re.sub('PLN', '', price)

View File

@ -208,6 +208,12 @@ class PocketBook(CybookG3):
id = 'pocketbook'
output_profile = 'cybookg3'
class PocketBook900(PocketBook):
name = 'PocketBook 900'
id = 'pocketbook900'
output_profile = 'pocketbook_900'
class iPhone(Device):
name = 'iPad or iPhone/iTouch + Stanza'

View File

@ -150,12 +150,21 @@ class Formatter(TemplateFormatter):
traceback.print_exc()
b = None
if b is not None and b['datatype'] == 'composite':
val = b.get('#value#', None)
if val is not None:
return val.replace('/', '_').replace('\\', '_')
if key in self.composite_values:
self.composite_values[key] = val
return self.composite_values[key]
try:
# We really should not get here, but it is safer to try
self.composite_values[key] = 'RECURSIVE_COMPOSITE FIELD (S2D) ' + key
self.composite_values[key] = \
self.vformat(b['display']['composite_template'], [], kwargs)
self.vformat(b['display']['composite_template'],
[], kwargs).replace('/', '_').replace('\\', '_')
return self.composite_values[key]
except Exception, e:
return unicode(e)
if key in kwargs:
val = kwargs[key]
if isinstance(val, list):
@ -170,6 +179,13 @@ def get_components(template, mi, id, timefmt='%b %Y', length=250,
sanitize_func=ascii_filename, replace_whitespace=False,
to_lowercase=False, safe_format=True):
# Note: the mi argument is assumed to be an instance of Metadata returned
# by db.get_metadata(). Reason: the composite columns should have already
# been evaluated, which get_metadata does. If the mi is something else and
# if the template uses composite columns, then a best-efforts attempt is
# made to evaluate them. This will fail if the template uses a user-defined
# template function.
tsorder = tweaks['save_template_title_series_sorting']
format_args = FORMAT_ARGS.copy()
format_args.update(mi.all_non_none_fields())

View File

@ -465,13 +465,12 @@ class BrowseServer(object):
if not cats and len(items) == 1:
# Only one item in category, go directly to book list
prefix = '' if self.is_wsgi else self.opts.url_prefix
html = get_category_items(category, items,
self.search_restriction_name, datatype,
self.opts.url_prefix)
href = re.search(r'<a href="([^"]+)"', html)
if href is not None:
raise cherrypy.HTTPRedirect(prefix+href.group(1))
raise cherrypy.HTTPRedirect(href.group(1))
if len(items) <= self.opts.max_opds_ungrouped_items:
script = 'false'

View File

@ -218,7 +218,7 @@ class ContentServer(object):
if format in ('MOBI', 'EPUB'):
# Write the updated file
from calibre.ebooks.metadata.meta import set_metadata
set_metadata(fmt, newmi, 'epub')
set_metadata(fmt, newmi, format.lower())
fmt.seek(0)
mt = guess_type('dummy.'+format.lower())[0]

View File

@ -94,3 +94,10 @@ def unquote(s):
ans = ans.decode('utf-8')
return ans
def cookie_time_fmt(time_t):
return time.strftime('%a, %d-%b-%Y %H:%M:%S GMT', time_t)
def cookie_max_age_to_expires(max_age):
gmt_expiration_time = time.gmtime(time.time() + max_age)
return cookie_time_fmt(gmt_expiration_time)

View File

@ -185,7 +185,7 @@ The first, most important step is to run |app| in debug mode. You can do this fr
calibre-debug -g
Or from within calibre by clicking the arrow next to the preferences button or using the `Ctrl+Shift+R` keyboard shortcut.
Or from within calibre by right-clicking the preferences button or using the `Ctrl+Shift+R` keyboard shortcut.
When running from the command line, debug output will be printed to the console, when running from within |app| the output will go to a txt file.

View File

@ -108,6 +108,8 @@ At the moment |app| has full support for the SONY PRS line, Barnes & Noble Nook
There is also a special ``User Defined`` device plugin that can be used to connect to arbitrary devices that present their memory as disk drives. See the device plugin ``Preferences -> Plugins -> Device Plugins -> User Defined`` and ``Preferences -> Miscelleaneous -> Get information to setup the user defined device`` for more information.
.. _devsupport:
How can I help get my device supported in |app|?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -219,7 +221,7 @@ How do I use |app| with my iPad/iPhone/iTouch?
Over the air
^^^^^^^^^^^^^^
The easiest way to browse your |app| collection on your Apple device (iPad/iPhone/iPod) is by using the calibre sontent server, which makes your collection available over the net. First perform the following steps in |app|
The easiest way to browse your |app| collection on your Apple device (iPad/iPhone/iPod) is by using the calibre content server, which makes your collection available over the net. First perform the following steps in |app|
* Set the Preferred Output Format in |app| to EPUB (The output format can be set under :guilabel:`Preferences->Interface->Behavior`)
* Set the output profile to iPad (this will work for iPhone/iPods as well), under :guilabel:`Preferences->Conversion->Common Options->Page Setup`
@ -258,10 +260,36 @@ Use the 'Connect to iTunes' method in the 'Getting started' instructions in `Cal
This method only works on Windows XP and higher, and OS X 10.5 and higher. Linux is not supported (iTunes is not available in linux) and OS X 10.4 is not supported.
How do I use |app| with my Android phone?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
How do I use |app| with my Android phone/tablet?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
First install the WordPlayer ebook reading app from the Android Marketplace onto you phone. Then simply plug your phone into the computer with a USB cable. |app| should automatically detect the phone and then you can transfer books to it by clicking the Send to Device button. |app| does not have support for every single androind device out there, so if you would like to have support for your device added, follow the instructions above for getting your device supported in |app|.
There are two ways that you can connect your Android device to calibre. Using a USB cable-- or wirelessly, over the air.
The USB cable method only works if your Android device can act as a USB disk, which some Android tablets cannot.
Using a USB cable
^^^^^^^^^^^^^^^^^^^^
First install either the WordPlayer or Aldiko ebook reading apps from the Android Marketplace onto your phone. Then simply plug your phone into the computer with a USB cable. |app| should automatically detect the phone and then you can transfer books to it by clicking the Send to Device button. |app| does not have support for every single android device out there, so if your device is not automatically detected, follow the instructions at :ref:`devsupport` to get your device supported in |app|.
Over the air
^^^^^^^^^^^^^^
The easiest way to browse your |app| collection on your Android device is by using the calibre content server, which makes your collection available over the net. First perform the following steps in |app|
* Set the Preferred Output Format in |app| to EPUB (The output format can be set under :guilabel:`Preferences->Interface->Behavior`)
* Set the output profile to Tablet (this will work for phones as well), under :guilabel:`Preferences->Conversion->Common Options->Page Setup`
* Convert the books you want to read on your device to EPUB format by selecting them and clicking the Convert button.
* Turn on the Content Server in |app|'s preferences and leave |app| running.
Now on your Android device, open the browser and browse to
http://192.168.1.2:8080/
Replace ``192.168.1.2`` with the local IP address of the computer running |app|. If your local network supports the use of computer names, you can replace the IP address with the network name of the computer. If you have changed the port the |app| content server is running on, you will have to change ``8080`` as well to the new port.
The local IP address is the IP address you computer is assigned on your home network. A quick Google search will tell you how to find out your local IP address. You can now browse your book collection and download books from |app| to your device to open with whatever ebook reading software you have on your android device.
Some reading programs support browsing the Calibre library directly. For example, in Aldiko, click My Catalogs, then + to add a catalog, then give the catalog a title such as "Calibre" and provide the URL listed above. You can now browse the Calibre library and download directly into the reading software.
Can I access my |app| books using the web browser in my Kindle or other reading device?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -438,9 +466,9 @@ How do I move my |app| library from one computer to another?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Simply copy the |app| library folder from the old to the new computer. You can find out what the library folder is by clicking the calibre icon in the toolbar. The very first item is the path to the library folder. Now on the new computer, start |app| for the first time. It will run the Welcome Wizard asking you for the location of the |app| library. Point it to the previously copied folder. If the computer you are transferring to already has a calibre installation, then the Welcome wizard wont run. In that case, click the calibre icon in the tooolbar and point it to the newly copied directory. You will now have two calibre libraries on your computer and you can switch between them by clicking the calibre icon on the toolbar.
Note that if you are transferring between different types of computers (for example Windows to OS X) then after doing the above you should also click the arrow next to the calibre icon on the tool bar, select Library Maintenance and run the Check Library action. It will warn you about any problems in your library, which you should fix by hand.
Note that if you are transferring between different types of computers (for example Windows to OS X) then after doing the above you should also right-click the calibre icon on the tool bar, select Library Maintenance and run the Check Library action. It will warn you about any problems in your library, which you should fix by hand.
.. note:: A |app| library is just a folder which contains all the book files and their metadata. All the metadata is stored in a single file called metadata.db, in the top level folder. If this file gets corrupted, you may see an empty list of books in |app|. In this case you can ask |app| to restore your books by clicking the arrow next to the |app| icon on the toolbar and selecting Library Maintenance->Restore Library.
.. note:: A |app| library is just a folder which contains all the book files and their metadata. All the metadata is stored in a single file called metadata.db, in the top level folder. If this file gets corrupted, you may see an empty list of books in |app|. In this case you can ask |app| to restore your books by doing a right-click on the |app| icon in the toolbar and selecting Library Maintenance->Restore Library.
The list of books in |app| is blank!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -449,9 +477,9 @@ In order to understand why that happened, you have to understand what a |app| li
There can be two reasons why |app| is showing a empty list of books:
* Your |app| library folder changed its location. This can happen if it was on an external disk and the drive letter for that disk changed. Or if you accidentally moved the folder. In this case, |app| cannot find its library and so starts up with an empty library instead. To remedy this, simply click the arrow next to the |app| icon in the |app| toolbar (it will say 0 books underneath it) and select Switch/create library. Click the little blue icon to select the new location of your |app| library and click OK.
* Your |app| library folder changed its location. This can happen if it was on an external disk and the drive letter for that disk changed. Or if you accidentally moved the folder. In this case, |app| cannot find its library and so starts up with an empty library instead. To remedy this, do a right-click on the |app| icon in the |app| toolbar (it will say 0 books underneath it) and select Switch/create library. Click the little blue icon to select the new location of your |app| library and click OK.
* Your metadata.db file was deleted/corrupted. In this case, you can ask |app| to rebuild the metadata.db from its backups. Click the arrow next to the |app| icon in the |app| toolbar (it will say 0 books underneath it) and select Library maintenance->Restore database. |app| will automatically rebuild metadata.db.
* Your metadata.db file was deleted/corrupted. In this case, you can ask |app| to rebuild the metadata.db from its backups. Click-and-hold the |app| icon in the |app| toolbar (it will say 0 books underneath it) and select Library maintenance->Restore database. |app| will automatically rebuild metadata.db.
Content From The Web
@ -604,7 +632,7 @@ Can I have the comment metadata show up on my reader?
Most readers do not support this. You should complain to the manufacturer about it and hopefully if enough people complain, things will change. In the meantime, you can insert the metadata, including comments into a "Jacket page" at the start of the ebook, by using the option to "Insert metadata as page at start of book" during conversion. The option is found in the :guilabel:`Structure Detection` section of the conversion settings. Note that for this to have effect you have to *convert* the book. If your book is already in a format that does not need conversion, you can convert from that format to the same format.
Another alternative is to create a catalog in ebook form containing a listing of all the books in your calibre library, with their metadata. Click the arrow next to the convert button to access the catalog creation tool. And before you ask, no you cannot have the catalog "link directly to" books on your reader.
Another alternative is to create a catalog in ebook form containing a listing of all the books in your calibre library, with their metadata. Click-and-hold the convert button to access the catalog creation tool. And before you ask, no you cannot have the catalog "link directly to" books on your reader.
How do I get |app| to use my HTTP proxy?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -30,7 +30,8 @@ Actions
:alt: The Actions Toolbar
:align: center
The actions toolbar provides convenient shortcuts to commonly used actions. Most of the action buttons have little arrows next to them. By clicking the arrows, you can perform variations on the default action. Please note that the actions toolbar will look slightly different depending on whether you have an ebook reader attached to your computer.
The actions toolbar provides convenient shortcuts to commonly used actions. If you right-click the buttons, you can perform variations on the default action.
Please note that the actions toolbar will look slightly different depending on whether you have an ebook reader attached to your computer.
.. contents::
:depth: 1
@ -43,7 +44,7 @@ Add books
.. |adbi| image:: images/add_books.png
:class: float-right-img
|adbi| The :guilabel:`Add books` action has six variations accessed by the clicking the down arrow on the right side of the button.
|adbi| The :guilabel:`Add books` action has six variations accessed by doing a right-click on the button.
1. **Add books from a single directory**: Opens a file chooser dialog and allows you to specify which books in a directory should be added. This action is *context sensitive*, i.e. it depends on which :ref:`catalog <catalogs>` you have selected. If you have selected the :guilabel:`Library`, books will be added to the library. If you have selected the ebook reader device, the books will be uploaded to the device, and so on.
@ -70,7 +71,7 @@ Edit metadata
.. |emii| image:: images/edit_meta_information.png
:class: float-right-img
|emii| The :guilabel:`Edit metadata` action has four variations which can be accessed by clicking the down arrow on the right side of the button.
|emii| The :guilabel:`Edit metadata` action has four variations which can be accessed by doing a right-click on the button.
1. **Edit metadata individually**: Allows you to edit the metadata of books one-by-one with the option of fetching metadata, including covers, from the Internet. It also allows you to add or remove particular ebook formats from a book.
2. **Edit metadata in bulk**: Allows you to edit common metadata fields for large numbers of books simulataneously. It operates on all the books you have selected in the :ref:`Library view <search_sort>`.
@ -93,7 +94,7 @@ you will have to find tools to liberate your books yourself and then use |app| t
For most people, conversion should be a simple one-click affair. If you want to learn more about the conversion process, see :ref:`conversion`.
The :guilabel:`Convert books` action has three variations, accessed by the arrow next to the button.
The :guilabel:`Convert books` action has three variations, accessed by doing a right-click on the button.
1. **Convert individually**: Allows you to specify conversion options to customize the conversion of each selected ebook.
@ -116,8 +117,7 @@ View
|vi| The :guilabel:`View` action displays the book in an ebook viewer program. |app| has a built-in viewer for many ebook formats.
For other formats it uses the default operating system application. You can configure which formats should open with the internal viewer via
Preferences->Behavior. If a book has more than one format, you can view a particular format by clicking the down arrow
on the right of the :guilabel:`View` button.
Preferences->Behavior. If a book has more than one format, you can view a particular format by doing a right-click on the button.
.. _send_to_device:
@ -127,7 +127,7 @@ Send to device
.. |stdi| image:: images/send_to_device.png
:class: float-right-img
|stdi| The :guilabel:`Send to device` action has eight variations, accessed by clicking the down arrow on the right of the button.
|stdi| The :guilabel:`Send to device` action has eight variations, accessed by doing a right-click on the button.
1. **Send to main memory**: The selected books are transferred to the main memory of the ebook reader.
2. **Send to card (A)**: The selected books are transferred to the storage card (A) on the ebook reader.
@ -152,7 +152,7 @@ Fetch news
The :guilabel:`Fetch news` action uses simple recipes (10-15 lines of code) for each news site. To learn how to create recipes for your own news sources, see :ref:`news`.
The :guilabel:`Fetch news` action has three variations, accessed by clicking the down arrow on the right of the button.
The :guilabel:`Fetch news` action has three variations, accessed by doing a right-click on the button.
1. **Schedule news download**: Allows you to schedule the download of of your selected news sources from a list of hundreds available. Scheduling can be set individually for each news source you select and the scheduling is flexible allowing you to select specific days of the week or a frequency of days between downloads.
2. **Add a custom news source**: Allows you to create a simple recipe for downloading news from a custom news site that you wish to access. Creating the recipe can be as simple as specifying an RSS news feed URL, or you can be more prescriptive by creating Python-based code for the task. For more information see :ref:`news`.
@ -197,7 +197,7 @@ Save to disk
.. |svdi| image:: images/save_to_disk.png
:class: float-right-img
|svdi| The :guilabel:`Save to disk` action has five variations, accessed by the arrow next to the button.
|svdi| The :guilabel:`Save to disk` action has five variations, accessed by doing a right-click on the button.
.. _save_to_disk_multiple:
@ -231,7 +231,7 @@ Connect/Share
|csi| The :guilabel:`Connect/Share` action allows you to manually connect to a device or folder on your computer. It also allows you to set up you |app| library for access via a web browser or email.
The :guilabel:`Connect/Share` action has four variations, accessed by clicking the down arrow on the right of the button.
The :guilabel:`Connect/Share` action has four variations, accessed by doing a right-click on the button.
1. **Connect to folder**: Allows you to connect to any folder on your computer as though it were a device and use all the facilities |app| has for devices with that folder. Useful if your device cannot be supported by |app| but is available as a USB disk.
@ -248,7 +248,7 @@ Remove books
.. |rbi| image:: images/remove_books.png
:class: float-right-img
|rbi| The :guilabel:`Remove books` action **deletes books permanently**, so use it with care. It is *context sensitive*, i.e. it depends on which :ref:`catalog <catalogs>` you have selected. If you have selected the :guilabel:`Library`, books will be removed from the library. If you have selected the ebook reader device, books will be removed from the device. To remove only a particular format for a given book use the :ref:`edit_meta_information` action. Remove books also has five variations which can be accessed by clicking the down arrow on the right side of the button.
|rbi| The :guilabel:`Remove books` action **deletes books permanently**, so use it with care. It is *context sensitive*, i.e. it depends on which :ref:`catalog <catalogs>` you have selected. If you have selected the :guilabel:`Library`, books will be removed from the library. If you have selected the ebook reader device, books will be removed from the device. To remove only a particular format for a given book use the :ref:`edit_meta_information` action. Remove books also has five variations which can be accessed by doing a right-click on the button.
1. **Remove selected books**: Allows you to **permanently** remove all books that are selected in the book list.
@ -272,7 +272,7 @@ Preferences
.. |cbi| image:: images/preferences.png
:class: float-right-img
|cbi| The :guilabel:`Preferences` action allows you to change the way various aspects of |app| work. It has four variations, accessed by clicking the down arrow on the right of the button.
|cbi| The :guilabel:`Preferences` action allows you to change the way various aspects of |app| work. It has four variations, accessed by doing a right-click on the button.
1. **Preferences**: Allows you to change the way various aspects of |app| work. Clicking the button also performs this action.
2. **Run welcome wizard**: Allows you to start the Welcome Wizard which appeared the first time you started |app|.

View File

@ -47,7 +47,9 @@ In addition, there is a button to automatically trim borders from the cover, in
Editing the metadata of many books at a time
---------------------------------------------
First select the books you want to edit by holding Ctrl or Shift and clicking on them. If you select more than one book, clicking the :guilabel:`Edit metadata` button will cause a new *Bulk* metadata edit dialog to open. Using this dialog, you can quickly set the author/publisher/rating/tags/series etc of a bunch of books to the same value. This is particularly useful if you have just imported a number of books that have some metadata in common. You can also click the arrow next to the :guilabel:`Edit metadata` button and select :guilabel:`Edit metadata individually` to use the powerful single book edit dialog from above for all the selected books in succession.
First select the books you want to edit by holding Ctrl or Shift and clicking on them. If you select more than one book, clicking the :guilabel:`Edit metadata` button will cause a new *Bulk* metadata edit dialog to open. Using this dialog, you can quickly set the author/publisher/rating/tags/series etc of a bunch of books to the same value. This is particularly useful if you have just imported a number of books that have some metadata in common. This dialog is very powerful, for example, it has a Search and Replace tab that you can use to perform bulk operations on metadata and even copy metadata from one column to another.
The normal edit metadata dialog also has Next and Previous buttons that you can use to edit the metadata of several books one after the other.
Search and replace
^^^^^^^^^^^^^^^^^^^^
@ -81,6 +83,6 @@ Search and replace is done after all the other metadata changes in the other tab
Bulk downloading of metadata
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you want to download the metadata for multiple books at once, click the arrow next to the :guilabel:`Edit metadata` button and select :guilabel:`Download metadata and covers`. You can choose to download only metadata, only covers, both or only social metadata (tags/rating/series).
If you want to download the metadata for multiple books at once, right-click the :guilabel:`Edit metadata` button and select :guilabel:`Download metadata`. You can choose to download only metadata, only covers, or both.

View File

@ -294,7 +294,7 @@ To learn more about writing advanced recipes using some of the facilities, avail
`BasicNewsRecipe <http://bazaar.launchpad.net/~kovid/calibre/trunk/annotate/head:/src/calibre/web/feeds/news.py>`_
The source code of ``BasicNewsRecipe``
`Built-in recipes <http://bazaar.launchpad.net/~kovid/calibre/trunk/files/head:/src/calibre/web/feeds/recipes/>`_
`Built-in recipes <http://bazaar.launchpad.net/~kovid/calibre/trunk/files/head:/recipes/>`_
The source code for the built-in recipes that come with |app|
`The calibre recipes forum <http://www.mobileread.com/forums/forumdisplay.php?f=228>`_

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