This commit is contained in:
Kovid Goyal 2009-01-20 16:38:22 -08:00
commit 6d65a57ee4
3 changed files with 70 additions and 70 deletions

View File

@ -7,6 +7,7 @@ Device driver for Bookeen's Cybook Gen 3
import os, shutil import os, shutil
from itertools import cycle from itertools import cycle
from calibre.devices.errors import FreeSpaceError
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
from calibre.devices.errors import FreeSpaceError from calibre.devices.errors import FreeSpaceError

View File

@ -18,21 +18,21 @@ class Device(_Device):
as USB Mass Storage devices. If you are writing such a driver, inherit from this as USB Mass Storage devices. If you are writing such a driver, inherit from this
class. class.
''' '''
VENDOR_ID = 0x0 VENDOR_ID = 0x0
PRODUCT_ID = 0x0 PRODUCT_ID = 0x0
BCD = None BCD = None
VENDOR_NAME = None VENDOR_NAME = None
WINDOWS_MAIN_MEM = None WINDOWS_MAIN_MEM = None
WINDOWS_CARD_MEM = None WINDOWS_CARD_MEM = None
OSX_MAIN_MEM = None OSX_MAIN_MEM = None
OSX_CARD_MEM = None OSX_CARD_MEM = None
MAIN_MEMORY_VOLUME_LABEL = '' MAIN_MEMORY_VOLUME_LABEL = ''
STORAGE_CARD_VOLUME_LABEL = '' STORAGE_CARD_VOLUME_LABEL = ''
FDI_TEMPLATE = \ FDI_TEMPLATE = \
''' '''
<device> <device>
@ -65,15 +65,15 @@ class Device(_Device):
</device> </device>
''' '''
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 __init__(self, key='-1', log_packets=False, report_progress=None) : def __init__(self, key='-1', log_packets=False, report_progress=None) :
self._main_prefix = self._card_prefix = None self._main_prefix = self._card_prefix = None
@classmethod @classmethod
def get_fdi(cls): def get_fdi(cls):
fdi = '' fdi = ''
fdi_base_values = dict( fdi_base_values = dict(
app=__appname__, app=__appname__,
deviceclass=cls.__name__, deviceclass=cls.__name__,
@ -92,12 +92,12 @@ class Device(_Device):
fdi_bcd_values['BCD_start'] = cls.FDI_BCD_TEMPLATE % dict(bcd=hex(bcd)) fdi_bcd_values['BCD_start'] = cls.FDI_BCD_TEMPLATE % dict(bcd=hex(bcd))
fdi_bcd_values['BCD_end'] = '</match>' fdi_bcd_values['BCD_end'] = '</match>'
fdi += cls.FDI_TEMPLATE % fdi_bcd_values fdi += cls.FDI_TEMPLATE % fdi_bcd_values
return fdi return fdi
def set_progress_reporter(self, report_progress): def set_progress_reporter(self, report_progress):
self.report_progress = report_progress self.report_progress = report_progress
def card_prefix(self, end_session=True): def card_prefix(self, end_session=True):
return self._card_prefix return self._card_prefix
@ -117,7 +117,7 @@ class Device(_Device):
else: raise else: raise
mult = sectors_per_cluster * bytes_per_sector mult = sectors_per_cluster * bytes_per_sector
return total_clusters * mult, free_clusters * mult return total_clusters * mult, free_clusters * mult
def total_space(self, end_session=True): def total_space(self, end_session=True):
msz = csz = 0 msz = csz = 0
print self._main_prefix print self._main_prefix
@ -131,9 +131,9 @@ class Device(_Device):
else: else:
msz = self._windows_space(self._main_prefix)[0] msz = self._windows_space(self._main_prefix)[0]
csz = self._windows_space(self._card_prefix)[0] csz = self._windows_space(self._card_prefix)[0]
return (msz, 0, csz) return (msz, 0, csz)
def free_space(self, end_session=True): def free_space(self, end_session=True):
msz = csz = 0 msz = csz = 0
if not iswindows: if not iswindows:
@ -146,15 +146,15 @@ class Device(_Device):
else: else:
msz = self._windows_space(self._main_prefix)[1] msz = self._windows_space(self._main_prefix)[1]
csz = self._windows_space(self._card_prefix)[1] csz = self._windows_space(self._card_prefix)[1]
return (msz, 0, csz) return (msz, 0, csz)
def windows_match_device(self, pnp_id, device_id): def windows_match_device(self, pnp_id, device_id):
pnp_id = pnp_id.upper() pnp_id = pnp_id.upper()
if device_id and pnp_id is not None: if device_id and pnp_id is not None:
device_id = device_id.upper() device_id = device_id.upper()
if 'VEN_' + self.VENDOR_NAME in pnp_id and 'PROD_' + device_id in pnp_id: if 'VEN_' + self.VENDOR_NAME in pnp_id and 'PROD_' + device_id in pnp_id:
return True return True
@ -162,32 +162,32 @@ class Device(_Device):
def windows_get_drive_prefix(self, drive): def windows_get_drive_prefix(self, drive):
prefix = None prefix = None
try: try:
partition = drive.associators("Win32_DiskDriveToDiskPartition")[0] partition = drive.associators("Win32_DiskDriveToDiskPartition")[0]
logical_disk = partition.associators('Win32_LogicalDiskToPartition')[0] logical_disk = partition.associators('Win32_LogicalDiskToPartition')[0]
prefix = logical_disk.DeviceID + os.sep prefix = logical_disk.DeviceID + os.sep
except IndexError: except IndexError:
pass pass
return prefix return prefix
def open_windows(self): def open_windows(self):
drives = {} drives = {}
wmi = __import__('wmi', globals(), locals(), [], -1) wmi = __import__('wmi', globals(), locals(), [], -1)
c = wmi.WMI() c = wmi.WMI()
for drive in c.Win32_DiskDrive(): for drive in c.Win32_DiskDrive():
if self.windows_match_device(str(drive.PNPDeviceID), self.WINDOWS_MAIN_MEM): if self.windows_match_device(str(drive.PNPDeviceID), self.WINDOWS_MAIN_MEM):
drives['main'] = self.windows_get_drive_prefix(drive) drives['main'] = self.windows_get_drive_prefix(drive)
elif self.windows_match_device(str(drive.PNPDeviceID), self.WINDOWS_CARD_MEM): elif self.windows_match_device(str(drive.PNPDeviceID), self.WINDOWS_CARD_MEM):
drives['card'] = self.windows_get_drive_prefix(drive) drives['card'] = self.windows_get_drive_prefix(drive)
if 'main' and 'card' in drives.keys(): if 'main' and 'card' in drives.keys():
break break
if not drives: if not drives:
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.') % self.__class__.__name__) raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.') % self.__class__.__name__)
self._main_prefix = drives.get('main', None) self._main_prefix = drives.get('main', None)
self._card_prefix = drives.get('card', None) self._card_prefix = drives.get('card', None)
@ -197,11 +197,11 @@ class Device(_Device):
ioreg = '/usr/sbin/ioreg' ioreg = '/usr/sbin/ioreg'
if not os.access(ioreg, os.X_OK): if not os.access(ioreg, os.X_OK):
ioreg = 'ioreg' ioreg = 'ioreg'
raw = subprocess.Popen((ioreg+' -w 0 -S -c IOMedia').split(), raw = subprocess.Popen((ioreg+' -w 0 -S -c IOMedia').split(),
stdout=subprocess.PIPE).stdout.read() stdout=subprocess.PIPE).stdout.read()
lines = raw.splitlines() lines = raw.splitlines()
names = {} names = {}
def get_dev_node(lines, loc): def get_dev_node(lines, loc):
for line in lines: for line in lines:
line = line.strip() line = line.strip()
@ -211,7 +211,7 @@ class Device(_Device):
if match is not None: if match is not None:
names[loc] = match.group(1) names[loc] = match.group(1)
break break
for i, line in enumerate(lines): for i, line in enumerate(lines):
if self.OSX_MAIN_MEM is not None and line.strip().endswith('<class IOMedia>') and self.OSX_MAIN_MEM in line: if self.OSX_MAIN_MEM is not None and line.strip().endswith('<class IOMedia>') and self.OSX_MAIN_MEM in line:
get_dev_node(lines[i+1:], 'main') get_dev_node(lines[i+1:], 'main')
@ -220,7 +220,7 @@ class Device(_Device):
if len(names.keys()) == 2: if len(names.keys()) == 2:
break break
return names return names
def open_osx(self): def open_osx(self):
mount = subprocess.Popen('mount', shell=True, stdout=subprocess.PIPE).stdout.read() mount = subprocess.Popen('mount', shell=True, stdout=subprocess.PIPE).stdout.read()
names = self.get_osx_mountpoints() names = self.get_osx_mountpoints()
@ -233,12 +233,12 @@ class Device(_Device):
if card_pat is not None: if card_pat is not None:
card_pat = dev_pat%card_pat card_pat = dev_pat%card_pat
self._card_prefix = re.search(card_pat, mount).group(2) + os.sep self._card_prefix = re.search(card_pat, mount).group(2) + os.sep
def open_linux(self): def open_linux(self):
import dbus import dbus
bus = dbus.SystemBus() bus = dbus.SystemBus()
hm = dbus.Interface(bus.get_object("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager"), "org.freedesktop.Hal.Manager") hm = dbus.Interface(bus.get_object("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager"), "org.freedesktop.Hal.Manager")
def conditional_mount(dev): def conditional_mount(dev):
mmo = bus.get_object("org.freedesktop.Hal", dev) mmo = bus.get_object("org.freedesktop.Hal", dev)
label = mmo.GetPropertyString('volume.label', dbus_interface='org.freedesktop.Hal.Device') label = mmo.GetPropertyString('volume.label', dbus_interface='org.freedesktop.Hal.Device')
@ -247,10 +247,10 @@ class Device(_Device):
fstype = mmo.GetPropertyString('volume.fstype', dbus_interface='org.freedesktop.Hal.Device') fstype = mmo.GetPropertyString('volume.fstype', dbus_interface='org.freedesktop.Hal.Device')
if is_mounted: if is_mounted:
return str(mount_point) return str(mount_point)
mmo.Mount(label, fstype, ['umask=077', 'uid='+str(os.getuid()), 'sync'], mmo.Mount(label, fstype, ['umask=077', 'uid='+str(os.getuid()), 'sync'],
dbus_interface='org.freedesktop.Hal.Device.Volume') dbus_interface='org.freedesktop.Hal.Device.Volume')
return os.path.normpath('/media/'+label)+'/' return os.path.normpath('/media/'+label)+'/'
mm = hm.FindDeviceStringMatch(__appname__+'.mainvolume', self.__class__.__name__) mm = hm.FindDeviceStringMatch(__appname__+'.mainvolume', self.__class__.__name__)
if not mm: if not mm:
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.')%(self.__class__.__name__,)) raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.')%(self.__class__.__name__,))
@ -261,13 +261,13 @@ class Device(_Device):
break break
except dbus.exceptions.DBusException: except dbus.exceptions.DBusException:
continue continue
if not self._main_prefix: if not self._main_prefix:
raise DeviceError('Could not open device for reading. Try a reboot.') raise DeviceError('Could not open device for reading. Try a reboot.')
self._card_prefix = None self._card_prefix = None
cards = hm.FindDeviceStringMatch(__appname__+'.cardvolume', self.__class__.__name__) cards = hm.FindDeviceStringMatch(__appname__+'.cardvolume', self.__class__.__name__)
for dev in cards: for dev in cards:
try: try:
self._card_prefix = conditional_mount(dev)+os.sep self._card_prefix = conditional_mount(dev)+os.sep

