mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
2 card support move SONY drivers to USBMS infrastructure.
This commit is contained in:
commit
b14f2be686
@ -165,6 +165,7 @@ class TXTMetadataReader(MetadataReaderPlugin):
|
||||
name = 'Read TXT metadata'
|
||||
file_types = set(['txt'])
|
||||
description = _('Read metadata from %s files') % 'TXT'
|
||||
author = 'John Schember'
|
||||
|
||||
def get_metadata(self, stream, ftype):
|
||||
from calibre.ebooks.metadata.txt import get_metadata
|
||||
|
@ -8,7 +8,7 @@ import os, shutil
|
||||
from itertools import cycle
|
||||
|
||||
from calibre.ebooks.metadata import authors_to_string
|
||||
from calibre.devices.errors import FreeSpaceError
|
||||
from calibre.devices.errors import DeviceError, FreeSpaceError
|
||||
from calibre.devices.usbms.driver import USBMS
|
||||
import calibre.devices.cybookg3.t2b as t2b
|
||||
|
||||
@ -23,28 +23,34 @@ class CYBOOKG3(USBMS):
|
||||
|
||||
VENDOR_NAME = 'BOOKEEN'
|
||||
WINDOWS_MAIN_MEM = 'CYBOOK_GEN3__-FD'
|
||||
WINDOWS_CARD_MEM = 'CYBOOK_GEN3__-SD'
|
||||
WINDOWS_CARD_A_MEM = 'CYBOOK_GEN3__-SD'
|
||||
|
||||
OSX_MAIN_MEM = 'Bookeen Cybook Gen3 -FD Media'
|
||||
OSX_CARD_MEM = 'Bookeen Cybook Gen3 -SD Media'
|
||||
OSX_CARD_A_MEM = 'Bookeen Cybook Gen3 -SD Media'
|
||||
|
||||
MAIN_MEMORY_VOLUME_LABEL = 'Cybook Gen 3 Main Memory'
|
||||
STORAGE_CARD_VOLUME_LABEL = 'Cybook Gen 3 Storage Card'
|
||||
|
||||
EBOOK_DIR_MAIN = "eBooks"
|
||||
EBOOK_DIR_CARD = "eBooks"
|
||||
EBOOK_DIR_CARD_A = "eBooks"
|
||||
THUMBNAIL_HEIGHT = 144
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
def upload_books(self, files, names, on_card=False, end_session=True,
|
||||
def upload_books(self, files, names, on_card=None, end_session=True,
|
||||
metadata=None):
|
||||
if on_card and not self._card_prefix:
|
||||
raise ValueError(_('The reader has no storage card connected.'))
|
||||
if on_card == 'carda' and not self._card_a_prefix:
|
||||
raise ValueError(_('The reader has no storage card in this slot.'))
|
||||
elif on_card == 'cardb' and not self._card_b_prefix:
|
||||
raise ValueError(_('The reader has no storage card in this slot.'))
|
||||
elif on_card and on_card not in ('carda', 'cardb'):
|
||||
raise DeviceError(_('The reader has no storage card in this slot.'))
|
||||
|
||||
if not on_card:
|
||||
path = os.path.join(self._main_prefix, self.EBOOK_DIR_MAIN)
|
||||
if on_card == 'carda':
|
||||
path = os.path.join(self._card_a_prefix, self.EBOOK_DIR_CARD_A)
|
||||
if on_card == 'cardb':
|
||||
path = os.path.join(self._card_b_prefix, self.EBOOK_DIR_CARD_B)
|
||||
else:
|
||||
path = os.path.join(self._card_prefix, self.EBOOK_DIR_CARD)
|
||||
path = os.path.join(self._main_prefix, self.EBOOK_DIR_MAIN)
|
||||
|
||||
def get_size(obj):
|
||||
if hasattr(obj, 'seek'):
|
||||
@ -57,10 +63,12 @@ class CYBOOKG3(USBMS):
|
||||
sizes = [get_size(f) for f in files]
|
||||
size = sum(sizes)
|
||||
|
||||
if on_card and size > self.free_space()[2] - 1024*1024:
|
||||
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
|
||||
if not on_card and size > self.free_space()[0] - 2*1024*1024:
|
||||
raise FreeSpaceError(_("There is insufficient free space in main memory"))
|
||||
if on_card == 'carda' and size > self.free_space()[1] - 1024*1024:
|
||||
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
|
||||
if on_card == 'cardb' and size > self.free_space()[2] - 1024*1024:
|
||||
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
|
||||
|
||||
paths = []
|
||||
names = iter(names)
|
||||
|
10
src/calibre/devices/eb600/driver.py
Executable file → Normal file
10
src/calibre/devices/eb600/driver.py
Executable file → Normal file
@ -17,24 +17,24 @@ class EB600(USBMS):
|
||||
|
||||
VENDOR_NAME = 'NETRONIX'
|
||||
WINDOWS_MAIN_MEM = 'EBOOK'
|
||||
WINDOWS_CARD_MEM = 'EBOOK'
|
||||
WINDOWS_CARD_A_MEM = 'EBOOK'
|
||||
|
||||
OSX_MAIN_MEM = 'EB600 Internal Storage Media'
|
||||
OSX_CARD_MEM = 'EB600 Card Storage Media'
|
||||
OSX_CARD_A_MEM = 'EB600 Card Storage Media'
|
||||
|
||||
MAIN_MEMORY_VOLUME_LABEL = 'EB600 Main Memory'
|
||||
STORAGE_CARD_VOLUME_LABEL = 'EB600 Storage Card'
|
||||
|
||||
EBOOK_DIR_MAIN = ''
|
||||
EBOOK_DIR_CARD = ''
|
||||
EBOOK_DIR_CARD_A = ''
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
def windows_sort_drives(self, drives):
|
||||
main = drives['main']
|
||||
card = drives['card']
|
||||
card = drives['carda']
|
||||
if card and main and card < main:
|
||||
drives['main'] = card
|
||||
drives['card'] = main
|
||||
drives['carda'] = main
|
||||
|
||||
return drives
|
||||
|
||||
|
@ -87,7 +87,13 @@ class Device(object):
|
||||
|
||||
def card_prefix(self, end_session=True):
|
||||
'''
|
||||
Return prefix to paths on the card or '' if no cards present.
|
||||
Return a 2 element list of the prefix to paths on the cards.
|
||||
If no card is present None is set for the card's prefix.
|
||||
E.G.
|
||||
('/place', '/place2')
|
||||
(None, 'place2')
|
||||
('place', None)
|
||||
(None, None)
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
@ -95,8 +101,8 @@ class Device(object):
|
||||
"""
|
||||
Get total space available on the mountpoints:
|
||||
1. Main memory
|
||||
2. Memory Stick
|
||||
3. SD Card
|
||||
2. Memory Card A
|
||||
3. Memory Card B
|
||||
|
||||
@return: A 3 element list with total space in bytes of (1, 2, 3). If a
|
||||
particular device doesn't have any of these locations it should return 0.
|
||||
@ -115,24 +121,25 @@ class Device(object):
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def books(self, oncard=False, end_session=True):
|
||||
def books(self, oncard=None, end_session=True):
|
||||
"""
|
||||
Return a list of ebooks on the device.
|
||||
@param oncard: If True return a list of ebooks on the storage card,
|
||||
otherwise return list of ebooks in main memory of device.
|
||||
If True and no books on card return empty list.
|
||||
@param oncard: If 'carda' or 'cardb' return a list of ebooks on the
|
||||
specific storage card, otherwise return list of ebooks
|
||||
in main memory of device. If a card is specified and no
|
||||
books are on the card return empty list.
|
||||
@return: A BookList.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def upload_books(self, files, names, on_card=False, end_session=True,
|
||||
def upload_books(self, files, names, on_card=None, end_session=True,
|
||||
metadata=None):
|
||||
'''
|
||||
Upload a list of books to the device. If a file already
|
||||
exists on the device, it should be replaced.
|
||||
This method should raise a L{FreeSpaceError} if there is not enough
|
||||
free space on the device. The text of the FreeSpaceError must contain the
|
||||
word "card" if C{on_card} is True otherwise it must contain the word "memory".
|
||||
word "card" if C{on_card} is not None otherwise it must contain the word "memory".
|
||||
@param files: A list of paths and/or file-like objects.
|
||||
@param names: A list of file names that the books should have
|
||||
once uploaded to the device. len(names) == len(files)
|
||||
@ -163,7 +170,8 @@ class Device(object):
|
||||
another dictionary that maps tag names to lists of book ids. The ids are
|
||||
ids from the book database.
|
||||
@param booklists: A tuple containing the result of calls to
|
||||
(L{books}(oncard=False), L{books}(oncard=True)).
|
||||
(L{books}(oncard=None), L{books}(oncard='carda'),
|
||||
L{books}(oncard='cardb')).
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@ -179,16 +187,18 @@ class Device(object):
|
||||
Remove books from the metadata list. This function must not communicate
|
||||
with the device.
|
||||
@param paths: paths to books on the device.
|
||||
@param booklists: A tuple containing the result of calls to
|
||||
(L{books}(oncard=False), L{books}(oncard=True)).
|
||||
@param booklists: A tuple containing the result of calls to
|
||||
(L{books}(oncard=None), L{books}(oncard='carda'),
|
||||
L{books}(oncard='cardb')).
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
def sync_booklists(self, booklists, end_session=True):
|
||||
'''
|
||||
Update metadata on device.
|
||||
@param booklists: A tuple containing the result of calls to
|
||||
(L{books}(oncard=False), L{books}(oncard=True)).
|
||||
@param booklists: A tuple containing the result of calls to
|
||||
(L{books}(oncard=None), L{books}(oncard='carda'),
|
||||
L{books}(oncard='cardb')).
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
|
6
src/calibre/devices/kindle/driver.py
Executable file → Normal file
6
src/calibre/devices/kindle/driver.py
Executable file → Normal file
@ -18,16 +18,16 @@ class KINDLE(USBMS):
|
||||
|
||||
VENDOR_NAME = 'KINDLE'
|
||||
WINDOWS_MAIN_MEM = 'INTERNAL_STORAGE'
|
||||
WINDOWS_CARD_MEM = 'CARD_STORAGE'
|
||||
WINDOWS_CARD_A_MEM = 'CARD_STORAGE'
|
||||
|
||||
OSX_MAIN_MEM = 'Kindle Internal Storage Media'
|
||||
OSX_CARD_MEM = 'Kindle Card Storage Media'
|
||||
OSX_CARD_A_MEM = 'Kindle Card Storage Media'
|
||||
|
||||
MAIN_MEMORY_VOLUME_LABEL = 'Kindle Main Memory'
|
||||
STORAGE_CARD_VOLUME_LABEL = 'Kindle Storage Card'
|
||||
|
||||
EBOOK_DIR_MAIN = "documents"
|
||||
EBOOK_DIR_CARD = "documents"
|
||||
EBOOK_DIR_CARD_A = "documents"
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
WIRELESS_FILE_NAME_PATTERN = re.compile(
|
||||
|
0
src/calibre/devices/prs500/driver.py
Executable file → Normal file
0
src/calibre/devices/prs500/driver.py
Executable file → Normal file
@ -380,14 +380,16 @@ class BookList(_BookList):
|
||||
item.setAttribute('id', str(map[id]))
|
||||
pl.appendChild(item)
|
||||
|
||||
def fix_ids(main, card):
|
||||
def fix_ids(main, carda, cardb):
|
||||
'''
|
||||
Adjust ids the XML databases.
|
||||
'''
|
||||
if hasattr(main, 'purge_empty_playlists'):
|
||||
main.purge_empty_playlists()
|
||||
if hasattr(card, 'purge_empty_playlists'):
|
||||
card.purge_empty_playlists()
|
||||
if hasattr(carda, 'purge_empty_playlists'):
|
||||
carda.purge_empty_playlists()
|
||||
if hasattr(cardb, 'purge_empty_playlists'):
|
||||
cardb.purge_empty_playlists()
|
||||
|
||||
def regen_ids(db):
|
||||
if not hasattr(db, 'root_element'):
|
||||
@ -413,6 +415,7 @@ def fix_ids(main, card):
|
||||
db.reorder_playlists()
|
||||
|
||||
regen_ids(main)
|
||||
regen_ids(card)
|
||||
regen_ids(carda)
|
||||
regen_ids(cardb)
|
||||
|
||||
main.set_next_id(str(main.max_id()+1))
|
||||
main.set_next_id(str(main.max_id()+1))
|
||||
|
@ -6,250 +6,43 @@ Device driver for the SONY PRS-505
|
||||
import sys, os, shutil, time, subprocess, re
|
||||
from itertools import cycle
|
||||
|
||||
from calibre.devices.interface import Device
|
||||
from calibre.devices.usbms.cli import CLI
|
||||
from calibre.devices.usbms.device import Device
|
||||
from calibre.devices.errors import DeviceError, FreeSpaceError
|
||||
from calibre.devices.prs505.books import BookList, fix_ids
|
||||
from calibre import iswindows, islinux, isosx, __appname__
|
||||
from calibre.devices.errors import PathError
|
||||
|
||||
class File(object):
|
||||
def __init__(self, path):
|
||||
stats = os.stat(path)
|
||||
self.is_dir = os.path.isdir(path)
|
||||
self.is_readonly = not os.access(path, os.W_OK)
|
||||
self.ctime = stats.st_ctime
|
||||
self.wtime = stats.st_mtime
|
||||
self.size = stats.st_size
|
||||
if path.endswith(os.sep):
|
||||
path = path[:-1]
|
||||
self.path = path
|
||||
self.name = os.path.basename(path)
|
||||
|
||||
|
||||
class PRS505(Device):
|
||||
VENDOR_ID = 0x054c #: SONY Vendor Id
|
||||
PRODUCT_ID = 0x031e #: Product Id for the PRS-505
|
||||
BCD = [0x229] #: Needed to disambiguate 505 and 700 on linux
|
||||
PRODUCT_NAME = 'PRS-505'
|
||||
VENDOR_NAME = 'SONY'
|
||||
class PRS505(CLI, Device):
|
||||
FORMATS = ['epub', 'lrf', 'lrx', 'rtf', 'pdf', 'txt']
|
||||
|
||||
VENDOR_ID = [0x054c] #: SONY Vendor Id
|
||||
PRODUCT_ID = [0x031e] #: Product Id for the PRS-505
|
||||
BCD = [0x229] #: Needed to disambiguate 505 and 700 on linux
|
||||
|
||||
MEDIA_XML = 'database/cache/media.xml'
|
||||
CACHE_XML = 'Sony Reader/database/cache.xml'
|
||||
VENDOR_NAME = 'SONY'
|
||||
WINDOWS_MAIN_MEM = 'PRS-505'
|
||||
WINDOWS_CARD_A_MEM = 'PRS-505/UC:MS'
|
||||
WINDOWS_CARD_B_MEM = 'PRS-505/UC:SD'
|
||||
|
||||
OSX_MAIN_MEM = 'Sony PRS-505/UC Media'
|
||||
OSX_CARD_A_MEM = 'Sony PRS-505/UC:MS Media'
|
||||
OSX_CARD_B_MEM = 'Sony PRS-505/UC:SD'
|
||||
|
||||
MAIN_MEMORY_VOLUME_LABEL = 'Sony Reader Main Memory'
|
||||
STORAGE_CARD_VOLUME_LABEL = 'Sony Reader Storage Card'
|
||||
|
||||
OSX_NAME = 'Sony PRS-505'
|
||||
MEDIA_XML = 'database/cache/media.xml'
|
||||
CACHE_XML = 'Sony Reader/database/cache.xml'
|
||||
|
||||
CARD_PATH_PREFIX = __appname__
|
||||
|
||||
FDI_TEMPLATE = \
|
||||
'''
|
||||
<device>
|
||||
<match key="info.category" string="volume">
|
||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
|
||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
|
||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.device_revision_bcd" int="%(bcd)s">
|
||||
<match key="volume.is_partition" bool="false">
|
||||
<merge key="volume.label" type="string">%(main_memory)s</merge>
|
||||
<merge key="%(app)s.mainvolume" type="string">%(deviceclass)s</merge>
|
||||
</match>
|
||||
</match>
|
||||
</match>
|
||||
</match>
|
||||
</match>
|
||||
</device>
|
||||
<device>
|
||||
<match key="info.category" string="volume">
|
||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
|
||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
|
||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.device_revision_bcd" int="%(bcd)s">
|
||||
<match key="volume.is_partition" bool="true">
|
||||
<merge key="volume.label" type="string">%(storage_card)s</merge>
|
||||
<merge key="%(app)s.cardvolume" type="string">%(deviceclass)s</merge>
|
||||
</match>
|
||||
</match>
|
||||
</match>
|
||||
</match>
|
||||
</match>
|
||||
</device>
|
||||
'''.replace('%(app)s', __appname__)
|
||||
|
||||
|
||||
def __init__(self, log_packets=False):
|
||||
self._main_prefix = self._card_prefix = None
|
||||
|
||||
@classmethod
|
||||
def get_fdi(cls):
|
||||
return cls.FDI_TEMPLATE%dict(
|
||||
deviceclass=cls.__name__,
|
||||
vendor_id=hex(cls.VENDOR_ID),
|
||||
product_id=hex(cls.PRODUCT_ID),
|
||||
bcd=hex(cls.BCD[0]),
|
||||
main_memory=cls.MAIN_MEMORY_VOLUME_LABEL,
|
||||
storage_card=cls.STORAGE_CARD_VOLUME_LABEL,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def is_device(cls, device_id):
|
||||
device_id = device_id.upper()
|
||||
if 'VEN_'+cls.VENDOR_NAME in device_id and \
|
||||
'PROD_'+cls.PRODUCT_NAME in device_id:
|
||||
return True
|
||||
vid, pid = hex(cls.VENDOR_ID)[2:], hex(cls.PRODUCT_ID)[2:]
|
||||
if len(vid) < 4: vid = '0'+vid
|
||||
if len(pid) < 4: pid = '0'+pid
|
||||
if 'VID_'+vid in device_id and \
|
||||
'PID_'+pid in device_id:
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def get_osx_mountpoints(cls, raw=None):
|
||||
if raw is None:
|
||||
ioreg = '/usr/sbin/ioreg'
|
||||
if not os.access(ioreg, os.X_OK):
|
||||
ioreg = 'ioreg'
|
||||
raw = subprocess.Popen((ioreg+' -w 0 -S -c IOMedia').split(),
|
||||
stdout=subprocess.PIPE).communicate()[0]
|
||||
lines = raw.splitlines()
|
||||
names = {}
|
||||
for i, line in enumerate(lines):
|
||||
if line.strip().endswith('<class IOMedia>') and cls.OSX_NAME in line:
|
||||
loc = 'stick' if ':MS' in line else 'card' if ':SD' in line else 'main'
|
||||
for line in lines[i+1:]:
|
||||
line = line.strip()
|
||||
if line.endswith('}'):
|
||||
break
|
||||
match = re.search(r'"BSD Name"\s+=\s+"(.*?)"', line)
|
||||
if match is not None:
|
||||
names[loc] = match.group(1)
|
||||
break
|
||||
if len(names.keys()) == 3:
|
||||
break
|
||||
return names
|
||||
|
||||
|
||||
def open_osx(self):
|
||||
mount = subprocess.Popen('mount', shell=True,
|
||||
stdout=subprocess.PIPE).stdout.read()
|
||||
names = self.get_osx_mountpoints()
|
||||
dev_pat = r'/dev/%s(\w*)\s+on\s+([^\(]+)\s+'
|
||||
if 'main' not in names.keys():
|
||||
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.')%self.__class__.__name__)
|
||||
main_pat = dev_pat%names['main']
|
||||
self._main_prefix = re.search(main_pat, mount).group(2) + os.sep
|
||||
card_pat = names['stick'] if 'stick' in names.keys() else names['card'] if 'card' in names.keys() else None
|
||||
if card_pat is not None:
|
||||
card_pat = dev_pat%card_pat
|
||||
self._card_prefix = re.search(card_pat, mount).group(2) + os.sep
|
||||
|
||||
|
||||
def open_windows(self):
|
||||
time.sleep(6)
|
||||
drives = []
|
||||
wmi = __import__('wmi', globals(), locals(), [], -1)
|
||||
c = wmi.WMI()
|
||||
for drive in c.Win32_DiskDrive():
|
||||
if self.__class__.is_device(str(drive.PNPDeviceID)):
|
||||
if drive.Partitions == 0:
|
||||
continue
|
||||
try:
|
||||
partition = drive.associators("Win32_DiskDriveToDiskPartition")[0]
|
||||
logical_disk = partition.associators('Win32_LogicalDiskToPartition')[0]
|
||||
prefix = logical_disk.DeviceID+os.sep
|
||||
drives.append((drive.Index, prefix))
|
||||
except IndexError:
|
||||
continue
|
||||
|
||||
|
||||
if not drives:
|
||||
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.')%self.__class__.__name__)
|
||||
|
||||
drives.sort(cmp=lambda a, b: cmp(a[0], b[0]))
|
||||
self._main_prefix = drives[0][1]
|
||||
if len(drives) > 1:
|
||||
self._card_prefix = drives[1][1]
|
||||
|
||||
|
||||
def open_linux(self):
|
||||
import dbus
|
||||
bus = dbus.SystemBus()
|
||||
hm = dbus.Interface(bus.get_object("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager"), "org.freedesktop.Hal.Manager")
|
||||
|
||||
def conditional_mount(dev, main_mem=True):
|
||||
mmo = bus.get_object("org.freedesktop.Hal", dev)
|
||||
label = mmo.GetPropertyString('volume.label', dbus_interface='org.freedesktop.Hal.Device')
|
||||
is_mounted = mmo.GetPropertyString('volume.is_mounted', dbus_interface='org.freedesktop.Hal.Device')
|
||||
mount_point = mmo.GetPropertyString('volume.mount_point', dbus_interface='org.freedesktop.Hal.Device')
|
||||
fstype = mmo.GetPropertyString('volume.fstype', dbus_interface='org.freedesktop.Hal.Device')
|
||||
if is_mounted:
|
||||
return str(mount_point)
|
||||
mmo.Mount(label, fstype, ['umask=077', 'uid='+str(os.getuid()), 'sync'],
|
||||
dbus_interface='org.freedesktop.Hal.Device.Volume')
|
||||
return os.path.normpath('/media/'+label)+'/'
|
||||
|
||||
|
||||
mm = hm.FindDeviceStringMatch(__appname__+'.mainvolume', self.__class__.__name__)
|
||||
if not mm:
|
||||
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.')%(self.__class__.__name__,))
|
||||
self._main_prefix = None
|
||||
for dev in mm:
|
||||
try:
|
||||
self._main_prefix = conditional_mount(dev)+os.sep
|
||||
break
|
||||
except dbus.exceptions.DBusException:
|
||||
continue
|
||||
|
||||
|
||||
if not self._main_prefix:
|
||||
raise DeviceError('Could not open device for reading. Try a reboot.')
|
||||
|
||||
self._card_prefix = None
|
||||
cards = hm.FindDeviceStringMatch(__appname__+'.cardvolume', self.__class__.__name__)
|
||||
keys = []
|
||||
for card in cards:
|
||||
keys.append(int('UC_SD' in bus.get_object("org.freedesktop.Hal", card).GetPropertyString('info.parent', dbus_interface='org.freedesktop.Hal.Device')))
|
||||
|
||||
cards = zip(cards, keys)
|
||||
cards.sort(cmp=lambda x, y: cmp(x[1], y[1]))
|
||||
cards = [i[0] for i in cards]
|
||||
|
||||
for dev in cards:
|
||||
try:
|
||||
self._card_prefix = conditional_mount(dev, False)+os.sep
|
||||
break
|
||||
except:
|
||||
import traceback
|
||||
print traceback
|
||||
continue
|
||||
|
||||
|
||||
def open(self):
|
||||
time.sleep(5)
|
||||
self._main_prefix = self._card_prefix = None
|
||||
if islinux:
|
||||
Device.open(self)
|
||||
|
||||
def write_cache(prefix):
|
||||
try:
|
||||
self.open_linux()
|
||||
except DeviceError:
|
||||
time.sleep(3)
|
||||
self.open_linux()
|
||||
if iswindows:
|
||||
try:
|
||||
self.open_windows()
|
||||
except DeviceError:
|
||||
time.sleep(3)
|
||||
self.open_windows()
|
||||
if isosx:
|
||||
try:
|
||||
self.open_osx()
|
||||
except DeviceError:
|
||||
time.sleep(3)
|
||||
self.open_osx()
|
||||
if self._card_prefix is not None:
|
||||
try:
|
||||
cachep = os.path.join(self._card_prefix, self.CACHE_XML)
|
||||
cachep = os.path.join(prefix, self.CACHE_XML)
|
||||
if not os.path.exists(cachep):
|
||||
os.makedirs(os.path.dirname(cachep), mode=0777)
|
||||
f = open(cachep, 'wb')
|
||||
@ -263,133 +56,47 @@ class PRS505(Device):
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
def set_progress_reporter(self, pr):
|
||||
self.report_progress = pr
|
||||
if self._card_a_prefix is not None:
|
||||
write_cache(self._card_a_prefix)
|
||||
if self._card_b_prefix is not None:
|
||||
write_cache(self._card_b_prefix)
|
||||
|
||||
def get_device_information(self, end_session=True):
|
||||
return (self.__class__.__name__, '', '', '')
|
||||
|
||||
def card_prefix(self, end_session=True):
|
||||
return self._card_prefix
|
||||
|
||||
@classmethod
|
||||
def _windows_space(cls, prefix):
|
||||
if prefix is None:
|
||||
return 0, 0
|
||||
win32file = __import__('win32file', globals(), locals(), [], -1)
|
||||
try:
|
||||
sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters = \
|
||||
win32file.GetDiskFreeSpace(prefix[:-1])
|
||||
except Exception, err:
|
||||
if getattr(err, 'args', [None])[0] == 21: # Disk not ready
|
||||
time.sleep(3)
|
||||
sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters = \
|
||||
win32file.GetDiskFreeSpace(prefix[:-1])
|
||||
else: raise
|
||||
mult = sectors_per_cluster * bytes_per_sector
|
||||
return total_clusters * mult, free_clusters * mult
|
||||
|
||||
def total_space(self, end_session=True):
|
||||
msz = csz = 0
|
||||
if not iswindows:
|
||||
if self._main_prefix is not None:
|
||||
stats = os.statvfs(self._main_prefix)
|
||||
msz = stats.f_frsize * (stats.f_blocks + stats.f_bavail - stats.f_bfree)
|
||||
if self._card_prefix is not None:
|
||||
stats = os.statvfs(self._card_prefix)
|
||||
csz = stats.f_frsize * (stats.f_blocks + stats.f_bavail - stats.f_bfree)
|
||||
else:
|
||||
msz = self._windows_space(self._main_prefix)[0]
|
||||
csz = self._windows_space(self._card_prefix)[0]
|
||||
|
||||
return (msz, 0, csz)
|
||||
|
||||
def free_space(self, end_session=True):
|
||||
msz = csz = 0
|
||||
if not iswindows:
|
||||
if self._main_prefix is not None:
|
||||
stats = os.statvfs(self._main_prefix)
|
||||
msz = stats.f_frsize * stats.f_bavail
|
||||
if self._card_prefix is not None:
|
||||
stats = os.statvfs(self._card_prefix)
|
||||
csz = stats.f_frsize * stats.f_bavail
|
||||
else:
|
||||
msz = self._windows_space(self._main_prefix)[1]
|
||||
csz = self._windows_space(self._card_prefix)[1]
|
||||
|
||||
return (msz, 0, csz)
|
||||
|
||||
def books(self, oncard=False, end_session=True):
|
||||
if oncard and self._card_prefix is None:
|
||||
def books(self, oncard=None, end_session=True):
|
||||
if oncard == 'carda' and not self._card_a_prefix:
|
||||
return []
|
||||
elif oncard == 'cardb' and not self._card_b_prefix:
|
||||
return []
|
||||
elif oncard and oncard != 'carda' and oncard != 'cardb':
|
||||
return []
|
||||
|
||||
db = self.__class__.CACHE_XML if oncard else self.__class__.MEDIA_XML
|
||||
prefix = self._card_prefix if oncard else self._main_prefix
|
||||
prefix = self._card_a_prefix if oncard == 'carda' else self._card_b_prefix if oncard == 'cardb' else self._main_prefix
|
||||
bl = BookList(open(prefix + db, 'rb'), prefix)
|
||||
paths = bl.purge_corrupted_files()
|
||||
for path in paths:
|
||||
path = os.path.join(self._card_prefix if oncard else self._main_prefix, path)
|
||||
path = os.path.join(prefix, path)
|
||||
if os.path.exists(path):
|
||||
os.unlink(path)
|
||||
return bl
|
||||
|
||||
def munge_path(self, path):
|
||||
if path.startswith('/') and not (path.startswith(self._main_prefix) or \
|
||||
(self._card_prefix and path.startswith(self._card_prefix))):
|
||||
path = self._main_prefix + path[1:]
|
||||
elif path.startswith('card:'):
|
||||
path = path.replace('card:', self._card_prefix[:-1])
|
||||
return path
|
||||
|
||||
def mkdir(self, path, end_session=True):
|
||||
""" Make directory """
|
||||
path = self.munge_path(path)
|
||||
os.mkdir(path)
|
||||
|
||||
def list(self, path, recurse=False, end_session=True, munge=True):
|
||||
if munge:
|
||||
path = self.munge_path(path)
|
||||
if os.path.isfile(path):
|
||||
return [(os.path.dirname(path), [File(path)])]
|
||||
entries = [File(os.path.join(path, f)) for f in os.listdir(path)]
|
||||
dirs = [(path, entries)]
|
||||
for _file in entries:
|
||||
if recurse and _file.is_dir:
|
||||
dirs[len(dirs):] = self.list(_file.path, recurse=True, munge=False)
|
||||
return dirs
|
||||
|
||||
def get_file(self, path, outfile, end_session=True):
|
||||
path = self.munge_path(path)
|
||||
src = open(path, 'rb')
|
||||
shutil.copyfileobj(src, outfile, 10*1024*1024)
|
||||
|
||||
def put_file(self, infile, path, replace_file=False, end_session=True):
|
||||
path = self.munge_path(path)
|
||||
if os.path.isdir(path):
|
||||
path = os.path.join(path, infile.name)
|
||||
if not replace_file and os.path.exists(path):
|
||||
raise PathError('File already exists: '+path)
|
||||
dest = open(path, 'wb')
|
||||
shutil.copyfileobj(infile, dest, 10*1024*1024)
|
||||
dest.flush()
|
||||
dest.close()
|
||||
|
||||
def rm(self, path, end_session=True):
|
||||
path = self.munge_path(path)
|
||||
os.unlink(path)
|
||||
|
||||
def touch(self, path, end_session=True):
|
||||
path = self.munge_path(path)
|
||||
if not os.path.exists(path):
|
||||
open(path, 'w').close()
|
||||
if not os.path.isdir(path):
|
||||
os.utime(path, None)
|
||||
|
||||
def upload_books(self, files, names, on_card=False, end_session=True,
|
||||
def upload_books(self, files, names, on_card=None, end_session=True,
|
||||
metadata=None):
|
||||
if on_card and not self._card_prefix:
|
||||
raise ValueError(_('The reader has no storage card connected.'))
|
||||
path = os.path.join(self._card_prefix, self.CARD_PATH_PREFIX) if on_card \
|
||||
else os.path.join(self._main_prefix, 'database', 'media', 'books')
|
||||
if on_card == 'carda' and not self._card_a_prefix:
|
||||
raise ValueError(_('The reader has no storage card in this slot.'))
|
||||
elif on_card == 'cardb' and not self._card_b_prefix:
|
||||
raise ValueError(_('The reader has no storage card in this slot.'))
|
||||
elif on_card and on_card not in ('carda', 'cardb'):
|
||||
raise DeviceError(_('The reader has no storage card in this slot.'))
|
||||
|
||||
if on_card == 'carda':
|
||||
path = os.path.join(self._card_a_prefix, self.CARD_PATH_PREFIX)
|
||||
elif on_card == 'cardb':
|
||||
path = os.path.join(self._card_b_prefix, self.CARD_PATH_PREFIX)
|
||||
else:
|
||||
path = os.path.join(self._main_prefix, 'database', 'media', 'books')
|
||||
|
||||
def get_size(obj):
|
||||
if hasattr(obj, 'seek'):
|
||||
@ -399,17 +106,15 @@ class PRS505(Device):
|
||||
return size
|
||||
return os.path.getsize(obj)
|
||||
|
||||
sizes = map(get_size, files)
|
||||
sizes = [get_size(f) for f in files]
|
||||
size = sum(sizes)
|
||||
space = self.free_space()
|
||||
mspace = space[0]
|
||||
cspace = space[2]
|
||||
if on_card and size > cspace - 1024*1024:
|
||||
raise FreeSpaceError("There is insufficient free space "+\
|
||||
"on the storage card")
|
||||
if not on_card and size > mspace - 2*1024*1024:
|
||||
raise FreeSpaceError("There is insufficient free space " +\
|
||||
"in main memory")
|
||||
|
||||
if not on_card and size > self.free_space()[0] - 2*1024*1024:
|
||||
raise FreeSpaceError(_("There is insufficient free space in main memory"))
|
||||
if on_card == 'carda' and size > self.free_space()[1] - 1024*1024:
|
||||
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
|
||||
if on_card == 'cardb' and size > self.free_space()[2] - 1024*1024:
|
||||
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
|
||||
|
||||
paths, ctimes = [], []
|
||||
|
||||
@ -435,11 +140,11 @@ class PRS505(Device):
|
||||
for location in locations:
|
||||
info = metadata.next()
|
||||
path = location[0]
|
||||
on_card = 1 if location[3] else 0
|
||||
blist = 2 if location[3] == 'cardb' else 1 if location[3] == 'carda' else 0
|
||||
name = path.rpartition(os.sep)[2]
|
||||
name = (cls.CARD_PATH_PREFIX+'/' if on_card else 'database/media/books/') + name
|
||||
name = (cls.CARD_PATH_PREFIX+'/' if blist else 'database/media/books/') + name
|
||||
name = name.replace('//', '/')
|
||||
booklists[on_card].add_book(info, name, *location[1:-1])
|
||||
booklists[blist].add_book(info, name, *location[1:-1])
|
||||
fix_ids(*booklists)
|
||||
|
||||
def delete_books(self, paths, end_session=True):
|
||||
@ -462,18 +167,13 @@ class PRS505(Device):
|
||||
f = open(self._main_prefix + self.__class__.MEDIA_XML, 'wb')
|
||||
booklists[0].write(f)
|
||||
f.close()
|
||||
if self._card_prefix is not None and hasattr(booklists[1], 'write'):
|
||||
if not os.path.exists(self._card_prefix):
|
||||
os.makedirs(self._card_prefix)
|
||||
f = open(self._card_prefix + self.__class__.CACHE_XML, 'wb')
|
||||
booklists[1].write(f)
|
||||
f.close()
|
||||
|
||||
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
def write_card_prefix(prefix, listid):
|
||||
if prefix is not None and hasattr(booklists[listid], 'write'):
|
||||
if not os.path.exists(prefix):
|
||||
os.makedirs(prefix)
|
||||
f = open(prefix + self.__class__.CACHE_XML, 'wb')
|
||||
booklists[listid].write(f)
|
||||
f.close()
|
||||
write_card_prefix(self._card_a_prefix, 1)
|
||||
write_card_prefix(self._card_b_prefix, 2)
|
||||
|
@ -10,6 +10,12 @@ from calibre.devices.prs505.driver import PRS505
|
||||
class PRS700(PRS505):
|
||||
|
||||
BCD = [0x31a]
|
||||
PRODUCT_NAME = 'PRS-700'
|
||||
OSX_NAME = 'Sony PRS-700'
|
||||
|
||||
|
||||
WINDOWS_MAIN_MEM = 'PRS-700'
|
||||
WINDOWS_CARD_A_MEM = 'PRS-700/UC:MS'
|
||||
WINDOWS_CARD_B_MEM = 'PRS-700/UC:SD'
|
||||
|
||||
OSX_MAIN_MEM = 'Sony PRS-700/UC Media'
|
||||
OSX_CARD_A_MEM = 'Sony PRS-700/UC:MS Media'
|
||||
OSX_CARD_B_MEM = 'Sony PRS-700/UC:SD'
|
||||
|
||||
|
82
src/calibre/devices/usbms/cli.py
Normal file
82
src/calibre/devices/usbms/cli.py
Normal file
@ -0,0 +1,82 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL 3'
|
||||
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, shutil
|
||||
|
||||
from calibre.devices.errors import PathError
|
||||
|
||||
class File(object):
|
||||
|
||||
def __init__(self, path):
|
||||
stats = os.stat(path)
|
||||
self.is_dir = os.path.isdir(path)
|
||||
self.is_readonly = not os.access(path, os.W_OK)
|
||||
self.ctime = stats.st_ctime
|
||||
self.wtime = stats.st_mtime
|
||||
self.size = stats.st_size
|
||||
if path.endswith(os.sep):
|
||||
path = path[:-1]
|
||||
self.path = path
|
||||
self.name = os.path.basename(path)
|
||||
|
||||
|
||||
class CLI(object):
|
||||
|
||||
def get_file(self, path, outfile, end_session=True):
|
||||
path = self.munge_path(path)
|
||||
with open(path, 'rb') as src:
|
||||
shutil.copyfileobj(src, outfile, 10*1024*1024)
|
||||
|
||||
def put_file(self, infile, path, replace_file=False, end_session=True):
|
||||
path = self.munge_path(path)
|
||||
if os.path.isdir(path):
|
||||
path = os.path.join(path, infile.name)
|
||||
if not replace_file and os.path.exists(path):
|
||||
raise PathError('File already exists: ' + path)
|
||||
dest = open(path, 'wb')
|
||||
shutil.copyfileobj(infile, dest, 10*1024*1024)
|
||||
dest.flush()
|
||||
dest.close()
|
||||
|
||||
def munge_path(self, path):
|
||||
if path.startswith('/') and not (path.startswith(self._main_prefix) or \
|
||||
(self._card_a_prefix and path.startswith(self._card_a_prefix)) or \
|
||||
(self._card_b_prefix and path.startswith(self._card_b_prefix))):
|
||||
path = self._main_prefix + path[1:]
|
||||
elif path.startswith('carda:'):
|
||||
path = path.replace('carda:', self._card_prefix[:-1])
|
||||
elif path.startswith('cardb:'):
|
||||
path = path.replace('cardb:', self._card_prefix[:-1])
|
||||
return path
|
||||
|
||||
def list(self, path, recurse=False, end_session=True, munge=True):
|
||||
if munge:
|
||||
path = self.munge_path(path)
|
||||
if os.path.isfile(path):
|
||||
return [(os.path.dirname(path), [File(path)])]
|
||||
entries = [File(os.path.join(path, f)) for f in os.listdir(path)]
|
||||
dirs = [(path, entries)]
|
||||
for _file in entries:
|
||||
if recurse and _file.is_dir:
|
||||
dirs[len(dirs):] = self.list(_file.path, recurse=True, munge=False)
|
||||
return dirs
|
||||
|
||||
def mkdir(self, path, end_session=True):
|
||||
if self.SUPPORTS_SUB_DIRS:
|
||||
path = self.munge_path(path)
|
||||
os.mkdir(path)
|
||||
|
||||
def rm(self, path, end_session=True):
|
||||
path = self.munge_path(path)
|
||||
self.delete_books([path])
|
||||
|
||||
def touch(self, path, end_session=True):
|
||||
path = self.munge_path(path)
|
||||
if not os.path.exists(path):
|
||||
open(path, 'w').close()
|
||||
if not os.path.isdir(path):
|
||||
os.utime(path, None)
|
@ -25,10 +25,12 @@ class Device(_Device):
|
||||
|
||||
VENDOR_NAME = None
|
||||
WINDOWS_MAIN_MEM = None
|
||||
WINDOWS_CARD_MEM = None
|
||||
WINDOWS_CARD_A_MEM = None
|
||||
WINDOWS_CARD_B_MEM = None
|
||||
|
||||
OSX_MAIN_MEM = None
|
||||
OSX_CARD_MEM = None
|
||||
OSX_CARD_A_MEM = None
|
||||
OSX_CARD_B_MEM = None
|
||||
|
||||
MAIN_MEMORY_VOLUME_LABEL = ''
|
||||
STORAGE_CARD_VOLUME_LABEL = ''
|
||||
@ -63,12 +65,26 @@ class Device(_Device):
|
||||
</match>
|
||||
</match>
|
||||
</device>
|
||||
<device>
|
||||
<match key="info.category" string="volume">
|
||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
|
||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
|
||||
%(BCD_start)s
|
||||
<match key="@info.parent:storage.lun" int="2">
|
||||
<merge key="volume.label" type="string">%(storage_card)s</merge>
|
||||
<merge key="%(app)s.cardvolume" type="string">%(deviceclass)s</merge>
|
||||
</match>
|
||||
%(BCD_end)s
|
||||
</match>
|
||||
</match>
|
||||
</match>
|
||||
</device>
|
||||
'''
|
||||
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) :
|
||||
self._main_prefix = self._card_prefix = None
|
||||
self._main_prefix = self._card_a_prefix = self._card_b_prefix = None
|
||||
|
||||
@classmethod
|
||||
def get_fdi(cls):
|
||||
@ -102,7 +118,7 @@ class Device(_Device):
|
||||
self.report_progress = report_progress
|
||||
|
||||
def card_prefix(self, end_session=True):
|
||||
return self._card_prefix
|
||||
return (self._card_a_prefix, self._card_b_prefix)
|
||||
|
||||
@classmethod
|
||||
def _windows_space(cls, prefix):
|
||||
@ -122,34 +138,41 @@ class Device(_Device):
|
||||
return total_clusters * mult, free_clusters * mult
|
||||
|
||||
def total_space(self, end_session=True):
|
||||
msz = csz = 0
|
||||
msz = casz = cbsz = 0
|
||||
if not iswindows:
|
||||
if self._main_prefix is not None:
|
||||
stats = os.statvfs(self._main_prefix)
|
||||
msz = stats.f_frsize * (stats.f_blocks + stats.f_bavail - stats.f_bfree)
|
||||
if self._card_prefix is not None:
|
||||
stats = os.statvfs(self._card_prefix)
|
||||
csz = stats.f_frsize * (stats.f_blocks + stats.f_bavail - stats.f_bfree)
|
||||
if self._card_a_prefix is not None:
|
||||
stats = os.statvfs(self._card_a_prefix)
|
||||
casz = stats.f_frsize * (stats.f_blocks + stats.f_bavail - stats.f_bfree)
|
||||
if self._card_b_prefix is not None:
|
||||
stats = os.statvfs(self._card_b_prefix)
|
||||
cbsz = stats.f_frsize * (stats.f_blocks + stats.f_bavail - stats.f_bfree)
|
||||
else:
|
||||
msz = self._windows_space(self._main_prefix)[0]
|
||||
csz = self._windows_space(self._card_prefix)[0]
|
||||
casz = self._windows_space(self._card_a_prefix)[0]
|
||||
cbsz = self._windows_space(self._card_b_prefix)[0]
|
||||
|
||||
return (msz, 0, csz)
|
||||
return (msz, casz, cbsz)
|
||||
|
||||
def free_space(self, end_session=True):
|
||||
msz = csz = 0
|
||||
msz = casz = cbsz = 0
|
||||
if not iswindows:
|
||||
if self._main_prefix is not None:
|
||||
stats = os.statvfs(self._main_prefix)
|
||||
msz = stats.f_frsize * stats.f_bavail
|
||||
if self._card_prefix is not None:
|
||||
stats = os.statvfs(self._card_prefix)
|
||||
csz = stats.f_frsize * stats.f_bavail
|
||||
if self._card_a_prefix is not None:
|
||||
stats = os.statvfs(self._card_a_prefix)
|
||||
casz = stats.f_frsize * stats.f_bavail
|
||||
if self._card_b_prefix is not None:
|
||||
stats = os.statvfs(self._card_b_prefix)
|
||||
cbsz = stats.f_frsize * stats.f_bavail
|
||||
else:
|
||||
msz = self._windows_space(self._main_prefix)[1]
|
||||
csz = self._windows_space(self._card_prefix)[1]
|
||||
|
||||
return (msz, 0, csz)
|
||||
return (msz, casz, cbsz)
|
||||
|
||||
def windows_match_device(self, pnp_id, device_id):
|
||||
pnp_id = pnp_id.upper()
|
||||
@ -190,15 +213,18 @@ class Device(_Device):
|
||||
for drive in c.Win32_DiskDrive():
|
||||
if self.windows_match_device(str(drive.PNPDeviceID), self.WINDOWS_MAIN_MEM):
|
||||
drives['main'] = self.windows_get_drive_prefix(drive)
|
||||
elif self.windows_match_device(str(drive.PNPDeviceID), self.WINDOWS_CARD_MEM):
|
||||
drives['card'] = self.windows_get_drive_prefix(drive)
|
||||
elif self.windows_match_device(str(drive.PNPDeviceID), self.WINDOWS_CARD_A_MEM):
|
||||
drives['carda'] = self.windows_get_drive_prefix(drive)
|
||||
elif self.windows_match_device(str(drive.PNPDeviceID), self.WINDOWS_CARD_B_MEM):
|
||||
drives['cardb'] = self.windows_get_drive_prefix(drive)
|
||||
|
||||
if 'main' in drives.keys() and 'card' in drives.keys():
|
||||
if 'main' in drives.keys() and 'carda' in drives.keys() and 'cardb' in drives.keys():
|
||||
break
|
||||
|
||||
drives = self.windows_sort_drives(drives)
|
||||
self._main_prefix = drives.get('main')
|
||||
self._card_prefix = drives.get('card')
|
||||
self._card_a_prefix = drives.get('carda')
|
||||
self._card_b_prefix = drives.get('cardb')
|
||||
|
||||
if not self._main_prefix:
|
||||
raise DeviceError(
|
||||
@ -228,9 +254,11 @@ class Device(_Device):
|
||||
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:
|
||||
get_dev_node(lines[i+1:], 'main')
|
||||
if self.OSX_CARD_MEM is not None and line.strip().endswith('<class IOMedia>') and self.OSX_CARD_MEM in line:
|
||||
get_dev_node(lines[i+1:], 'card')
|
||||
if len(names.keys()) == 2:
|
||||
if self.OSX_CARD_A_MEM is not None and line.strip().endswith('<class IOMedia>') and self.OSX_CARD_A_MEM in line:
|
||||
get_dev_node(lines[i+1:], 'carda')
|
||||
if self.OSX_CARD_B_MEM is not None and line.strip().endswith('<class IOMedia>') and self.OSX_CARD_B_MEM in line:
|
||||
get_dev_node(lines[i+1:], 'cardb')
|
||||
if len(names.keys()) == 3:
|
||||
break
|
||||
return names
|
||||
|
||||
@ -242,10 +270,18 @@ class Device(_Device):
|
||||
raise DeviceError(_('Unable to detect the %s disk drive. Try rebooting.')%self.__class__.__name__)
|
||||
main_pat = dev_pat % names['main']
|
||||
self._main_prefix = re.search(main_pat, mount).group(2) + os.sep
|
||||
card_pat = names['card'] if 'card' in names.keys() else None
|
||||
if card_pat is not None:
|
||||
card_pat = dev_pat % card_pat
|
||||
self._card_prefix = re.search(card_pat, mount).group(2) + os.sep
|
||||
card_a_pat = names['carda'] if 'carda' in names.keys() else None
|
||||
card_b_pat = names['cardb'] if 'cardb' in names.keys() else None
|
||||
|
||||
def get_card_prefix(pat):
|
||||
if pat is not None:
|
||||
pat = dev_pat % pat
|
||||
return re.search(pat, mount).group(2) + os.sep
|
||||
else:
|
||||
return None
|
||||
|
||||
self._card_a_prefix = get_card_prefix(card_a_pat)
|
||||
self._card_b_prefix = get_card_prefix(card_b_pat)
|
||||
|
||||
def open_linux(self):
|
||||
import dbus
|
||||
@ -278,21 +314,24 @@ class Device(_Device):
|
||||
if not self._main_prefix:
|
||||
raise DeviceError('Could not open device for reading. Try a reboot.')
|
||||
|
||||
self._card_prefix = None
|
||||
self._card_a_prefix = self._card_b_prefix = None
|
||||
cards = hm.FindDeviceStringMatch(__appname__+'.cardvolume', self.__class__.__name__)
|
||||
|
||||
for dev in cards:
|
||||
def mount_card(dev):
|
||||
try:
|
||||
self._card_prefix = conditional_mount(dev)+os.sep
|
||||
break
|
||||
return conditional_mount(dev)+os.sep
|
||||
except:
|
||||
import traceback
|
||||
print traceback
|
||||
continue
|
||||
|
||||
if len(cards) >= 1:
|
||||
self._card_a_prefix = mount_card(cards[0])
|
||||
if len(cards) >=2:
|
||||
self._card_b_prefix = mount_card(cards[1])
|
||||
|
||||
def open(self):
|
||||
time.sleep(5)
|
||||
self._main_prefix = self._card_prefix = None
|
||||
self._main_prefix = self._card_a_prefix = self._card_b_prefix = None
|
||||
if islinux:
|
||||
try:
|
||||
self.open_linux()
|
||||
|
@ -12,28 +12,19 @@ from itertools import cycle
|
||||
|
||||
from calibre.ebooks.metadata.meta import metadata_from_formats, path_to_ext
|
||||
from calibre.ebooks.metadata import authors_to_string
|
||||
from calibre.devices.usbms.cli import CLI
|
||||
from calibre.devices.usbms.device import Device
|
||||
from calibre.devices.usbms.books import BookList, Book
|
||||
from calibre.devices.errors import FreeSpaceError, PathError
|
||||
from calibre.devices.errors import DeviceError, FreeSpaceError
|
||||
from calibre.devices.mime import mime_type_ext
|
||||
|
||||
class File(object):
|
||||
def __init__(self, path):
|
||||
stats = os.stat(path)
|
||||
self.is_dir = os.path.isdir(path)
|
||||
self.is_readonly = not os.access(path, os.W_OK)
|
||||
self.ctime = stats.st_ctime
|
||||
self.wtime = stats.st_mtime
|
||||
self.size = stats.st_size
|
||||
if path.endswith(os.sep):
|
||||
path = path[:-1]
|
||||
self.path = path
|
||||
self.name = os.path.basename(path)
|
||||
|
||||
class USBMS(Device):
|
||||
# CLI must come before Device as it implments the CLI functions that
|
||||
# are inherited from the device interface in Device.
|
||||
class USBMS(CLI, Device):
|
||||
FORMATS = []
|
||||
EBOOK_DIR_MAIN = ''
|
||||
EBOOK_DIR_CARD = ''
|
||||
EBOOK_DIR_CARD_A = ''
|
||||
EBOOK_DIR_CARD_B = ''
|
||||
SUPPORTS_SUB_DIRS = False
|
||||
CAN_SET_METADATA = False
|
||||
|
||||
@ -48,14 +39,18 @@ class USBMS(Device):
|
||||
"""
|
||||
return (self.__class__.__name__, '', '', '')
|
||||
|
||||
def books(self, oncard=False, end_session=True):
|
||||
def books(self, oncard=None, end_session=True):
|
||||
bl = BookList()
|
||||
|
||||
if oncard and self._card_prefix is None:
|
||||
if oncard == 'carda' and not self._card_a_prefix:
|
||||
return bl
|
||||
elif oncard == 'cardb' and not self._card_b_prefix:
|
||||
return bl
|
||||
elif oncard and oncard != 'carda' and oncard != 'cardb':
|
||||
return bl
|
||||
|
||||
prefix = self._card_prefix if oncard else self._main_prefix
|
||||
ebook_dir = self.EBOOK_DIR_CARD if oncard else self.EBOOK_DIR_MAIN
|
||||
prefix = self._card_a_prefix if oncard == 'carda' else self._card_b_prefix if oncard == 'cardb' else self._main_prefix
|
||||
ebook_dir = self.EBOOK_DIR_CARD_A if oncard == 'carda' else self.EBOOK_DIR_CARD_B if oncard == 'cardb' else self.EBOOK_DIR_MAIN
|
||||
|
||||
# Get all books in the ebook_dir directory
|
||||
if self.SUPPORTS_SUB_DIRS:
|
||||
@ -71,15 +66,21 @@ class USBMS(Device):
|
||||
bl.append(self.__class__.book_from_path(os.path.join(path, filename)))
|
||||
return bl
|
||||
|
||||
def upload_books(self, files, names, on_card=False, end_session=True,
|
||||
def upload_books(self, files, names, on_card=None, end_session=True,
|
||||
metadata=None):
|
||||
if on_card and not self._card_prefix:
|
||||
raise ValueError(_('The reader has no storage card connected.'))
|
||||
if on_card == 'carda' and not self._card_a_prefix:
|
||||
raise ValueError(_('The reader has no storage card in this slot.'))
|
||||
elif on_card == 'cardb' and not self._card_b_prefix:
|
||||
raise ValueError(_('The reader has no storage card in this slot.'))
|
||||
elif on_card and on_card not in ('carda', 'cardb'):
|
||||
raise DeviceError(_('The reader has no storage card in this slot.'))
|
||||
|
||||
if not on_card:
|
||||
path = os.path.join(self._main_prefix, self.EBOOK_DIR_MAIN)
|
||||
if on_card == 'carda':
|
||||
path = os.path.join(self._card_a_prefix, self.EBOOK_DIR_CARD_A)
|
||||
if on_card == 'cardb':
|
||||
path = os.path.join(self._card_b_prefix, self.EBOOK_DIR_CARD_B)
|
||||
else:
|
||||
path = os.path.join(self._card_prefix, self.EBOOK_DIR_CARD)
|
||||
path = os.path.join(self._main_prefix, self.EBOOK_DIR_MAIN)
|
||||
|
||||
def get_size(obj):
|
||||
if hasattr(obj, 'seek'):
|
||||
@ -92,10 +93,12 @@ class USBMS(Device):
|
||||
sizes = [get_size(f) for f in files]
|
||||
size = sum(sizes)
|
||||
|
||||
if on_card and size > self.free_space()[2] - 1024*1024:
|
||||
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
|
||||
if not on_card and size > self.free_space()[0] - 2*1024*1024:
|
||||
raise FreeSpaceError(_("There is insufficient free space in main memory"))
|
||||
if on_card == 'carda' and size > self.free_space()[1] - 1024*1024:
|
||||
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
|
||||
if on_card == 'cardb' and size > self.free_space()[2] - 1024*1024:
|
||||
raise FreeSpaceError(_("There is insufficient free space on the storage card"))
|
||||
|
||||
paths = []
|
||||
names = iter(names)
|
||||
@ -147,12 +150,12 @@ class USBMS(Device):
|
||||
def add_books_to_metadata(cls, locations, metadata, booklists):
|
||||
for location in locations:
|
||||
path = location[0]
|
||||
on_card = 1 if location[1] else 0
|
||||
blist = 2 if location[1] == 'cardb' else 1 if location[1] == 'carda' else 0
|
||||
|
||||
book = cls.book_from_path(path)
|
||||
|
||||
if not book in booklists[on_card]:
|
||||
booklists[on_card].append(book)
|
||||
if not book in booklists[blist]:
|
||||
booklists[blist].append(book)
|
||||
|
||||
|
||||
def delete_books(self, paths, end_session=True):
|
||||
@ -180,58 +183,6 @@ class USBMS(Device):
|
||||
# the Sony Readers.
|
||||
pass
|
||||
|
||||
def get_file(self, path, outfile, end_session=True):
|
||||
path = self.munge_path(path)
|
||||
with open(path, 'rb') as src:
|
||||
shutil.copyfileobj(src, outfile, 10*1024*1024)
|
||||
|
||||
def put_file(self, infile, path, replace_file=False, end_session=True):
|
||||
path = self.munge_path(path)
|
||||
if os.path.isdir(path):
|
||||
path = os.path.join(path, infile.name)
|
||||
if not replace_file and os.path.exists(path):
|
||||
raise PathError('File already exists: ' + path)
|
||||
dest = open(path, 'wb')
|
||||
shutil.copyfileobj(infile, dest, 10*1024*1024)
|
||||
dest.flush()
|
||||
dest.close()
|
||||
|
||||
def munge_path(self, path):
|
||||
if path.startswith('/') and not (path.startswith(self._main_prefix) or \
|
||||
(self._card_prefix and path.startswith(self._card_prefix))):
|
||||
path = self._main_prefix + path[1:]
|
||||
elif path.startswith('card:'):
|
||||
path = path.replace('card:', self._card_prefix[:-1])
|
||||
return path
|
||||
|
||||
def list(self, path, recurse=False, end_session=True, munge=True):
|
||||
if munge:
|
||||
path = self.munge_path(path)
|
||||
if os.path.isfile(path):
|
||||
return [(os.path.dirname(path), [File(path)])]
|
||||
entries = [File(os.path.join(path, f)) for f in os.listdir(path)]
|
||||
dirs = [(path, entries)]
|
||||
for _file in entries:
|
||||
if recurse and _file.is_dir:
|
||||
dirs[len(dirs):] = self.list(_file.path, recurse=True, munge=False)
|
||||
return dirs
|
||||
|
||||
def mkdir(self, path, end_session=True):
|
||||
if self.SUPPORTS_SUB_DIRS:
|
||||
path = self.munge_path(path)
|
||||
os.mkdir(path)
|
||||
|
||||
def rm(self, path, end_session=True):
|
||||
path = self.munge_path(path)
|
||||
self.delete_books([path])
|
||||
|
||||
def touch(self, path, end_session=True):
|
||||
path = self.munge_path(path)
|
||||
if not os.path.exists(path):
|
||||
open(path, 'w').close()
|
||||
if not os.path.isdir(path):
|
||||
os.utime(path, None)
|
||||
|
||||
@classmethod
|
||||
def metadata_from_path(cls, path):
|
||||
return metadata_from_formats([path])
|
||||
|
@ -139,9 +139,10 @@ class DeviceManager(Thread):
|
||||
|
||||
def _books(self):
|
||||
'''Get metadata from device'''
|
||||
mainlist = self.device.books(oncard=False, end_session=False)
|
||||
cardlist = self.device.books(oncard=True)
|
||||
return (mainlist, cardlist)
|
||||
mainlist = self.device.books(oncard=None, end_session=False)
|
||||
cardalist = self.device.books(oncard='carda')
|
||||
cardblist = self.device.books(oncard='cardb')
|
||||
return (mainlist, cardalist, cardblist)
|
||||
|
||||
def books(self, done):
|
||||
'''Return callable that returns the list of books on device as two booklists'''
|
||||
@ -156,12 +157,12 @@ class DeviceManager(Thread):
|
||||
return self.create_job(self._sync_booklists, done, args=[booklists],
|
||||
description=_('Send metadata to device'))
|
||||
|
||||
def _upload_books(self, files, names, on_card=False, metadata=None):
|
||||
def _upload_books(self, files, names, on_card=None, metadata=None):
|
||||
'''Upload books to device: '''
|
||||
return self.device.upload_books(files, names, on_card,
|
||||
metadata=metadata, end_session=False)
|
||||
|
||||
def upload_books(self, done, files, names, on_card=False, titles=None,
|
||||
def upload_books(self, done, files, names, on_card=None, titles=None,
|
||||
metadata=None):
|
||||
desc = _('Upload %d books to device')%len(names)
|
||||
if titles:
|
||||
@ -197,6 +198,7 @@ class DeviceManager(Thread):
|
||||
|
||||
def _view_book(self, path, target):
|
||||
f = open(target, 'wb')
|
||||
print self.device
|
||||
self.device.get_file(path, f)
|
||||
f.close()
|
||||
return target
|
||||
@ -256,24 +258,27 @@ class DeviceMenu(QMenu):
|
||||
self.connect(action2, SIGNAL('a_s(QAction)'),
|
||||
self.action_triggered)
|
||||
|
||||
|
||||
|
||||
|
||||
_actions = [
|
||||
('main:', False, False, ':/images/reader.svg',
|
||||
_('Send to main memory')),
|
||||
('card:0', False, False, ':/images/sd.svg',
|
||||
_('Send to storage card')),
|
||||
('carda:0', False, False, ':/images/sd.svg',
|
||||
_('Send to storage card A')),
|
||||
('cardb:0', False, False, ':/images/sd.svg',
|
||||
_('Send to storage card B')),
|
||||
'-----',
|
||||
('main:', True, False, ':/images/reader.svg',
|
||||
_('Send to main memory')),
|
||||
('card:0', True, False, ':/images/sd.svg',
|
||||
_('Send to storage card')),
|
||||
('carda:0', True, False, ':/images/sd.svg',
|
||||
_('Send to storage card A')),
|
||||
('cardb:0', True, False, ':/images/sd.svg',
|
||||
_('Send to storage card B')),
|
||||
'-----',
|
||||
('main:', False, True, ':/images/reader.svg',
|
||||
_('Send specific format to main memory')),
|
||||
('card:0', False, True, ':/images/sd.svg',
|
||||
_('Send specific format to storage card')),
|
||||
('carda:0', False, True, ':/images/sd.svg',
|
||||
_('Send specific format to storage card A')),
|
||||
('cardb:0', False, True, ':/images/sd.svg',
|
||||
_('Send specific format to storage card B')),
|
||||
|
||||
]
|
||||
if default_account is not None:
|
||||
@ -335,7 +340,7 @@ class DeviceMenu(QMenu):
|
||||
|
||||
def enable_device_actions(self, enable):
|
||||
for action in self.actions:
|
||||
if action.dest[:4] in ('main', 'card'):
|
||||
if action.dest in ('main:', 'carda:0', 'cardb:0'):
|
||||
action.setEnabled(enable)
|
||||
|
||||
class Emailer(Thread):
|
||||
@ -412,16 +417,23 @@ class DeviceGUI(object):
|
||||
d.exec_()
|
||||
fmt = d.format().lower()
|
||||
dest, sub_dest = dest.split(':')
|
||||
if dest in ('main', 'card'):
|
||||
if dest in ('main', 'carda', 'cardb'):
|
||||
if not self.device_connected or not self.device_manager:
|
||||
error_dialog(self, _('No device'),
|
||||
_('Cannot send: No device is connected')).exec_()
|
||||
return
|
||||
on_card = dest == 'card'
|
||||
if on_card and not self.device_manager.has_card():
|
||||
if dest == 'carda' and not self.device_manager.has_card():
|
||||
error_dialog(self, _('No card'),
|
||||
_('Cannot send: Device has no storage card')).exec_()
|
||||
return
|
||||
if dest == 'cardb' and not self.device_manager.has_card():
|
||||
error_dialog(self, _('No card'),
|
||||
_('Cannot send: Device has no storage card')).exec_()
|
||||
return
|
||||
if dest == 'main':
|
||||
on_card = None
|
||||
else:
|
||||
on_card = dest
|
||||
self.sync_to_device(on_card, delete, fmt)
|
||||
elif dest == 'mail':
|
||||
to, fmts = sub_dest.split(';')
|
||||
@ -680,7 +692,7 @@ class DeviceGUI(object):
|
||||
cp, fs = job.result
|
||||
self.location_view.model().update_devices(cp, fs)
|
||||
|
||||
def upload_books(self, files, names, metadata, on_card=False, memory=None):
|
||||
def upload_books(self, files, names, metadata, on_card=None, memory=None):
|
||||
'''
|
||||
Upload books to device.
|
||||
:param files: List of either paths to files or file like objects
|
||||
@ -719,7 +731,7 @@ class DeviceGUI(object):
|
||||
|
||||
self.upload_booklists()
|
||||
|
||||
view = self.card_view if on_card else self.memory_view
|
||||
view = self.card_a_view if on_card == 'carda' else self.card_b_view if on_card == 'cardb' else self.memory_view
|
||||
view.model().resort(reset=False)
|
||||
view.model().research()
|
||||
for f in files:
|
||||
|
@ -305,7 +305,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
similar_menu=similar_menu)
|
||||
self.memory_view.set_context_menu(None, None, None,
|
||||
self.action_view, self.action_save, None, None)
|
||||
self.card_view.set_context_menu(None, None, None,
|
||||
self.card_a_view.set_context_menu(None, None, None,
|
||||
self.action_view, self.action_save, None, None)
|
||||
self.card_b_view.set_context_menu(None, None, None,
|
||||
self.action_view, self.action_save, None, None)
|
||||
QObject.connect(self.library_view,
|
||||
SIGNAL('files_dropped(PyQt_PyObject)'),
|
||||
@ -315,11 +317,12 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
('connect_to_book_display',
|
||||
self.status_bar.book_info.show_data),
|
||||
]:
|
||||
for view in (self.library_view, self.memory_view, self.card_view):
|
||||
for view in (self.library_view, self.memory_view, self.card_a_view, self.card_b_view):
|
||||
getattr(view, func)(target)
|
||||
|
||||
self.memory_view.connect_dirtied_signal(self.upload_booklists)
|
||||
self.card_view.connect_dirtied_signal(self.upload_booklists)
|
||||
self.card_a_view.connect_dirtied_signal(self.upload_booklists)
|
||||
self.card_b_view.connect_dirtied_signal(self.upload_booklists)
|
||||
|
||||
self.show()
|
||||
if self.system_tray_icon.isVisible() and opts.start_in_tray:
|
||||
@ -582,10 +585,12 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
if idx == 1:
|
||||
return self.memory_view
|
||||
if idx == 2:
|
||||
return self.card_view
|
||||
return self.card_a_view
|
||||
if idx == 3:
|
||||
return self.card_b_view
|
||||
|
||||
def booklists(self):
|
||||
return self.memory_view.model().db, self.card_view.model().db
|
||||
return self.memory_view.model().db, self.card_a_view.model().db, self.card_b_view.model().db
|
||||
|
||||
|
||||
|
||||
@ -647,12 +652,14 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
else:
|
||||
self.device_job_exception(job)
|
||||
return
|
||||
mainlist, cardlist = job.result
|
||||
mainlist, cardalist, cardblist = job.result
|
||||
self.memory_view.set_database(mainlist)
|
||||
self.memory_view.set_editable(self.device_manager.device_class.CAN_SET_METADATA)
|
||||
self.card_view.set_database(cardlist)
|
||||
self.card_view.set_editable(self.device_manager.device_class.CAN_SET_METADATA)
|
||||
for view in (self.memory_view, self.card_view):
|
||||
self.card_a_view.set_database(cardalist)
|
||||
self.card_a_view.set_editable(self.device_manager.device_class.CAN_SET_METADATA)
|
||||
self.card_b_view.set_database(cardblist)
|
||||
self.card_b_view.set_editable(self.device_manager.device_class.CAN_SET_METADATA)
|
||||
for view in (self.memory_view, self.card_a_view, self.card_b_view):
|
||||
view.sortByColumn(3, Qt.DescendingOrder)
|
||||
if not view.restore_column_widths():
|
||||
view.resizeColumnsToContents()
|
||||
@ -793,8 +800,12 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
return
|
||||
view.model().delete_books(rows)
|
||||
else:
|
||||
view = self.memory_view if self.stack.currentIndex() == 1 \
|
||||
else self.card_view
|
||||
if self.stack.currentIndex() == 1:
|
||||
view = self.memory_view
|
||||
elif self.stack.currentIndex() == 2:
|
||||
view = self.card_a_view
|
||||
else:
|
||||
view = self.card_b_view
|
||||
paths = view.model().paths(rows)
|
||||
job = self.remove_paths(paths)
|
||||
self.delete_memory[job] = (paths, view.model())
|
||||
@ -809,7 +820,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
'''
|
||||
Called once deletion is done on the device
|
||||
'''
|
||||
for view in (self.memory_view, self.card_view):
|
||||
for view in (self.memory_view, self.card_a_view, self.card_b_view):
|
||||
view.model().deletion_done(job, bool(job.exception))
|
||||
if job.exception is not None:
|
||||
self.device_job_exception(job)
|
||||
@ -1318,10 +1329,11 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
|
||||
'''
|
||||
Called when a location icon is clicked (e.g. Library)
|
||||
'''
|
||||
page = 0 if location == 'library' else 1 if location == 'main' else 2
|
||||
page = 0 if location == 'library' else 1 if location == 'main' else 2 if location == 'carda' else 3
|
||||
self.stack.setCurrentIndex(page)
|
||||
view = self.memory_view if page == 1 else \
|
||||
self.card_view if page == 2 else None
|
||||
self.card_a_view if page == 2 else \
|
||||
self.card_b_view if page == 3 else None
|
||||
if view:
|
||||
if view.resize_on_select:
|
||||
view.resizeRowsToContents()
|
||||
|
@ -288,7 +288,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex" >
|
||||
<number>0</number>
|
||||
<number>3</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="library" >
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2" >
|
||||
@ -417,10 +417,48 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page" >
|
||||
<widget class="QWidget" name="card_a_memory" >
|
||||
<layout class="QGridLayout" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="DeviceBooksView" name="card_view" >
|
||||
<widget class="DeviceBooksView" name="card_a_view" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Expanding" hsizetype="Preferred" >
|
||||
<horstretch>10</horstretch>
|
||||
<verstretch>10</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="acceptDrops" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragEnabled" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropOverwriteMode" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="dragDropMode" >
|
||||
<enum>QAbstractItemView::DragDrop</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionBehavior" >
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="card_b_memory" >
|
||||
<layout class="QGridLayout" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="DeviceBooksView" name="card_b_view" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Expanding" hsizetype="Preferred" >
|
||||
<horstretch>10</horstretch>
|
||||
|
@ -8,7 +8,7 @@ import os, sys, traceback, urlparse
|
||||
|
||||
from BeautifulSoup import BeautifulSoup, Tag
|
||||
|
||||
from calibre.ebooks.epub.iterator import EbookIterator
|
||||
from calibre.ebooks.oeb.iterator import EbookIterator
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
@ -171,17 +171,20 @@ class LocationModel(QAbstractListModel):
|
||||
QAbstractListModel.__init__(self, parent)
|
||||
self.icons = [QVariant(QIcon(':/library')),
|
||||
QVariant(QIcon(':/images/reader.svg')),
|
||||
QVariant(QIcon(':/images/sd.svg')),
|
||||
QVariant(QIcon(':/images/sd.svg'))]
|
||||
self.text = [_('Library\n%d\nbooks'),
|
||||
_('Reader\n%s\navailable'),
|
||||
_('Card\n%s\navailable')]
|
||||
self.free = [-1, -1]
|
||||
_('Card A\n%s\navailable'),
|
||||
_('Card B\n%s\navailable')]
|
||||
self.free = [-1, -1, -1]
|
||||
self.count = 0
|
||||
self.highlight_row = 0
|
||||
self.tooltips = [
|
||||
_('Click to see the list of books available on your computer'),
|
||||
_('Click to see the list of books in the main memory of your reader'),
|
||||
_('Click to see the list of books on the storage card in your reader')
|
||||
_('Click to see the list of books on storage card A in your reader'),
|
||||
_('Click to see the list of books on storage card B in your reader')
|
||||
]
|
||||
|
||||
def rowCount(self, parent):
|
||||
@ -218,9 +221,14 @@ class LocationModel(QAbstractListModel):
|
||||
|
||||
def update_devices(self, cp=None, fs=[-1, -1, -1]):
|
||||
self.free[0] = fs[0]
|
||||
self.free[1] = max(fs[1:])
|
||||
if cp == None:
|
||||
self.free[1] = fs[1]
|
||||
self.free[2] = fs[2]
|
||||
if cp != None:
|
||||
self.free[1] = fs[1] if fs[1] else -1
|
||||
self.free[2] = fs[2] if fs[2] else -1
|
||||
else:
|
||||
self.free[1] = -1
|
||||
self.free[2] = -1
|
||||
self.reset()
|
||||
|
||||
def location_changed(self, row):
|
||||
@ -244,12 +252,12 @@ class LocationView(QListView):
|
||||
def current_changed(self, current, previous):
|
||||
if current.isValid():
|
||||
i = current.row()
|
||||
location = 'library' if i == 0 else 'main' if i == 1 else 'card'
|
||||
location = 'library' if i == 0 else 'main' if i == 1 else 'carda' if i == 2 else 'cardb'
|
||||
self.emit(SIGNAL('location_selected(PyQt_PyObject)'), location)
|
||||
self.model().location_changed(i)
|
||||
|
||||
def location_changed(self, row):
|
||||
if 0 <= row and row <= 2:
|
||||
if 0 <= row and row <= 3:
|
||||
self.model().location_changed(row)
|
||||
|
||||
class JobsView(TableView):
|
||||
|
Loading…
x
Reference in New Issue
Block a user