mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 18:24:30 -04:00
Sync to trunk.
This commit is contained in:
commit
8dcd163846
@ -19,6 +19,76 @@
|
|||||||
# new recipes:
|
# new recipes:
|
||||||
# - title:
|
# - 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
|
- version: 0.8.19
|
||||||
date: 2011-09-16
|
date: 2011-09-16
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class BasicUserRecipe1316245412(BasicNewsRecipe):
|
class BasicUserRecipe1316245412(BasicNewsRecipe):
|
||||||
|
#from calibre.utils.magick import Image, PixelWand
|
||||||
title = u'Cicero Online'
|
title = u'Cicero Online'
|
||||||
description = u'Magazin f\xfcr politische Kultur'
|
description = u'Magazin f\xfcr politische Kultur (RSS Version)'
|
||||||
publisher = 'Ringier Publishing GmbH'
|
publisher = 'Ringier Publishing GmbH'
|
||||||
category = 'news, politics, Germany'
|
category = 'news, politics, Germany'
|
||||||
language = 'de'
|
language = 'de'
|
||||||
encoding = 'UTF-8'
|
encoding = 'UTF-8'
|
||||||
__author__ = 'Armin Geller' # Upd. 2011-09-19
|
__author__ = 'Armin Geller' # Upd. 2011-09-23
|
||||||
|
|
||||||
oldest_article = 7
|
oldest_article = 7
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
@ -18,16 +19,20 @@ class BasicUserRecipe1316245412(BasicNewsRecipe):
|
|||||||
|
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
dict(name='div', attrs={'id':["header", "navigation", "skip-link", "header-print", "header-print-url", "meta-toolbar", "footer"]}),
|
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",
|
dict(name='div', attrs={'class':["region region-sidebar-first column sidebar", "breadcrumb",
|
||||||
"field field-name-field-show-teaser-right field-type-list-boolean field-label-above"]}),
|
"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='div', attrs={'title':["Dossier Auswahl"]}),
|
||||||
dict(name='h2', attrs={'class':["title comment-form"]}),
|
dict(name='h2', attrs={'class':["title comment-form"]}),
|
||||||
dict(name='form', attrs={'class':["comment-form user-info-from-cookie"]}),
|
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"]}),
|
||||||
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
|
|
||||||
]
|
]
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
|
@ -2,6 +2,11 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
'''
|
'''
|
||||||
|
Changelog:
|
||||||
|
2011-09-24
|
||||||
|
Changed cover (drMerry)
|
||||||
|
'''
|
||||||
|
'''
|
||||||
news.cnet.com
|
news.cnet.com
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@ -9,7 +14,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
|||||||
|
|
||||||
class CnetNews(BasicNewsRecipe):
|
class CnetNews(BasicNewsRecipe):
|
||||||
title = 'CNET News'
|
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.'
|
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'
|
publisher = 'CNET'
|
||||||
category = 'news, IT, USA'
|
category = 'news, IT, USA'
|
||||||
|
21
recipes/den_of_geek.recipe
Normal file
21
recipes/den_of_geek.recipe
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class AdvancedUserRecipe1316944753(BasicNewsRecipe):
|
||||||
|
title = u'Den of Geek'
|
||||||
|
__author__ = 'Jaded'
|
||||||
|
language = 'en'
|
||||||
|
description = 'From science fiction enthusiasts through to gaming fanatics, Den of Geek has become the one-stop UK website for people genuinely passionate about their entertainment media. Den of Geek covers popular culture but always with an edgy, UK centric slant that sets it apart from the crowd.'
|
||||||
|
category = 'Movies, TV, Games, Comics, Cult, News, Reviews'
|
||||||
|
language = 'en'
|
||||||
|
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
auto_cleanup = True
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = True
|
||||||
|
publication_type = 'newsportal'
|
||||||
|
masthead_url ='http://www.denofgeek.com/siteimage/scale/0/0/logo.gif'
|
||||||
|
cover_url ='http://a5.sphotos.ak.fbcdn.net/hphotos-ak-snc6/166479_180131695357862_139191826118516_354818_4993703_n.jpg'
|
||||||
|
|
||||||
|
feeds = [(u'Movies', u'http://www.denofgeek.com/movies/rss/'), (u'TV', u'http://www.denofgeek.com/television/rss/'), (u'Comics & Books', u'http://www.denofgeek.com/comics/rss/'), (u'Games', u'http://www.denofgeek.com/games/rss/'), (u'DVD/Blu-ray', u'http://www.denofgeek.com/Reviews/rss/')]
|
@ -1,6 +1,3 @@
|
|||||||
'''
|
|
||||||
dnaindia.com
|
|
||||||
'''
|
|
||||||
import re
|
import re
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
@ -12,6 +9,10 @@ class DNAIndia(BasicNewsRecipe):
|
|||||||
language = 'en_IN'
|
language = 'en_IN'
|
||||||
|
|
||||||
encoding = 'cp1252'
|
encoding = 'cp1252'
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
auto_cleanup = True
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
('Top News', 'http://www.dnaindia.com/syndication/rss_topnews.xml'),
|
('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'),
|
('World', 'http://www.dnaindia.com/syndication/rss,catid-9.xml'),
|
||||||
('Money', 'http://www.dnaindia.com/syndication/rss,catid-4.xml'),
|
('Money', 'http://www.dnaindia.com/syndication/rss,catid-4.xml'),
|
||||||
('Sports', 'http://www.dnaindia.com/syndication/rss,catid-6.xml'),
|
('Sports', 'http://www.dnaindia.com/syndication/rss,catid-6.xml'),
|
||||||
('After Hours', 'http://www.dnaindia.com/syndication/rss,catid-7.xml'),
|
('After Hours', 'http://www.dnaindia.com/syndication/rss,catid-7.xml')
|
||||||
('Digital Life', 'http://www.dnaindia.com/syndication/rss,catid-1089741.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):
|
def print_version(self, url):
|
||||||
match = re.search(r'newsid=(\d+)', url)
|
match = re.search(r'newsid=(\d+)', url)
|
||||||
|
21
recipes/ekantipur.recipe
Normal file
21
recipes/ekantipur.recipe
Normal 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')]
|
||||||
|
|
||||||
|
|
BIN
recipes/icons/den_of_geek.png
Normal file
BIN
recipes/icons/den_of_geek.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
@ -29,7 +29,7 @@ class Kurier(BasicNewsRecipe):
|
|||||||
, 'language' : language
|
, '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'})]
|
keep_only_tags = [dict(attrs={'id':'content'})]
|
||||||
remove_tags_after = dict(attrs={'id':'author'})
|
remove_tags_after = dict(attrs={'id':'author'})
|
||||||
remove_attributes = ['width','height']
|
remove_attributes = ['width','height']
|
||||||
|
@ -95,7 +95,7 @@ class WeeklyLWN(BasicNewsRecipe):
|
|||||||
break
|
break
|
||||||
|
|
||||||
article = dict(
|
article = dict(
|
||||||
title=tag_title.string,
|
title=self.tag_to_string(tag_title),
|
||||||
url= 'http://lwn.net' + tag_url['href'].split('#')[0] + '?format=printable',
|
url= 'http://lwn.net' + tag_url['href'].split('#')[0] + '?format=printable',
|
||||||
description='', content='', date='')
|
description='', content='', date='')
|
||||||
articles[section].append(article)
|
articles[section].append(article)
|
||||||
|
14
recipes/republica.recipe
Normal file
14
recipes/republica.recipe
Normal 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
53
recipes/sign_on_sd.recipe
Normal 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')
|
||||||
|
]
|
||||||
|
|
@ -19,6 +19,6 @@ class TazRSSRecipe(BasicNewsRecipe):
|
|||||||
feeds = [(u'TAZ main feed', u'http://www.taz.de/rss.xml')]
|
feeds = [(u'TAZ main feed', u'http://www.taz.de/rss.xml')]
|
||||||
keep_only_tags = [dict(name='div', attrs={'class': 'sect sect_article'})]
|
keep_only_tags = [dict(name='div', attrs={'class': 'sect sect_article'})]
|
||||||
remove_tags_after = dict(name='div', attrs={'class': 'rack'})
|
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=['div'], attrs={'class': 'artikelwerbung'}),
|
||||||
dict(name=['ul'], attrs={'class': 'toolbar'}),]
|
dict(name=['ul'], attrs={'class': 'toolbar'}),]
|
||||||
|
@ -9,10 +9,15 @@ class TimesOfIndia(BasicNewsRecipe):
|
|||||||
max_articles_per_feed = 25
|
max_articles_per_feed = 25
|
||||||
|
|
||||||
no_stylesheets = True
|
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 = [
|
remove_tags = [
|
||||||
dict(style=lambda x: x and 'float' in x),
|
{'class':re.compile('tabsintbgshow|prvnxtbg')},
|
||||||
{'class':['prvnxtbg', 'footbdrin', 'bcclftr']},
|
{'id':['fbrecommend', 'relmaindiv']}
|
||||||
]
|
]
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
@ -41,6 +46,8 @@ class TimesOfIndia(BasicNewsRecipe):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def get_article_url(self, article):
|
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)
|
url = BasicNewsRecipe.get_article_url(self, article)
|
||||||
if '/0Ltimesofindia' in url:
|
if '/0Ltimesofindia' in url:
|
||||||
url = url.partition('/0L')[-1]
|
url = url.partition('/0L')[-1]
|
||||||
@ -61,6 +68,3 @@ class TimesOfIndia(BasicNewsRecipe):
|
|||||||
|
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
|
||||||
return soup
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
__author__ = 'Darko Spasovski'
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2011, Darko Spasovski <darko.spasovski at gmail.com>'
|
__copyright__ = '2011, Darko Spasovski <darko.spasovski at gmail.com>'
|
||||||
'''
|
'''
|
||||||
@ -9,10 +10,11 @@ utrinski.com.mk
|
|||||||
import re
|
import re
|
||||||
import datetime
|
import datetime
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||||
|
from calibre import browser
|
||||||
|
|
||||||
class UtrinskiVesnik(BasicNewsRecipe):
|
class UtrinskiVesnik(BasicNewsRecipe):
|
||||||
|
|
||||||
__author__ = 'Darko Spasovski'
|
|
||||||
INDEX = 'http://www.utrinski.com.mk/'
|
INDEX = 'http://www.utrinski.com.mk/'
|
||||||
title = 'Utrinski Vesnik'
|
title = 'Utrinski Vesnik'
|
||||||
description = 'Daily Macedonian newspaper'
|
description = 'Daily Macedonian newspaper'
|
||||||
@ -21,7 +23,6 @@ class UtrinskiVesnik(BasicNewsRecipe):
|
|||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
publication_type = 'newspaper'
|
publication_type = 'newspaper'
|
||||||
category = 'news, Macedonia'
|
category = 'news, Macedonia'
|
||||||
oldest_article = 2
|
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
@ -47,25 +48,29 @@ class UtrinskiVesnik(BasicNewsRecipe):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def parse_index(self):
|
def parse_index(self):
|
||||||
soup = self.index_to_soup(self.INDEX)
|
|
||||||
feeds = []
|
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
|
sectionTitle = section.contents[0].string
|
||||||
tocItemTable = section.findAllPrevious('table')[1]
|
sectionUrl = self.INDEX + section['href'].strip()
|
||||||
if tocItemTable is None: continue
|
# 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 = []
|
articles = []
|
||||||
while True:
|
for sectionArticle in sectionArticles:
|
||||||
tocItemTable = tocItemTable.nextSibling
|
# article title = anchor's contents, article url = anchor's href
|
||||||
if tocItemTable is None: break
|
articleTitle = sectionArticle.contents[0].string.strip()
|
||||||
article = tocItemTable.findAll('a', attrs={'class': 'WB_UTRINSKIVESNIK_TocItem'})
|
articleUrl = self.INDEX + sectionArticle['href'].strip()
|
||||||
if len(article)==0: break
|
articleDate = datetime.datetime.today().strftime('%d.%m.%Y')
|
||||||
title = self.tag_to_string(article[0], use_alt=True).strip()
|
articles.append({'title': articleTitle, 'url':articleUrl, 'description':'', 'date': articleDate})
|
||||||
articles.append({'title': title, 'url':'http://www.utrinski.com.mk/' + article[0]['href'], 'description':'', 'date':''})
|
|
||||||
if articles:
|
if articles:
|
||||||
feeds.append((sectionTitle, articles))
|
feeds.append((sectionTitle, articles))
|
||||||
return feeds
|
return feeds
|
||||||
|
|
||||||
|
|
||||||
def get_cover_url(self):
|
def get_cover_url(self):
|
||||||
datum = datetime.datetime.today().strftime('%d_%m_%Y')
|
datum = datetime.datetime.today().strftime('%d_%m_%Y')
|
||||||
return 'http://www.utrinski.com.mk/WBStorage/Files/' + datum + '.jpg'
|
return 'http://www.utrinski.com.mk/WBStorage/Files/' + datum + '.jpg'
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
'''
|
||||||
|
Changelog:
|
||||||
|
2011-09-24
|
||||||
|
Changed cover (drMerry)
|
||||||
|
'''
|
||||||
'''
|
'''
|
||||||
Fetch xkcd.
|
Fetch xkcd.
|
||||||
'''
|
'''
|
||||||
@ -9,9 +13,10 @@ import time, re
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class XkcdCom(BasicNewsRecipe):
|
class XkcdCom(BasicNewsRecipe):
|
||||||
|
cover_url = 'http://imgs.xkcd.com/s/9be30a7.png'
|
||||||
title = 'xkcd'
|
title = 'xkcd'
|
||||||
description = 'A webcomic of romance and math humor.'
|
description = 'A webcomic of romance and math humor.'
|
||||||
__author__ = 'Martin Pitt'
|
__author__ = 'Martin Pitt updated by DrMerry.'
|
||||||
language = 'en'
|
language = 'en'
|
||||||
|
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
|
1448
setup/iso_639/ca.po
1448
setup/iso_639/ca.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = u'calibre'
|
__appname__ = u'calibre'
|
||||||
numeric_version = (0, 8, 19)
|
numeric_version = (0, 8, 20)
|
||||||
__version__ = u'.'.join(map(unicode, numeric_version))
|
__version__ = u'.'.join(map(unicode, numeric_version))
|
||||||
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
|
@ -653,6 +653,7 @@ class KindleDXOutput(OutputProfile):
|
|||||||
return u'%s <br/><span style="color: white">%s</span>' % (', '.join(tags),
|
return u'%s <br/><span style="color: white">%s</span>' % (', '.join(tags),
|
||||||
'ttt '.join(tags)+'ttt ')
|
'ttt '.join(tags)+'ttt ')
|
||||||
|
|
||||||
|
|
||||||
class IlliadOutput(OutputProfile):
|
class IlliadOutput(OutputProfile):
|
||||||
|
|
||||||
name = 'Illiad'
|
name = 'Illiad'
|
||||||
@ -731,12 +732,23 @@ class BambookOutput(OutputProfile):
|
|||||||
fbase = 12
|
fbase = 12
|
||||||
fsizes = [10, 12, 14, 16]
|
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,
|
output_profiles = [OutputProfile, SonyReaderOutput, SonyReader300Output,
|
||||||
SonyReader900Output, MSReaderOutput, MobipocketOutput, HanlinV3Output,
|
SonyReader900Output, MSReaderOutput, MobipocketOutput, HanlinV3Output,
|
||||||
HanlinV5Output, CybookG3Output, CybookOpusOutput, KindleOutput,
|
HanlinV5Output, CybookG3Output, CybookOpusOutput, KindleOutput,
|
||||||
iPadOutput, KoboReaderOutput, TabletOutput, SamsungGalaxy,
|
iPadOutput, KoboReaderOutput, TabletOutput, SamsungGalaxy,
|
||||||
SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput,
|
SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput,
|
||||||
IRexDR1000Output, IRexDR800Output, JetBook5Output, NookOutput,
|
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()))
|
output_profiles.sort(cmp=lambda x,y:cmp(x.name.lower(), y.name.lower()))
|
||||||
|
@ -7,6 +7,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
SPOOL_SIZE = 30*1024*1024
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Rewrite of the calibre database backend.
|
Rewrite of the calibre database backend.
|
||||||
|
@ -8,7 +8,7 @@ __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
# Imports {{{
|
# Imports {{{
|
||||||
import os, shutil, uuid, json, glob, time, tempfile
|
import os, shutil, uuid, json, glob, time
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
import apsw
|
import apsw
|
||||||
@ -16,7 +16,8 @@ import apsw
|
|||||||
from calibre import isbytestring, force_unicode, prints
|
from calibre import isbytestring, force_unicode, prints
|
||||||
from calibre.constants import (iswindows, filesystem_encoding,
|
from calibre.constants import (iswindows, filesystem_encoding,
|
||||||
preferred_encoding)
|
preferred_encoding)
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile, SpooledTemporaryFile
|
||||||
|
from calibre.db import SPOOL_SIZE
|
||||||
from calibre.db.schema_upgrades import SchemaUpgrade
|
from calibre.db.schema_upgrades import SchemaUpgrade
|
||||||
from calibre.library.field_metadata import FieldMetadata
|
from calibre.library.field_metadata import FieldMetadata
|
||||||
from calibre.ebooks.metadata import title_sort, author_to_author_sort
|
from calibre.ebooks.metadata import title_sort, author_to_author_sort
|
||||||
@ -38,7 +39,7 @@ Differences in semantics from pysqlite:
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
SPOOL_SIZE = 30*1024*1024
|
|
||||||
|
|
||||||
class DynamicFilter(object): # {{{
|
class DynamicFilter(object): # {{{
|
||||||
|
|
||||||
@ -805,7 +806,7 @@ class DB(object):
|
|||||||
shutil.copyfileobj(f, pt)
|
shutil.copyfileobj(f, pt)
|
||||||
return pt.name
|
return pt.name
|
||||||
if as_file:
|
if as_file:
|
||||||
ret = tempfile.SpooledTemporaryFile(SPOOL_SIZE)
|
ret = SpooledTemporaryFile(SPOOL_SIZE)
|
||||||
shutil.copyfileobj(f, ret)
|
shutil.copyfileobj(f, ret)
|
||||||
ret.seek(0)
|
ret.seek(0)
|
||||||
else:
|
else:
|
||||||
|
@ -4,6 +4,10 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
import cStringIO
|
||||||
|
|
||||||
from calibre.devices.usbms.driver import USBMS
|
from calibre.devices.usbms.driver import USBMS
|
||||||
|
|
||||||
class ANDROID(USBMS):
|
class ANDROID(USBMS):
|
||||||
@ -209,4 +213,63 @@ class WEBOS(USBMS):
|
|||||||
VENDOR_NAME = 'HP'
|
VENDOR_NAME = 'HP'
|
||||||
WINDOWS_MAIN_MEM = 'WEBOS-DEVICE'
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,11 +51,11 @@ class KOBO(USBMS):
|
|||||||
|
|
||||||
EXTRA_CUSTOMIZATION_MESSAGE = [
|
EXTRA_CUSTOMIZATION_MESSAGE = [
|
||||||
_('The Kobo supports several collections including ')+\
|
_('The Kobo supports several collections including ')+\
|
||||||
'Read, Closed, Im_Reading ' +\
|
'Read, Closed, Im_Reading. ' +\
|
||||||
_('Create tags for automatic management'),
|
_('Create tags for automatic management'),
|
||||||
]
|
]
|
||||||
|
|
||||||
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(['tags'])
|
EXTRA_CUSTOMIZATION_DEFAULT = [', '.join(['tags'])]
|
||||||
|
|
||||||
OPT_COLLECTIONS = 0
|
OPT_COLLECTIONS = 0
|
||||||
|
|
||||||
|
@ -73,6 +73,20 @@ class Worker(Thread): # Get details {{{
|
|||||||
8: ['août'],
|
8: ['août'],
|
||||||
9: ['sept'],
|
9: ['sept'],
|
||||||
12: ['déc'],
|
12: ['déc'],
|
||||||
|
},
|
||||||
|
'es': {
|
||||||
|
1: ['enero'],
|
||||||
|
2: ['febrero'],
|
||||||
|
3: ['marzo'],
|
||||||
|
4: ['abril'],
|
||||||
|
5: ['mayo'],
|
||||||
|
6: ['junio'],
|
||||||
|
7: ['julio'],
|
||||||
|
8: ['agosto'],
|
||||||
|
9: ['septiembre', 'setiembre'],
|
||||||
|
10: ['octubre'],
|
||||||
|
11: ['noviembre'],
|
||||||
|
12: ['diciembre'],
|
||||||
},
|
},
|
||||||
'jp': {
|
'jp': {
|
||||||
1: [u'1月'],
|
1: [u'1月'],
|
||||||
@ -101,13 +115,16 @@ class Worker(Thread): # Get details {{{
|
|||||||
text()="Dettagli prodotto" or \
|
text()="Dettagli prodotto" or \
|
||||||
text()="Product details" or \
|
text()="Product details" or \
|
||||||
text()="Détails sur le produit" or \
|
text()="Détails sur le produit" or \
|
||||||
|
text()="Detalles del producto" or \
|
||||||
text()="登録情報"]/../div[@class="content"]
|
text()="登録情報"]/../div[@class="content"]
|
||||||
'''
|
'''
|
||||||
|
# Editor: is for Spanish
|
||||||
self.publisher_xpath = '''
|
self.publisher_xpath = '''
|
||||||
descendant::*[starts-with(text(), "Publisher:") or \
|
descendant::*[starts-with(text(), "Publisher:") or \
|
||||||
starts-with(text(), "Verlag:") or \
|
starts-with(text(), "Verlag:") or \
|
||||||
starts-with(text(), "Editore:") or \
|
starts-with(text(), "Editore:") or \
|
||||||
starts-with(text(), "Editeur") or \
|
starts-with(text(), "Editeur") or \
|
||||||
|
starts-with(text(), "Editor:") or \
|
||||||
starts-with(text(), "出版社:")]
|
starts-with(text(), "出版社:")]
|
||||||
'''
|
'''
|
||||||
self.language_xpath = '''
|
self.language_xpath = '''
|
||||||
@ -116,12 +133,14 @@ class Worker(Thread): # Get details {{{
|
|||||||
or text() = "Language" \
|
or text() = "Language" \
|
||||||
or text() = "Sprache:" \
|
or text() = "Sprache:" \
|
||||||
or text() = "Lingua:" \
|
or text() = "Lingua:" \
|
||||||
|
or text() = "Idioma:" \
|
||||||
or starts-with(text(), "Langue") \
|
or starts-with(text(), "Langue") \
|
||||||
or starts-with(text(), "言語") \
|
or starts-with(text(), "言語") \
|
||||||
]
|
]
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.ratings_pat = re.compile(
|
self.ratings_pat = re.compile(
|
||||||
r'([0-9.]+) ?(out of|von|su|étoiles sur|つ星のうち) ([\d\.]+)( (stars|Sternen|stelle)){0,1}')
|
r'([0-9.]+) ?(out of|von|su|étoiles sur|つ星のうち|de un máximo de) ([\d\.]+)( (stars|Sternen|stelle|estrellas)){0,1}')
|
||||||
|
|
||||||
lm = {
|
lm = {
|
||||||
'eng': ('English', 'Englisch'),
|
'eng': ('English', 'Englisch'),
|
||||||
@ -143,6 +162,7 @@ class Worker(Thread): # Get details {{{
|
|||||||
for i, vals in self.months.iteritems():
|
for i, vals in self.months.iteritems():
|
||||||
for x in vals:
|
for x in vals:
|
||||||
ans = ans.replace(x, self.english_months[i])
|
ans = ans.replace(x, self.english_months[i])
|
||||||
|
ans = ans.replace(' de ', ' ')
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -422,6 +442,7 @@ class Amazon(Source):
|
|||||||
'uk' : _('UK'),
|
'uk' : _('UK'),
|
||||||
'it' : _('Italy'),
|
'it' : _('Italy'),
|
||||||
'jp' : _('Japan'),
|
'jp' : _('Japan'),
|
||||||
|
'es' : _('Spain'),
|
||||||
}
|
}
|
||||||
|
|
||||||
options = (
|
options = (
|
||||||
@ -789,6 +810,16 @@ if __name__ == '__main__': # tests {{{
|
|||||||
),
|
),
|
||||||
] # }}}
|
] # }}}
|
||||||
|
|
||||||
|
es_tests = [ # {{{
|
||||||
|
(
|
||||||
|
{'identifiers':{'isbn': '8483460831'}},
|
||||||
|
[title_test('Tiempos Interesantes',
|
||||||
|
exact=True), authors_test(['Terry Pratchett'])
|
||||||
|
]
|
||||||
|
|
||||||
|
),
|
||||||
|
] # }}}
|
||||||
|
|
||||||
jp_tests = [ # {{{
|
jp_tests = [ # {{{
|
||||||
( # isbn -> title, authors
|
( # isbn -> title, authors
|
||||||
{'identifiers':{'isbn': '9784101302720' }},
|
{'identifiers':{'isbn': '9784101302720' }},
|
||||||
@ -804,6 +835,6 @@ if __name__ == '__main__': # tests {{{
|
|||||||
] # }}}
|
] # }}}
|
||||||
|
|
||||||
test_identify_plugin(Amazon.name, com_tests)
|
test_identify_plugin(Amazon.name, com_tests)
|
||||||
#test_identify_plugin(Amazon.name, jp_tests)
|
#test_identify_plugin(Amazon.name, es_tests)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -249,6 +249,7 @@ class SaveWorker(Thread):
|
|||||||
recs[pref.name] = getattr(self.opts, pref.name)
|
recs[pref.name] = getattr(self.opts, pref.name)
|
||||||
|
|
||||||
plugboards = self.db.prefs.get('plugboards', {})
|
plugboards = self.db.prefs.get('plugboards', {})
|
||||||
|
template_functions = self.db.prefs.get('user_template_functions', [])
|
||||||
|
|
||||||
for i, task in enumerate(tasks):
|
for i, task in enumerate(tasks):
|
||||||
tids = [x[-1] for x in task]
|
tids = [x[-1] for x in task]
|
||||||
@ -260,7 +261,7 @@ class SaveWorker(Thread):
|
|||||||
job = ParallelJob('save_book',
|
job = ParallelJob('save_book',
|
||||||
'Save books (%d of %d)'%(i, len(tasks)),
|
'Save books (%d of %d)'%(i, len(tasks)),
|
||||||
lambda x,y:x,
|
lambda x,y:x,
|
||||||
args=[tids, dpath, plugboards, self.path, recs])
|
args=[tids, dpath, plugboards, template_functions, self.path, recs])
|
||||||
jobs.add(job)
|
jobs.add(job)
|
||||||
server.add_job(job)
|
server.add_job(job)
|
||||||
|
|
||||||
@ -312,9 +313,12 @@ class SaveWorker(Thread):
|
|||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def save_book(ids, dpath, plugboards, path, recs, notification=lambda x,y:x):
|
def save_book(ids, dpath, plugboards, template_functions, path, recs,
|
||||||
|
notification=lambda x,y:x):
|
||||||
from calibre.library.save_to_disk import config, save_serialized_to_disk
|
from calibre.library.save_to_disk import config, save_serialized_to_disk
|
||||||
from calibre.customize.ui import apply_null_metadata
|
from calibre.customize.ui import apply_null_metadata
|
||||||
|
from calibre.utils.formatter_functions import load_user_template_functions
|
||||||
|
load_user_template_functions(template_functions)
|
||||||
opts = config().parse()
|
opts = config().parse()
|
||||||
for name in recs:
|
for name in recs:
|
||||||
setattr(opts, name, recs[name])
|
setattr(opts, name, recs[name])
|
||||||
|
@ -367,9 +367,11 @@ class MobiMLizer(object):
|
|||||||
istate.fgcolor = style['color']
|
istate.fgcolor = style['color']
|
||||||
istate.strikethrough = style['text-decoration'] == 'line-through'
|
istate.strikethrough = style['text-decoration'] == 'line-through'
|
||||||
istate.underline = style['text-decoration'] == 'underline'
|
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'
|
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'
|
istate.family = 'sans-serif'
|
||||||
else:
|
else:
|
||||||
istate.family = 'serif'
|
istate.family = 'serif'
|
||||||
|
@ -144,8 +144,15 @@ class BookHeader(object):
|
|||||||
self.codec = 'cp1252' if not user_encoding else user_encoding
|
self.codec = 'cp1252' if not user_encoding else user_encoding
|
||||||
log.warn('Unknown codepage %d. Assuming %s' % (self.codepage,
|
log.warn('Unknown codepage %d. Assuming %s' % (self.codepage,
|
||||||
self.codec))
|
self.codec))
|
||||||
|
# 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
|
if (ident == 'TEXTREAD' or self.length < 0xE4 or
|
||||||
0xF8 < self.length or # 0xF8 is a correct MOBI header with DRM fields
|
self.length > max_header_length or
|
||||||
(try_extra_data_fix and self.length == 0xE4)):
|
(try_extra_data_fix and self.length == 0xE4)):
|
||||||
self.extra_flags = 0
|
self.extra_flags = 0
|
||||||
else:
|
else:
|
||||||
|
@ -216,7 +216,7 @@ class Stylizer(object):
|
|||||||
if t:
|
if t:
|
||||||
text += u'\n\n' + force_unicode(t, u'utf-8')
|
text += u'\n\n' + force_unicode(t, u'utf-8')
|
||||||
if text:
|
if text:
|
||||||
text = XHTML_CSS_NAMESPACE + elem.text
|
text = XHTML_CSS_NAMESPACE + text
|
||||||
text = oeb.css_preprocessor(text)
|
text = oeb.css_preprocessor(text)
|
||||||
stylesheet = parser.parseString(text, href=cssname)
|
stylesheet = parser.parseString(text, href=cssname)
|
||||||
stylesheet.namespaces['h'] = XHTML_NS
|
stylesheet.namespaces['h'] = XHTML_NS
|
||||||
|
@ -126,7 +126,7 @@ void XMLImages::add(GfxState *state, Object *ref, Stream *str,
|
|||||||
|
|
||||||
if (img->type == jpeg) {
|
if (img->type == jpeg) {
|
||||||
int c;
|
int c;
|
||||||
str = ((DCTStream *)str)->getRawStream();
|
str = str->getNextStream();
|
||||||
str->reset();
|
str->reset();
|
||||||
|
|
||||||
// copy the stream
|
// copy the stream
|
||||||
|
@ -625,7 +625,12 @@ static string get_link_dest(LinkAction *link, PDFDoc *doc) {
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (POPPLER_MAJOR_VERSION == 0) && (POPPLER_MINOR_VERSION < 17)
|
||||||
void XMLOutputDev::process_link(Link* link){
|
void XMLOutputDev::process_link(Link* link){
|
||||||
|
#else
|
||||||
|
void XMLOutputDev::process_link(AnnotLink* link){
|
||||||
|
#endif
|
||||||
|
|
||||||
double _x1, _y1, _x2, _y2;
|
double _x1, _y1, _x2, _y2;
|
||||||
int x1, y1, x2, y2;
|
int x1, y1, x2, y2;
|
||||||
|
|
||||||
|
@ -244,6 +244,11 @@ class XMLOutputDev : public OutputDev {
|
|||||||
XMLImages *images;
|
XMLImages *images;
|
||||||
PDFDoc *doc;
|
PDFDoc *doc;
|
||||||
|
|
||||||
|
#if (POPPLER_MAJOR_VERSION == 0) && (POPPLER_MINOR_VERSION < 17)
|
||||||
void process_link(Link* link);
|
void process_link(Link* link);
|
||||||
|
#else
|
||||||
|
void process_link(AnnotLink* link);
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,10 @@ class PDFWriter(QObject): # {{{
|
|||||||
printer.setOutputFormat(QPrinter.NativeFormat)
|
printer.setOutputFormat(QPrinter.NativeFormat)
|
||||||
self.view.print_(printer)
|
self.view.print_(printer)
|
||||||
printer.abort()
|
printer.abort()
|
||||||
|
else:
|
||||||
|
# The document is so corrupt that we can't render the page.
|
||||||
|
self.loop.exit(0)
|
||||||
|
raise Exception('Document cannot be rendered.')
|
||||||
self._render_book()
|
self._render_book()
|
||||||
|
|
||||||
def _delete_tmpdir(self):
|
def _delete_tmpdir(self):
|
||||||
@ -207,8 +211,11 @@ class PDFWriter(QObject): # {{{
|
|||||||
try:
|
try:
|
||||||
outPDF = PdfFileWriter(title=self.metadata.title, author=self.metadata.author)
|
outPDF = PdfFileWriter(title=self.metadata.title, author=self.metadata.author)
|
||||||
for item in self.combine_queue:
|
for item in self.combine_queue:
|
||||||
with open(item, 'rb') as item_stream:
|
# The input PDF stream must remain open until the final PDF
|
||||||
inputPDF = PdfFileReader(item_stream)
|
# is written to disk. PyPDF references pages added to the
|
||||||
|
# final PDF from the input PDF on disk. It does not store
|
||||||
|
# the pages in memory so we can't close the input PDF.
|
||||||
|
inputPDF = PdfFileReader(open(item, 'rb'))
|
||||||
for page in inputPDF.pages:
|
for page in inputPDF.pages:
|
||||||
outPDF.addPage(page)
|
outPDF.addPage(page)
|
||||||
outPDF.write(self.out_stream)
|
outPDF.write(self.out_stream)
|
||||||
|
@ -28,7 +28,7 @@ class Tokenize:
|
|||||||
self.__bug_handler = bug_handler
|
self.__bug_handler = bug_handler
|
||||||
self.__copy = copy
|
self.__copy = copy
|
||||||
self.__write_to = tempfile.mktemp()
|
self.__write_to = tempfile.mktemp()
|
||||||
# self.__out_file = out_file
|
# self.__write_to = out_file
|
||||||
self.__compile_expressions()
|
self.__compile_expressions()
|
||||||
#variables
|
#variables
|
||||||
self.__uc_char = 0
|
self.__uc_char = 0
|
||||||
@ -41,14 +41,11 @@ class Tokenize:
|
|||||||
|
|
||||||
def __remove_uc_chars(self, startchar, token):
|
def __remove_uc_chars(self, startchar, token):
|
||||||
for i in xrange(startchar, len(token)):
|
for i in xrange(startchar, len(token)):
|
||||||
#handle the case of an uc char with a terminating blank before ansi char
|
if self.__uc_char:
|
||||||
if token[i] == " " and self.__uc_char:
|
|
||||||
continue
|
|
||||||
elif self.__uc_char:
|
|
||||||
self.__uc_char -= 1
|
self.__uc_char -= 1
|
||||||
else:
|
else:
|
||||||
return token[i:]
|
return token[i:]
|
||||||
#if only " " and char to skip
|
#if only char to skip
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def __unicode_process(self, token):
|
def __unicode_process(self, token):
|
||||||
@ -90,7 +87,7 @@ class Tokenize:
|
|||||||
self.__reini_utf8_counters()
|
self.__reini_utf8_counters()
|
||||||
#get value and handle negative case
|
#get value and handle negative case
|
||||||
uni_char = int(match_obj.group(1))
|
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:
|
if uni_char < 0:
|
||||||
uni_char += 65536
|
uni_char += 65536
|
||||||
uni_char = unichr(uni_char).encode('ascii', 'xmlcharrefreplace')
|
uni_char = unichr(uni_char).encode('ascii', 'xmlcharrefreplace')
|
||||||
@ -199,7 +196,7 @@ class Tokenize:
|
|||||||
|
|
||||||
# import sys
|
# import sys
|
||||||
# def main(args=sys.argv):
|
# def main(args=sys.argv):
|
||||||
# if len(args) < 1:
|
# if len(args) < 2:
|
||||||
# print 'No file'
|
# print 'No file'
|
||||||
# return
|
# return
|
||||||
# file = 'data_tokens.txt'
|
# file = 'data_tokens.txt'
|
||||||
@ -211,3 +208,5 @@ class Tokenize:
|
|||||||
|
|
||||||
# if __name__ == '__main__':
|
# if __name__ == '__main__':
|
||||||
# sys.exit(main())
|
# sys.exit(main())
|
||||||
|
|
||||||
|
# calibre-debug -e src/calibre/ebooks/rtf2xml/tokenize.py
|
@ -18,7 +18,6 @@ class ToolBar(QToolBar): # {{{
|
|||||||
|
|
||||||
def __init__(self, donate, location_manager, parent):
|
def __init__(self, donate, location_manager, parent):
|
||||||
QToolBar.__init__(self, parent)
|
QToolBar.__init__(self, parent)
|
||||||
self.setContextMenuPolicy(Qt.PreventContextMenu)
|
|
||||||
self.setMovable(False)
|
self.setMovable(False)
|
||||||
self.setFloatable(False)
|
self.setFloatable(False)
|
||||||
self.setOrientation(Qt.Horizontal)
|
self.setOrientation(Qt.Horizontal)
|
||||||
@ -53,8 +52,14 @@ class ToolBar(QToolBar): # {{{
|
|||||||
style = Qt.ToolButtonIconOnly
|
style = Qt.ToolButtonIconOnly
|
||||||
return style
|
return style
|
||||||
|
|
||||||
def contextMenuEvent(self, *args):
|
def contextMenuEvent(self, ev):
|
||||||
pass
|
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):
|
def update_lm_actions(self):
|
||||||
for ac in self.added_actions:
|
for ac in self.added_actions:
|
||||||
|
@ -207,10 +207,15 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
cols = sorted([k for k in displayable_columns(fm)])
|
cols = sorted([k for k in displayable_columns(fm)])
|
||||||
self.colored_field.addItems(cols)
|
self.colored_field.addItems(cols)
|
||||||
self.colored_field.setCurrentIndex(self.colored_field.findText(color_field))
|
self.colored_field.setCurrentIndex(self.colored_field.findText(color_field))
|
||||||
|
colors = QColor.colorNames()
|
||||||
|
colors.sort()
|
||||||
|
self.color_name.addItems(colors)
|
||||||
else:
|
else:
|
||||||
self.colored_field.setVisible(False)
|
self.colored_field.setVisible(False)
|
||||||
self.colored_field_label.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:
|
if mi:
|
||||||
self.mi = mi
|
self.mi = mi
|
||||||
else:
|
else:
|
||||||
@ -235,6 +240,7 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
self.textbox.setPlainText(text)
|
self.textbox.setPlainText(text)
|
||||||
self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK'))
|
self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK'))
|
||||||
self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel'))
|
self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel'))
|
||||||
|
self.color_copy_button.clicked.connect(self.color_to_clipboard)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(P('template-functions.json'), 'rb') as f:
|
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">'
|
'<a href="http://manual.calibre-ebook.com/template_ref.html">'
|
||||||
'%s</a>'%tt)
|
'%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):
|
def textbox_changed(self):
|
||||||
cur_text = unicode(self.textbox.toPlainText())
|
cur_text = unicode(self.textbox.toPlainText())
|
||||||
if self.last_text != cur_text:
|
if self.last_text != cur_text:
|
||||||
|
@ -21,8 +21,8 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout">
|
<layout class="QGridLayout">
|
||||||
<item>
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="colored_field_label">
|
<widget class="QLabel" name="colored_field_label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Set the color of the column:</string>
|
<string>Set the color of the column:</string>
|
||||||
@ -32,10 +32,35 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="0" column="1">
|
||||||
<widget class="QComboBox" name="colored_field">
|
<widget class="QComboBox" name="colored_field">
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -223,6 +223,7 @@ class LayoutMixin(object): # {{{
|
|||||||
self.bd_splitter.addWidget(self.book_details)
|
self.bd_splitter.addWidget(self.book_details)
|
||||||
self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False)
|
self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False)
|
||||||
self.centralwidget.layout().addWidget(self.bd_splitter)
|
self.centralwidget.layout().addWidget(self.bd_splitter)
|
||||||
|
button_order = ('tb', 'bd', 'cb')
|
||||||
# }}}
|
# }}}
|
||||||
else: # wide {{{
|
else: # wide {{{
|
||||||
self.bd_splitter = Splitter('book_details_splitter',
|
self.bd_splitter = Splitter('book_details_splitter',
|
||||||
@ -237,10 +238,11 @@ class LayoutMixin(object): # {{{
|
|||||||
self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,
|
self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,
|
||||||
QSizePolicy.Expanding))
|
QSizePolicy.Expanding))
|
||||||
self.centralwidget.layout().addWidget(self.bd_splitter)
|
self.centralwidget.layout().addWidget(self.bd_splitter)
|
||||||
|
button_order = ('tb', 'cb', 'bd')
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
self.status_bar = StatusBar(self)
|
self.status_bar = StatusBar(self)
|
||||||
for x in ('cb', 'tb', 'bd'):
|
for x in button_order:
|
||||||
button = getattr(self, x+'_splitter').button
|
button = getattr(self, x+'_splitter').button
|
||||||
button.setIconSize(QSize(24, 24))
|
button.setIconSize(QSize(24, 24))
|
||||||
if isosx:
|
if isosx:
|
||||||
|
@ -170,6 +170,10 @@ class GuiRunner(QObject):
|
|||||||
sys.excepthook = main.unhandled_exception
|
sys.excepthook = main.unhandled_exception
|
||||||
if len(self.args) > 1:
|
if len(self.args) > 1:
|
||||||
p = os.path.abspath(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)
|
add_filesystem_book(p)
|
||||||
self.app.file_event_hook = add_filesystem_book
|
self.app.file_event_hook = add_filesystem_book
|
||||||
self.main = main
|
self.main = main
|
||||||
|
@ -7,7 +7,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import textwrap, re, os, errno
|
import textwrap, re, os, errno, shutil
|
||||||
|
|
||||||
from PyQt4.Qt import (Qt, QDateEdit, QDate, pyqtSignal, QMessageBox,
|
from PyQt4.Qt import (Qt, QDateEdit, QDate, pyqtSignal, QMessageBox,
|
||||||
QIcon, QToolButton, QWidget, QLabel, QGridLayout, QApplication,
|
QIcon, QToolButton, QWidget, QLabel, QGridLayout, QApplication,
|
||||||
@ -33,8 +33,9 @@ from calibre.gui2.comments_editor import Editor
|
|||||||
from calibre.library.comments import comments_to_html
|
from calibre.library.comments import comments_to_html
|
||||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||||
from calibre.utils.icu import strcmp
|
from calibre.utils.icu import strcmp
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile, SpooledTemporaryFile
|
||||||
from calibre.gui2.languages import LanguagesEdit as LE
|
from calibre.gui2.languages import LanguagesEdit as LE
|
||||||
|
from calibre.db import SPOOL_SIZE
|
||||||
|
|
||||||
def save_dialog(parent, title, msg, det_msg=''):
|
def save_dialog(parent, title, msg, det_msg=''):
|
||||||
d = QMessageBox(parent)
|
d = QMessageBox(parent)
|
||||||
@ -43,8 +44,6 @@ def save_dialog(parent, title, msg, det_msg=''):
|
|||||||
d.setStandardButtons(QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
|
d.setStandardButtons(QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
|
||||||
return d.exec_()
|
return d.exec_()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
The interface common to all widgets used to set basic metadata
|
The interface common to all widgets used to set basic metadata
|
||||||
class BasicMetadataWidget(object):
|
class BasicMetadataWidget(object):
|
||||||
@ -731,7 +730,11 @@ class FormatsManager(QWidget):
|
|||||||
else:
|
else:
|
||||||
old_extensions.add(ext)
|
old_extensions.add(ext)
|
||||||
for ext in new_extensions:
|
for ext in new_extensions:
|
||||||
db.add_format(id_, ext, open(paths[ext], 'rb'), notify=False,
|
with SpooledTemporaryFile(SPOOL_SIZE) as spool:
|
||||||
|
with open(paths[ext], 'rb') as f:
|
||||||
|
shutil.copyfileobj(f, spool)
|
||||||
|
spool.seek(0)
|
||||||
|
db.add_format(id_, ext, spool, notify=False,
|
||||||
index_is_id=True)
|
index_is_id=True)
|
||||||
dbfmts = db.formats(id_, index_is_id=True)
|
dbfmts = db.formats(id_, index_is_id=True)
|
||||||
db_extensions = set([f.lower() for f in (dbfmts.split(',') if dbfmts
|
db_extensions = set([f.lower() for f in (dbfmts.split(',') if dbfmts
|
||||||
|
@ -234,7 +234,7 @@
|
|||||||
<widget class="QLabel" name="label_13">
|
<widget class="QLabel" name="label_13">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><p>Remember to leave calibre running as the server only runs as long as calibre is running.
|
<string><p>Remember to leave calibre running as the server only runs as long as calibre is running.
|
||||||
<p>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>
|
<p>To connect to the calibre server from your device you should use a URL of the form <b>http://myhostname:8080</b> 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>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" colspan="2">
|
<item row="2" column="0" colspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox">
|
<widget class="QGroupBox" name="groupBox">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>A&vailable actions</string>
|
<string>A&vailable actions</string>
|
||||||
@ -62,7 +62,7 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="2">
|
<item row="2" column="2">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QToolButton" name="add_action_button">
|
<widget class="QToolButton" name="add_action_button">
|
||||||
@ -122,7 +122,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="3" colspan="2">
|
<item row="2" column="3" colspan="2">
|
||||||
<widget class="QGroupBox" name="groupBox_2">
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>&Current actions</string>
|
<string>&Current actions</string>
|
||||||
@ -210,6 +210,16 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="0" colspan="5">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string><p>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, <b>click the dropdown above</b> and select which toolbar/menu you want to customize.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
|
@ -208,6 +208,12 @@ class PocketBook(CybookG3):
|
|||||||
id = 'pocketbook'
|
id = 'pocketbook'
|
||||||
output_profile = 'cybookg3'
|
output_profile = 'cybookg3'
|
||||||
|
|
||||||
|
class PocketBook900(PocketBook):
|
||||||
|
|
||||||
|
name = 'PocketBook 900'
|
||||||
|
id = 'pocketbook900'
|
||||||
|
output_profile = 'pocketbook_900'
|
||||||
|
|
||||||
class iPhone(Device):
|
class iPhone(Device):
|
||||||
|
|
||||||
name = 'iPad or iPhone/iTouch + Stanza'
|
name = 'iPad or iPhone/iTouch + Stanza'
|
||||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
The database used to store ebook metadata
|
The database used to store ebook metadata
|
||||||
'''
|
'''
|
||||||
import os, sys, shutil, cStringIO, glob, time, functools, traceback, re, \
|
import os, sys, shutil, cStringIO, glob, time, functools, traceback, re, \
|
||||||
json, uuid, tempfile, hashlib, copy
|
json, uuid, hashlib, copy
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import threading, random
|
import threading, random
|
||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
@ -26,7 +26,8 @@ from calibre.library.sqlite import connect, IntegrityError
|
|||||||
from calibre.library.prefs import DBPrefs
|
from calibre.library.prefs import DBPrefs
|
||||||
from calibre.ebooks.metadata.book.base import Metadata
|
from calibre.ebooks.metadata.book.base import Metadata
|
||||||
from calibre.constants import preferred_encoding, iswindows, filesystem_encoding
|
from calibre.constants import preferred_encoding, iswindows, filesystem_encoding
|
||||||
from calibre.ptempfile import PersistentTemporaryFile, base_dir
|
from calibre.ptempfile import (PersistentTemporaryFile,
|
||||||
|
base_dir, SpooledTemporaryFile)
|
||||||
from calibre.customize.ui import run_plugins_on_import
|
from calibre.customize.ui import run_plugins_on_import
|
||||||
from calibre import isbytestring
|
from calibre import isbytestring
|
||||||
from calibre.utils.filenames import ascii_filename
|
from calibre.utils.filenames import ascii_filename
|
||||||
@ -610,7 +611,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
with lopen(os.path.join(tpath, 'cover.jpg'), 'wb') as f:
|
with lopen(os.path.join(tpath, 'cover.jpg'), 'wb') as f:
|
||||||
f.write(cdata)
|
f.write(cdata)
|
||||||
for format in formats:
|
for format in formats:
|
||||||
with tempfile.SpooledTemporaryFile(max_size=SPOOL_SIZE) as stream:
|
with SpooledTemporaryFile(SPOOL_SIZE) as stream:
|
||||||
try:
|
try:
|
||||||
self.copy_format_to(id, format, stream, index_is_id=True)
|
self.copy_format_to(id, format, stream, index_is_id=True)
|
||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
@ -694,7 +695,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
shutil.copyfileobj(f, pt)
|
shutil.copyfileobj(f, pt)
|
||||||
return pt.name
|
return pt.name
|
||||||
if as_file:
|
if as_file:
|
||||||
ret = tempfile.SpooledTemporaryFile(SPOOL_SIZE)
|
ret = SpooledTemporaryFile(SPOOL_SIZE)
|
||||||
shutil.copyfileobj(f, ret)
|
shutil.copyfileobj(f, ret)
|
||||||
ret.seek(0)
|
ret.seek(0)
|
||||||
else:
|
else:
|
||||||
@ -1282,7 +1283,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
shutil.copyfileobj(f, pt)
|
shutil.copyfileobj(f, pt)
|
||||||
ret = pt.name
|
ret = pt.name
|
||||||
elif as_file:
|
elif as_file:
|
||||||
ret = tempfile.SpooledTemporaryFile(max_size=SPOOL_SIZE)
|
ret = SpooledTemporaryFile(SPOOL_SIZE)
|
||||||
shutil.copyfileobj(f, ret)
|
shutil.copyfileobj(f, ret)
|
||||||
ret.seek(0)
|
ret.seek(0)
|
||||||
# Various bits of code try to use the name as the default
|
# Various bits of code try to use the name as the default
|
||||||
|
@ -150,12 +150,21 @@ class Formatter(TemplateFormatter):
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
b = None
|
b = None
|
||||||
if b is not None and b['datatype'] == 'composite':
|
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:
|
if key in self.composite_values:
|
||||||
|
self.composite_values[key] = val
|
||||||
return self.composite_values[key]
|
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] = 'RECURSIVE_COMPOSITE FIELD (S2D) ' + key
|
||||||
self.composite_values[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]
|
return self.composite_values[key]
|
||||||
|
except Exception, e:
|
||||||
|
return unicode(e)
|
||||||
if key in kwargs:
|
if key in kwargs:
|
||||||
val = kwargs[key]
|
val = kwargs[key]
|
||||||
if isinstance(val, list):
|
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,
|
sanitize_func=ascii_filename, replace_whitespace=False,
|
||||||
to_lowercase=False, safe_format=True):
|
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']
|
tsorder = tweaks['save_template_title_series_sorting']
|
||||||
format_args = FORMAT_ARGS.copy()
|
format_args = FORMAT_ARGS.copy()
|
||||||
format_args.update(mi.all_non_none_fields())
|
format_args.update(mi.all_non_none_fields())
|
||||||
|
@ -94,3 +94,10 @@ def unquote(s):
|
|||||||
ans = ans.decode('utf-8')
|
ans = ans.decode('utf-8')
|
||||||
return ans
|
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)
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ The first, most important step is to run |app| in debug mode. You can do this fr
|
|||||||
|
|
||||||
calibre-debug -g
|
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.
|
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.
|
||||||
|
|
||||||
|
@ -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.
|
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|?
|
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
|
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 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`
|
* 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.
|
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?
|
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.
|
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!
|
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:
|
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
|
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.
|
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?
|
How do I get |app| to use my HTTP proxy?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -30,7 +30,8 @@ Actions
|
|||||||
:alt: The Actions Toolbar
|
:alt: The Actions Toolbar
|
||||||
:align: center
|
: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::
|
.. contents::
|
||||||
:depth: 1
|
:depth: 1
|
||||||
@ -43,7 +44,7 @@ Add books
|
|||||||
.. |adbi| image:: images/add_books.png
|
.. |adbi| image:: images/add_books.png
|
||||||
:class: float-right-img
|
: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.
|
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
|
.. |emii| image:: images/edit_meta_information.png
|
||||||
:class: float-right-img
|
: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.
|
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>`.
|
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`.
|
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.
|
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.
|
|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
|
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
|
Preferences->Behavior. If a book has more than one format, you can view a particular format by doing a right-click on the button.
|
||||||
on the right of the :guilabel:`View` button.
|
|
||||||
|
|
||||||
|
|
||||||
.. _send_to_device:
|
.. _send_to_device:
|
||||||
@ -127,7 +127,7 @@ Send to device
|
|||||||
.. |stdi| image:: images/send_to_device.png
|
.. |stdi| image:: images/send_to_device.png
|
||||||
:class: float-right-img
|
: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.
|
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.
|
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 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.
|
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`.
|
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
|
.. |svdi| image:: images/save_to_disk.png
|
||||||
:class: float-right-img
|
: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:
|
.. _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.
|
|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.
|
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
|
.. |rbi| image:: images/remove_books.png
|
||||||
:class: float-right-img
|
: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.
|
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
|
.. |cbi| image:: images/preferences.png
|
||||||
:class: float-right-img
|
: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.
|
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|.
|
2. **Run welcome wizard**: Allows you to start the Welcome Wizard which appeared the first time you started |app|.
|
||||||
|
@ -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
|
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
|
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
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
@ -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>`_
|
`BasicNewsRecipe <http://bazaar.launchpad.net/~kovid/calibre/trunk/annotate/head:/src/calibre/web/feeds/news.py>`_
|
||||||
The source code of ``BasicNewsRecipe``
|
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 source code for the built-in recipes that come with |app|
|
||||||
|
|
||||||
`The calibre recipes forum <http://www.mobileread.com/forums/forumdisplay.php?f=228>`_
|
`The calibre recipes forum <http://www.mobileread.com/forums/forumdisplay.php?f=228>`_
|
||||||
|
@ -181,4 +181,17 @@ class TemporaryFile(object):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SpooledTemporaryFile(tempfile.SpooledTemporaryFile):
|
||||||
|
|
||||||
|
def __init__(self, max_size=0, suffix="", prefix="", dir=None, mode='w+b',
|
||||||
|
bufsize=-1):
|
||||||
|
if prefix == None:
|
||||||
|
prefix = ''
|
||||||
|
if suffix is None:
|
||||||
|
suffix = ''
|
||||||
|
if dir is None:
|
||||||
|
dir = base_dir()
|
||||||
|
tempfile.SpooledTemporaryFile.__init__(self, max_size=max_size, suffix=suffix,
|
||||||
|
prefix=prefix, dir=dir, mode=mode, bufsize=bufsize)
|
||||||
|
|
||||||
|
|
||||||
|
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