Sync to trunk.

This commit is contained in:
John Schember 2009-12-27 21:22:28 -05:00
commit b61f5fcd3b
28 changed files with 9545 additions and 1022 deletions

View File

@ -4,6 +4,41 @@
# for important features/bug fixes. # for important features/bug fixes.
# Also, each release can have new and improved recipes. # Also, each release can have new and improved recipes.
- version: 0.6.31
date: 2009-12-27
new features:
- title: "Support for the SONY PRS 900 and the Airis dBook"
type: major
- title: "Device detection on OS X now directly queries the IOKit registry instead of parsing the output of the ioreg command."
description: >
"The logic for device detection in OS X is very similar to that in linux. This means that if a windows driver
for a device is written, it should work with no modification on both OS X and Linux."
bug fixes:
- title: "Fix a major regression in the 0.6.30 news download system that caused a lot of recipes to fail"
- title: "Make PRS 500 driver thread safe."
tickets: [4307]
- title: "Fix ebook viewer not working when launched as standalone program to view PDF files on windows"
- title: "PDB Output: Fix italics"
new recipes:
- title: The Hartford Courant
author: Being
- title: National Post
author: Nick Redding
- title: The Columbus Dispatch
author: kwetal
- version: 0.6.30 - version: 0.6.30
date: 2009-12-26 date: 2009-12-26

View File

@ -0,0 +1,90 @@
from __future__ import with_statement
__license__ = 'GPL 3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.web.feeds.news import BasicNewsRecipe
class ChicagoTribune(BasicNewsRecipe):
title = 'The Hartford Courant'
__author__ = 'Being and Sujata Raman'
description = 'Politics, local and business news from Hartford'
language = 'en'
use_embedded_content = False
no_stylesheets = True
remove_javascript = True
keep_only_tags = [dict(name='div', attrs={'class':["story","entry-asset asset hentry"]}),
dict(name='div', attrs={'id':["pagebody","story","maincontentcontainer"]}),
]
remove_tags_after = [ {'class':['photo_article',]} ]
remove_tags = [{'id':["moduleArticleTools","content-bottom","rail","articleRelates module","toolSet","relatedrailcontent","div-wrapper","beta","atp-comments","footer"]},
{'class':["clearfix","relatedTitle","articleRelates module","asset-footer","tools","comments","featurePromo","featurePromo fp-topjobs brownBackground","clearfix fullSpan brownBackground","curvedContent"]},
dict(name='font',attrs={'id':["cr-other-headlines"]})]
extra_css = '''
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
.byline {font-family:Arial,Helvetica,sans-serif; font-size:xx-small;}
.date {font-family:Arial,Helvetica,sans-serif; font-size:xx-small;}
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
.copyright {font-family:Arial,Helvetica,sans-serif;font-size:xx-small;text-align:center}
.story{font-family:Arial,Helvetica,sans-serif;font-size:small;}
.entry-asset asset hentry{font-family:Arial,Helvetica,sans-serif;font-size:small;}
.pagebody{font-family:Arial,Helvetica,sans-serif;font-size:small;}
.maincontentcontainer{font-family:Arial,Helvetica,sans-serif;font-size:small;}
.story-body{font-family:Arial,Helvetica,sans-serif;font-size:small;}
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
'''
feeds = [
('Breaking News', 'http://feeds.feedburner.com/courant-breaking-news/'),
('Nation/World News', 'http://feeds.feedburner.com/courant-nation-world/'),
('Connecticut News', 'http://feeds.feedburner.com/courant-connecticut-news/'),
('Hartford News', 'http://feeds.feedburner.com/courant-hartford/'),
('West Hartford News', 'http://feeds.feedburner.com/courant-west-hartford/'),
('Bristol', 'http://feeds.feedburner.com/courant-bristol/'),
('Politics', 'http://feeds.feedburner.com/courant-politics/'),
('Opinion', 'http://feeds.feedburner.com/courant-opinion/'),
('Editorials', 'http://feeds.feedburner.com/courant-editorials/'),
('Letters', 'http://feeds.feedburner.com/courant-letters/'),
('Bob Englehart', 'http://feeds2.feedburner.com/BobEnglehartEnglehartsView'),
('Business', 'http://feeds.feedburner.com/courant-business/'),
('Sports', 'http://feeds.feedburner.com/courant-sports/'),
('Features', 'http://feeds.feedburner.com/courant-features/'),
('Consumer', 'http://feeds.feedburner.com/courant-consumer/'),
('Shopping', 'http://feeds.feedburner.com/courant-shopping/'),
('Arts & Theater', 'http://feeds.feedburner.com/courant-entertainment/'),
('Entertainment', 'http://feeds.feedburner.com/courant-stage/'),
('Music', 'http://feeds.feedburner.com/courant-music/'),
('TV', 'http://feeds.feedburner.com/courant-tv/'),
('Movies', 'http://feeds.feedburner.com/courant-movies/'),
#('Metromix headlines', 'http://feeds.feedburner.com/metromix/topheadlines/'),
#('Metromix events', 'http://feeds.feedburner.com/metromix/events/'),
#('Metromix restaurants', 'http://feeds.feedburner.com/metromix/restaurants/'),
('Outdoors', 'http://feeds.feedburner.com/courant-outdoors/'),
('Peter Marteka', 'http://feeds.feedburner.com/courant-marteka-column/'),
('Susan Campbell', 'http://feeds.feedburner.com/courant-campbell-column/'),
('Helen Ubinas', 'http://feeds.feedburner.com/courant-helen-ubinas-column/'),
('Jim Shea', 'http://feeds.feedburner.com/courant-jim-shea-column/'),
('Tom Condon', 'http://feeds.feedburner.com/courant-tom-condon-column/'),
('Colin McEnroe', 'http://feeds.feedburner.com/courant-colin-mcenroe-column/'),
]
def get_article_url(self, article):
print article.get('feedburner_origlink', article.get('guid', article.get('link')))
return article.get('feedburner_origlink', article.get('guid', article.get('link')))
def postprocess_html(self, soup, first_fetch):
for t in soup.findAll(['table', 'tr', 'td']):
t.name = 'div'
for tag in soup.findAll('form', dict(attrs={'name':["comments_form"]})):
tag.extract()
for tag in soup.findAll('font', dict(attrs={'id':["cr-other-headlines"]})):
tag.extract()
return soup

View File

@ -0,0 +1,20 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1261379503(BasicNewsRecipe):
title = u'National Post'
language = 'en_CA'
__author__ = 'Nick Redding'
description = u"News from Canada"
oldest_article = 2
max_articles_per_feed = 25
keep_only_tags = [dict(name='div', attrs={'id':'content'})]
remove_tags = [dict(name='div', attrs={'class':'story-tools'}),dict(name='div', attrs={'class':'newsblock'}),dict(name='p', attrs={'class':'border-top'}),dict(name='div', attrs={'id':'footer'})]
feeds = [(u'News Headlines', u'http://www.nationalpost.com/scripts/sp6query.aspx?catalog=ntnp&type=stry&tags=section| news'),
(u'FP Headlines', u'http://www.nationalpost.com/scripts/sp6query.aspx?catalog=ntnp&type=stry&tags=section| financial%20post|storytype|business'),
(u'Arts & Life Headlines', u'http://www.nationalpost.com/scripts/sp6query.aspx?catalog=ntnp&type=stry&tags=section| arts%20%26%20life|storytype|news'),
(u'Canada News', u'http://www.nationalpost.com/scripts/sp6query.aspx?catalog=ntnp&type=stry&tags=storytyp e|webcanada&feed=rss'),
(u'World News', u'http://www.nationalpost.com/scripts/sp6query.aspx?catalog=ntnp&type=stry&tags=storytyp e|webworld&feed=rss'),(u'Editorial', u'http://www.nationalpost.com/scripts/sp6query.aspx?catalog=ntnp&type=stry&tags=section| editorial'),
(u'FP Opinion', u'http://www.nationalpost.com/scripts/columnists.aspx?publication=national+post&columnty pe=fp')]

View File

@ -72,7 +72,7 @@ class Translations(POT):
if os.path.exists(pycountry): if os.path.exists(pycountry):
iso639 = self.j(pycountry, 'iso639.mo') iso639 = self.j(pycountry, 'iso639.mo')
dest = self.j(self.d(dest), self.b(iso639)) dest = self.j(self.d(dest), self.b(iso639))
if self.newer(dest, iso639): if self.newer(dest, iso639) and os.path.exists(iso639):
self.info('\tCopying ISO 639 translations') self.info('\tCopying ISO 639 translations')
shutil.copy2(iso639, dest) shutil.copy2(iso639, dest)
else: else:

View File

@ -2,7 +2,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__ = 'calibre' __appname__ = 'calibre'
__version__ = '0.6.30' __version__ = '0.6.31'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re import re
@ -24,6 +24,7 @@ isosx = 'darwin' in sys.platform.lower()
isnewosx = isosx and getattr(sys, 'new_app_bundle', False) isnewosx = isosx and getattr(sys, 'new_app_bundle', False)
islinux = not(iswindows or isosx) islinux = not(iswindows or isosx)
isfrozen = hasattr(sys, 'frozen') isfrozen = hasattr(sys, 'frozen')
isunix = isosx or islinux
try: try:
preferred_encoding = locale.getpreferredencoding() preferred_encoding = locale.getpreferredencoding()

View File

@ -257,6 +257,7 @@ class SonyReader900Output(SonyReaderOutput):
description = _('This profile is intended for the SONY PRS-900.') description = _('This profile is intended for the SONY PRS-900.')
screen_size = (600, 999) screen_size = (600, 999)
comic_screen_size = screen_size
class JetBook5Output(OutputProfile): class JetBook5Output(OutputProfile):

View File

@ -62,7 +62,7 @@ def migrate(old, new):
def debug_device_driver(): def debug_device_driver():
from calibre.devices import debug from calibre.devices import debug
print debug() debug(ioreg_to_tmp=True, buf=sys.stdout)
if iswindows: if iswindows:
raw_input('Press Enter to continue...') raw_input('Press Enter to continue...')

View File

