Merge from trunk

This commit is contained in:
Charles Haley 2010-06-06 00:26:03 +01:00
commit 2c3ea53b0e

View File

@ -31,6 +31,37 @@ if isosx:
if iswindows:
import pythoncom, win32com.client
class ITUNES(DevicePlugin):
'''
try:
pythoncom.CoInitialize()
finally:
pythoncom.CoUninitialize()
'''
name = 'Apple device interface'
gui_name = 'Apple device'
icon = I('devices/ipad.png')
description = _('Communicate with iBooks through iTunes.')
supported_platforms = ['osx','windows']
author = 'GRiker'
#: The version of this plugin as a 3-tuple (major, minor, revision)
version = (0, 4, 0)
OPEN_FEEDBACK_MESSAGE = _(
'Apple device detected, launching iTunes, please wait ...')
FORMATS = ['epub']
# Product IDs:
# 0x1292:iPhone 3G
# 0x129a:iPad
VENDOR_ID = [0x05ac]
PRODUCT_ID = [0x129a]
BCD = [0x01]
# iTunes enumerations
Sources = [
'Unknown',
'Library',
@ -48,34 +79,27 @@ if iswindows:
'BMP'
]
class ITUNES(DevicePlugin):
'''
try:
pythoncom.CoInitialize()
finally:
pythoncom.CoUninitialize()
'''
PlaylistKind = [
'Unknown',
'Library',
'User',
'CD',
'Device',
'Radio Tuner'
]
name = 'Apple device interface'
gui_name = 'Apple device'
icon = I('devices/ipad.png')
description = _('Communicate with iBooks through iTunes.')
supported_platforms = ['osx','windows']
author = 'GRiker'
#: The version of this plugin as a 3-tuple (major, minor, revision)
version = (1, 0, 0)
OPEN_FEEDBACK_MESSAGE = _(
'Apple device detected, launching iTunes, please wait ...')
FORMATS = ['epub']
# Product IDs:
# 0x1292:iPhone 3G
# 0x129a:iPad
VENDOR_ID = [0x05ac]
PRODUCT_ID = [0x129a]
BCD = [0x01]
PlaylistSpecialKind = [
'Unknown',
'Purchased Music',
'Party Shuffle',
'Podcasts',
'Folder',
'Video',
'Music',
'Movies',
'TV Shows',
'Books',
]
# Properties
cached_books = {}
@ -459,12 +483,14 @@ class ITUNES(DevicePlugin):
if isosx:
self.iTunes.eject(self.sources['iPod'])
elif iswindows:
try:
pythoncom.CoInitialize()
self.iTunes = win32com.client.Dispatch("iTunes.Application")
self.iTunes.sources.ItemByName(self.sources['iPod']).EjectIPod()
finally:
pythoncom.CoUninitialize()
if 'iPod' in self.sources:
try:
pythoncom.CoInitialize()
self.iTunes = win32com.client.Dispatch("iTunes.Application")
self.iTunes.sources.ItemByName(self.sources['iPod']).EjectIPod()
finally:
pythoncom.CoUninitialize()
self.iTunes = None
self.sources = None
@ -635,7 +661,7 @@ class ITUNES(DevicePlugin):
if self.update_needed:
if DEBUG:
self.log.info(' calling _update_device')
self._update_device(msg=self.update_msg)
self._update_device(msg=self.update_msg, wait=False)
self.update_needed = False
# Get actual size of updated books on device
@ -736,12 +762,13 @@ class ITUNES(DevicePlugin):
self.problem_msg = _("Some cover art could not be converted.\n"
"Click 'Show Details' for a list.")
if DEBUG:
self.log.info("ITUNES.upload_books():")
self._dump_files(files, header='upload_books()')
self._dump_cached_books('upload_books()')
self._dump_update_list('upload_books()')
if isosx:
if DEBUG:
self.log.info("ITUNES.upload_books():")
self._dump_files(files, header='upload_books()')
self._dump_cached_books('upload_books()')
self._dump_update_list('upload_books()')
for (i,file) in enumerate(files):
path = self.path_template % (metadata[i].title, metadata[i].author[0])
# Delete existing from Library|Books, add to self.update_list
@ -836,11 +863,36 @@ class ITUNES(DevicePlugin):
try:
pythoncom.CoInitialize()
self.iTunes = win32com.client.Dispatch("iTunes.Application")
lib = self.iTunes.sources.ItemByName('Library')
lib_playlists = [pl.Name for pl in lib.Playlists]
if not 'Books' in lib_playlists:
self.log.error(" no 'Books' playlist in Library")
library_books = lib.Playlists.ItemByName('Books')
for source in self.iTunes.sources:
if source.Kind == self.Sources.index('Library'):
lib = source
if DEBUG:
self.log.info(" Library source: '%s' kind: %s" % (lib.Name, self.Sources[lib.Kind]))
break
else:
if DEBUG:
self.log.info(" Library source not found")
if lib is not None:
lib_books = None
for pl in lib.Playlists:
if self.PlaylistKind[pl.Kind] == 'User' and self.PlaylistSpecialKind[pl.SpecialKind] == 'Books':
if DEBUG:
self.log.info(" Books playlist: '%s' special_kind: '%s'" % (pl.Name, self.PlaylistSpecialKind[pl.SpecialKind]))
lib_books = pl
break
else:
if DEBUG:
self.log.error(" no Books playlist found")
#
# lib = self.iTunes.sources.ItemByName('Library')
# lib_playlists = [pl.Name for pl in lib.Playlists]
# if not 'Books' in lib_playlists:
# self.log.error(" no 'Books' playlist in Library")
# library_books = lib.Playlists.ItemByName('Books')
#
for (i,file) in enumerate(files):
path = self.path_template % (metadata[i].title, metadata[i].author[0])
@ -859,10 +911,10 @@ class ITUNES(DevicePlugin):
# Add to iTunes Library|Books
if isinstance(file,PersistentTemporaryFile):
op_status = library_books.AddFile(file._name)
op_status = lib_books.AddFile(file._name)
self.log.info("ITUNES.upload_books():\n iTunes adding '%s'" % file._name)
else:
op_status = library_books.AddFile(file)
op_status = lib_books.AddFile(file)
self.log.info(" iTunes adding '%s'" % file)
if DEBUG:
@ -1060,20 +1112,6 @@ class ITUNES(DevicePlugin):
ub['author']))
self.log.info()
def _find_device_book(self, cached_book):
'''
Windows-only method to get a handle to a device book in the current pythoncom session
'''
SearchField = ['All','Visible','Artists','Titles','Composers','SongNames']
if iswindows:
dev_books = self.iTunes.sources.ItemByName(self.sources['iPod']).Playlists.ItemByName('Books')
hits = dev_books.Search(cached_book['title'],SearchField.index('Titles'))
if hits:
for hit in hits:
if hit.Artist == cached_book['author']:
return hit
return None
def _find_library_book(self, cached_book):
'''
Windows-only method to get a handle to a library book in the current pythoncom session
@ -1083,7 +1121,28 @@ class ITUNES(DevicePlugin):
if DEBUG:
self.log.info("ITUNES._find_library_book()")
self.log.info(" looking for '%s' by %s" % (cached_book['title'], cached_book['author']))
lib_books = self.iTunes.sources.ItemByName('Library').Playlists.ItemByName('Books')
for source in self.iTunes.sources:
if source.Kind == self.Sources.index('Library'):
lib = source
if DEBUG:
self.log.info(" Library source: '%s' kind: %s" % (lib.Name, self.Sources[lib.Kind]))
break
else:
if DEBUG:
self.log.info(" Library source not found")
if lib is not None:
lib_books = None
for pl in lib.Playlists:
if self.PlaylistKind[pl.Kind] == 'User' and self.PlaylistSpecialKind[pl.SpecialKind] == 'Books':
if DEBUG:
self.log.info(" Books playlist: '%s' special_kind: '%s'" % (pl.Name, self.PlaylistSpecialKind[pl.SpecialKind]))
lib_books = pl
break
else:
if DEBUG:
self.log.error(" no Books playlist found")
attempts = 9
while attempts:
@ -1121,12 +1180,6 @@ class ITUNES(DevicePlugin):
except:
zfw = zipfile.ZipFile(archive_path, mode='a')
else:
# if DEBUG:
# if isosx:
# self.log.info("ITUNES._generate_thumbnail(): cached thumb found for '%s'" % book.name())
# elif iswindows:
# self.log.info("ITUNES._generate_thumbnail(): cached thumb found for '%s'" % book.Name)
return thumb_data
if isosx:
@ -1160,7 +1213,7 @@ class ITUNES(DevicePlugin):
return None
# Save the cover from iTunes
tmp_thumb = os.path.join(tempfile.gettempdir(), "thumb.%s" % ArtworkFormat[book.Artwork.Item(1).Format])
tmp_thumb = os.path.join(tempfile.gettempdir(), "thumb.%s" % self.ArtworkFormat[book.Artwork.Item(1).Format])
book.Artwork.Item(1).SaveArtworkToFile(tmp_thumb)
try:
# Resize the cover
@ -1184,8 +1237,6 @@ class ITUNES(DevicePlugin):
def _get_device_book_size(self, title, author):
'''
Fetch the size of a book stored on the device
Windows: If sync-in-progress, this call blocked until sync completes
'''
if DEBUG:
self.log.info("ITUNES._get_device_book_size():\n looking for title: '%s' author: '%s'" %
@ -1214,53 +1265,134 @@ class ITUNES(DevicePlugin):
def _get_device_books(self):
'''
Assumes pythoncom wrapper
Assumes pythoncom wrapper for Windows
'''
if DEBUG:
self.log.info("\nITUNES._get_device_books()")
device_books = []
if isosx:
if 'iPod' in self.sources:
connected_device = self.sources['iPod']
if 'Books' in self.iTunes.sources[connected_device].playlists.name():
return self.iTunes.sources[connected_device].playlists['Books'].file_tracks()
return []
device = self.iTunes.sources[connected_device]
for pl in device.playlists():
if pl.special_kind() == appscript.k.Books:
if DEBUG:
self.log.info(" Book playlist: '%s' special_kind: '%s'" % (pl.name(), pl.special_kind()))
books = pl.file_tracks()
break
else:
self.log.error(" book_playlist not found")
for book in books:
if book.kind() in ['Book','Protected book']:
device_books.append(book)
else:
if DEBUG:
self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind()))
elif iswindows:
if 'iPod' in self.sources:
connected_device = self.sources['iPod']
dev = self.iTunes.sources.ItemByName(connected_device)
dev_playlists = [pl.Name for pl in dev.Playlists]
if 'Books' in dev_playlists:
return self.iTunes.sources.ItemByName(connected_device).Playlists.ItemByName('Books').Tracks
else:
return []
if DEBUG:
self.log.warning('ITUNES._get_device_book(): No iPod device connected')
return []
try:
pythoncom.CoInitialize()
connected_device = self.sources['iPod']
device = self.iTunes.sources.ItemByName(connected_device)
dev_books = None
for pl in device.Playlists:
if self.PlaylistKind[pl.Kind] == 'User' and self.PlaylistSpecialKind[pl.SpecialKind] == 'Books':
if DEBUG:
self.log.info(" Books playlist: '%s' special_kind: '%s'" % (pl.Name, self.PlaylistSpecialKind[pl.SpecialKind]))
dev_books = pl.Tracks
break
else:
if DEBUG:
self.log.info(" no Books playlist found")
for book in dev_books:
if book.KindAsString in ['Book','Protected book']:
device_books.append(book)
else:
self.log.info(" ignoring '%s' of type %s" % (book.Name, book.KindAsString))
finally:
pythoncom.CoUninitialize()
return device_books
def _get_library_books(self):
'''
Populate a dict of paths from iTunes Library|Books
'''
if DEBUG:
self.log.info("\nITUNES._get_library_books()")
library_books = {}
lib = None
if isosx:
lib = self.iTunes.sources['library']
if 'Books' in lib.playlists.name():
lib_books = lib.playlists['Books'].file_tracks()
for source in self.iTunes.sources():
if source.kind() == appscript.k.library:
lib = source
if DEBUG:
self.log.info(" Library source: '%s' kind: %s" % (lib.name(), lib.kind()))
break
else:
if DEBUG:
self.log.error(' Library source not found')
if lib is not None:
lib_books = None
for pl in lib.playlists():
if pl.special_kind() == appscript.k.Books:
if DEBUG:
self.log.info(" Books playlist: '%s' special_kind: '%s'" % (pl.name(), pl.special_kind()))
break
lib_books = pl.file_tracks()
for book in lib_books:
path = self.path_template % (book.name(), book.artist())
library_books[path] = book
if book.kind() in ['Book','Protected book']:
path = self.path_template % (book.name(), book.artist())
library_books[path] = book
else:
if DEBUG:
self.log.info(" ignoring library book of type '%s'" % book.kind())
else:
if DEBUG:
self.log.info('ITUNES._get_library_books():\n No Books playlist')
elif iswindows:
lib = None
try:
pythoncom.CoInitialize()
self.iTunes = win32com.client.Dispatch("iTunes.Application")
lib = self.iTunes.sources.ItemByName('Library')
lib_playlists = [pl.Name for pl in lib.Playlists]
if 'Books' in lib_playlists:
lib_books = lib.Playlists.ItemByName('Books').Tracks
for source in self.iTunes.sources:
if source.Kind == self.Sources.index('Library'):
lib = source
self.log.info(" Library source: '%s' kind: %s" % (lib.Name, self.Sources[lib.Kind]))
break
else:
self.log.error(" Library source not found")
if lib is not None:
lib_books = None
for pl in lib.Playlists:
if self.PlaylistKind[pl.Kind] == 'User' and self.PlaylistSpecialKind[pl.SpecialKind] == 'Books':
if DEBUG:
self.log.info(" Books playlist: '%s' special_kind: '%s'" % (pl.Name, self.PlaylistSpecialKind[pl.SpecialKind]))
lib_books = pl.Tracks
break
else:
if DEBUG:
self.log.error(" no Books playlist found")
for book in lib_books:
path = self.path_template % (book.Name, book.Artist)
library_books[path] = book
if book.KindAsString in ['Book','Protected book']:
path = self.path_template % (book.Name, book.Artist)
library_books[path] = book
else:
if DEBUG:
self.log.info(" ignoring '%s' of type %s" % (book.Name, book.KindAsString))
finally:
pythoncom.CoUninitialize()
@ -1455,11 +1587,9 @@ class ITUNES(DevicePlugin):
sys.stdout.write('\n')
sys.stdout.flush()
break
finally:
pythoncom.CoUninitialize()
class BookList(list):
'''
A list of books. Each Book object must have the fields: