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.devices.apple.driver import ITUNES
from calibre.devices.hanlin.driver import HANLINV3, HANLINV5, BOOX
from calibre.devices.blackberry.driver import BLACKBERRY
from calibre.devices.cybook.driver import CYBOOK
@ -495,6 +495,7 @@ plugins += [
]
# Order here matters. The first matched device is the one used.
plugins += [
ITUNES,
HANLINV3,
HANLINV5,
BLACKBERRY,

View File

@ -5,22 +5,83 @@
22 May 2010
'''
import datetime
from calibre.constants import isosx, iswindows
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'
gui_name = 'Apple device'
icon = I('devices/iPad.png')
description = _('Communicate with iBooks through iTunes.')
supported_platforms = ['windows','osx']
author = 'GRiker'
FORMATS = ['epub']
VENDOR_ID = [0x0830]
PRODUCT_ID = [0x8004, 0x8002, 0x0101]
BCD = [0x0316]
VENDOR_ID = [0x05ac]
# 0x129a:iPad 0x1292:iPhone 3G
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
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
``(vendor_id, product_id, bcd)``.
'''
print "ITUNES:can_handle_windows()"
return True
def can_handle(self, device_info, debug=False):
def card_prefix(self, end_session=True):
'''
Unix version of :method:`can_handle_windows`
:param device_info: Is a tupe of (vid, pid, bcd, manufacturer, product,
serial number)
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)
'''
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):
'''
@ -58,14 +170,16 @@ class iDevice(DevicePlugin):
this function that should serve as a good example for USB Mass storage
devices.
'''
print "iDevice(): I am here!"
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.
'''
raise NotImplementedError()
print "ITUNES.open()"
if isosx:
# Launch iTunes if not already running
running_apps = appscript.app('System Events')
if not 'iTunes' in running_apps.processes.name():
print " launching iTunes"
app = appscript.app('iTunes', hide=True)
app.run()
self.app = app
# May need to set focus back to calibre here?
def post_yank_cleanup(self):
'''
@ -73,6 +187,37 @@ class iDevice(DevicePlugin):
'''
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):
'''
@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
task does not have any progress information
'''
raise NotImplementedError()
print "ITUNES:set_progress_reporter()"
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)
"""
raise NotImplementedError()
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.
'''
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.
If no card is present None is set for the card's prefix.
E.G.
('/place', '/place2')
(None, 'place2')
('place', None)
(None, None)
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()
print "ITUNES:sync_booklists():"
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
particular device doesn't have any of these locations it should return 0.
"""
raise NotImplementedError()
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()
print "ITUNES:total_space()"
def upload_books(self, files, names, on_card=None, end_session=True,
metadata=None):
@ -158,79 +282,16 @@ class iDevice(DevicePlugin):
'''
raise NotImplementedError()
@classmethod
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
# Private methods
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()
@classmethod
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 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()
sources = self._app.sources()
names = [s.name() for s in sources]
kinds = [s.kind() for s in sources]
return dict(zip(kinds,names))
class BookList(list):
'''
@ -249,19 +310,20 @@ class BookList(list):
__getslice__ = None
__setslice__ = None
def __init__(self, oncard, prefix, settings):
def __init__(self):
pass
def supports_collections(self):
''' Return True if the the device supports collections for this book list. '''
raise NotImplementedError()
return False
def add_book(self, book, replace_metadata):
'''
Add the book to the booklist. Intent is to maintain any device-internal
metadata. Return True if booklists must be sync'ed
'''
raise NotImplementedError()
print "adding %s" % book
self.append(book)
def remove_book(self, book):
'''
@ -281,4 +343,22 @@ class BookList(list):
: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'])