@ -27,14 +27,15 @@ def strftime(epoch, zone=time.gmtime):
src[2] = INVERSE_MONTH_MAP[int(src[2])] src[2] = INVERSE_MONTH_MAP[int(src[2])]
return ' '.join(src) return ' '.join(src)
def debug(): def debug(ioreg_to_tmp=False, buf=None):
from calibre.customize.ui import device_plugins from calibre.customize.ui import device_plugins
from calibre.devices.scanner import DeviceScanner from calibre.devices.scanner import DeviceScanner
from calibre.constants import iswindows, isosx, __version__ from calibre.constants import iswindows, isosx, __version__
from calibre import prints from calibre import prints
oldo, olde = sys.stdout, sys.stderr oldo, olde = sys.stdout, sys.stderr
buf = StringIO() if buf is None:
buf = StringIO()
sys.stdout = sys.stderr = buf sys.stdout = sys.stderr = buf
try: try:
out = partial(prints, file=buf) out = partial(prints, file=buf)
@ -82,16 +83,17 @@ def debug():
connected_devices = [] connected_devices = []
for dev in device_plugins(): for dev in device_plugins():
out('Looking for', dev.__class__.__name__) out('Looking for', dev.__class__.__name__)
connected = s.is_device_connected(dev, debug=True) connected, det = s.is_device_connected(dev, debug=True)
if connected: if connected:
connected_devices.append(dev) connected_devices.append((dev, det))
errors = {} errors = {}
success = False success = False
for dev in connected_devices: for dev, det in connected_devices:
out('Device possibly connected:', dev.__class__.name) out('Device possibly connected:', dev.__class__.name)
out('Trying to open device...', end=' ') out('Trying to open device...', end=' ')
try: try:
dev.reset(detected_device=det)
dev.open() dev.open()
out('OK') out('OK')
except: except:
@ -112,11 +114,17 @@ def debug():
out(' ') out(' ')
if ioreg is not None: if ioreg is not None:
ioreg = 'IOREG Output\n'+ioreg
out(' ') out(' ')
out('IOREG Output') if ioreg_to_tmp:
out(ioreg) open('/tmp/ioreg.txt', 'wb').write(ioreg)
out('Dont forget to send the contents of /tmp/ioreg.txt')
out('You can open it with the command: open /tmp/ioreg.txt')
else:
out(ioreg)
return buf.getvalue().decode('utf-8') if hasattr(buf, 'getvalue'):
return buf.getvalue().decode('utf-8')
finally: finally:
sys.stdout = oldo sys.stdout = oldo
sys.stderr = olde sys.stderr = olde

View File

@ -10,7 +10,7 @@ Device driver for Bookeen's Cybook Gen 3
import os import os
from calibre import islinux from calibre.constants import isunix
from calibre.devices.usbms.driver import USBMS from calibre.devices.usbms.driver import USBMS
import calibre.devices.cybookg3.t2b as t2b import calibre.devices.cybookg3.t2b as t2b
@ -55,7 +55,7 @@ class CYBOOKG3(USBMS):
@classmethod @classmethod
def can_handle(cls, device_info, debug=False): def can_handle(cls, device_info, debug=False):
if islinux: if isunix:
return device_info[3] == 'Bookeen' and device_info[4] == 'Cybook Gen3' return device_info[3] == 'Bookeen' and device_info[4] == 'Cybook Gen3'
return True return True
@ -87,6 +87,6 @@ class CYBOOK_OPUS(CYBOOKG3):
@classmethod @classmethod
def can_handle(cls, device_info, debug=False): def can_handle(cls, device_info, debug=False):
if islinux: if isunix:
return device_info[3] == 'Bookeen' return device_info[3] == 'Bookeen'
return True return True

View File

@ -104,12 +104,12 @@ class DevicePlugin(Plugin):
@classmethod @classmethod
def is_usb_connected(cls, devices_on_system, debug=False): def is_usb_connected(cls, devices_on_system, debug=False):
''' '''
Return True if a device handled by this plugin is currently connected. Return True, device_info if a device handled by this plugin is currently connected.
:param devices_on_system: List of devices currently connected :param devices_on_system: List of devices currently connected
''' '''
if iswindows: if iswindows:
return cls.is_usb_connected_windows(devices_on_system, debug=debug) return cls.is_usb_connected_windows(devices_on_system, debug=debug), None
vendors_on_system = set([x[0] for x in devices_on_system]) vendors_on_system = set([x[0] for x in devices_on_system])
vendors = cls.VENDOR_ID if hasattr(cls.VENDOR_ID, '__len__') else [cls.VENDOR_ID] vendors = cls.VENDOR_ID if hasattr(cls.VENDOR_ID, '__len__') else [cls.VENDOR_ID]
@ -134,18 +134,20 @@ class DevicePlugin(Plugin):
if debug: if debug:
cls.print_usb_device_info(dev) cls.print_usb_device_info(dev)
if cls.can_handle(dev, debug=debug): if cls.can_handle(dev, debug=debug):
return True return True, dev
return False return False, None
def reset(self, key='-1', log_packets=False, report_progress=None) : def reset(self, key='-1', log_packets=False, report_progress=None,
detected_device=None) :
""" """
@param key: The key to unlock the device :key: The key to unlock the device
@param log_packets: If true the packet stream to/from the device is logged :log_packets: If true the packet stream to/from the device is logged
@param report_progress: Function that is called with a % progress :report_progress: Function that is called with a % progress
(number between 0 and 100) for various tasks (number between 0 and 100) for various tasks
If it is called with -1 that means that the If it is called with -1 that means that the
task does not have any progress information task does not have any progress information
:detected_device: Device information from the device scanner
""" """
raise NotImplementedError() raise NotImplementedError()

View File

@ -206,9 +206,10 @@ def main():
scanner.scan() scanner.scan()
connected_devices = [] connected_devices = []
for d in device_plugins(): for d in device_plugins():
if scanner.is_device_connected(d): ok, det = scanner.is_device_connected(d)
if ok:
dev = d dev = d
dev.reset(log_packets=options.log_packets) dev.reset(log_packets=options.log_packets, detected_device=det)
connected_devices.append(dev) connected_devices.append(dev)
if dev is None: if dev is None:

View File