View File

@ -27,7 +27,6 @@ class File(object):
self.path = path self.path = path
self.name = os.path.basename(path) self.name = os.path.basename(path)
class USBMS(Device): class USBMS(Device):
FORMATS = [] FORMATS = []
EBOOK_DIR_MAIN = '' EBOOK_DIR_MAIN = ''
@ -36,38 +35,38 @@ class USBMS(Device):
def __init__(self, key='-1', log_packets=False, report_progress=None): def __init__(self, key='-1', log_packets=False, report_progress=None):
pass pass
def get_device_information(self, end_session=True): def get_device_information(self, end_session=True):
""" """
Ask device for device information. See L{DeviceInfoQuery}. Ask device for device information. See L{DeviceInfoQuery}.
@return: (device name, device version, software version on device, mime type) @return: (device name, device version, software version on device, mime type)
""" """
return (self.__class__.__name__, '', '', '') return (self.__class__.__name__, '', '', '')
def books(self, oncard=False, end_session=True): def books(self, oncard=False, end_session=True):
bl = BookList() bl = BookList()
if oncard and self._card_prefix is None: if oncard and self._card_prefix is None:
return bl return bl
prefix = self._card_prefix if oncard else self._main_prefix prefix = self._card_prefix if oncard else self._main_prefix
ebook_dir = self.EBOOK_DIR_CARD if oncard else self.EBOOK_DIR_MAIN ebook_dir = self.EBOOK_DIR_CARD if oncard else self.EBOOK_DIR_MAIN
# Get all books in all directories under the root ebook_dir directory # Get all books in all directories under the root ebook_dir directory
for path, dirs, files in os.walk(os.path.join(prefix, ebook_dir)): for path, dirs, files in os.walk(os.path.join(prefix, ebook_dir)):
# Filter out anything that isn't in the list of supported ebook types # Filter out anything that isn't in the list of supported ebook types
for book_type in self.FORMATS: for book_type in self.FORMATS:
for filename in fnmatch.filter(files, '*.%s' % (book_type)): for filename in fnmatch.filter(files, '*.%s' % (book_type)):
title, author, mime = self.__class__.extract_book_metadata_by_filename(filename) title, author, mime = self.__class__.extract_book_metadata_by_filename(filename)
bl.append(Book(os.path.join(path, filename), title, author, mime)) bl.append(Book(os.path.join(path, filename), title, author, mime))
return bl return bl
def upload_books(self, files, names, on_card=False, end_session=True, def upload_books(self, files, names, on_card=False, end_session=True,
metadata=None): metadata=None):
if on_card and not self._card_prefix: if on_card and not self._card_prefix:
raise ValueError(_('The reader has no storage card connected.')) raise ValueError(_('The reader has no storage card connected.'))
if not on_card: if not on_card:
path = os.path.join(self._main_prefix, self.EBOOK_DIR_MAIN) path = os.path.join(self._main_prefix, self.EBOOK_DIR_MAIN)
else: else:
@ -84,21 +83,21 @@ class USBMS(Device):
sizes = map(get_size, files) sizes = map(get_size, files)
size = sum(sizes) size = sum(sizes)
if on_card and size > self.free_space()[2] - 1024*1024: if on_card and size > self.free_space()[2] - 1024*1024:
raise FreeSpaceError(_("There is insufficient free space on the storage card")) raise FreeSpaceError(_("There is insufficient free space on the storage card"))
if not on_card and size > self.free_space()[0] - 2*1024*1024: if not on_card and size > self.free_space()[0] - 2*1024*1024:
raise FreeSpaceError(_("There is insufficient free space in main memory")) raise FreeSpaceError(_("There is insufficient free space in main memory"))
paths = [] paths = []
names = iter(names) names = iter(names)
metadata = iter(metadata) metadata = iter(metadata)
for infile in files: for infile in files:
newpath = path newpath = path
if self.SUPPORTS_SUB_DIRS: if self.SUPPORTS_SUB_DIRS:
mdata = metadata.next() mdata = metadata.next()
if 'tags' in mdata.keys(): if 'tags' in mdata.keys():
for tag in mdata['tags']: for tag in mdata['tags']:
if tag.startswith('/'): if tag.startswith('/'):
@ -108,32 +107,32 @@ class USBMS(Device):
if not os.path.exists(newpath): if not os.path.exists(newpath):
os.makedirs(newpath) os.makedirs(newpath)
filepath = os.path.join(newpath, names.next()) filepath = os.path.join(newpath, names.next())
paths.append(filepath) paths.append(filepath)
if hasattr(infile, 'read'): if hasattr(infile, 'read'):
infile.seek(0) infile.seek(0)
dest = open(filepath, 'wb') dest = open(filepath, 'wb')
shutil.copyfileobj(infile, dest, 10*1024*1024) shutil.copyfileobj(infile, dest, 10*1024*1024)
dest.flush() dest.flush()
dest.close() dest.close()
else: else:
shutil.copy2(infile, filepath) shutil.copy2(infile, filepath)
return zip(paths, cycle([on_card])) return zip(paths, cycle([on_card]))
@classmethod @classmethod
def add_books_to_metadata(cls, locations, metadata, booklists): def add_books_to_metadata(cls, locations, metadata, booklists):
for location in locations: for location in locations:
path = location[0] path = location[0]
on_card = 1 if location[1] else 0 on_card = 1 if location[1] else 0
title, author, mime = cls.extract_book_metadata_by_filename(os.path.basename(path)) title, author, mime = cls.extract_book_metadata_by_filename(os.path.basename(path))
booklists[on_card].append(Book(path, title, author, mime)) booklists[on_card].append(Book(path, title, author, mime))
def delete_books(self, paths, end_session=True): def delete_books(self, paths, end_session=True):
for path in paths: for path in paths:
if os.path.exists(path): if os.path.exists(path):
@ -144,7 +143,7 @@ class USBMS(Device):
os.removedirs(os.path.dirname(path)) os.removedirs(os.path.dirname(path))
except: except:
pass pass
@classmethod @classmethod
def remove_books_from_metadata(cls, paths, booklists): def remove_books_from_metadata(cls, paths, booklists):
for path in paths: for path in paths:
@ -152,14 +151,14 @@ class USBMS(Device):
for book in bl: for book in bl:
if path.endswith(book.path): if path.endswith(book.path):
bl.remove(book) bl.remove(book)
def sync_booklists(self, booklists, end_session=True): def sync_booklists(self, booklists, end_session=True):
# There is no meta data on the device to update. The device is treated # There is no meta data on the device to update. The device is treated
# as a mass storage device and does not use a meta data xml file like # as a mass storage device and does not use a meta data xml file like
# the Sony Readers. # the Sony Readers.
pass pass
def get_file(self, path, outfile, end_session=True): def get_file(self, path, outfile, end_session=True):
path = self.munge_path(path) path = self.munge_path(path)
src = open(path, 'rb') src = open(path, 'rb')
shutil.copyfileobj(src, outfile, 10*1024*1024) shutil.copyfileobj(src, outfile, 10*1024*1024)
@ -229,7 +228,7 @@ class USBMS(Device):
# the filename without the extension # the filename without the extension
else: else:
book_title = os.path.splitext(filename)[0].replace('_', ' ') book_title = os.path.splitext(filename)[0].replace('_', ' ')
fileext = os.path.splitext(filename)[1][1:] fileext = os.path.splitext(filename)[1][1:]
if fileext in cls.FORMATS: if fileext in cls.FORMATS: