GwR early apple driver

This commit is contained in:
GRiker 2010-05-23 10:15:07 -06:00
parent ddda93ea7a
commit 007cf9d6c1
2 changed files with 215 additions and 134 deletions

View File

@ -430,7 +430,7 @@ from calibre.ebooks.txt.output import TXTOutput
from calibre.customize.profiles import input_profiles, output_profiles from calibre.customize.profiles import input_profiles, output_profiles
from calibre.devices.apple.driver import ITUNES
from calibre.devices.hanlin.driver import HANLINV3, HANLINV5, BOOX from calibre.devices.hanlin.driver import HANLINV3, HANLINV5, BOOX
from calibre.devices.blackberry.driver import BLACKBERRY from calibre.devices.blackberry.driver import BLACKBERRY
from calibre.devices.cybook.driver import CYBOOK from calibre.devices.cybook.driver import CYBOOK
@ -495,6 +495,7 @@ plugins += [
] ]
# Order here matters. The first matched device is the one used. # Order here matters. The first matched device is the one used.
plugins += [ plugins += [
ITUNES,
HANLINV3, HANLINV3,
HANLINV5, HANLINV5,
BLACKBERRY, BLACKBERRY,

View File

@ -5,22 +5,83 @@
22 May 2010 22 May 2010
''' '''
import datetime
from calibre.constants import isosx, iswindows
from calibre.devices.interface import DevicePlugin from calibre.devices.interface import DevicePlugin
#from calibre.ebooks.metadata import MetaInformation
from calibre.utils.config import Config
class iDevice(DevicePlugin): if isosx:
print "running in OSX"
import appscript
if iswindows:
print "running in Windows"
import win32com.client
class ITUNES(DevicePlugin):
name = 'Apple device interface' name = 'Apple device interface'
gui_name = 'Apple device' gui_name = 'Apple device'
icon = I('devices/iPad.png')
description = _('Communicate with iBooks through iTunes.')
supported_platforms = ['windows','osx'] supported_platforms = ['windows','osx']
author = 'GRiker' author = 'GRiker'
FORMATS = ['epub'] FORMATS = ['epub']
VENDOR_ID = [0x0830] VENDOR_ID = [0x05ac]
PRODUCT_ID = [0x8004, 0x8002, 0x0101] # 0x129a:iPad 0x1292:iPhone 3G
BCD = [0x0316] PRODUCT_ID = [0x129a,0x1292]
BCD = [0x01]
def is_usb_connected(self, device_on_system): app = None
is_connected = False
# Public methods
def add_books_to_metadata(cls, locations, metadata, booklists):
'''
Add locations to the booklists. This function must not communicate with
the device.
@param locations: Result of a call to L{upload_books}
@param metadata: List of MetaInformation objects, same as for
:method:`upload_books`.
@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 books(self, oncard=None, end_session=True):
"""
Return a list of ebooks on the device.
@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.
"""
print "ITUNES:books(oncard=%s)" % oncard
if not oncard:
myBooks = BookList()
book = Book()
myBooks.add_book(book, False)
print "len(myBooks): %d" % len(myBooks)
return myBooks
else:
return []
def can_handle(self, device_info, debug=False):
'''
Unix version of :method:`can_handle_windows`
:param device_info: Is a tupe of (vid, pid, bcd, manufacturer, product,
serial number)
'''
print "ITUNES:can_handle()"
return True return True
def can_handle_windows(self, device_id, debug=False): def can_handle_windows(self, device_id, debug=False):
@ -35,17 +96,68 @@ class iDevice(DevicePlugin):
:param device_info: On windows a device ID string. On Unix a tuple of :param device_info: On windows a device ID string. On Unix a tuple of
``(vendor_id, product_id, bcd)``. ``(vendor_id, product_id, bcd)``.
''' '''
print "ITUNES:can_handle_windows()"
return True return True
def can_handle(self, device_info, debug=False): def card_prefix(self, end_session=True):
''' '''
Unix version of :method:`can_handle_windows` 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.
:param device_info: Is a tupe of (vid, pid, bcd, manufacturer, product, E.G.
serial number) ('/place', '/place2')
(None, 'place2')
('place', None)
(None, None)
''' '''
print "ITUNES:card_prefix()"
return (None,None)
return True def config_widget(cls):
'''
Should return a QWidget. The QWidget contains the settings for the device interface
'''
raise NotImplementedError()
def delete_books(self, paths, end_session=True):
'''
Delete books at paths on device.
'''
raise NotImplementedError()
def eject(self):
'''
Un-mount / eject the device from the OS. This does not check if there
are pending GUI jobs that need to communicate with the device.
'''
print "ITUNES:eject()"
def free_space(self, end_session=True):
"""
Get free space available on the mountpoints:
1. Main memory
2. Card A
3. Card B
@return: A 3 element list with free space in bytes of (1, 2, 3). If a
particular device doesn't have any of these locations it should return -1.
"""
print "ITUNES:free_space()"
return (0,-1,-1)
def get_device_information(self, end_session=True):
"""
Ask device for device information. See L{DeviceInfoQuery}.
@return: (device name, device version, software version on device, mime type)
"""
print "ITUNES:get_device_information()"
return ('iPad','hw v1.0','sw v1.0', 'mime type')
def get_file(self, path, outfile, end_session=True):
'''
Read the file at C{path} on the device and write it to outfile.
@param outfile: file object like C{sys.stdout} or the result of an C{open} call
'''
raise NotImplementedError()
def open(self): def open(self):
''' '''
@ -58,14 +170,16 @@ class iDevice(DevicePlugin):
this function that should serve as a good example for USB Mass storage this function that should serve as a good example for USB Mass storage
devices. devices.
''' '''
print "iDevice(): I am here!" print "ITUNES.open()"
if isosx:
def eject(self): # Launch iTunes if not already running
''' running_apps = appscript.app('System Events')
Un-mount / eject the device from the OS. This does not check if there if not 'iTunes' in running_apps.processes.name():
are pending GUI jobs that need to communicate with the device. print " launching iTunes"
''' app = appscript.app('iTunes', hide=True)
raise NotImplementedError() app.run()
self.app = app
# May need to set focus back to calibre here?
def post_yank_cleanup(self): def post_yank_cleanup(self):
''' '''
@ -73,6 +187,37 @@ class iDevice(DevicePlugin):
''' '''
raise NotImplementedError() raise NotImplementedError()
def remove_books_from_metadata(cls, paths, booklists):
'''
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=None), L{books}(oncard='carda'),
L{books}(oncard='cardb')).
'''
raise NotImplementedError()
def reset(self, key='-1', log_packets=False, report_progress=None,
detected_device=None) :
"""
:key: The key to unlock the device
:log_packets: If true the packet stream to/from the device is logged
:report_progress: Function that is called with a % progress
(number between 0 and 100) for various tasks
If it is called with -1 that means that the
task does not have any progress information
:detected_device: Device information from the device scanner
"""
print "ITUNE.reset()"
def save_settings(cls, settings_widget):
'''
Should save settings to disk. Takes the widget created in config_widget
and saves all settings to disk.
'''
raise NotImplementedError()
def set_progress_reporter(self, report_progress): def set_progress_reporter(self, report_progress):
''' '''
@param report_progress: Function that is called with a % progress @param report_progress: Function that is called with a % progress
@ -80,26 +225,28 @@ class iDevice(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
''' '''
raise NotImplementedError() print "ITUNES:set_progress_reporter()"
def get_device_information(self, end_session=True): def settings(cls):
""" '''
Ask device for device information. See L{DeviceInfoQuery}. Should return an opts object. The opts object should have one attribute
@return: (device name, device version, software version on device, mime type) `format_map` which is an ordered list of formats for the device.
""" '''
raise NotImplementedError() print "ITUNES.settings()"
klass = cls if isinstance(cls, type) else cls.__class__
c = Config('device_drivers_%s' % klass.__name__, _('settings for device drivers'))
c.add_opt('format_map', default=cls.FORMATS,
help=_('Ordered list of formats the device will accept'))
return c.parse()
def card_prefix(self, end_session=True): def sync_booklists(self, booklists, end_session=True):
''' '''
Return a 2 element list of the prefix to paths on the cards. Update metadata on device.
If no card is present None is set for the card's prefix. @param booklists: A tuple containing the result of calls to
E.G. (L{books}(oncard=None), L{books}(oncard='carda'),
('/place', '/place2') L{books}(oncard='cardb')).
(None, 'place2')
('place', None)
(None, None)
''' '''
raise NotImplementedError() print "ITUNES:sync_booklists():"
def total_space(self, end_session=True): def total_space(self, end_session=True):
""" """
@ -111,30 +258,7 @@ class iDevice(DevicePlugin):
@return: A 3 element list with total space in bytes of (1, 2, 3). If a @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. particular device doesn't have any of these locations it should return 0.
""" """
raise NotImplementedError() print "ITUNES:total_space()"
def free_space(self, end_session=True):
"""
Get free space available on the mountpoints:
1. Main memory
2. Card A
3. Card B
@return: A 3 element list with free space in bytes of (1, 2, 3). If a
particular device doesn't have any of these locations it should return -1.
"""
raise NotImplementedError()
def books(self, oncard=None, end_session=True):
"""
Return a list of ebooks on the device.
@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=None, end_session=True, def upload_books(self, files, names, on_card=None, end_session=True,
metadata=None): metadata=None):
@ -158,79 +282,16 @@ class iDevice(DevicePlugin):
''' '''
raise NotImplementedError() raise NotImplementedError()
@classmethod # Private methods
def add_books_to_metadata(cls, locations, metadata, booklists):
'''
Add locations to the booklists. This function must not communicate with
the device.
@param locations: Result of a call to L{upload_books}
@param metadata: List of MetaInformation objects, same as for
:method:`upload_books`.
@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 delete_books(self, paths, end_session=True): def _get_source(self):
''' '''
Delete books at paths on device. Get iTunes sources (Library, iPod, Radio ...)
''' '''
raise NotImplementedError() sources = self._app.sources()
names = [s.name() for s in sources]
@classmethod kinds = [s.kind() for s in sources]
def remove_books_from_metadata(cls, paths, booklists): return dict(zip(kinds,names))
'''
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=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=None), L{books}(oncard='carda'),
L{books}(oncard='cardb')).
'''
raise NotImplementedError()
def get_file(self, path, outfile, end_session=True):
'''
Read the file at C{path} on the device and write it to outfile.
@param outfile: file object like C{sys.stdout} or the result of an C{open} call
'''
raise NotImplementedError()
@classmethod
def config_widget(cls):
'''
Should return a QWidget. The QWidget contains the settings for the device interface
'''
raise NotImplementedError()
@classmethod
def save_settings(cls, settings_widget):
'''
Should save settings to disk. Takes the widget created in config_widget
and saves all settings to disk.
'''
raise NotImplementedError()
@classmethod
def settings(cls):
'''
Should return an opts object. The opts object should have one attribute
`format_map` which is an ordered list of formats for the device.
'''
raise NotImplementedError()
class BookList(list): class BookList(list):
''' '''
@ -249,19 +310,20 @@ class BookList(list):
__getslice__ = None __getslice__ = None
__setslice__ = None __setslice__ = None
def __init__(self, oncard, prefix, settings): def __init__(self):
pass pass
def supports_collections(self): def supports_collections(self):
''' Return True if the the device supports collections for this book list. ''' ''' Return True if the the device supports collections for this book list. '''
raise NotImplementedError() return False
def add_book(self, book, replace_metadata): def add_book(self, book, replace_metadata):
''' '''
Add the book to the booklist. Intent is to maintain any device-internal Add the book to the booklist. Intent is to maintain any device-internal
metadata. Return True if booklists must be sync'ed metadata. Return True if booklists must be sync'ed
''' '''
raise NotImplementedError() print "adding %s" % book
self.append(book)
def remove_book(self, book): def remove_book(self, book):
''' '''
@ -281,4 +343,22 @@ class BookList(list):
:param collection_attributes: A list of attributes of the Book object :param collection_attributes: A list of attributes of the Book object
''' '''
raise NotImplementedError() return {}
class Book(object):
'''
A simple class describing a book in the iTunes Books Library.
These seem to be the minimum Book attributes needed.
'''
def __init__(self):
setattr(self,'title','A Book Title')
setattr(self,'authors',['John Doe'])
setattr(self,'path','some/path.epub')
setattr(self,'size',1234567)
setattr(self,'datetime',datetime.datetime.now().timetuple())
setattr(self,'thumbnail',None)
setattr(self,'db_id',0)
setattr(self,'device_collections',[])
setattr(self,'tags',['Genre'])