mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
KG revisions
This commit is contained in:
commit
4266e94984
@ -1,38 +1,47 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
bbc.co.uk
|
||||
news.bbc.co.uk
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import re
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class BBC(BasicNewsRecipe):
|
||||
title = u'The BBC'
|
||||
__author__ = 'Kovid Goyal ans Sujata Raman'
|
||||
description = 'Global news and current affairs from the British Broadcasting Corporation'
|
||||
language = 'en'
|
||||
title = 'The BBC'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Global news and current affairs from the British Broadcasting Corporation'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
#delay = 1
|
||||
use_embedded_content = False
|
||||
encoding = 'utf8'
|
||||
publisher = 'BBC'
|
||||
category = 'news, UK, world'
|
||||
language = 'en_GB'
|
||||
publication_type = 'newsportal'
|
||||
extra_css = ' body{ font-family: Verdana,Helvetica,Arial,sans-serif } .introduction{font-weight: bold} .story-feature{display: block; padding: 0; border: 1px solid; width: 40%; font-size: small} .story-feature h2{text-align: center; text-transform: uppercase} '
|
||||
preprocess_regexps = [(re.compile(r'<!--.*?-->', re.DOTALL), lambda m: '')]
|
||||
|
||||
no_stylesheets = True
|
||||
remove_tags = [dict(name='div', attrs={'class':'footer'}),
|
||||
{'id' : ['popstory','blq-footer']},
|
||||
{'class' : ['arrup','links','relatedbbcsites','arr','promobottombg','bbccom_visibility_hidden', 'sharesb', 'sib606', 'mvtb', 'storyextra', 'sidebar1', 'bbccom_text','promotopbg', 'gppromo','promotopbg','bbccom_display_none']},
|
||||
]
|
||||
conversion_options = {
|
||||
'comments' : description
|
||||
,'tags' : category
|
||||
,'language' : language
|
||||
,'publisher' : publisher
|
||||
,'linearize_tables': True
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'mainwrapper'})]
|
||||
|
||||
extra_css = '''
|
||||
body{font-family:Arial,Helvetica,sans-serif; font-size:small; align:left}
|
||||
h1{font-size:large;}
|
||||
.sh{font-size:large; font-weight:bold}
|
||||
.cap{font-size:xx-small; }
|
||||
.lu{font-size:xx-small; }
|
||||
.ds{font-size:xx-small; }
|
||||
.mvb{font-size:xx-small;}
|
||||
.by1{font-size:x-small; color:#666666}
|
||||
.byd{font-size:x-small;}
|
||||
'''
|
||||
keep_only_tags = [
|
||||
dict(attrs={'id' :['meta-information','story-body']})
|
||||
,dict(attrs={'class':['mxb' ,'storybody' ]})
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name=['object','link','table'])
|
||||
,dict(attrs={'class':['caption','caption full-width','story-actions','hidden','sharesb','audioInStoryC']})
|
||||
]
|
||||
remove_tags_after = dict(attrs={'class':'sharesb'})
|
||||
remove_attributes = ['width','height']
|
||||
|
||||
feeds = [
|
||||
('News Front Page', 'http://newsrss.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml'),
|
||||
@ -50,22 +59,3 @@ class BBC(BasicNewsRecipe):
|
||||
('Africa', 'http://newsrss.bbc.co.uk/rss/newsonline_world_edition/africa/rss.xml'),
|
||||
]
|
||||
|
||||
def postprocess_html(self, soup, first):
|
||||
|
||||
for tag in soup.findAll(name= 'img', alt=""):
|
||||
tag.extract()
|
||||
|
||||
for item in soup.findAll(align = "right"):
|
||||
del item['align']
|
||||
|
||||
for tag in soup.findAll(name=['table', 'tr', 'td']):
|
||||
tag.name = 'div'
|
||||
|
||||
return soup
|
||||
|
||||
|
||||
|
||||
# def print_version(self, url):
|
||||
# return url.replace('http://', 'http://newsvote.bbc.co.uk/mpapps/pagetools/print/')
|
||||
|
||||
|
||||
|
@ -3,7 +3,7 @@ __copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
news.bbc.co.uk
|
||||
'''
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class BBC(BasicNewsRecipe):
|
||||
@ -18,22 +18,28 @@ class BBC(BasicNewsRecipe):
|
||||
encoding = 'utf8'
|
||||
publisher = 'BBC'
|
||||
category = 'news, UK, world'
|
||||
language = 'en'
|
||||
extra_css = ' body{ font-family: sans-serif; } .headline{font-size: xx-large; font-weight: bold} .ibox{display: block; margin: 20px 50px; padding: 10px; border: 1px solid } '
|
||||
|
||||
language = 'en_GB'
|
||||
publication_type = 'newsportal'
|
||||
extra_css = ' body{ font-family: Verdana,Helvetica,Arial,sans-serif } .introduction{font-weight: bold} .story-feature{display: block; padding: 0; border: 1px solid; width: 40%; font-size: small} .story-feature h2{text-align: center; text-transform: uppercase} '
|
||||
preprocess_regexps = [(re.compile(r'<!--.*?-->', re.DOTALL), lambda m: '')]
|
||||
conversion_options = {
|
||||
'comments' : description
|
||||
,'tags' : category
|
||||
,'language' : language
|
||||
,'publisher' : publisher
|
||||
,'linearize_tables': True
|
||||
}
|
||||
|
||||
remove_tags_before = dict(name='div',attrs={'class':'headline'})
|
||||
remove_tags_after = dict(name='div', attrs={'class':'footer'})
|
||||
remove_tags = [
|
||||
dict(name=['object','link','script','iframe'])
|
||||
,dict(name='div', attrs={'class':'footer'})
|
||||
keep_only_tags = [
|
||||
dict(attrs={'id' :['meta-information','story-body']})
|
||||
,dict(attrs={'class':['mxb' ,'storybody' ]})
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name=['object','link','table','img'])
|
||||
,dict(attrs={'class':['caption','caption full-width','story-actions','hidden','sharesb','audioInStoryC']})
|
||||
]
|
||||
remove_tags_after = dict(attrs={'class':'sharesb'})
|
||||
remove_attributes = ['width','height']
|
||||
|
||||
feeds = [
|
||||
('News Front Page', 'http://newsrss.bbc.co.uk/rss/newsonline_world_edition/front_page/rss.xml'),
|
||||
@ -51,10 +57,3 @@ class BBC(BasicNewsRecipe):
|
||||
('Africa', 'http://newsrss.bbc.co.uk/rss/newsonline_world_edition/africa/rss.xml'),
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
emp,sep,rstrip = url.partition('http://')
|
||||
return 'http://newsvote.bbc.co.uk/mpapps/pagetools/print/' + rstrip
|
||||
|
||||
def get_article_url(self, article):
|
||||
return article.get('guid', None)
|
||||
|
||||
|
24
resources/recipes/las_vegas_review.recipe
Normal file
24
resources/recipes/las_vegas_review.recipe
Normal file
@ -0,0 +1,24 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1274742400(BasicNewsRecipe):
|
||||
|
||||
title = u'Las Vegas Review Journal'
|
||||
__author__ = 'Joel'
|
||||
language = 'en'
|
||||
|
||||
oldest_article = 7
|
||||
|
||||
max_articles_per_feed = 100
|
||||
|
||||
feeds = [
|
||||
(u'News', u'http://www.lvrj.com/news.rss'),
|
||||
(u'Business', u'http://www.lvrj.com/business.rss'),
|
||||
(u'Living', u'http://www.lvrj.com/living.rss'),
|
||||
(u'Opinion', u'http://www.lvrj.com/opinion.rss'),
|
||||
(u'Neon', u'http://www.lvrj.com/neon.rss'),
|
||||
(u'Image', u'http://www.lvrj.com/image.rss'),
|
||||
(u'Home & Garden', u'http://www.lvrj.com/home_and_garden.rss'),
|
||||
(u'Furniture & Design', u'http://www.lvrj.com/furniture_and_design.rss'),
|
||||
(u'Drive', u'http://www.lvrj.com/drive.rss'),
|
||||
(u'Real Estate', u'http://www.lvrj.com/real_estate.rss'),
|
||||
(u'Sports', u'http://www.lvrj.com/sports.rss')]
|
@ -10,13 +10,13 @@ import time
|
||||
from calibre import entity_to_unicode
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag, NavigableString, \
|
||||
Comment, BeautifulStoneSoup
|
||||
Comment, BeautifulStoneSoup
|
||||
|
||||
class NYTimes(BasicNewsRecipe):
|
||||
|
||||
title = 'New York Times Top Stories'
|
||||
__author__ = 'GRiker'
|
||||
language = 'en'
|
||||
language = _('English')
|
||||
description = 'Top Stories from the New York Times'
|
||||
|
||||
# List of sections typically included in Top Stories. Use a keyword from the
|
||||
@ -388,6 +388,10 @@ class NYTimes(BasicNewsRecipe):
|
||||
return ans
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
# Skip ad pages before actual article
|
||||
skip_tag = soup.find(True, {'name':'skip'})
|
||||
if skip_tag is not None:
|
||||
soup = self.index_to_soup(skip_tag.parent['href'])
|
||||
return self.strip_anchors(soup)
|
||||
|
||||
def postprocess_html(self,soup, True):
|
||||
|
@ -82,6 +82,7 @@ class NYTimes(BasicNewsRecipe):
|
||||
'articleExtras',
|
||||
'articleInline',
|
||||
'blog_sidebar',
|
||||
'businessSearchBar',
|
||||
'cCol',
|
||||
'entertainmentSearchBar',
|
||||
'footer',
|
||||
@ -286,9 +287,14 @@ class NYTimes(BasicNewsRecipe):
|
||||
raw = self.browser.open('http://www.nytimes.com'+content).read()
|
||||
return BeautifulSoup(raw.decode('cp1252', 'replace'))
|
||||
'''
|
||||
# Skip ad pages before actual article
|
||||
skip_tag = soup.find(True, {'name':'skip'})
|
||||
if skip_tag is not None:
|
||||
soup = self.index_to_soup(skip_tag.parent['href'])
|
||||
return self.strip_anchors(soup)
|
||||
|
||||
def postprocess_html(self,soup, True):
|
||||
print "\npostprocess_html()\n"
|
||||
|
||||
if self.one_picture_per_article:
|
||||
# Remove all images after first
|
||||
@ -411,6 +417,7 @@ class NYTimes(BasicNewsRecipe):
|
||||
return soup
|
||||
|
||||
def postprocess_book(self, oeb, opts, log) :
|
||||
print "\npostprocess_book()\n"
|
||||
|
||||
def extract_byline(href) :
|
||||
# <meta name="byline" content=
|
||||
|
31
resources/recipes/trv.recipe
Normal file
31
resources/recipes/trv.recipe
Normal file
@ -0,0 +1,31 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Vadim Dyadkin dyadkin@lns.pnpi.spb.ru'
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Trv(BasicNewsRecipe):
|
||||
|
||||
|
||||
title = u'\u0422\u0440\u043e\u0438\u0446\u043a\u0438\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442'
|
||||
language = 'ru'
|
||||
__author__ = 'Vadim Dyadkin'
|
||||
oldest_article = 30
|
||||
max_articles_per_feed = 100
|
||||
recursion = 4
|
||||
no_stylesheets = True
|
||||
simultaneous_downloads = 1
|
||||
|
||||
keep_only_tags = [dict(name='h1'),
|
||||
dict(name='div', attrs={'id' : 'content'})
|
||||
]
|
||||
|
||||
remove_tags = [dict(name='div', attrs={'class' : ['dateright',
|
||||
'postmeta', 'adsense-post', 'comments', 'nocomments', 'widgetarea',
|
||||
'breadcrumb']}), {'id' : ['sidebar', 'l_sidebar', 'r_sidebar', 'footer',
|
||||
'homepageright0']}, {'style' : 'clear:both;'},
|
||||
dict(name='ul'),
|
||||
dict(name='h2')
|
||||
]
|
||||
|
||||
feeds = [(u'\u0422\u0440\u043e\u0438\u0446\u043a\u0438\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442',
|
||||
u'http://trv-science.ru/feed/')]
|
@ -16,13 +16,15 @@ class Wired(BasicNewsRecipe):
|
||||
publisher = 'Conde Nast Digital'
|
||||
category = 'news, games, IT, gadgets'
|
||||
oldest_article = 32
|
||||
delay = 1
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
encoding = 'utf-8'
|
||||
use_embedded_content = False
|
||||
masthead_url = 'http://www.wired.com/images/home/wired_logo.gif'
|
||||
language = 'en'
|
||||
extra_css = ' body{font-family: sans-serif} .entryDescription li {display: inline; list-style-type: none} '
|
||||
publication_type = 'magazine'
|
||||
extra_css = ' body{font-family: Arial,Verdana,sans-serif} .entryDescription li {display: inline; list-style-type: none} '
|
||||
index = 'http://www.wired.com/magazine/'
|
||||
|
||||
preprocess_regexps = [(re.compile(r'<meta name="Title".*<title>', re.DOTALL|re.IGNORECASE),lambda match: '<title>')]
|
||||
@ -38,6 +40,8 @@ class Wired(BasicNewsRecipe):
|
||||
remove_tags = [
|
||||
dict(name=['object','embed','iframe','link'])
|
||||
,dict(name='div', attrs={'class':['podcast_storyboard','tweetmeme_button']})
|
||||
,dict(attrs={'id':'ff_bottom_nav'})
|
||||
,dict(name='a',attrs={'href':'http://www.wired.com/app'})
|
||||
]
|
||||
remove_attributes = ['height','width']
|
||||
|
||||
@ -72,17 +76,18 @@ class Wired(BasicNewsRecipe):
|
||||
farticles = []
|
||||
for item in features.findAll('div',attrs={'class':'section'}):
|
||||
divurl = item.find('div',attrs={'class':'feature-header'})
|
||||
divdesc = item.find('div',attrs={'class':'feature-text'})
|
||||
url = 'http://www.wired.com' + divurl.a['href']
|
||||
title = self.tag_to_string(divurl.a)
|
||||
description = self.tag_to_string(divdesc)
|
||||
date = strftime(self.timefmt)
|
||||
farticles.append({
|
||||
'title' :title
|
||||
,'date' :date
|
||||
,'url' :url
|
||||
,'description':description
|
||||
})
|
||||
if divurl:
|
||||
divdesc = item.find('div',attrs={'class':'feature-text'})
|
||||
url = 'http://www.wired.com' + divurl.a['href']
|
||||
title = self.tag_to_string(divurl.a)
|
||||
description = self.tag_to_string(divdesc)
|
||||
date = strftime(self.timefmt)
|
||||
farticles.append({
|
||||
'title' :title
|
||||
,'date' :date
|
||||
,'url' :url
|
||||
,'description':description
|
||||
})
|
||||
totalfeeds.append(('Featured Articles', farticles))
|
||||
#department feeds
|
||||
departments = ['rants','start','test','play','found']
|
||||
|
@ -444,7 +444,7 @@ from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX
|
||||
from calibre.devices.nook.driver import NOOK
|
||||
from calibre.devices.prs505.driver import PRS505
|
||||
from calibre.devices.android.driver import ANDROID, S60
|
||||
from calibre.devices.nokia.driver import N770, N810
|
||||
from calibre.devices.nokia.driver import N770, N810, E71X
|
||||
from calibre.devices.eslick.driver import ESLICK
|
||||
from calibre.devices.nuut2.driver import NUUT2
|
||||
from calibre.devices.iriver.driver import IRIVER_STORY
|
||||
@ -453,8 +453,9 @@ from calibre.devices.hanvon.driver import N516, EB511, ALEX, AZBOOKA, THEBOOK
|
||||
from calibre.devices.edge.driver import EDGE
|
||||
from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS
|
||||
from calibre.devices.sne.driver import SNE
|
||||
from calibre.devices.misc import PALMPRE, KOBO, AVANT
|
||||
from calibre.devices.misc import PALMPRE, AVANT
|
||||
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
|
||||
from calibre.devices.kobo.driver import KOBO
|
||||
|
||||
from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon
|
||||
from calibre.library.catalog import CSV_XML, EPUB_MOBI
|
||||
@ -514,6 +515,7 @@ plugins += [
|
||||
ANDROID,
|
||||
S60,
|
||||
N770,
|
||||
E71X,
|
||||
N810,
|
||||
COOL_ER,
|
||||
ESLICK,
|
||||
|
@ -1,11 +1,11 @@
|
||||
'''
|
||||
Device driver for iTunes
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
GRiker
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Gregory Riker'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
22 May 2010
|
||||
'''
|
||||
import atexit, cStringIO, datetime, os, re, shutil, sys, time, zipfile
|
||||
|
||||
import cStringIO, os, re, shutil, sys, time, zipfile
|
||||
|
||||
from calibre.constants import DEBUG
|
||||
from calibre import fit_image
|
||||
@ -13,43 +13,32 @@ from calibre.constants import isosx, iswindows
|
||||
from calibre.devices.interface import DevicePlugin
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.library.server.utils import strftime
|
||||
from calibre.ptempfile import PersistentTemporaryFile, cleanup
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.utils.config import Config, config_dir
|
||||
from calibre.utils.date import parse_date
|
||||
from calibre.utils.logging import Log
|
||||
from calibre.devices.errors import UserFeedback
|
||||
|
||||
from PIL import Image as PILImage, TarIO
|
||||
from PIL import Image as PILImage
|
||||
|
||||
if isosx:
|
||||
import appscript, osax
|
||||
import appscript
|
||||
|
||||
if iswindows:
|
||||
import win32com.client
|
||||
|
||||
class UserInteractionRequired(Exception):
|
||||
pass
|
||||
|
||||
class UserFeedback(Exception):
|
||||
INFO = 0
|
||||
WARN = 1
|
||||
ERROR = 2
|
||||
|
||||
def __init__(self, msg, details, level):
|
||||
Exception.__init__(self, msg)
|
||||
self.level = level
|
||||
self.details = details
|
||||
self.msg = msg
|
||||
#if iswindows:
|
||||
# import win32com.client
|
||||
|
||||
class ITUNES(DevicePlugin):
|
||||
|
||||
name = 'Apple device interface'
|
||||
gui_name = 'Apple device'
|
||||
icon = I('devices/ipad.png')
|
||||
description = _('Communicate with iBooks through iTunes.')
|
||||
supported_platforms = ['windows','osx']
|
||||
supported_platforms = ['osx']
|
||||
author = 'GRiker'
|
||||
driver_version = '0.1'
|
||||
|
||||
OPEN_FEEDBACK_MESSAGE = _('Apple device detected, launching iTunes')
|
||||
OPEN_FEEDBACK_MESSAGE = _(
|
||||
'Apple device detected, launching iTunes, please wait...')
|
||||
|
||||
FORMATS = ['epub']
|
||||
|
||||
@ -356,7 +345,7 @@ class ITUNES(DevicePlugin):
|
||||
# Init the iTunes source list
|
||||
names = [s.name() for s in self.iTunes.sources()]
|
||||
kinds = [str(s.kind()).rpartition('.')[2] for s in self.iTunes.sources()]
|
||||
self.sources = sources = dict(zip(kinds,names))
|
||||
self.sources = dict(zip(kinds,names))
|
||||
|
||||
# Check to see if Library|Books out of sync with Device|Books
|
||||
if 'iPod' in self.sources and self.presync:
|
||||
@ -711,7 +700,6 @@ class ITUNES(DevicePlugin):
|
||||
'''
|
||||
if 'iPod' in self.sources:
|
||||
device = self.sources['iPod']
|
||||
device_books = []
|
||||
if 'Books' in self.iTunes.sources[device].playlists.name():
|
||||
return self.iTunes.sources[device].playlists['Books'].file_tracks()
|
||||
|
||||
|
@ -22,9 +22,20 @@ class DeviceError(ProtocolError):
|
||||
""" Raised when device is not found """
|
||||
def __init__(self, msg=None):
|
||||
if msg is None:
|
||||
msg = "Unable to find SONY Reader. Is it connected?"
|
||||
msg = "Unable to find SONY Reader. Is it connected?"
|
||||
ProtocolError.__init__(self, msg)
|
||||
|
||||
class UserFeedback(DeviceError):
|
||||
INFO = 0
|
||||
WARN = 1
|
||||
ERROR = 2
|
||||
|
||||
def __init__(self, msg, details, level):
|
||||
Exception.__init__(self, msg)
|
||||
self.level = level
|
||||
self.details = details
|
||||
self.msg = msg
|
||||
|
||||
class DeviceBusy(ProtocolError):
|
||||
""" Raised when device is busy """
|
||||
def __init__(self, uerr=""):
|
||||
@ -57,8 +68,8 @@ class ControlError(ProtocolError):
|
||||
self.query = query
|
||||
self.response = response
|
||||
ProtocolError.__init__(self, desc)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
def __str__(self):
|
||||
if self.query and self.response:
|
||||
return "Got unexpected response:\n" + \
|
||||
"query:\n"+str(self.query.query)+"\n"+\
|
||||
|
@ -47,6 +47,10 @@ class DevicePlugin(Plugin):
|
||||
# Used by gui2.ui:annotations_fetched() and devices.kindle.driver:get_annotations()
|
||||
UserAnnotation = namedtuple('Annotation','type, value')
|
||||
|
||||
#: GUI displays this as a message if not None. Useful if opening can take a
|
||||
#: long time
|
||||
OPEN_FEEDBACK_MESSAGE = None
|
||||
|
||||
@classmethod
|
||||
def get_gui_name(cls):
|
||||
if hasattr(cls, 'gui_name'):
|
||||
|
9
src/calibre/devices/kobo/__init__.py
Normal file
9
src/calibre/devices/kobo/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
|
31
src/calibre/devices/kobo/driver.py
Normal file
31
src/calibre/devices/kobo/driver.py
Normal file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from calibre.devices.usbms.driver import USBMS
|
||||
|
||||
class KOBO(USBMS):
|
||||
|
||||
name = 'Kobo Reader Device Interface'
|
||||
gui_name = 'Kobo Reader'
|
||||
description = _('Communicate with the Kobo Reader')
|
||||
author = 'Kovid Goyal'
|
||||
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
|
||||
# Ordered list of supported formats
|
||||
FORMATS = ['epub', 'pdf']
|
||||
|
||||
VENDOR_ID = [0x2237]
|
||||
PRODUCT_ID = [0x4161]
|
||||
BCD = [0x0110]
|
||||
|
||||
VENDOR_NAME = 'KOBO_INC'
|
||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '.KOBOEREADER'
|
||||
|
||||
EBOOK_DIR_MAIN = ''
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
@ -28,27 +28,6 @@ class PALMPRE(USBMS):
|
||||
|
||||
EBOOK_DIR_MAIN = 'E-books'
|
||||
|
||||
class KOBO(USBMS):
|
||||
|
||||
name = 'Kobo Reader Device Interface'
|
||||
gui_name = 'Kobo Reader'
|
||||
description = _('Communicate with the Kobo Reader')
|
||||
author = 'Kovid Goyal'
|
||||
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
|
||||
# Ordered list of supported formats
|
||||
FORMATS = ['epub', 'pdf']
|
||||
|
||||
VENDOR_ID = [0x2237]
|
||||
PRODUCT_ID = [0x4161]
|
||||
BCD = [0x0110]
|
||||
|
||||
VENDOR_NAME = 'KOBO_INC'
|
||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '.KOBOEREADER'
|
||||
|
||||
EBOOK_DIR_MAIN = ''
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
class AVANT(USBMS):
|
||||
name = 'Booq Avant Device Interface'
|
||||
|
@ -45,3 +45,25 @@ class N810(N770):
|
||||
WINDOWS_MAIN_MEM = 'N810'
|
||||
|
||||
MAIN_MEMORY_VOLUME_LABEL = 'N810 Main Memory'
|
||||
|
||||
class E71X(USBMS):
|
||||
|
||||
name = 'Nokia E71X device interface'
|
||||
gui_name = 'Nokia E71X'
|
||||
description = 'Communicate with the Nokia E71X'
|
||||
author = 'Kovid Goyal'
|
||||
supported_platforms = ['windows', 'linux', 'osx']
|
||||
|
||||
VENDOR_ID = [0x421]
|
||||
PRODUCT_ID = [0x1a0]
|
||||
BCD = [0x100]
|
||||
|
||||
|
||||
FORMATS = ['mobi', 'prc']
|
||||
|
||||
EBOOK_DIR_MAIN = 'eBooks'
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
VENDOR_NAME = 'NOKIA'
|
||||
WINDOWS_MAIN_MEM = 'S60'
|
||||
|
||||
|
@ -117,7 +117,7 @@ class EPUBInput(InputFormatPlugin):
|
||||
encfile = os.path.abspath(os.path.join('META-INF', 'encryption.xml'))
|
||||
opf = None
|
||||
for f in walk(u'.'):
|
||||
if f.lower().endswith('.opf'):
|
||||
if f.lower().endswith('.opf') and '__MACOSX' not in f:
|
||||
opf = os.path.abspath(f)
|
||||
break
|
||||
path = getattr(stream, 'name', 'stream')
|
||||
@ -146,6 +146,10 @@ class EPUBInput(InputFormatPlugin):
|
||||
self.rationalize_cover(opf, log)
|
||||
|
||||
self.optimize_opf_parsing = opf
|
||||
for x in opf.itermanifest():
|
||||
if x.get('media-type', '') == 'application/x-dtbook+xml':
|
||||
raise ValueError(
|
||||
'EPUB files with DTBook markup are not supported')
|
||||
|
||||
with open('content.opf', 'wb') as nopf:
|
||||
nopf.write(opf.render())
|
||||
|
@ -106,7 +106,7 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
recommendations = set([('pretty_print', True, OptionRecommendation.HIGH)])
|
||||
|
||||
|
||||
def workaround_webkit_quirks(self):
|
||||
def workaround_webkit_quirks(self): # {{{
|
||||
from calibre.ebooks.oeb.base import XPath
|
||||
for x in self.oeb.spine:
|
||||
root = x.data
|
||||
@ -120,8 +120,9 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
for pre in XPath('//h:pre')(body):
|
||||
if not pre.text and len(pre) == 0:
|
||||
pre.tag = 'div'
|
||||
# }}}
|
||||
|
||||
def upshift_markup(self):
|
||||
def upshift_markup(self): # {{{
|
||||
'Upgrade markup to comply with XHTML 1.1 where possible'
|
||||
from calibre.ebooks.oeb.base import XPath
|
||||
for x in self.oeb.spine:
|
||||
@ -135,6 +136,7 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
for u in XPath('//h:u')(root):
|
||||
u.tag = 'span'
|
||||
u.set('style', 'text-decoration:underline')
|
||||
# }}}
|
||||
|
||||
def convert(self, oeb, output_path, input_plugin, opts, log):
|
||||
self.log, self.opts, self.oeb = log, opts, oeb
|
||||
@ -161,8 +163,10 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
self.workaround_sony_quirks()
|
||||
|
||||
if self.oeb.toc.count() == 0:
|
||||
self.log.warn('This EPUB file has no Table of Contents. It will '
|
||||
'not validate via epubcheck')
|
||||
self.log.warn('This EPUB file has no Table of Contents. '
|
||||
'Creating a default TOC')
|
||||
first = iter(self.oeb.spine).next()
|
||||
self.oeb.toc.add(_('Start'), first.href)
|
||||
|
||||
from calibre.ebooks.oeb.base import OPF
|
||||
identifiers = oeb.metadata['identifier']
|
||||
@ -202,7 +206,7 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
self.log.info('EPUB extracted to', opts.extract_to)
|
||||
epub.close()
|
||||
|
||||
def encrypt_fonts(self, uris, tdir, uuid):
|
||||
def encrypt_fonts(self, uris, tdir, uuid): # {{{
|
||||
from binascii import unhexlify
|
||||
|
||||
key = re.sub(r'[^a-fA-F0-9]', '', uuid)
|
||||
@ -247,6 +251,7 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
ans += (u'\n'.join(fonts)).encode('utf-8')
|
||||
ans += '\n</encryption>'
|
||||
return ans
|
||||
# }}}
|
||||
|
||||
def condense_ncx(self, ncx_path):
|
||||
if not self.opts.pretty_print:
|
||||
@ -259,7 +264,7 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
compressed = etree.tostring(tree.getroot(), encoding='utf-8')
|
||||
open(ncx_path, 'wb').write(compressed)
|
||||
|
||||
def workaround_ade_quirks(self):
|
||||
def workaround_ade_quirks(self): # {{{
|
||||
'''
|
||||
Perform various markup transforms to get the output to render correctly
|
||||
in the quirky ADE.
|
||||
@ -388,8 +393,9 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
else:
|
||||
self.oeb.log.warn('No stylesheet found')
|
||||
|
||||
# }}}
|
||||
|
||||
def workaround_sony_quirks(self):
|
||||
def workaround_sony_quirks(self): # {{{
|
||||
'''
|
||||
Perform toc link transforms to alleviate slow loading.
|
||||
'''
|
||||
@ -436,3 +442,6 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
|
||||
if self.oeb.toc:
|
||||
simplify_toc_entry(self.oeb.toc)
|
||||
|
||||
# }}}
|
||||
|
||||
|
@ -21,7 +21,9 @@ from calibre.utils.logging import Log
|
||||
from calibre import guess_type, prints
|
||||
from calibre.ebooks.oeb.transforms.cover import CoverManager
|
||||
|
||||
TITLEPAGE = CoverManager.SVG_TEMPLATE.decode('utf-8').replace('__ar__', 'none')
|
||||
TITLEPAGE = CoverManager.SVG_TEMPLATE.decode('utf-8').replace(\
|
||||
'__ar__', 'none').replace('__viewbox__', '0 0 600 800'
|
||||
).replace('__width__', '600').replace('__height__', '800')
|
||||
|
||||
def character_count(html):
|
||||
'''
|
||||
|
@ -81,6 +81,8 @@ class DeviceJob(BaseJob):
|
||||
|
||||
class DeviceManager(Thread):
|
||||
|
||||
open_feedback = pyqtSignal(object)
|
||||
|
||||
def __init__(self, connected_slot, job_manager, sleep_time=2):
|
||||
'''
|
||||
:sleep_time: Time to sleep between device probes in secs
|
||||
@ -114,6 +116,8 @@ class DeviceManager(Thread):
|
||||
|
||||
def do_connect(self, connected_devices, is_folder_device):
|
||||
for dev, detected_device in connected_devices:
|
||||
if dev.OPEN_FEEDBACK_MESSAGE is not None:
|
||||
self.open_feedback.emit(dev.OPEN_FEEDBACK_MESSAGE)
|
||||
dev.reset(detected_device=detected_device,
|
||||
report_progress=self.report_progress)
|
||||
try:
|
||||
|
@ -335,7 +335,9 @@ Future conversion of these books will use the default settings.</string>
|
||||
<tabstop>tags</tabstop>
|
||||
<tabstop>remove_tags</tabstop>
|
||||
<tabstop>series</tabstop>
|
||||
<tabstop>autonumber_series</tabstop>
|
||||
<tabstop>remove_format</tabstop>
|
||||
<tabstop>swap_title_and_author</tabstop>
|
||||
<tabstop>button_box</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
|
@ -30,6 +30,7 @@ from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.utils.config import prefs, dynamic
|
||||
from calibre.utils.ipc.server import Server
|
||||
from calibre.utils.search_query_parser import saved_searches
|
||||
from calibre.devices.errors import UserFeedback
|
||||
from calibre.gui2 import warning_dialog, choose_files, error_dialog, \
|
||||
question_dialog,\
|
||||
pixmap_to_data, choose_dir, \
|
||||
@ -234,6 +235,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
####################### Setup device detection ########################
|
||||
self.device_manager = DeviceManager(Dispatcher(self.device_detected),
|
||||
self.job_manager)
|
||||
self.device_manager.open_feedback.connect(self.status.showMessage,
|
||||
type=Qt.QueuedConnection)
|
||||
self.device_manager.start()
|
||||
|
||||
|
||||
@ -2327,6 +2330,14 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
'''
|
||||
Handle exceptions in threaded device jobs.
|
||||
'''
|
||||
if isinstance(getattr(job, 'exception', None), UserFeedback):
|
||||
ex = job.exception
|
||||
func = {UserFeedback.ERROR:error_dialog,
|
||||
UserFeedback.WARNING:warning_dialog,
|
||||
UserFeedback.INFO:info_dialog}[ex.level]
|
||||
return func(self, _('Failed'), ex.msg, det_msg=ex.details if
|
||||
ex.details else '', show=True)
|
||||
|
||||
try:
|
||||
if 'Could not read 32 bytes on the control bus.' in \
|
||||
unicode(job.details):
|
||||
|
Loading…
x
Reference in New Issue
Block a user