@ -39,6 +39,7 @@ from tempfile import TemporaryFile
from array import array from array import array
from functools import wraps from functools import wraps
from StringIO import StringIO from StringIO import StringIO
from threading import RLock
from calibre.devices.interface import DevicePlugin from calibre.devices.interface import DevicePlugin
from calibre.devices.libusb import Error as USBError from calibre.devices.libusb import Error as USBError
@ -52,6 +53,7 @@ from calibre.devices.usbms.deviceconfig import DeviceConfig
# Protocol versions this driver has been tested with # Protocol versions this driver has been tested with
KNOWN_USB_PROTOCOL_VERSIONS = [0x3030303030303130L] KNOWN_USB_PROTOCOL_VERSIONS = [0x3030303030303130L]
lock = RLock()
class File(object): class File(object):
""" """
@ -161,40 +163,44 @@ class PRS500(DeviceConfig, DevicePlugin):
""" """
@wraps(func) @wraps(func)
def run_session(*args, **kwargs): def run_session(*args, **kwargs):
dev = args[0] with lock:
res = None dev = args[0]
try: res = None
if not dev.handle: try:
dev.open() if not hasattr(dev, 'in_session'):
if not getattr(dev, 'in_session', False): dev.reset()
dev.send_validated_command(BeginEndSession(end=False)) if not dev.handle:
dev.in_session = True dev.open()
res = func(*args, **kwargs) if not getattr(dev, 'in_session', False):
except ArgumentError: dev.send_validated_command(BeginEndSession(end=False))
dev.in_session = True
res = func(*args, **kwargs)
except ArgumentError:
if not kwargs.has_key("end_session") or kwargs["end_session"]:
dev.send_validated_command(BeginEndSession(end=True))
dev.in_session = False
raise
except USBError, err:
if "No such device" in str(err):
raise DeviceError()
elif "Connection timed out" in str(err):
dev.close()
raise TimeoutError(func.__name__)
elif "Protocol error" in str(err):
dev.close()
raise ProtocolError("There was an unknown error in the"+\
" protocol. Contact " + __author__)
dev.close()
raise
if not kwargs.has_key("end_session") or kwargs["end_session"]: if not kwargs.has_key("end_session") or kwargs["end_session"]:
dev.send_validated_command(BeginEndSession(end=True)) dev.send_validated_command(BeginEndSession(end=True))
dev.in_session = False dev.in_session = False
raise return res
except USBError, err:
if "No such device" in str(err):
raise DeviceError()
elif "Connection timed out" in str(err):
dev.close()
raise TimeoutError(func.__name__)
elif "Protocol error" in str(err):
dev.close()
raise ProtocolError("There was an unknown error in the"+\
" protocol. Contact " + __author__)
dev.close()
raise
if not kwargs.has_key("end_session") or kwargs["end_session"]:
dev.send_validated_command(BeginEndSession(end=True))
dev.in_session = False
return res
return run_session return run_session
def reset(self, key='-1', log_packets=False, report_progress=None) : def reset(self, key='-1', log_packets=False, report_progress=None,
detected_device=None) :
""" """
@param key: The key to unlock the device @param key: The key to unlock the device
@param log_packets: If true the packet stream to/from the device is logged @param log_packets: If true the packet stream to/from the device is logged
@ -203,17 +209,18 @@ class PRS500(DeviceConfig, DevicePlugin):
If it is called with -1 that means that the If it is called with -1 that means that the
task does not have any progress information task does not have any progress information
""" """
self.device = get_device_by_id(self.VENDOR_ID, self.PRODUCT_ID) with lock:
# Handle that is used to communicate with device. Setup in L{open} self.device = get_device_by_id(self.VENDOR_ID, self.PRODUCT_ID)
self.handle = None # Handle that is used to communicate with device. Setup in L{open}
self.in_session = False self.handle = None
self.log_packets = log_packets self.in_session = False
self.report_progress = report_progress self.log_packets = log_packets
if len(key) > 8: self.report_progress = report_progress
key = key[:8] if len(key) > 8:
elif len(key) < 8: key = key[:8]
key += ''.join(['\0' for i in xrange(8 - len(key))]) elif len(key) < 8:
self.key = key key += ''.join(['\0' for i in xrange(8 - len(key))])
self.key = key
def reconnect(self): def reconnect(self):
""" Only recreates the device node and deleted the connection handle """ """ Only recreates the device node and deleted the connection handle """
@ -243,64 +250,66 @@ class PRS500(DeviceConfig, DevicePlugin):
Also initialize the device. Also initialize the device.
See the source code for the sequence of initialization commands. See the source code for the sequence of initialization commands.
""" """
if not hasattr(self, 'key'): with lock:
self.key = '-1\0\0\0\0\0\0' if not hasattr(self, 'key'):
self.device = get_device_by_id(self.VENDOR_ID, self.PRODUCT_ID) self.reset()
if not self.device: self.device = get_device_by_id(self.VENDOR_ID, self.PRODUCT_ID)
raise DeviceError() if not self.device:
configs = self.device.configurations raise DeviceError()
try: configs = self.device.configurations
self.handle = self.device.open()
config = configs[0]
try: try:
self.handle.set_configuration(configs[0]) self.handle = self.device.open()
except USBError: config = configs[0]
self.handle.set_configuration(configs[1]) try:
config = configs[1] self.handle.set_configuration(configs[0])
_id = config.interface.contents.altsetting.contents except USBError:
ed1 = _id.endpoint[0] self.handle.set_configuration(configs[1])
ed2 = _id.endpoint[1] config = configs[1]
if ed1.EndpointAddress == self.BULK_IN_EP: _id = config.interface.contents.altsetting.contents
red, wed = ed1, ed2 ed1 = _id.endpoint[0]
else: ed2 = _id.endpoint[1]
red, wed = ed2, ed1 if ed1.EndpointAddress == self.BULK_IN_EP:
self.bulk_read_max_packet_size = red.MaxPacketSize red, wed = ed1, ed2
self.bulk_write_max_packet_size = wed.MaxPacketSize else:
self.handle.claim_interface(self.INTERFACE_ID) red, wed = ed2, ed1
except USBError, err: self.bulk_read_max_packet_size = red.MaxPacketSize
raise DeviceBusy(str(err)) self.bulk_write_max_packet_size = wed.MaxPacketSize
# Large timeout as device may still be initializing self.handle.claim_interface(self.INTERFACE_ID)
res = self.send_validated_command(GetUSBProtocolVersion(), timeout=20000) except USBError, err:
if res.code != 0: raise DeviceBusy(str(err))
raise ProtocolError("Unable to get USB Protocol version.") # Large timeout as device may still be initializing
version = self._bulk_read(24, data_type=USBProtocolVersion)[0].version res = self.send_validated_command(GetUSBProtocolVersion(), timeout=20000)
if version not in KNOWN_USB_PROTOCOL_VERSIONS: if res.code != 0:
print >> sys.stderr, "WARNING: Usb protocol version " + \ raise ProtocolError("Unable to get USB Protocol version.")
hex(version) + " is unknown" version = self._bulk_read(24, data_type=USBProtocolVersion)[0].version
res = self.send_validated_command(SetBulkSize(\ if version not in KNOWN_USB_PROTOCOL_VERSIONS:
chunk_size = 512*self.bulk_read_max_packet_size, \ print >> sys.stderr, "WARNING: Usb protocol version " + \
unknown = 2)) hex(version) + " is unknown"
if res.code != 0: res = self.send_validated_command(SetBulkSize(\
raise ProtocolError("Unable to set bulk size.") chunk_size = 512*self.bulk_read_max_packet_size, \
res = self.send_validated_command(UnlockDevice(key=self.key))#0x312d)) unknown = 2))
if res.code != 0: if res.code != 0:
raise DeviceLocked() raise ProtocolError("Unable to set bulk size.")
res = self.send_validated_command(SetTime()) res = self.send_validated_command(UnlockDevice(key=self.key))#0x312d))
if res.code != 0: if res.code != 0:
raise ProtocolError("Could not set time on device") raise DeviceLocked()
res = self.send_validated_command(SetTime())
if res.code != 0:
raise ProtocolError("Could not set time on device")
def eject(self): def eject(self):
pass pass
def close(self): def close(self):
""" Release device interface """ """ Release device interface """
try: with lock:
self.handle.reset() try:
self.handle.release_interface(self.INTERFACE_ID) self.handle.reset()
except Exception, err: self.handle.release_interface(self.INTERFACE_ID)
print >> sys.stderr, err except Exception, err:
self.handle, self.device = None, None print >> sys.stderr, err
self.in_session = False self.handle, self.device = None, None
self.in_session = False
def _send_command(self, command, response_type=Response, timeout=1000): def _send_command(self, command, response_type=Response, timeout=1000):
""" """
@ -312,17 +321,18 @@ class PRS500(DeviceConfig, DevicePlugin):
@param timeout: The time to wait for a response from the @param timeout: The time to wait for a response from the
device, in milliseconds. If there is no response, a L{usb.USBError} is raised. device, in milliseconds. If there is no response, a L{usb.USBError} is raised.
""" """
if self.log_packets: with lock:
self.log_packet(command, "Command") if self.log_packets:
bytes_sent = self.handle.control_msg(0x40, 0x80, command) self.log_packet(command, "Command")
if bytes_sent != len(command): bytes_sent = self.handle.control_msg(0x40, 0x80, command)
raise ControlError(desc="Could not send control request to device\n"\ if bytes_sent != len(command):
+ str(command)) raise ControlError(desc="Could not send control request to device\n"\
response = response_type(self.handle.control_msg(0xc0, 0x81, \ + str(command))
Response.SIZE, timeout=timeout)) response = response_type(self.handle.control_msg(0xc0, 0x81, \
if self.log_packets: Response.SIZE, timeout=timeout))
self.log_packet(response, "Response") if self.log_packets:
return response self.log_packet(response, "Response")
return response
def send_validated_command(self, command, cnumber=None, \ def send_validated_command(self, command, cnumber=None, \
response_type=Response, timeout=1000): response_type=Response, timeout=1000):
@ -346,42 +356,43 @@ class PRS500(DeviceConfig, DevicePlugin):
@param packet_size: Size of packets to be sent to device. @param packet_size: Size of packets to be sent to device.
C{data} is broken up into packets to be sent to device. C{data} is broken up into packets to be sent to device.
""" """
def bulk_write_packet(packet): with lock:
self.handle.bulk_write(self.BULK_OUT_EP, packet) def bulk_write_packet(packet):
if self.log_packets: self.handle.bulk_write(self.BULK_OUT_EP, packet)
self.log_packet(Answer(packet), "Answer h->d") if self.log_packets:
self.log_packet(Answer(packet), "Answer h->d")
bytes_left = len(data) bytes_left = len(data)
if bytes_left + 16 <= packet_size: if bytes_left + 16 <= packet_size:
packet_size = bytes_left +16 packet_size = bytes_left +16
first_packet = Answer(bytes_left+16) first_packet = Answer(bytes_left+16)
first_packet[16:] = data first_packet[16:] = data
first_packet.length = len(data) first_packet.length = len(data)
else: else:
first_packet = Answer(packet_size) first_packet = Answer(packet_size)
first_packet[16:] = data[0:packet_size-16] first_packet[16:] = data[0:packet_size-16]
first_packet.length = packet_size-16 first_packet.length = packet_size-16
first_packet.number = 0x10005 first_packet.number = 0x10005
bulk_write_packet(first_packet) bulk_write_packet(first_packet)
pos = first_packet.length pos = first_packet.length
bytes_left -= first_packet.length bytes_left -= first_packet.length
while bytes_left > 0: while bytes_left > 0:
endpos = pos + packet_size if pos + packet_size <= len(data) \ endpos = pos + packet_size if pos + packet_size <= len(data) \
else len(data) else len(data)
bulk_write_packet(data[pos:endpos]) bulk_write_packet(data[pos:endpos])
bytes_left -= endpos - pos bytes_left -= endpos - pos
pos = endpos pos = endpos
res = Response(self.handle.control_msg(0xc0, 0x81, Response.SIZE, \ res = Response(self.handle.control_msg(0xc0, 0x81, Response.SIZE, \
timeout=5000)) timeout=5000))
if self.log_packets: if self.log_packets:
self.log_packet(res, "Response") self.log_packet(res, "Response")
if res.rnumber != 0x10005 or res.code != 0: if res.rnumber != 0x10005 or res.code != 0:
raise ProtocolError("Sending via Bulk Transfer failed with response:\n"\ raise ProtocolError("Sending via Bulk Transfer failed with response:\n"\
+str(res)) +str(res))
if res.data_size != len(data): if res.data_size != len(data):
raise ProtocolError("Unable to transfer all data to device. "+\ raise ProtocolError("Unable to transfer all data to device. "+\
"Response packet:\n"\ "Response packet:\n"\
+str(res)) +str(res))
def _bulk_read(self, bytes, command_number=0x00, packet_size=0x1000, \ def _bulk_read(self, bytes, command_number=0x00, packet_size=0x1000, \
@ -394,31 +405,32 @@ class PRS500(DeviceConfig, DevicePlugin):
@return: A list of packets read from the device. @return: A list of packets read from the device.
Each packet is of type data_type Each packet is of type data_type
""" """
msize = self.bulk_read_max_packet_size with lock:
def bulk_read_packet(data_type=Answer, size=0x1000): msize = self.bulk_read_max_packet_size
rsize = size def bulk_read_packet(data_type=Answer, size=0x1000):
if size % msize: rsize = size
rsize = size - size % msize + msize if size % msize:
data = data_type(self.handle.bulk_read(self.BULK_IN_EP, rsize)) rsize = size - size % msize + msize
if self.log_packets: data = data_type(self.handle.bulk_read(self.BULK_IN_EP, rsize))
self.log_packet(data, "Answer d->h") if self.log_packets:
if len(data) != size: self.log_packet(data, "Answer d->h")
raise ProtocolError("Unable to read " + str(size) + " bytes from "\ if len(data) != size:
"device. Read: " + str(len(data)) + " bytes") raise ProtocolError("Unable to read " + str(size) + " bytes from "\
return data "device. Read: " + str(len(data)) + " bytes")
return data
bytes_left = bytes bytes_left = bytes
packets = [] packets = []
while bytes_left > 0: while bytes_left > 0:
if packet_size > bytes_left: if packet_size > bytes_left:
packet_size = bytes_left packet_size = bytes_left
packet = bulk_read_packet(data_type=data_type, size=packet_size) packet = bulk_read_packet(data_type=data_type, size=packet_size)
bytes_left -= len(packet) bytes_left -= len(packet)
packets.append(packet) packets.append(packet)
self.send_validated_command(\ self.send_validated_command(\
AcknowledgeBulkRead(packets[0].number), \ AcknowledgeBulkRead(packets[0].number), \
cnumber=command_number) cnumber=command_number)
return packets return packets
@safe @safe
def get_device_information(self, end_session=True): def get_device_information(self, end_session=True):

View File

@ -22,9 +22,32 @@ from itertools import repeat
from calibre.devices.interface import DevicePlugin from calibre.devices.interface import DevicePlugin
from calibre.devices.errors import DeviceError, FreeSpaceError from calibre.devices.errors import DeviceError, FreeSpaceError
from calibre.devices.usbms.deviceconfig import DeviceConfig from calibre.devices.usbms.deviceconfig import DeviceConfig
from calibre import iswindows, islinux, isosx, __appname__ from calibre.constants import iswindows, islinux, isosx, __appname__, plugins
from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to
if isosx:
usbobserver, usbobserver_err = plugins['usbobserver']
class USBDevice:
def __init__(self, dev):
self.idVendor = dev[0]
self.idProduct = dev[1]
self.bcdDevice = dev[2]
self.manufacturer = dev[3]
self.product = dev[4]
self.serial = dev[5]
def match_serial(self, serial):
return self.serial and self.serial == serial
def match_numbers(self, vid, pid, bcd):
return self.idVendor == vid and self.idProduct == pid and self.bcdDevice == bcd
def match_strings(self, vid, pid, bcd, man, prod):
return self.match_numbers(vid, pid, bcd) and \
self.manufacturer == man and self.product == prod
class Device(DeviceConfig, DevicePlugin): class Device(DeviceConfig, DevicePlugin):
''' '''
@ -108,8 +131,13 @@ class Device(DeviceConfig, DevicePlugin):
FDI_LUNS = {'lun0':0, 'lun1':1, 'lun2':2} FDI_LUNS = {'lun0':0, 'lun1':1, 'lun2':2}
FDI_BCD_TEMPLATE = '<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.device_revision_bcd" int="%(bcd)s">' FDI_BCD_TEMPLATE = '<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.device_revision_bcd" int="%(bcd)s">'
def reset(self, key='-1', log_packets=False, report_progress=None) : def reset(self, key='-1', log_packets=False, report_progress=None,
detected_device=None):
self._main_prefix = self._card_a_prefix = self._card_b_prefix = None self._main_prefix = self._card_a_prefix = self._card_b_prefix = None
try:
self.detected_device = USBDevice(detected_device)
except: # On windows detected_device is None
self.detected_device = None
@classmethod @classmethod
def get_gui_name(cls): def get_gui_name(cls):
@ -391,29 +419,82 @@ class Device(DeviceConfig, DevicePlugin):
raise raise
time.sleep(2) time.sleep(2)
def open_osx(self): def _osx_bsd_names(self):
mount = self.osx_run_mount() if usbobserver_err:
names = self.get_osx_mountpoints() raise RuntimeError('Failed to load usbobserver: '+usbobserver_err)
dev_pat = r'/dev/%s(\w*)\s+on\s+([^\(]+)\s+' drives = usbobserver.get_usb_drives()
if 'main' not in names.keys(): matches = []
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.')%self.__class__.__name__) d = self.detected_device
main_pat = dev_pat % names['main'] if d.serial:
main_match = re.search(main_pat, mount) for path, vid, pid, bcd, ven, prod, serial in drives:
if main_match is None: if d.match_serial(serial):
raise DeviceError(_('Unable to detect the %s mount point. Try rebooting.')%self.__class__.__name__) matches.append(path)
self._main_prefix = main_match.group(2) + os.sep if not matches:
card_a_pat = names['carda'] if 'carda' in names.keys() else None if d.manufacturer and d.product:
card_b_pat = names['cardb'] if 'cardb' in names.keys() else None for path, vid, pid, bcd, man, prod, serial in drives:
if d.match_strings(vid, pid, bcd, man, prod):
def get_card_prefix(pat): matches.append(path)
if pat is not None:
pat = dev_pat % pat
return re.search(pat, mount).group(2) + os.sep
else: else:
return None for path, vid, pid, bcd, man, prod, serial in drives:
if d.match_numbers(vid, pid, bcd):
matches.append(path)
if not matches:
raise DeviceError(
'Could not detect BSD names for %s. Try rebooting.' % self.name)
self._card_a_prefix = get_card_prefix(card_a_pat) pat = re.compile(r'(?P<m>\d+)([a-z]+(?P<p>\d+)){0,1}')
self._card_b_prefix = get_card_prefix(card_b_pat) def nums(x):
m = pat.search(x)
if m is None:
return (10000, 0)
g = m.groupdict()
if g['p'] is None:
g['p'] = 0
return map(int, (g.get('m'), g.get('p')))
def dcmp(x, y):
x = x.rpartition('/')[-1]
y = y.rpartition('/')[-1]
x, y = nums(x), nums(y)
ans = cmp(x[0], y[0])
if ans == 0:
ans = cmp(x[1], y[1])
return ans
matches.sort(cmp=dcmp)
drives = {'main':matches[0]}
if len(matches) > 1:
drives['carda'] = matches[1]
if len(matches) > 2:
drives['cardb'] = matches[2]
return drives
def osx_bsd_names(self):
try:
return self._osx_bsd_names()
except:
time.sleep(2)
return self._osx_bsd_names()
def open_osx(self):
drives = self.osx_bsd_names()
bsd_drives = dict(**drives)
drives = self.osx_sort_names(drives)
mount_map = usbobserver.get_mounted_filesystems()
for k, v in drives.items():
drives[k] = mount_map.get(v, None)
if drives['main'] is None:
print bsd_drives, mount_map, drives
raise DeviceError(_('Unable to detect the %s mount point. Try rebooting.')%self.__class__.__name__)
self._main_prefix = drives['main']+os.sep
def get_card_prefix(c):
ans = drives.get(c, None)
if ans is not None:
ans += os.sep
return ans
self._card_a_prefix = get_card_prefix('carda')
self._card_b_prefix = get_card_prefix('cardb')
def find_device_nodes(self): def find_device_nodes(self):

View File

@ -33,10 +33,6 @@ class USBMS(CLI, Device):
FORMATS = [] FORMATS = []
CAN_SET_METADATA = False CAN_SET_METADATA = False
def reset(self, key='-1', log_packets=False, report_progress=None):
Device.reset(self, key=key, log_packets=log_packets,
report_progress=report_progress)
def get_device_information(self, end_session=True): def get_device_information(self, end_session=True):
self.report_progress(1.0, _('Get device information...')) self.report_progress(1.0, _('Get device information...'))
return (self.__class__.__name__, '', '', '') return (self.__class__.__name__, '', '', '')

View File

@ -24,112 +24,283 @@
#include <stdio.h> #include <stdio.h>
#include <CoreFoundation/CFNumber.h>
#include <IOKit/usb/IOUSBLib.h> #include <IOKit/usb/IOUSBLib.h>
#include <IOKit/IOCFPlugIn.h> #include <IOKit/IOCFPlugIn.h>
#include <IOKit/IOKitLib.h> #include <IOKit/IOKitLib.h>
#include <mach/mach.h> #include <IOKit/storage/IOMedia.h>
#include <IOKit/IOBSD.h>
#include <IOKit/usb/USBSpec.h>
CFStringRef USB_PROPS[3] = { CFSTR("USB Vendor Name"), CFSTR("USB Product Name"), CFSTR("USB Serial Number") }; #include <mach/mach.h>
#include <sys/param.h>
#include <paths.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#ifndef kUSBVendorString
#define kUSBVendorString "USB Vendor Name"
#endif
#ifndef kUSBProductString
#define kUSBProductString "USB Product Name"
#endif
#ifndef kUSBSerialNumberString
#define kUSBSerialNumberString "USB Serial Number"
#endif
#define NUKE(x) Py_XDECREF(x); x = NULL;
static PyObject* static PyObject*
get_iokit_string_property(io_service_t dev, int prop) { usbobserver_get_iokit_string_property(io_service_t dev, CFStringRef prop) {
CFTypeRef PropRef; CFTypeRef PropRef;
char buf[500]; char buf[500];
PropRef = IORegistryEntryCreateCFProperty(dev, USB_PROPS[prop], kCFAllocatorDefault, 0); PropRef = IORegistryEntryCreateCFProperty(dev, prop, kCFAllocatorDefault, 0);
if (PropRef) { if (PropRef) {
if(!CFStringGetCString(PropRef, buf, 500, kCFStringEncodingUTF8)) buf[0] = '\0'; if(!CFStringGetCString(PropRef, buf, 500, kCFStringEncodingUTF8)) buf[0] = '\0';
} else buf[0] = '\0'; CFRelease(PropRef);
} else buf[0] = '\0';
return PyUnicode_DecodeUTF8(buf, strlen(buf), "replace"); return PyUnicode_DecodeUTF8(buf, strlen(buf), "replace");
} }
static PyObject*
usbobserver_get_iokit_number_property(io_service_t dev, CFStringRef prop) {
CFTypeRef PropRef;
long val = 0;
PropRef = IORegistryEntryCreateCFProperty(dev, prop, kCFAllocatorDefault, 0);
if (PropRef) {
CFNumberGetValue((CFNumberRef)PropRef, kCFNumberLongType, &val);
CFRelease(PropRef);
}
return PyLong_FromLong(val);
}
static PyObject * static PyObject *
usbobserver_get_usb_devices(PyObject *self, PyObject *args) { usbobserver_get_usb_devices(PyObject *self, PyObject *args) {
CFMutableDictionaryRef matchingDict; CFMutableDictionaryRef matchingDict;
kern_return_t kr; kern_return_t kr;
PyObject *devices, *device;
io_service_t usbDevice;
PyObject *vendor, *product, *bcd;
PyObject *manufacturer, *productn, *serial;
//Set up matching dictionary for class IOUSBDevice and its subclasses
matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
if (!matchingDict) {
PyErr_SetString(PyExc_RuntimeError, "Couldn't create a USB matching dictionary");
return NULL;
}
io_iterator_t iter;
IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
io_service_t usbDevice;
IOCFPlugInInterface **plugInInterface = NULL;
SInt32 score;
IOUSBDeviceInterface182 **dev = NULL;
UInt16 vendor, product, bcd;
PyObject *manufacturer, *productn, *serial;
PyObject *devices, *device; //Set up matching dictionary for class IOUSBDevice and its subclasses
devices = PyList_New(0); matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
if (devices == NULL) { if (!matchingDict) {
PyErr_NoMemory(); PyErr_SetString(PyExc_RuntimeError, "Couldn't create a USB matching dictionary");
return NULL; return NULL;
}
while ((usbDevice = IOIteratorNext(iter))) {
plugInInterface = NULL; dev = NULL;
//Create an intermediate plugin
kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
if ((kIOReturnSuccess != kr) || !plugInInterface) {
printf("Unable to create a plug-in (%08x)\n", kr); continue;
}
//Now create the device interface
HRESULT result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID)&dev);
if (result || !dev) {
printf("Couldn't create a device interface (%08x)\n", (int) result);
continue;
} }
kr = (*dev)->GetDeviceVendor(dev, &vendor); io_iterator_t iter;
kr = (*dev)->GetDeviceProduct(dev, &product); kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
kr = (*dev)->GetDeviceReleaseNumber(dev, &bcd); if (KERN_SUCCESS != kr) {
printf("IOServiceGetMatchingServices returned 0x%08x\n", kr);
manufacturer = get_iokit_string_property(usbDevice, 0); PyErr_SetString(PyExc_RuntimeError, "Could not run IO Matching");
if (manufacturer == NULL) manufacturer = Py_None; return NULL;
productn = get_iokit_string_property(usbDevice, 1);
if (productn == NULL) productn = Py_None;
serial = get_iokit_string_property(usbDevice, 2);
if (serial == NULL) serial = Py_None;
device = Py_BuildValue("(iiiNNN)", vendor, product, bcd, manufacturer, productn, serial);
if (device == NULL) {
IOObjectRelease(usbDevice);
(*plugInInterface)->Release(plugInInterface);
(*dev)->Release(dev);
Py_DECREF(devices);
return NULL;
}
if (PyList_Append(devices, device) == -1) {
IOObjectRelease(usbDevice);
(*plugInInterface)->Release(plugInInterface);
(*dev)->Release(dev);
Py_DECREF(devices);
Py_DECREF(device);
return NULL;
} }
IOObjectRelease(usbDevice); devices = PyList_New(0);
(*plugInInterface)->Release(plugInInterface); if (devices == NULL) {
(*dev)->Release(dev); PyErr_NoMemory();
Py_DECREF(device); return NULL;
} }
while ((usbDevice = IOIteratorNext(iter))) {
vendor = usbobserver_get_iokit_number_property(usbDevice, CFSTR(kUSBVendorID));
product = usbobserver_get_iokit_number_property(usbDevice, CFSTR(kUSBProductID));
bcd = usbobserver_get_iokit_number_property(usbDevice, CFSTR(kUSBDeviceReleaseNumber));
manufacturer = usbobserver_get_iokit_string_property(usbDevice, CFSTR(kUSBVendorString));
productn = usbobserver_get_iokit_string_property(usbDevice, CFSTR(kUSBProductString));
serial = usbobserver_get_iokit_string_property(usbDevice, CFSTR(kUSBSerialNumberString));
if (usbDevice) IOObjectRelease(usbDevice);
if (vendor != NULL && product != NULL && bcd != NULL) {
if (manufacturer == NULL) { manufacturer = Py_None; Py_INCREF(Py_None); }
if (productn == NULL) { productn = Py_None; Py_INCREF(Py_None); }
if (serial == NULL) { serial = Py_None; Py_INCREF(Py_None); }
device = Py_BuildValue("(OOOOOO)", vendor, product, bcd, manufacturer, productn, serial);
if (device != NULL) {
PyList_Append(devices, device);
Py_DECREF(device);
}
}
NUKE(vendor); NUKE(product); NUKE(bcd); NUKE(manufacturer);
NUKE(productn); NUKE(serial);
}
return devices; if (iter) IOObjectRelease(iter);
return devices;
}
static PyObject*
usbobserver_get_bsd_path(io_object_t dev) {
char cpath[ MAXPATHLEN ];
CFTypeRef PropRef;
size_t dev_path_length;
cpath[0] = '\0';
PropRef = IORegistryEntryCreateCFProperty(dev, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
if (!PropRef) return NULL;
strcpy(cpath, _PATH_DEV);
dev_path_length = strlen(cpath);
if (!CFStringGetCString(PropRef,
cpath + dev_path_length,
MAXPATHLEN - dev_path_length,
kCFStringEncodingUTF8)) return NULL;
return PyUnicode_DecodeUTF8(cpath, strlen(cpath), "replace");
}
static PyObject*
usbobserver_find_prop(io_registry_entry_t e, CFStringRef key, int is_string )
{
char buf[500]; long val = 0;
PyObject *ans;
IOOptionBits bits = kIORegistryIterateRecursively | kIORegistryIterateParents;
CFTypeRef PropRef = IORegistryEntrySearchCFProperty( e, kIOServicePlane, key, NULL, bits );
if (!PropRef) return NULL;
buf[0] = '\0';
if(is_string) {
CFStringGetCString(PropRef, buf, 500, kCFStringEncodingUTF8);
ans = PyUnicode_DecodeUTF8(buf, strlen(buf), "replace");
} else {
CFNumberGetValue((CFNumberRef)PropRef, kCFNumberLongType, &val);
ans = PyLong_FromLong(val);
}
CFRelease(PropRef);
return ans;
}
static PyObject*
usbobserver_get_usb_drives(PyObject *self, PyObject *args) {
CFMutableDictionaryRef matchingDict;
kern_return_t kr = KERN_FAILURE;
io_iterator_t iter;
io_object_t next;
PyObject *ans = NULL, *bsd_path = NULL, *t = NULL, *vid, *pid, *bcd, *manufacturer, *product, *serial;
//Set up matching dictionary for class IOMedia and its subclasses
matchingDict = IOServiceMatching(kIOMediaClass);
if (!matchingDict) {
PyErr_SetString(PyExc_RuntimeError, "Couldn't create a Media matching dictionary");
return NULL;
}
// Only want writable and ejectable leaf nodes
CFDictionarySetValue(matchingDict, CFSTR(kIOMediaWritableKey), kCFBooleanTrue);
CFDictionarySetValue(matchingDict, CFSTR(kIOMediaLeafKey), kCFBooleanTrue);
CFDictionarySetValue(matchingDict, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
ans = PyList_New(0);
if (ans == NULL) return PyErr_NoMemory();
kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
if (KERN_SUCCESS != kr) {
printf("IOServiceGetMatchingServices returned 0x%08x\n", kr);
PyErr_SetString(PyExc_RuntimeError, "Could not run IO Matching");
return NULL;
}
while ((next = IOIteratorNext(iter))) {
bsd_path = usbobserver_get_bsd_path(next);
vid = usbobserver_find_prop(next, CFSTR(kUSBVendorID), 0);
pid = usbobserver_find_prop(next, CFSTR(kUSBProductID), 0);
bcd = usbobserver_find_prop(next, CFSTR(kUSBDeviceReleaseNumber), 0);
manufacturer = usbobserver_find_prop(next, CFSTR(kUSBVendorString), 1);
product = usbobserver_find_prop(next, CFSTR(kUSBProductString), 1);
serial = usbobserver_find_prop(next, CFSTR(kUSBSerialNumberString), 1);
IOObjectRelease(next);
if (bsd_path != NULL && vid != NULL && pid != NULL && bcd != NULL) {
if (manufacturer == NULL) { manufacturer = Py_None; Py_INCREF(Py_None); }
if (product == NULL) { product = Py_None; Py_INCREF(Py_None); }
if (serial == NULL) { serial = Py_None; Py_INCREF(Py_None); }
t = Py_BuildValue("(OOOOOOO)", bsd_path, vid, pid, bcd, manufacturer, product, serial);
if (t != NULL) {
PyList_Append(ans, t);
Py_DECREF(t); t = NULL;
}
}
NUKE(bsd_path); NUKE(vid); NUKE(pid); NUKE(bcd);
NUKE(manufacturer); NUKE(product); NUKE(serial);
}
if (iter) IOObjectRelease(iter);
return ans;
}
static PyObject*
usbobserver_get_mounted_filesystems(PyObject *self, PyObject *args) {
struct statfs *buf, t;
int num, i;
PyObject *ans, *key, *val;
num = getfsstat(NULL, 0, MNT_NOWAIT);
if (num == -1) {
PyErr_SetString(PyExc_RuntimeError, "Initial call to getfsstat failed");
return NULL;
}
ans = PyDict_New();
if (ans == NULL) return PyErr_NoMemory();
buf = (struct statfs*)calloc(num, sizeof(struct statfs));
if (buf == NULL) return PyErr_NoMemory();
num = getfsstat(buf, num*sizeof(struct statfs), MNT_NOWAIT);
if (num == -1) {
PyErr_SetString(PyExc_RuntimeError, "Call to getfsstat failed");
return NULL;
}
for (i = 0 ; i < num; i++) {
t = buf[i];
key = PyBytes_FromString(t.f_mntfromname);
val = PyBytes_FromString(t.f_mntonname);
if (key != NULL && val != NULL) {
PyDict_SetItem(ans, key, val);
}
NUKE(key); NUKE(val);
}
free(buf);
return ans;
} }
static PyMethodDef usbobserver_methods[] = { static PyMethodDef usbobserver_methods[] = {
{"get_usb_devices", usbobserver_get_usb_devices, METH_VARARGS, {"get_usb_devices", usbobserver_get_usb_devices, METH_VARARGS,
"Get list of connected USB devices. Returns a list of tuples. Each tuple is of the form (vendor_id, product_id)." "Get list of connected USB devices. Returns a list of tuples. Each tuple is of the form (vendor_id, product_id, bcd, manufacturer, product, serial number)."
}, },
{"get_usb_drives", usbobserver_get_usb_drives, METH_VARARGS,
"Get list of mounted drives. Returns a list of tuples, each of the form (name, bsd_path)."
},
{"get_mounted_filesystems", usbobserver_get_mounted_filesystems, METH_VARARGS,
"Get mapping of mounted filesystems. Mapping is from BSD name to mount point."
},
{NULL, NULL, 0, NULL} {NULL, NULL, 0, NULL}
}; };

View File

@ -12,6 +12,7 @@ import subprocess
from functools import partial from functools import partial
from calibre.ebooks import ConversionError, DRMError from calibre.ebooks import ConversionError, DRMError
from calibre.ptempfile import PersistentTemporaryFile
from calibre import isosx, iswindows, islinux from calibre import isosx, iswindows, islinux
from calibre import CurrentDir from calibre import CurrentDir
@ -45,8 +46,10 @@ def pdftohtml(output_dir, pdf_path, no_images):
if no_images: if no_images:
cmd.append('-i') cmd.append('-i')
logf = PersistentTemporaryFile('pdftohtml_log')
try: try:
p = popen(cmd, stderr=subprocess.PIPE) p = popen(cmd, stderr=logf._fd, stdout=logf._fd,
stdin=subprocess.PIPE)
except OSError, err: except OSError, err:
if err.errno == 2: if err.errno == 2:
raise ConversionError(_('Could not find pdftohtml, check it is in your PATH')) raise ConversionError(_('Could not find pdftohtml, check it is in your PATH'))
@ -62,10 +65,13 @@ def pdftohtml(output_dir, pdf_path, no_images):
continue continue
else: else:
raise raise
logf.flush()
logf.close()
out = open(logf.name, 'rb').read()
if ret != 0: if ret != 0:
err = p.stderr.read() raise ConversionError(out)
raise ConversionError(err) print "pdftohtml log:"
print out
if not os.path.exists(index) or os.stat(index).st_size < 100: if not os.path.exists(index) or os.stat(index).st_size < 100:
raise DRMError() raise DRMError()

View File

@ -94,8 +94,8 @@ class DeviceManager(Thread):
import pythoncom import pythoncom
pythoncom.CoInitialize() pythoncom.CoInitialize()
try: try:
for dev in connected_devices: for dev, detected_device in connected_devices:
dev.reset() dev.reset(detected_device=detected_device)
try: try:
dev.open() dev.open()
except: except:
@ -116,10 +116,10 @@ class DeviceManager(Thread):
self.scanner.scan() self.scanner.scan()
connected_devices = [] connected_devices = []
for device in self.devices: for device in self.devices:
connected = self.scanner.is_device_connected(device[0]) connected, detected_device = self.scanner.is_device_connected(device[0])
if connected and not device[1] and not device[2]: if connected and not device[1] and not device[2]:
# If connected and not showing in GUI and not ejected # If connected and not showing in GUI and not ejected
connected_devices.append(device[0]) connected_devices.append((device[0], detected_device))
device[1] = True device[1] = True
elif not connected and device[1]: elif not connected and device[1]:
# Disconnected but showing in GUI # Disconnected but showing in GUI

View File

@ -92,6 +92,12 @@ class Sony505(Sony500):
name = 'SONY Reader Pocket/Touch Edition' name = 'SONY Reader Pocket/Touch Edition'
id = 'prs505' id = 'prs505'
class Sony900(Sony505):
name = 'SONY Reader Daily Edition'
id = 'prs900'
output_profile = 'sony900'
class Nook(Sony505): class Nook(Sony505):
id = 'nook' id = 'nook'
name = 'Nook' name = 'Nook'

View File

@ -81,7 +81,7 @@ Device Integration
What devices does |app| support? What devices does |app| support?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
At the moment |app| has full support for the SONY PRS 300/500/505/600/700, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Netronix EB600, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Italica, various Android phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk. At the moment |app| has full support for the SONY PRS 300/500/505/600/700/900, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Netronix EB600, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Italica, eClicto, Iriver Story, Airis dBook, various Android phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
How can I help get my device supported in |app|? How can I help get my device supported in |app|?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -7,14 +7,14 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: calibre\n" "Project-Id-Version: calibre\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2009-12-25 04:54+0000\n" "POT-Creation-Date: 2009-12-26 16:57+0000\n"
"PO-Revision-Date: 2009-12-25 19:11+0000\n" "PO-Revision-Date: 2009-12-26 16:45+0000\n"
"Last-Translator: Kovid Goyal <Unknown>\n" "Last-Translator: Kovid Goyal <Unknown>\n"
"Language-Team: Arabic <ar@li.org>\n" "Language-Team: Arabic <ar@li.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-26 04:31+0000\n" "X-Launchpad-Export-Date: 2009-12-27 04:31+0000\n"
"X-Generator: Launchpad (build Unknown)\n" "X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41 #: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
@ -64,8 +64,8 @@ msgstr "لا يفعل شيءً"
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:79 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:79
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:155 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:155
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:593 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:594
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:783 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:784
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:896 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:896
@ -132,7 +132,7 @@ msgstr "لا يفعل شيءً"
#: /home/kovid/work/calibre/src/calibre/library/server.py:645 #: /home/kovid/work/calibre/src/calibre/library/server.py:645
#: /home/kovid/work/calibre/src/calibre/library/server.py:717 #: /home/kovid/work/calibre/src/calibre/library/server.py:717
#: /home/kovid/work/calibre/src/calibre/library/server.py:764 #: /home/kovid/work/calibre/src/calibre/library/server.py:764
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:107
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77
@ -7361,10 +7361,18 @@ msgid "English (TH)"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:103 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:103
msgid "Dutch (NL)" msgid "English (CY)"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:104 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:104
msgid "German (AT)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105
msgid "Dutch (NL)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:106
msgid "Dutch (BE)" msgid "Dutch (BE)"
msgstr "" msgstr ""

View File

@ -4,9 +4,9 @@
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: calibre 0.6.30\n" "Project-Id-Version: calibre 0.6.31\n"
"POT-Creation-Date: 2009-12-26 08:40+MST\n" "POT-Creation-Date: 2009-12-27 16:02+MST\n"
"PO-Revision-Date: 2009-12-26 08:40+MST\n" "PO-Revision-Date: 2009-12-27 16:02+MST\n"
"Last-Translator: Automatically generated\n" "Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n" "Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -22,11 +22,13 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:44 #: /home/kovid/work/calibre/src/calibre/customize/__init__.py:44
#: /home/kovid/work/calibre/src/calibre/devices/jetbook/driver.py:94 #: /home/kovid/work/calibre/src/calibre/devices/jetbook/driver.py:94
#: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:54 #: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:54
#: /home/kovid/work/calibre/src/calibre/devices/nook/driver.py:70
#: /home/kovid/work/calibre/src/calibre/devices/nook/driver.py:71
#: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:58 #: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:58
#: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:199 #: /home/kovid/work/calibre/src/calibre/devices/prs505/books.py:199
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:789 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:870
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:792 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:873
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:206 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:202
#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:414 #: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:414
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:67 #: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:67
#: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:69 #: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:69
@ -236,72 +238,90 @@ msgid "This profile tries to provide sane defaults and is useful if you know not
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:56 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:56
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:208 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:235
msgid "This profile is intended for the SONY PRS line. The 500/505/700 etc." msgid "This profile is intended for the SONY PRS line. The 500/505/600/700 etc."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:69 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:68
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:243 msgid "This profile is intended for the SONY PRS 300."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:77
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:257
msgid "This profile is intended for the SONY PRS-900."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:85
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:287
msgid "This profile is intended for the Microsoft Reader." msgid "This profile is intended for the Microsoft Reader."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:80 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:96
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:254 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:298
msgid "This profile is intended for the Mobipocket books." msgid "This profile is intended for the Mobipocket books."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:93 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:109
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:267 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:311
msgid "This profile is intended for the Hanlin V3/V5 and its clones." msgid "This profile is intended for the Hanlin V3 and its clones."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:105 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:121
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:279 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:323
msgid "This profile is intended for the Hanlin V5 and its clones."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:131
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:331
msgid "This profile is intended for the Cybook G3." msgid "This profile is intended for the Cybook G3."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:118 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:144
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:292 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:344
msgid "This profile is intended for the Cybook Opus." msgid "This profile is intended for the Cybook Opus."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:130 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:156
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:303 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:355
msgid "This profile is intended for the Amazon Kindle." msgid "This profile is intended for the Amazon Kindle."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:142 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:168
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:336 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:388
msgid "This profile is intended for the Irex Illiad." msgid "This profile is intended for the Irex Illiad."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:154 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:180
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:349 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:401
msgid "This profile is intended for the IRex Digital Reader 1000." msgid "This profile is intended for the IRex Digital Reader 1000."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:168 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:194
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:363 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:415
msgid "This profile is intended for the B&N Nook." msgid "This profile is intended for the B&N Nook."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:186 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:213
msgid "Output profile" msgid "Output profile"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:190 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:217
msgid "This profile tries to provide sane defaults and is useful if you want to produce a document intended to be read at a computer or on a range of devices." msgid "This profile tries to provide sane defaults and is useful if you want to produce a document intended to be read at a computer or on a range of devices."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:220 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:248
msgid "This profile is intended for the SONY PRS-300."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:266
msgid "This profile is intended for the 5-inch JetBook." msgid "This profile is intended for the 5-inch JetBook."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:231 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:275
msgid "This profile is intended for the SONY PRS line. The 500/505/700 etc, in landscape mode. Mainly useful for comics." msgid "This profile is intended for the SONY PRS line. The 500/505/700 etc, in landscape mode. Mainly useful for comics."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/customize/profiles.py:320 #: /home/kovid/work/calibre/src/calibre/customize/profiles.py:372
msgid "This profile is intended for the Amazon Kindle DX." msgid "This profile is intended for the Amazon Kindle DX."
msgstr "" msgstr ""
@ -369,33 +389,21 @@ msgstr ""
msgid "Comma separated list of directories to send e-books to on the device. The first one that exists will be used" msgid "Comma separated list of directories to send e-books to on the device. The first one that exists will be used"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/bebook/driver.py:19
msgid "Communicate with the BeBook eBook reader."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/bebook/driver.py:95
msgid "Communicate with the BeBook Mini eBook reader."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/blackberry/driver.py:12 #: /home/kovid/work/calibre/src/calibre/devices/blackberry/driver.py:12
msgid "Communicate with the Blackberry smart phone." msgid "Communicate with the Blackberry smart phone."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/blackberry/driver.py:13 #: /home/kovid/work/calibre/src/calibre/devices/blackberry/driver.py:13
#: /home/kovid/work/calibre/src/calibre/devices/nuut2/driver.py:18 #: /home/kovid/work/calibre/src/calibre/devices/nuut2/driver.py:18
#: /home/kovid/work/calibre/src/calibre/devices/prs500/driver.py:88 #: /home/kovid/work/calibre/src/calibre/devices/prs500/driver.py:90
msgid "Kovid Goyal" msgid "Kovid Goyal"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/boox/driver.py:17
msgid "Communicate with the BOOX eBook reader."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:21 #: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:21
msgid "Communicate with the Cybook Gen 3 eBook reader." msgid "Communicate with the Cybook Gen 3 eBook reader."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:68 #: /home/kovid/work/calibre/src/calibre/devices/cybookg3/driver.py:67
msgid "Communicate with the Cybook Opus eBook reader." msgid "Communicate with the Cybook Opus eBook reader."
msgstr "" msgstr ""
@ -407,6 +415,18 @@ msgstr ""
msgid "Communicate with the ESlick eBook reader." msgid "Communicate with the ESlick eBook reader."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/hanlin/driver.py:19
msgid "Communicate with Hanlin V3 eBook readers."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/hanlin/driver.py:95
msgid "Communicate with Hanlin V5 eBook readers."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/hanlin/driver.py:113
msgid "Communicate with the BOOX eBook reader."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/iliad/driver.py:16 #: /home/kovid/work/calibre/src/calibre/devices/iliad/driver.py:16
msgid "Communicate with the IRex Iliad eBook reader." msgid "Communicate with the IRex Iliad eBook reader."
msgstr "" msgstr ""
@ -437,8 +457,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/jetbook/driver.py:78 #: /home/kovid/work/calibre/src/calibre/devices/jetbook/driver.py:78
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:134 #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:134
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:136 #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:136
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:120 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:116
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:122 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:118
msgid "Transferring books to device..." msgid "Transferring books to device..."
msgstr "" msgstr ""
@ -474,7 +494,7 @@ msgstr ""
msgid "Communicate with the Nuut2 eBook reader." msgid "Communicate with the Nuut2 eBook reader."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/prs500/driver.py:87 #: /home/kovid/work/calibre/src/calibre/devices/prs500/driver.py:89
msgid "Communicate with the Sony PRS-500 eBook reader." msgid "Communicate with the Sony PRS-500 eBook reader."
msgstr "" msgstr ""
@ -483,12 +503,12 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:100 #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:100
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:103 #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:103
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:114 #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:114
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:49 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:45
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:52 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:48
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:55 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:51
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:75 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:71
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:85 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:81
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:94 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:90
msgid "Getting list of books on device..." msgid "Getting list of books on device..."
msgstr "" msgstr ""
@ -498,64 +518,63 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:167 #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:167
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:174 #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:174
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:148 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:144
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:163 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:159
msgid "Removing books from device..." msgid "Removing books from device..."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:202 #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:202
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:178 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:174
msgid "Sending metadata to device..." msgid "Sending metadata to device..."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:208 #: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:208
msgid "Communicate with the Sony PRS-600/700 eBook reader." msgid "Communicate with the Sony PRS-600/700/900 eBook reader."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:308 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:336
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:399
msgid "Unable to detect the %s disk drive. Try rebooting." msgid "Unable to detect the %s disk drive. Try rebooting."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:403 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:489
msgid "Unable to detect the %s mount point. Try rebooting." msgid "Unable to detect the %s mount point. Try rebooting."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:470 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:551
msgid "Unable to detect the %s disk drive." msgid "Unable to detect the %s disk drive."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:563 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:644
msgid "Could not find mount helper: %s." msgid "Could not find mount helper: %s."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:575 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:656
msgid "Unable to detect the %s disk drive. Your kernel is probably exporting a deprecated version of SYSFS." msgid "Unable to detect the %s disk drive. Your kernel is probably exporting a deprecated version of SYSFS."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:583 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:664
msgid "Unable to mount main memory (Error code: %d)" msgid "Unable to mount main memory (Error code: %d)"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:720 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:801
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:722 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:803
msgid "The reader has no storage card in this slot." msgid "The reader has no storage card in this slot."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:724 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:805
msgid "Selected slot: %s is not supported." msgid "Selected slot: %s is not supported."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:757 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:838
msgid "There is insufficient free space in main memory" msgid "There is insufficient free space in main memory"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:759 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:840
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:761 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:842
msgid "There is insufficient free space on the storage card" msgid "There is insufficient free space on the storage card"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:772 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:853
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:232 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:232
#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:125 #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:125
#: /home/kovid/work/calibre/src/calibre/library/database2.py:1065 #: /home/kovid/work/calibre/src/calibre/library/database2.py:1065
@ -594,17 +613,17 @@ msgstr ""
msgid "Communicate with an eBook reader." msgid "Communicate with an eBook reader."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:41 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:37
msgid "Get device information..." msgid "Get device information..."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:136 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:132
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:144 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:140
msgid "Adding books to device metadata listing..." msgid "Adding books to device metadata listing..."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:167 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:163
#: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:172 #: /home/kovid/work/calibre/src/calibre/devices/usbms/driver.py:168
msgid "Removing books from device metadata listing..." msgid "Removing books from device metadata listing..."
msgstr "" msgstr ""
@ -1902,7 +1921,7 @@ msgstr ""
msgid "The orientation of the page. Default is portrait. Choices are %s" msgid "The orientation of the page. Default is portrait. Choices are %s"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftohtml.py:52 #: /home/kovid/work/calibre/src/calibre/ebooks/pdf/pdftohtml.py:55
msgid "Could not find pdftohtml, check it is in your PATH" msgid "Could not find pdftohtml, check it is in your PATH"
msgstr "" msgstr ""
@ -2048,7 +2067,7 @@ msgid "Limit max simultaneous jobs to number of CPUs"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:127 #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:127
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:404 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:410
msgid "Copied" msgid "Copied"
msgstr "" msgstr ""
@ -3459,7 +3478,7 @@ msgid "Failed to start content server"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:698 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:698
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:514 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:520
msgid "Select location for books" msgid "Select location for books"
msgstr "" msgstr ""
@ -5354,7 +5373,7 @@ msgid "Bad database location"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:471 #: /home/kovid/work/calibre/src/calibre/gui2/ui.py:471
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:522 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:528
msgid "Calibre Library" msgid "Calibre Library"
msgstr "" msgstr ""
@ -6119,40 +6138,40 @@ msgstr ""
msgid "Title Case" msgid "Title Case"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:295 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:301
msgid "If you use the WordPlayer e-book app on your Android phone, you can access your calibre book collection directly on the device. To do this you have to turn on the content server." msgid "If you use the WordPlayer e-book app on your Android phone, you can access your calibre book collection directly on the device. To do this you have to turn on the content server."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:299 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:305
msgid "Remember to leave calibre running as the server only runs as long as calibre is running." msgid "Remember to leave calibre running as the server only runs as long as calibre is running."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:301 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:307
msgid "You have to add the URL http://myhostname:8080 as your calibre library in WordPlayer. Here myhostname should be the fully qualified hostname or the IP address of the computer calibre is running on." msgid "You have to add the URL http://myhostname:8080 as your calibre library in WordPlayer. Here myhostname should be the fully qualified hostname or the IP address of the computer calibre is running on."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:378 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:384
msgid "Moving library..." msgid "Moving library..."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:394 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:400
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:395 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:401
msgid "Failed to move library" msgid "Failed to move library"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:449 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:455
msgid "Invalid database" msgid "Invalid database"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:450 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:456
msgid "<p>An invalid library already exists at %s, delete it before trying to move the existing library.<br>Error: %s" msgid "<p>An invalid library already exists at %s, delete it before trying to move the existing library.<br>Error: %s"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:461 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:467
msgid "Could not move library" msgid "Could not move library"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:589 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/__init__.py:595
msgid "welcome wizard" msgid "welcome wizard"
msgstr "" msgstr ""
@ -6970,50 +6989,50 @@ msgstr ""
msgid "Custom" msgid "Custom"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:455 #: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:459
msgid "" msgid ""
"%prog URL\n" "%prog URL\n"
"\n" "\n"
"Where URL is for example http://google.com" "Where URL is for example http://google.com"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:458 #: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:462
msgid "Base directory into which URL is saved. Default is %default" msgid "Base directory into which URL is saved. Default is %default"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:461 #: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:465
msgid "Timeout in seconds to wait for a response from the server. Default: %default s" msgid "Timeout in seconds to wait for a response from the server. Default: %default s"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:464 #: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:468
msgid "Maximum number of levels to recurse i.e. depth of links to follow. Default %default" msgid "Maximum number of levels to recurse i.e. depth of links to follow. Default %default"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:467 #: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:471
msgid "The maximum number of files to download. This only applies to files from <a href> tags. Default is %default" msgid "The maximum number of files to download. This only applies to files from <a href> tags. Default is %default"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:469 #: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:473
msgid "Minimum interval in seconds between consecutive fetches. Default is %default s" msgid "Minimum interval in seconds between consecutive fetches. Default is %default s"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:471 #: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:475
msgid "The character encoding for the websites you are trying to download. The default is to try and guess the encoding." msgid "The character encoding for the websites you are trying to download. The default is to try and guess the encoding."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:473 #: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:477
msgid "Only links that match this regular expression will be followed. This option can be specified multiple times, in which case as long as a link matches any one regexp, it will be followed. By default all links are followed." msgid "Only links that match this regular expression will be followed. This option can be specified multiple times, in which case as long as a link matches any one regexp, it will be followed. By default all links are followed."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:475 #: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:479
msgid "Any link that matches this regular expression will be ignored. This option can be specified multiple times, in which case as long as any regexp matches a link, it will be ignored.By default, no links are ignored. If both --filter-regexp and --match-regexp are specified, then --filter-regexp is applied first." msgid "Any link that matches this regular expression will be ignored. This option can be specified multiple times, in which case as long as any regexp matches a link, it will be ignored.By default, no links are ignored. If both --filter-regexp and --match-regexp are specified, then --filter-regexp is applied first."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:477 #: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:481
msgid "Do not download CSS stylesheets." msgid "Do not download CSS stylesheets."
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:478 #: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:482
msgid "Show detailed output information. Useful for debugging" msgid "Show detailed output information. Useful for debugging"
msgstr "" msgstr ""

View File

@ -7,14 +7,14 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: de\n" "Project-Id-Version: de\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-12-25 04:54+0000\n" "POT-Creation-Date: 2009-12-26 16:57+0000\n"
"PO-Revision-Date: 2009-12-25 21:08+0000\n" "PO-Revision-Date: 2009-12-26 20:24+0000\n"
"Last-Translator: S. Dorscht <Unknown>\n" "Last-Translator: S. Dorscht <Unknown>\n"
"Language-Team: American English <kde-i18n-doc@lists.kde.org>\n" "Language-Team: American English <kde-i18n-doc@lists.kde.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-26 04:32+0000\n" "X-Launchpad-Export-Date: 2009-12-27 04:31+0000\n"
"X-Generator: Launchpad (build Unknown)\n" "X-Generator: Launchpad (build Unknown)\n"
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
@ -65,8 +65,8 @@ msgstr "Macht absolut gar nichts"
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:79 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:79
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:155 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:155
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:593 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:594
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:783 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:784
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:896 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:896
@ -133,7 +133,7 @@ msgstr "Macht absolut gar nichts"
#: /home/kovid/work/calibre/src/calibre/library/server.py:645 #: /home/kovid/work/calibre/src/calibre/library/server.py:645
#: /home/kovid/work/calibre/src/calibre/library/server.py:717 #: /home/kovid/work/calibre/src/calibre/library/server.py:717
#: /home/kovid/work/calibre/src/calibre/library/server.py:764 #: /home/kovid/work/calibre/src/calibre/library/server.py:764
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:107
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77
@ -8227,10 +8227,18 @@ msgid "English (TH)"
msgstr "Englisch (TH)" msgstr "Englisch (TH)"
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:103 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:103
msgid "English (CY)"
msgstr "Englisch (CY)"
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:104
msgid "German (AT)"
msgstr "Deutsch (AT)"
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105
msgid "Dutch (NL)" msgid "Dutch (NL)"
msgstr "Holländisch (NL)" msgstr "Holländisch (NL)"
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:104 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:106
msgid "Dutch (BE)" msgid "Dutch (BE)"
msgstr "Holländisch (BE)" msgstr "Holländisch (BE)"

View File

@ -10,14 +10,14 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: es\n" "Project-Id-Version: es\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-12-25 04:54+0000\n" "POT-Creation-Date: 2009-12-26 16:57+0000\n"
"PO-Revision-Date: 2009-12-26 01:25+0000\n" "PO-Revision-Date: 2009-12-26 16:48+0000\n"
"Last-Translator: mosteo <alejandro@mosteo.com>\n" "Last-Translator: mosteo <alejandro@mosteo.com>\n"
"Language-Team: Spanish\n" "Language-Team: Spanish\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-26 04:33+0000\n" "X-Launchpad-Export-Date: 2009-12-27 04:32+0000\n"
"X-Generator: Launchpad (build Unknown)\n" "X-Generator: Launchpad (build Unknown)\n"
#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41 #: /home/kovid/work/calibre/src/calibre/customize/__init__.py:41
@ -67,8 +67,8 @@ msgstr "No hacer nada en absoluto"
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:79 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:79
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:155 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:155
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:593 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:594
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:783 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:784
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:896 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:896
@ -135,7 +135,7 @@ msgstr "No hacer nada en absoluto"
#: /home/kovid/work/calibre/src/calibre/library/server.py:645 #: /home/kovid/work/calibre/src/calibre/library/server.py:645
#: /home/kovid/work/calibre/src/calibre/library/server.py:717 #: /home/kovid/work/calibre/src/calibre/library/server.py:717
#: /home/kovid/work/calibre/src/calibre/library/server.py:764 #: /home/kovid/work/calibre/src/calibre/library/server.py:764
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:107
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77
@ -8181,10 +8181,18 @@ msgid "English (TH)"
msgstr "Inglés (TH)" msgstr "Inglés (TH)"
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:103 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:103
msgid "English (CY)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:104
msgid "German (AT)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105
msgid "Dutch (NL)" msgid "Dutch (NL)"
msgstr "Neerlandés (NL)" msgstr "Neerlandés (NL)"
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:104 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:106
msgid "Dutch (BE)" msgid "Dutch (BE)"
msgstr "Neerlandés (BE)" msgstr "Neerlandés (BE)"

File diff suppressed because it is too large Load Diff

View File

@ -7,14 +7,14 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: calibre\n" "Project-Id-Version: calibre\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2009-12-25 04:54+0000\n" "POT-Creation-Date: 2009-12-26 16:57+0000\n"
"PO-Revision-Date: 2009-12-25 19:14+0000\n" "PO-Revision-Date: 2009-12-26 16:50+0000\n"
"Last-Translator: Kovid Goyal <Unknown>\n" "Last-Translator: Kovid Goyal <Unknown>\n"
"Language-Team: Latvian <ivars_a@inbox.lv>\n" "Language-Team: Latvian <ivars_a@inbox.lv>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-26 04:32+0000\n" "X-Launchpad-Export-Date: 2009-12-27 04:31+0000\n"
"X-Generator: Launchpad (build Unknown)\n" "X-Generator: Launchpad (build Unknown)\n"
"X-Poedit-Country: LATVIA\n" "X-Poedit-Country: LATVIA\n"
"X-Poedit-Language: Latvian\n" "X-Poedit-Language: Latvian\n"
@ -66,8 +66,8 @@ msgstr "Pilnīgi neko nedara"
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:79 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:79
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:155 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:155
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:593 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:594
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:783 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:784
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:896 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:896
@ -134,7 +134,7 @@ msgstr "Pilnīgi neko nedara"
#: /home/kovid/work/calibre/src/calibre/library/server.py:645 #: /home/kovid/work/calibre/src/calibre/library/server.py:645
#: /home/kovid/work/calibre/src/calibre/library/server.py:717 #: /home/kovid/work/calibre/src/calibre/library/server.py:717
#: /home/kovid/work/calibre/src/calibre/library/server.py:764 #: /home/kovid/work/calibre/src/calibre/library/server.py:764
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:107
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77
@ -7354,10 +7354,18 @@ msgid "English (TH)"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:103 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:103
msgid "Dutch (NL)" msgid "English (CY)"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:104 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:104
msgid "German (AT)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105
msgid "Dutch (NL)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:106
msgid "Dutch (BE)" msgid "Dutch (BE)"
msgstr "" msgstr ""

View File

@ -6,14 +6,14 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: calibre 0.4.55\n" "Project-Id-Version: calibre 0.4.55\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-12-25 04:54+0000\n" "POT-Creation-Date: 2009-12-26 16:57+0000\n"
"PO-Revision-Date: 2009-12-25 19:27+0000\n" "PO-Revision-Date: 2009-12-26 16:54+0000\n"
"Last-Translator: Kovid Goyal <Unknown>\n" "Last-Translator: Kovid Goyal <Unknown>\n"
"Language-Team: American English <kde-i18n-doc@lists.kde.org>\n" "Language-Team: American English <kde-i18n-doc@lists.kde.org>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2009-12-26 04:32+0000\n" "X-Launchpad-Export-Date: 2009-12-27 04:31+0000\n"
"X-Generator: Launchpad (build Unknown)\n" "X-Generator: Launchpad (build Unknown)\n"
"X-Poedit-Country: RUSSIAN FEDERATION\n" "X-Poedit-Country: RUSSIAN FEDERATION\n"
"X-Poedit-Language: Russian\n" "X-Poedit-Language: Russian\n"
@ -68,8 +68,8 @@ msgstr "Ничего не делает"
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:79 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:79
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:121
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:155 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:155
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:593 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:594
#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:783 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:784
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49
#: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51
#: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:896 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:896
@ -136,7 +136,7 @@ msgstr "Ничего не делает"
#: /home/kovid/work/calibre/src/calibre/library/server.py:645 #: /home/kovid/work/calibre/src/calibre/library/server.py:645
#: /home/kovid/work/calibre/src/calibre/library/server.py:717 #: /home/kovid/work/calibre/src/calibre/library/server.py:717
#: /home/kovid/work/calibre/src/calibre/library/server.py:764 #: /home/kovid/work/calibre/src/calibre/library/server.py:764
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:107
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:45
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:63
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77 #: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:77
@ -7760,10 +7760,18 @@ msgid "English (TH)"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:103 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:103
msgid "Dutch (NL)" msgid "English (CY)"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:104 #: /home/kovid/work/calibre/src/calibre/utils/localization.py:104
msgid "German (AT)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105
msgid "Dutch (NL)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/utils/localization.py:106
msgid "Dutch (BE)" msgid "Dutch (BE)"
msgstr "" msgstr ""

File diff suppressed because it is too large Load Diff

View File

@ -190,10 +190,14 @@ class RecursiveFetcher(object):
time.sleep(delta) time.sleep(delta)
if isinstance(url, unicode): if isinstance(url, unicode):
url = url.encode('utf-8') url = url.encode('utf-8')
purl = list(urlparse.urlparse(url)) # Not sure is this is really needed as I think mechanize
for i in range(2, 6): # handles quoting automatically, but leaving it in
purl[i] = quote(purl[i]) # in case it breaks something
url = urlparse.urlunparse(purl) if re.search(r'\s+|,', url) is not None:
purl = list(urlparse.urlparse(url))
for i in range(2, 6):
purl[i] = quote(purl[i])
url = urlparse.urlunparse(purl)
try: try:
open_func = getattr(self.browser, 'open_novisit', self.browser.open) open_func = getattr(self.browser, 'open_novisit', self.browser.open)
with closing(open_func(url, timeout=self.timeout)) as f: with closing(open_func(url, timeout=self.timeout)) as f: