iTunes driver: Various bug fixes. Add option to disable cover fetching for speedup with large book collections in iTunes.

This commit is contained in:
Kovid Goyal 2010-06-30 08:50:48 -06:00
commit 47479e630d
5 changed files with 445 additions and 256 deletions

View File

@ -89,6 +89,7 @@ class NYTimes(BasicNewsRecipe):
'relatedSearchesModule', 'relatedSearchesModule',
'side_tool', 'side_tool',
'singleAd', 'singleAd',
'subNavigation clearfix',
'subNavigation tabContent active', 'subNavigation tabContent active',
'subNavigation tabContent active clearfix', 'subNavigation tabContent active clearfix',
]}), ]}),
@ -460,8 +461,10 @@ class NYTimes(BasicNewsRecipe):
if mp_off >= 0: if mp_off >= 0:
c = c[:mp_off] c = c[:mp_off]
emTag.insert(0, c) emTag.insert(0, c)
hrTag = Tag(soup, 'hr') #hrTag = Tag(soup, 'hr')
#hrTag['style'] = "margin-top:0em;margin-bottom:0em" #hrTag['class'] = 'caption_divider'
hrTag = Tag(soup, 'div')
hrTag['class'] = 'divider'
emTag.insert(1, hrTag) emTag.insert(1, hrTag)
caption.replaceWith(emTag) caption.replaceWith(emTag)

View File

@ -76,6 +76,7 @@ class NYTimes(BasicNewsRecipe):
'relatedSearchesModule', 'relatedSearchesModule',
'side_tool', 'side_tool',
'singleAd', 'singleAd',
'subNavigation clearfix',
'subNavigation tabContent active', 'subNavigation tabContent active',
'subNavigation tabContent active clearfix', 'subNavigation tabContent active clearfix',
]}), ]}),
@ -350,8 +351,10 @@ class NYTimes(BasicNewsRecipe):
if mp_off >= 0: if mp_off >= 0:
c = c[:mp_off] c = c[:mp_off]
emTag.insert(0, c) emTag.insert(0, c)
hrTag = Tag(soup, 'hr') #hrTag = Tag(soup, 'hr')
#hrTag['style'] = "margin-top:0em;margin-bottom:0em" #hrTag['class'] = 'caption_divider'
hrTag = Tag(soup, 'div')
hrTag['class'] = 'divider'
emTag.insert(1, hrTag) emTag.insert(1, hrTag)
caption.replaceWith(emTag) caption.replaceWith(emTag)

View File

@ -275,13 +275,44 @@ class iPadOutput(OutputProfile):
# touchscreen_news_css {{{ # touchscreen_news_css {{{
touchscreen_news_css = u''' touchscreen_news_css = u'''
/* hr used in articles */ /* hr used in articles */
.article_articles_list {
width:18%;
}
.article_link {
color: #593f29;
font-style: italic;
}
.article_next {
-webkit-border-top-right-radius:4px;
-webkit-border-bottom-right-radius:4px;
font-style: italic;
width:32%;
}
.article_prev {
-webkit-border-top-left-radius:4px;
-webkit-border-bottom-left-radius:4px;
font-style: italic;
width:32%;
}
.article_sections_list {
width:18%;
}
.articles_link {
font-weight: bold;
}
.sections_link {
font-weight: bold;
}
.caption_divider { .caption_divider {
border:#ccc 1px solid; border:#ccc 1px solid;
} }
.touchscreen_navbar { .touchscreen_navbar {
background:#ccc; background:#c3bab2;
border:#ccc 1px solid; border:#ccc 0px solid;
border-collapse:separate; border-collapse:separate;
border-spacing:1px; border-spacing:1px;
margin-left: 5%; margin-left: 5%;
@ -292,22 +323,16 @@ class iPadOutput(OutputProfile):
.touchscreen_navbar td { .touchscreen_navbar td {
background:#fff; background:#fff;
font-family:Helvetica; font-family:Helvetica;
font-size:90%; font-size:80%;
padding: 5px; /* UI touchboxes use 8px padding */
padding: 6px;
text-align:center; text-align:center;
} }
.touchscreen_navbar td:first-child {
-webkit-border-top-left-radius:4px;
-webkit-border-bottom-left-radius:4px;
}
.touchscreen_navbar td:last-child {
-webkit-border-top-right-radius:4px;
-webkit-border-bottom-right-radius:4px;
}
.feed_link { .touchscreen_navbar td a:link {
font-style: italic; color: #593f29;
} text-decoration: none;
}
/* Index formatting */ /* Index formatting */
.publish_date { .publish_date {
@ -318,12 +343,50 @@ class iPadOutput(OutputProfile):
border-top:1px solid gray; border-top:1px solid gray;
} }
hr.caption_divider {
border-color:black;
border-style:solid;
border-width:1px;
}
/* Feed summary formatting */ /* Feed summary formatting */
.article_summary {
display:inline-block;
}
.feed {
font-family:sans-serif;
font-weight:bold;
font-size:larger;
}
.feed_link {
font-style: italic;
}
.feed_next {
-webkit-border-top-right-radius:4px;
-webkit-border-bottom-right-radius:4px;
font-style: italic;
width:40%;
}
.feed_prev {
-webkit-border-top-left-radius:4px;
-webkit-border-bottom-left-radius:4px;
font-style: italic;
width:40%;
}
.feed_title { .feed_title {
text-align: center; text-align: center;
font-size: 160%; font-size: 160%;
} }
.feed_up {
font-weight: bold;
width:20%;
}
.summary_headline { .summary_headline {
font-weight:bold; font-weight:bold;
text-align:left; text-align:left;
@ -338,12 +401,6 @@ class iPadOutput(OutputProfile):
text-align:left; text-align:left;
} }
.feed {
font-family:sans-serif;
font-weight:bold;
font-size:larger;
}
''' '''
# }}} # }}}

View File

@ -20,7 +20,7 @@ from calibre.utils.config import config_dir
from calibre.utils.date import isoformat, now, parse_date from calibre.utils.date import isoformat, now, parse_date
from calibre.utils.localization import get_lang from calibre.utils.localization import get_lang
from calibre.utils.logging import Log from calibre.utils.logging import Log
from calibre.utils.zipfile import ZipFile from calibre.utils.zipfile import ZipFile, safe_replace
from PIL import Image as PILImage from PIL import Image as PILImage
@ -38,6 +38,7 @@ if iswindows:
class DriverBase(DeviceConfig, DevicePlugin): class DriverBase(DeviceConfig, DevicePlugin):
# Needed for config_widget to work # Needed for config_widget to work
FORMATS = ['epub', 'pdf'] FORMATS = ['epub', 'pdf']
SUPPORTS_SUB_DIRS = True # To enable second checkbox in customize widget
@classmethod @classmethod
def _config_base_name(cls): def _config_base_name(cls):
@ -87,7 +88,7 @@ class ITUNES(DriverBase):
supported_platforms = ['osx','windows'] supported_platforms = ['osx','windows']
author = 'GRiker' author = 'GRiker'
#: The version of this plugin as a 3-tuple (major, minor, revision) #: The version of this plugin as a 3-tuple (major, minor, revision)
version = (0,8,0) version = (0,9,0)
OPEN_FEEDBACK_MESSAGE = _( OPEN_FEEDBACK_MESSAGE = _(
'Apple device detected, launching iTunes, please wait ...') 'Apple device detected, launching iTunes, please wait ...')
@ -106,53 +107,55 @@ class ITUNES(DriverBase):
BCD = [0x01] BCD = [0x01]
# iTunes enumerations # iTunes enumerations
Sources = [ Audiobooks = [
'Unknown', 'Audible file',
'Library', 'MPEG audio file',
'iPod', 'Protected AAC audio file'
'AudioCD', ]
'MP3CD',
'Device',
'RadioTuner',
'SharedLibrary']
ArtworkFormat = [ ArtworkFormat = [
'Unknown', 'Unknown',
'JPEG', 'JPEG',
'PNG', 'PNG',
'BMP' 'BMP'
] ]
PlaylistKind = [ PlaylistKind = [
'Unknown', 'Unknown',
'Library', 'Library',
'User', 'User',
'CD', 'CD',
'Device', 'Device',
'Radio Tuner' 'Radio Tuner'
] ]
PlaylistSpecialKind = [ PlaylistSpecialKind = [
'Unknown', 'Unknown',
'Purchased Music', 'Purchased Music',
'Party Shuffle', 'Party Shuffle',
'Podcasts', 'Podcasts',
'Folder', 'Folder',
'Video', 'Video',
'Music', 'Music',
'Movies', 'Movies',
'TV Shows', 'TV Shows',
'Books', 'Books',
] ]
SearchField = [ SearchField = [
'All', 'All',
'Visible', 'Visible',
'Artists', 'Artists',
'Albums', 'Albums',
'Composers', 'Composers',
'SongNames', 'SongNames',
] ]
Sources = [
'Unknown',
'Library',
'iPod',
'AudioCD',
'MP3CD',
'Device',
'RadioTuner',
'SharedLibrary'
]
# Cover art size limits # Cover art size limits
MAX_COVER_WIDTH = 510 MAX_COVER_WIDTH = 510
@ -161,6 +164,7 @@ class ITUNES(DriverBase):
# Properties # Properties
cached_books = {} cached_books = {}
cache_dir = os.path.join(config_dir, 'caches', 'itunes') cache_dir = os.path.join(config_dir, 'caches', 'itunes')
archive_path = os.path.join(cache_dir, "thumbs.zip")
description_prefix = "added by calibre" description_prefix = "added by calibre"
ejected = False ejected = False
iTunes= None iTunes= None
@ -168,7 +172,7 @@ class ITUNES(DriverBase):
library_orphans = None library_orphans = None
log = Log() log = Log()
manual_sync_mode = False manual_sync_mode = False
path_template = 'iTunes/%s - %s.epub' path_template = 'iTunes/%s - %s.%s'
problem_titles = [] problem_titles = []
problem_msg = None problem_msg = None
report_progress = None report_progress = None
@ -252,7 +256,7 @@ class ITUNES(DriverBase):
(new_book.title, new_book.author)) (new_book.title, new_book.author))
booklists[0].append(new_book) booklists[0].append(new_book)
if DEBUG: if False:
self._dump_booklist(booklists[0],header='after',indent=2) self._dump_booklist(booklists[0],header='after',indent=2)
self._dump_cached_books(header='after',indent=2) self._dump_cached_books(header='after',indent=2)
@ -273,10 +277,13 @@ class ITUNES(DriverBase):
""" """
if not oncard: if not oncard:
if DEBUG: if DEBUG:
self.log.info("ITUNES:books(oncard=%s)" % oncard) self.log.info("ITUNES:books():")
if self.settings().use_subdirs:
self.log.info(" Cover fetching/caching enabled")
else:
self.log.info(" Cover fetching/caching disabled")
# Fetch a list of books from iPod device connected to iTunes # Fetch a list of books from iPod device connected to iTunes
if 'iPod' in self.sources: if 'iPod' in self.sources:
booklist = BookList(self.log) booklist = BookList(self.log)
cached_books = {} cached_books = {}
@ -287,11 +294,12 @@ class ITUNES(DriverBase):
book_count = float(len(device_books)) book_count = float(len(device_books))
for (i,book) in enumerate(device_books): for (i,book) in enumerate(device_books):
this_book = Book(book.name(), book.artist()) this_book = Book(book.name(), book.artist())
this_book.path = self.path_template % (book.name(), book.artist()) format = 'pdf' if book.kind().startswith('PDF') else 'epub'
this_book.path = self.path_template % (book.name(), book.artist(),format)
try: try:
this_book.datetime = parse_date(str(book.date_added())).timetuple() this_book.datetime = parse_date(str(book.date_added())).timetuple()
except: except:
pass this_book.datetime = time.gmtime()
this_book.db_id = None this_book.db_id = None
this_book.device_collections = [] this_book.device_collections = []
this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None
@ -325,11 +333,12 @@ class ITUNES(DriverBase):
book_count = float(len(device_books)) book_count = float(len(device_books))
for (i,book) in enumerate(device_books): for (i,book) in enumerate(device_books):
this_book = Book(book.Name, book.Artist) this_book = Book(book.Name, book.Artist)
this_book.path = self.path_template % (book.Name, book.Artist) format = 'pdf' if book.KindAsString.startswith('PDF') else 'epub'
this_book.path = self.path_template % (book.Name, book.Artist,format)
try: try:
this_book.datetime = parse_date(str(book.DateAdded)).timetuple() this_book.datetime = parse_date(str(book.DateAdded)).timetuple()
except: except:
pass this_book.datetime = time.gmtime()
this_book.db_id = None this_book.db_id = None
this_book.device_collections = [] this_book.device_collections = []
this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None
@ -532,8 +541,10 @@ class ITUNES(DriverBase):
# Turn off the Save template # Turn off the Save template
cw.opt_save_template.setVisible(False) cw.opt_save_template.setVisible(False)
cw.label.setVisible(False) cw.label.setVisible(False)
# Repurpose the checkbox # Repurpose the metadata checkbox
cw.opt_read_metadata.setText(_("Use Series as Category in iTunes/iBooks")) cw.opt_read_metadata.setText(_("Use Series as Category in iTunes/iBooks"))
# Repurpose the use_subdirs checkbox
cw.opt_use_subdirs.setText(_("Cache covers from iTunes/iBooks"))
return cw return cw
def delete_books(self, paths, end_session=True): def delete_books(self, paths, end_session=True):
@ -691,21 +702,19 @@ class ITUNES(DriverBase):
self.log.info("ITUNES.open()") self.log.info("ITUNES.open()")
# Confirm/create thumbs archive # Confirm/create thumbs archive
archive_path = os.path.join(self.cache_dir, "thumbs.zip")
if not os.path.exists(self.cache_dir): if not os.path.exists(self.cache_dir):
if DEBUG: if DEBUG:
self.log.info(" creating thumb cache '%s'" % self.cache_dir) self.log.info(" creating thumb cache '%s'" % self.cache_dir)
os.makedirs(self.cache_dir) os.makedirs(self.cache_dir)
if not os.path.exists(archive_path): if not os.path.exists(self.archive_path):
self.log.info(" creating zip archive") self.log.info(" creating zip archive")
zfw = ZipFile(archive_path, mode='w') zfw = ZipFile(self.archive_path, mode='w')
zfw.writestr("iTunes Thumbs Archive",'') zfw.writestr("iTunes Thumbs Archive",'')
zfw.close() zfw.close()
else: else:
if DEBUG: if DEBUG:
self.log.info(" existing thumb cache at '%s'" % archive_path) self.log.info(" existing thumb cache at '%s'" % self.archive_path)
def remove_books_from_metadata(self, paths, booklists): def remove_books_from_metadata(self, paths, booklists):
''' '''
@ -722,21 +731,61 @@ class ITUNES(DriverBase):
if DEBUG: if DEBUG:
self.log.info("ITUNES.remove_books_from_metadata()") self.log.info("ITUNES.remove_books_from_metadata()")
for path in paths: for path in paths:
self._dump_cached_book(self.cached_books[path], indent=2) if DEBUG:
self._dump_cached_book(self.cached_books[path], indent=2)
self.log.info(" looking for '%s' by '%s' (%s)" %
(self.cached_books[path]['title'],
self.cached_books[path]['author'],
self.cached_books[path]['uuid']))
# Purge the booklist, self.cached_books # Purge the booklist, self.cached_books, thumb cache
for i,bl_book in enumerate(booklists[0]): for i,bl_book in enumerate(booklists[0]):
if False: if False:
self.log.info(" evaluating '%s'" % bl_book.uuid) self.log.info(" evaluating '%s' by '%s' (%s)" %
if bl_book.uuid == self.cached_books[path]['uuid']: (bl_book.title, bl_book.author,bl_book.uuid))
# Remove from booklists[0]
booklists[0].pop(i)
found = False
if bl_book.uuid == self.cached_books[path]['uuid']:
if False:
self.log.info(" matched with uuid")
booklists[0].pop(i)
found = True
elif bl_book.title == self.cached_books[path]['title'] and \
bl_book.author[0] == self.cached_books[path]['author']:
if False:
self.log.info(" matched with title + author")
booklists[0].pop(i)
found = True
if found:
# Remove from self.cached_books
for cb in self.cached_books: for cb in self.cached_books:
if self.cached_books[cb]['uuid'] == self.cached_books[path]['uuid']: if self.cached_books[cb]['uuid'] == self.cached_books[path]['uuid']:
self.cached_books.pop(cb) self.cached_books.pop(cb)
break break
# Remove from thumb from thumb cache
thumb_path = path.rpartition('.')[0] + '.jpg'
zf = ZipFile(self.archive_path,'a')
fnames = zf.namelist()
try:
thumb = [x for x in fnames if thumb_path in x][0]
except:
thumb = None
if thumb:
if DEBUG:
self.log.info(" deleting '%s' from cover cache" % (thumb_path))
zf.delete(thumb_path)
else:
if DEBUG:
self.log.info(" '%s' not found in cover cache" % thumb_path)
zf.close()
break break
# else:
# if DEBUG:
# self.log.error(" unable to find '%s' by '%s' (%s)" %
# (bl_book.title, bl_book.author,bl_book.uuid))
if False: if False:
self._dump_booklist(booklists[0], indent = 2) self._dump_booklist(booklists[0], indent = 2)
@ -842,12 +891,11 @@ class ITUNES(DriverBase):
self.log.info("ITUNES.upload_books()") self.log.info("ITUNES.upload_books()")
self._dump_files(files, header='upload_books()',indent=2) self._dump_files(files, header='upload_books()',indent=2)
self._dump_update_list(header='upload_books()',indent=2) self._dump_update_list(header='upload_books()',indent=2)
#self.log.info(" self.settings().format_map: %s" % self.settings().format_map)
if isosx: if isosx:
for (i,file) in enumerate(files): for (i,file) in enumerate(files):
format = file.rpartition('.')[2].lower() format = file.rpartition('.')[2].lower()
path = self.path_template % (metadata[i].title, metadata[i].author[0]) path = self.path_template % (metadata[i].title, metadata[i].author[0],format)
self._remove_existing_copy(path, metadata[i]) self._remove_existing_copy(path, metadata[i])
fpath = self._get_fpath(file, metadata[i], format, update_md=True) fpath = self._get_fpath(file, metadata[i], format, update_md=True)
db_added, lb_added = self._add_new_copy(fpath, metadata[i]) db_added, lb_added = self._add_new_copy(fpath, metadata[i])
@ -856,7 +904,10 @@ class ITUNES(DriverBase):
new_booklist.append(this_book) new_booklist.append(this_book)
self._update_iTunes_metadata(metadata[i], db_added, lb_added, this_book) self._update_iTunes_metadata(metadata[i], db_added, lb_added, this_book)
# Add new_book to self.cached_paths # Add new_book to self.cached_books
if DEBUG:
self.log.info(" adding '%s' by '%s' ['%s'] to self.cached_books" %
( metadata[i].title, metadata[i].author, metadata[i].uuid))
self.cached_books[this_book.path] = { self.cached_books[this_book.path] = {
'author': metadata[i].author, 'author': metadata[i].author,
'dev_book': db_added, 'dev_book': db_added,
@ -877,7 +928,7 @@ class ITUNES(DriverBase):
for (i,file) in enumerate(files): for (i,file) in enumerate(files):
format = file.rpartition('.')[2].lower() format = file.rpartition('.')[2].lower()
path = self.path_template % (metadata[i].title, metadata[i].author[0]) path = self.path_template % (metadata[i].title, metadata[i].author[0],format)
self._remove_existing_copy(path, metadata[i]) self._remove_existing_copy(path, metadata[i])
fpath = self._get_fpath(file, metadata[i],format, update_md=True) fpath = self._get_fpath(file, metadata[i],format, update_md=True)
db_added, lb_added = self._add_new_copy(fpath, metadata[i]) db_added, lb_added = self._add_new_copy(fpath, metadata[i])
@ -1091,7 +1142,7 @@ class ITUNES(DriverBase):
thumb = None thumb = None
if metadata.cover: if metadata.cover:
if (format == 'epub'): if format == 'epub':
# Pre-shrink cover # Pre-shrink cover
# self.MAX_COVER_WIDTH, self.MAX_COVER_HEIGHT # self.MAX_COVER_WIDTH, self.MAX_COVER_HEIGHT
try: try:
@ -1171,17 +1222,18 @@ class ITUNES(DriverBase):
# Refresh the thumbnail cache # Refresh the thumbnail cache
if DEBUG: if DEBUG:
self.log.info( " refreshing cached thumb for '%s'" % metadata.title) self.log.info( " refreshing cached thumb for '%s'" % metadata.title)
archive_path = os.path.join(self.cache_dir, "thumbs.zip") zfw = ZipFile(self.archive_path, mode='a')
zfw = ZipFile(archive_path, mode='a')
thumb_path = path.rpartition('.')[0] + '.jpg' thumb_path = path.rpartition('.')[0] + '.jpg'
zfw.writestr(thumb_path, thumb) zfw.writestr(thumb_path, thumb)
except: except:
self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0])) self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0]))
self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title)) self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title))
finally: finally:
zfw.close() zfw.close()
else:
if DEBUG:
self.log.info(" no cover defined in metadata for '%s'" % metadata.title)
return thumb return thumb
def _create_new_book(self,fpath, metadata, path, db_added, lb_added, thumb, format): def _create_new_book(self,fpath, metadata, path, db_added, lb_added, thumb, format):
@ -1190,8 +1242,9 @@ class ITUNES(DriverBase):
if DEBUG: if DEBUG:
self.log.info(" ITUNES._create_new_book()") self.log.info(" ITUNES._create_new_book()")
this_book = Book(metadata.title, metadata.author[0]) #this_book = Book(metadata.title, metadata.author[0])
this_book = Book(metadata.title, ' & '.join(metadata.author))
this_book.datetime = time.gmtime()
this_book.db_id = None this_book.db_id = None
this_book.device_collections = [] this_book.device_collections = []
this_book.format = format this_book.format = format
@ -1207,13 +1260,13 @@ class ITUNES(DriverBase):
try: try:
this_book.datetime = parse_date(str(lb_added.date_added())).timetuple() this_book.datetime = parse_date(str(lb_added.date_added())).timetuple()
except: except:
this_book.datetime = time.gmtime() pass
elif db_added: elif db_added:
this_book.size = self._get_device_book_size(fpath, db_added.size()) this_book.size = self._get_device_book_size(fpath, db_added.size())
try: try:
this_book.datetime = parse_date(str(db_added.date_added())).timetuple() this_book.datetime = parse_date(str(db_added.date_added())).timetuple()
except: except:
this_book.datetime = time.gmtime() pass
elif iswindows: elif iswindows:
if lb_added: if lb_added:
@ -1221,13 +1274,13 @@ class ITUNES(DriverBase):
try: try:
this_book.datetime = parse_date(str(lb_added.DateAdded)).timetuple() this_book.datetime = parse_date(str(lb_added.DateAdded)).timetuple()
except: except:
this_book.datetime = time.gmtime() pass
elif db_added: elif db_added:
this_book.size = self._get_device_book_size(fpath, db_added.Size) this_book.size = self._get_device_book_size(fpath, db_added.Size)
try: try:
this_book.datetime = parse_date(str(db_added.DateAdded)).timetuple() this_book.datetime = parse_date(str(db_added.DateAdded)).timetuple()
except: except:
this_book.datetime = time.gmtime() pass
return this_book return this_book
@ -1244,7 +1297,8 @@ class ITUNES(DriverBase):
plist = None plist = None
if plist: if plist:
if DEBUG: if DEBUG:
self.log.info(" deleting %s from %s" % (pl_name,fpath)) self.log.info(" _delete_iTunesMetadata_plist():")
self.log.info(" deleting '%s'\n from '%s'" % (pl_name,fpath))
zf.delete(pl_name) zf.delete(pl_name)
zf.close() zf.close()
@ -1494,27 +1548,45 @@ class ITUNES(DriverBase):
if iswindows: if iswindows:
dev_books = self._get_device_books_playlist() dev_books = self._get_device_books_playlist()
if DEBUG: if DEBUG:
self.log.info(" ITUNES._find_device_book(uuid)") self.log.info(" ITUNES._find_device_book()")
self.log.info(" searching for %s ('%s' by %s)" % self.log.info(" searching for '%s' by '%s' (%s)" %
(search['uuid'], search['title'], search['author'])) (search['title'], search['author'],search['uuid']))
attempts = 9 attempts = 9
while attempts: while attempts:
# Try by uuid - only one hit # Try by uuid - only one hit
hits = dev_books.Search(search['uuid'],self.SearchField.index('All')) if 'uuid' in search and search['uuid']:
if hits: if DEBUG:
hit = hits[0] self.log.info(" searching by uuid '%s' ..." % search['uuid'])
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer)) hits = dev_books.Search(search['uuid'],self.SearchField.index('All'))
return hit if hits:
hit = hits[0]
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
return hit
# Try by author - there could be multiple hits # Try by author - there could be multiple hits
hits = dev_books.Search(search['author'],self.SearchField.index('Artists')) if search['author']:
if DEBUG:
self.log.info(" searching by author '%s' ..." % search['author'])
hits = dev_books.Search(search['author'],self.SearchField.index('Artists'))
if hits:
for hit in hits:
if hit.Name == search['title']:
if DEBUG:
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
return hit
# Search by title if no author available
if DEBUG:
self.log.info(" searching by title '%s' ..." % search['title'])
hits = dev_books.Search(search['title'],self.SearchField.index('All'))
if hits: if hits:
for hit in hits: for hit in hits:
if hit.Name == search['title']: if hit.Name == search['title']:
if DEBUG: if DEBUG:
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer)) self.log.info(" found '%s'" % (hit.Name))
return hit return hit
# PDF just sent, title not updated yet, look for export pattern
# PDF metadata was rewritten at export as 'safe(title) - safe(author)' # PDF metadata was rewritten at export as 'safe(title) - safe(author)'
if search['format'] == 'pdf': if search['format'] == 'pdf':
title = re.sub(r'[^0-9a-zA-Z ]', '_', search['title']) title = re.sub(r'[^0-9a-zA-Z ]', '_', search['title'])
@ -1547,12 +1619,14 @@ class ITUNES(DriverBase):
if iswindows: if iswindows:
if DEBUG: if DEBUG:
self.log.info(" ITUNES._find_library_book()") self.log.info(" ITUNES._find_library_book()")
'''
if 'uuid' in search: if 'uuid' in search:
self.log.info(" looking for '%s' by %s (%s)" % self.log.info(" looking for '%s' by %s (%s)" %
(search['title'], search['author'], search['uuid'])) (search['title'], search['author'], search['uuid']))
else: else:
self.log.info(" looking for '%s' by %s" % self.log.info(" looking for '%s' by %s" %
(search['title'], search['author'])) (search['title'], search['author']))
'''
for source in self.iTunes.sources: for source in self.iTunes.sources:
if source.Kind == self.Sources.index('Library'): if source.Kind == self.Sources.index('Library'):
@ -1577,10 +1651,11 @@ class ITUNES(DriverBase):
if DEBUG: if DEBUG:
self.log.error(" no Books playlist found") self.log.error(" no Books playlist found")
attempts = 9 attempts = 9
while attempts: while attempts:
# Find book whose Album field = search['uuid'] # Find book whose Album field = search['uuid']
if 'uuid' in search: if 'uuid' in search and search['uuid']:
if DEBUG: if DEBUG:
self.log.info(" searching by uuid '%s' ..." % search['uuid']) self.log.info(" searching by uuid '%s' ..." % search['uuid'])
hits = lib_books.Search(search['uuid'],self.SearchField.index('All')) hits = lib_books.Search(search['uuid'],self.SearchField.index('All'))
@ -1590,16 +1665,30 @@ class ITUNES(DriverBase):
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer)) self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
return hit return hit
# Search by author if known
if search['author']:
if DEBUG:
self.log.info(" searching by author '%s' ..." % search['author'])
hits = lib_books.Search(search['author'],self.SearchField.index('Artists'))
if hits:
for hit in hits:
if hit.Name == search['title']:
if DEBUG:
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
return hit
# Search by title if no author available
if DEBUG: if DEBUG:
self.log.info(" searching by author '%s' ..." % search['author']) self.log.info(" searching by title '%s' ..." % search['title'])
hits = lib_books.Search(search['author'],self.SearchField.index('Artists')) hits = lib_books.Search(search['title'],self.SearchField.index('All'))
if hits: if hits:
for hit in hits: for hit in hits:
if hit.Name == search['title']: if hit.Name == search['title']:
if DEBUG: if DEBUG:
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer)) self.log.info(" found '%s'" % (hit.Name))
return hit return hit
# PDF just sent, title not updated yet, look for export pattern
# PDF metadata was rewritten at export as 'safe(title) - safe(author)' # PDF metadata was rewritten at export as 'safe(title) - safe(author)'
if search['format'] == 'pdf': if search['format'] == 'pdf':
title = re.sub(r'[^0-9a-zA-Z ]', '_', search['title']) title = re.sub(r'[^0-9a-zA-Z ]', '_', search['title'])
@ -1633,96 +1722,116 @@ class ITUNES(DriverBase):
as of iTunes 9.2, iBooks 1.1, can't set artwork for PDF files via automation as of iTunes 9.2, iBooks 1.1, can't set artwork for PDF files via automation
''' '''
archive_path = os.path.join(self.cache_dir, "thumbs.zip") # self.settings().use_subdirs is a repurposed DeviceConfig field
thumb_path = book_path.rpartition('.')[0] + '.jpg' # We're using it to skip fetching/caching covers to speed things up
format = book_path.rpartition('.')[2].lower() if not self.settings().use_subdirs:
thumb_data = None
try:
zfr = ZipFile(archive_path)
thumb_data = zfr.read(thumb_path)
zfr.close()
except:
zfw = ZipFile(archive_path, mode='a')
else:
return thumb_data return thumb_data
self.log.info(" ITUNES._generate_thumbnail()") thumb_path = book_path.rpartition('.')[0] + '.jpg'
format = book_path.rpartition('.')[2].lower()
if isosx: if isosx:
if format == 'epub': title = book.name()
try: elif iswindows:
if False: title = book.Name
self.log.info(" fetching artwork from %s\n %s" % (book_path,book))
# Resize the cover
data = book.artworks[1].raw_data().data
#self._dump_hex(data[:256])
img_data = cStringIO.StringIO(data)
im = PILImage.open(img_data)
scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80)
im = im.resize((int(width),int(height)), PILImage.ANTIALIAS)
img_data.close()
thumb = cStringIO.StringIO() try:
im.convert('RGB').save(thumb,'JPEG') zfr = ZipFile(self.archive_path)
thumb_data = thumb.getvalue() thumb_data = zfr.read(thumb_path)
thumb.close() if thumb_data == 'None':
if False:
self.log.info(" ITUNES._generate_thumbnail()\n returning None from cover cache for '%s'" % title)
zfr.close()
return None
except:
zfw = ZipFile(self.archive_path, mode='a')
else:
if False:
self.log.info(" returning thumb from cache for '%s'" % title)
return thumb_data
# Cache the tagged thumb if DEBUG:
if DEBUG: self.log.info(" ITUNES._generate_thumbnail():")
self.log.info(" generated thumb for '%s', caching" % book.name()) if isosx:
zfw.writestr(thumb_path, thumb_data)
zfw.close() # Fetch the artwork from iTunes
return thumb_data try:
except: data = book.artworks[1].raw_data().data
self.log.error(" error generating thumb for '%s'" % book.name()) except:
try: # If no artwork, write an empty marker to cache
zfw.close()
except:
pass
return None
else:
if DEBUG: if DEBUG:
self.log.info(" unable to generate PDF thumbs") self.log.error(" error fetching iTunes artwork for '%s'" % title)
zfw.writestr(thumb_path, 'None')
zfw.close()
return None return None
elif iswindows: # Generate a thumb
try:
img_data = cStringIO.StringIO(data)
im = PILImage.open(img_data)
scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80)
im = im.resize((int(width),int(height)), PILImage.ANTIALIAS)
thumb = cStringIO.StringIO()
im.convert('RGB').save(thumb,'JPEG')
thumb_data = thumb.getvalue()
thumb.close()
if False:
self.log.info(" generated thumb for '%s', caching" % title)
# Cache the tagged thumb
zfw.writestr(thumb_path, thumb_data)
except:
if DEBUG:
self.log.error(" error generating thumb for '%s', caching empty marker" % book.name())
self._dump_hex(data[:32])
thumb_data = None
# Cache the empty cover
zfw.writestr(thumb_path, 'None')
finally:
img_data.close()
zfw.close()
return thumb_data
elif iswindows:
if not book.Artwork.Count: if not book.Artwork.Count:
if DEBUG: if DEBUG:
self.log.info(" no artwork available for '%s'" % book.Name) self.log.info(" no artwork available for '%s'" % book.Name)
zfw.writestr(thumb_path, 'None')
zfw.close()
return None return None
if format == 'epub': # Fetch the artwork from iTunes
# Save the cover from iTunes
try:
tmp_thumb = os.path.join(tempfile.gettempdir(), "thumb.%s" % self.ArtworkFormat[book.Artwork.Item(1).Format])
book.Artwork.Item(1).SaveArtworkToFile(tmp_thumb)
# Resize the cover
im = PILImage.open(tmp_thumb)
scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80)
im = im.resize((int(width),int(height)), PILImage.ANTIALIAS)
thumb = cStringIO.StringIO()
im.convert('RGB').save(thumb,'JPEG')
thumb_data = thumb.getvalue()
os.remove(tmp_thumb)
thumb.close()
# Cache the tagged thumb try:
if DEBUG: tmp_thumb = os.path.join(tempfile.gettempdir(), "thumb.%s" % self.ArtworkFormat[book.Artwork.Item(1).Format])
self.log.info(" generated thumb for '%s', caching" % book.Name) book.Artwork.Item(1).SaveArtworkToFile(tmp_thumb)
zfw.writestr(thumb_path, thumb_data) # Resize the cover
zfw.close() im = PILImage.open(tmp_thumb)
return thumb_data scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80)
except: im = im.resize((int(width),int(height)), PILImage.ANTIALIAS)
self.log.error(" error generating thumb for '%s'" % book.Name) thumb = cStringIO.StringIO()
try: im.convert('RGB').save(thumb,'JPEG')
zfw.close() thumb_data = thumb.getvalue()
except: os.remove(tmp_thumb)
pass thumb.close()
return None if False:
else: self.log.info(" generated thumb for '%s', caching" % book.Name)
# Cache the tagged thumb
zfw.writestr(thumb_path, thumb_data)
except:
if DEBUG: if DEBUG:
self.log.info(" unable to generate PDF thumbs") self.log.error(" error generating thumb for '%s', caching empty marker" % book.Name)
return None self._dump_hex(data[:32])
thumb_data = None
# Cache the empty cover
zfw.writestr(thumb_path,'None')
finally:
zfw.close()
return thumb_data
def _get_device_book_size(self, file, compressed_size): def _get_device_book_size(self, file, compressed_size):
''' '''
@ -1766,7 +1875,7 @@ class ITUNES(DriverBase):
for book in books: for book in books:
# This may need additional entries for international iTunes users # This may need additional entries for international iTunes users
if book.kind() in ['MPEG audio file']: if book.kind() in self.Audiobooks:
if DEBUG: if DEBUG:
self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind())) self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind()))
else: else:
@ -1798,7 +1907,7 @@ class ITUNES(DriverBase):
for book in dev_books: for book in dev_books:
# This may need additional entries for international iTunes users # This may need additional entries for international iTunes users
if book.KindAsString in ['MPEG audio file']: if book.KindAsString in self.Audiobooks:
if DEBUG: if DEBUG:
self.log.info(" ignoring '%s' of type '%s'" % (book.Name, book.KindAsString)) self.log.info(" ignoring '%s' of type '%s'" % (book.Name, book.KindAsString))
else: else:
@ -1899,12 +2008,13 @@ class ITUNES(DriverBase):
lib_books = pl.file_tracks() lib_books = pl.file_tracks()
for book in lib_books: for book in lib_books:
# This may need additional entries for international iTunes users # This may need additional entries for international iTunes users
if book.kind() in ['MPEG audio file']: if book.kind() in self.Audiobooks:
if DEBUG: if DEBUG:
self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind())) self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind()))
else: else:
# Collect calibre orphans - remnants of recipe uploads # Collect calibre orphans - remnants of recipe uploads
path = self.path_template % (book.name(), book.artist()) format = 'pdf' if book.kind().startswith('PDF') else 'epub'
path = self.path_template % (book.name(), book.artist(),format)
if str(book.description()).startswith(self.description_prefix): if str(book.description()).startswith(self.description_prefix):
try: try:
if book.location() == appscript.k.missing_value: if book.location() == appscript.k.missing_value:
@ -1917,7 +2027,8 @@ class ITUNES(DriverBase):
library_books[path] = book library_books[path] = book
if DEBUG: if DEBUG:
self.log.info(" %-30.30s %-30.30s %-40.40s [%s]" % (book.name(), book.artist(), book.album(), book.kind())) self.log.info(" %-30.30s %-30.30s %-40.40s [%s]" %
(book.name(), book.artist(), book.album(), book.kind()))
else: else:
if DEBUG: if DEBUG:
self.log.info(' no Library playlists') self.log.info(' no Library playlists')
@ -1955,11 +2066,12 @@ class ITUNES(DriverBase):
try: try:
for book in lib_books: for book in lib_books:
# This may need additional entries for international iTunes users # This may need additional entries for international iTunes users
if book.KindAsString in ['MPEG audio file']: if book.KindAsString in self.Audiobooks:
if DEBUG: if DEBUG:
self.log.info(" ignoring %-30.30s of type '%s'" % (book.Name, book.KindAsString)) self.log.info(" ignoring %-30.30s of type '%s'" % (book.Name, book.KindAsString))
else: else:
path = self.path_template % (book.Name, book.Artist) format = 'pdf' if book.KindAsString.startswith('PDF') else 'epub'
path = self.path_template % (book.Name, book.Artist,format)
# Collect calibre orphans # Collect calibre orphans
if book.Description.startswith(self.description_prefix): if book.Description.startswith(self.description_prefix):
@ -2174,7 +2286,9 @@ class ITUNES(DriverBase):
# Delete existing from Device|Books, add to self.update_list # Delete existing from Device|Books, add to self.update_list
# for deletion from booklist[0] during add_books_to_metadata # for deletion from booklist[0] during add_books_to_metadata
for book in self.cached_books: for book in self.cached_books:
if self.cached_books[book]['uuid'] == metadata.uuid: if self.cached_books[book]['uuid'] == metadata.uuid and \
self.cached_books[book]['title'] == metadata.title and \
self.cached_books[book]['author'] == metadata.authors[0]:
self.update_list.append(self.cached_books[book]) self.update_list.append(self.cached_books[book])
self._remove_from_device(self.cached_books[book]) self._remove_from_device(self.cached_books[book])
if DEBUG: if DEBUG:
@ -2191,17 +2305,17 @@ class ITUNES(DriverBase):
# Delete existing from Library|Books, add to self.update_list # Delete existing from Library|Books, add to self.update_list
# for deletion from booklist[0] during add_books_to_metadata # for deletion from booklist[0] during add_books_to_metadata
for book in self.cached_books: for book in self.cached_books:
if (self.cached_books[book]['uuid'] == metadata.uuid) or \ if self.cached_books[book]['uuid'] == metadata.uuid and \
(self.cached_books[book]['title'] == metadata.title and \ self.cached_books[book]['title'] == metadata.title and \
self.cached_books[book]['author'] == metadata.authors[0]): self.cached_books[book]['author'] == metadata.authors[0]:
self.update_list.append(self.cached_books[book]) self.update_list.append(self.cached_books[book])
self._remove_from_iTunes(self.cached_books[book]) self._remove_from_iTunes(self.cached_books[book])
if DEBUG: if DEBUG:
self.log.info( " deleting library book '%s'" % metadata.title) self.log.info( " deleting library book '%s'" % metadata.title)
break break
else: else:
if DEBUG: if DEBUG:
self.log.info(" '%s' not found in cached_books" % metadata.title) self.log.info(" '%s' not found in cached_books" % metadata.title)
def _remove_from_device(self, cached_book): def _remove_from_device(self, cached_book):
''' '''
@ -2209,18 +2323,20 @@ class ITUNES(DriverBase):
''' '''
self.log.info(" ITUNES._remove_from_device()") self.log.info(" ITUNES._remove_from_device()")
if isosx: if isosx:
if False: if DEBUG:
self.log.info(" deleting %s" % cached_book['dev_book']) self.log.info(" deleting '%s' from iDevice" % cached_book['title'])
cached_book['dev_book'].delete() cached_book['dev_book'].delete()
elif iswindows: elif iswindows:
dev_pl = self._get_device_books_playlist() hit = self._find_device_book(cached_book)
hits = dev_pl.Search(cached_book['uuid'],self.SearchField.index('All')) if hit:
if hits: if DEBUG:
hit = hits[0] self.log.info(" deleting '%s' from iDevice" % cached_book['title'])
if False:
self.log.info(" deleting '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Album))
hit.Delete() hit.Delete()
else:
if DEBUG:
self.log.warning(" unable to remove '%s' by '%s' (%s) from device" %
(cached_book['title'],cached_book['author'],cached_book['uuid']))
def _remove_from_iTunes(self, cached_book): def _remove_from_iTunes(self, cached_book):
''' '''
@ -2263,7 +2379,7 @@ class ITUNES(DriverBase):
except: except:
# We get here if there was an error with .location().path # We get here if there was an error with .location().path
if DEBUG: if DEBUG:
self.log.info(" '%s' not found in iTunes" % cached_book['title']) self.log.info(" '%s' not in iTunes storage" % cached_book['title'])
try: try:
self.iTunes.delete(cached_book['lib_book']) self.iTunes.delete(cached_book['lib_book'])
@ -2281,7 +2397,8 @@ class ITUNES(DriverBase):
path = book.Location path = book.Location
except: except:
book = self._find_library_book(cached_book) book = self._find_library_book(cached_book)
path = book.Location if book:
path = book.Location
if book: if book:
if self.iTunes_media and path.startswith(self.iTunes_media): if self.iTunes_media and path.startswith(self.iTunes_media):
@ -2292,7 +2409,7 @@ class ITUNES(DriverBase):
try: try:
os.remove(path) os.remove(path)
except: except:
self.log.warning(" could not find '%s' in iTunes storage" % path) self.log.warning(" '%s' not in iTunes storage" % path)
try: try:
os.rmdir(storage_path[0]) os.rmdir(storage_path[0])
self.log.info(" removed folder '%s'" % storage_path[0]) self.log.info(" removed folder '%s'" % storage_path[0])
@ -2431,7 +2548,8 @@ class ITUNES(DriverBase):
if isosx: if isosx:
if lb_added: if lb_added:
lb_added.album.set(metadata.title) lb_added.album.set(metadata.title)
lb_added.artist.set(metadata.authors[0]) #lb_added.artist.set(metadata.authors[0])
lb_added.artist.set(' & '.join(metadata.authors))
lb_added.composer.set(metadata.uuid) lb_added.composer.set(metadata.uuid)
lb_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) lb_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
lb_added.enabled.set(True) lb_added.enabled.set(True)
@ -2442,7 +2560,8 @@ class ITUNES(DriverBase):
if db_added: if db_added:
db_added.album.set(metadata.title) db_added.album.set(metadata.title)
db_added.artist.set(metadata.authors[0]) #db_added.artist.set(metadata.authors[0])
db_added.artist.set(' & '.join(metadata.authors))
db_added.composer.set(metadata.uuid) db_added.composer.set(metadata.uuid)
db_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) db_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
db_added.enabled.set(True) db_added.enabled.set(True)
@ -2499,7 +2618,8 @@ class ITUNES(DriverBase):
elif iswindows: elif iswindows:
if lb_added: if lb_added:
lb_added.Album = metadata.title lb_added.Album = metadata.title
lb_added.Artist = metadata.authors[0] #lb_added.Artist = metadata.authors[0]
lb_added.Artist = ' & '.join(metadata.authors)
lb_added.Composer = metadata.uuid lb_added.Composer = metadata.uuid
lb_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) lb_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
lb_added.Enabled = True lb_added.Enabled = True
@ -2510,7 +2630,8 @@ class ITUNES(DriverBase):
if db_added: if db_added:
db_added.Album = metadata.title db_added.Album = metadata.title
db_added.Artist = metadata.authors[0] #db_added.Artist = metadata.authors[0]
db_added.Artist = ' & '.join(metadata.authors)
db_added.Composer = metadata.uuid db_added.Composer = metadata.uuid
db_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) db_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
db_added.Enabled = True db_added.Enabled = True
@ -2627,7 +2748,11 @@ class ITUNES_ASYNC(ITUNES):
""" """
if not oncard: if not oncard:
if DEBUG: if DEBUG:
self.log.info("ITUNES_ASYNC:books(oncard=%s)" % oncard) self.log.info("ITUNES_ASYNC:books()")
if self.settings().use_subdirs:
self.log.info(" Cover fetching/caching enabled")
else:
self.log.info(" Cover fetching/caching disabled")
# Fetch a list of books from iTunes # Fetch a list of books from iTunes
@ -2638,13 +2763,15 @@ class ITUNES_ASYNC(ITUNES):
library_books = self._get_library_books() library_books = self._get_library_books()
book_count = float(len(library_books)) book_count = float(len(library_books))
for (i,book) in enumerate(library_books): for (i,book) in enumerate(library_books):
format = 'pdf' if library_books[book].kind().startswith('PDF') else 'epub'
this_book = Book(library_books[book].name(), library_books[book].artist()) this_book = Book(library_books[book].name(), library_books[book].artist())
this_book.path = self.path_template % (library_books[book].name(), this_book.path = self.path_template % (library_books[book].name(),
library_books[book].artist()) library_books[book].artist(),
format)
try: try:
this_book.datetime = parse_date(str(library_books[book].date_added())).timetuple() this_book.datetime = parse_date(str(library_books[book].date_added())).timetuple()
except: except:
pass this_book.datetime = time.gmtime()
this_book.db_id = None this_book.db_id = None
this_book.device_collections = [] this_book.device_collections = []
#this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None #this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None
@ -2664,7 +2791,7 @@ class ITUNES_ASYNC(ITUNES):
'lib_book':library_books[book], 'lib_book':library_books[book],
'dev_book':None, 'dev_book':None,
'uuid': library_books[book].composer(), 'uuid': library_books[book].composer(),
#'format': 'pdf' if book.KindAsString.startswith('PDF') else 'epub' 'format': format
} }
if self.report_progress is not None: if self.report_progress is not None:
@ -2678,12 +2805,14 @@ class ITUNES_ASYNC(ITUNES):
book_count = float(len(library_books)) book_count = float(len(library_books))
for (i,book) in enumerate(library_books): for (i,book) in enumerate(library_books):
this_book = Book(library_books[book].Name, library_books[book].Artist) this_book = Book(library_books[book].Name, library_books[book].Artist)
format = 'pdf' if library_books[book].KindAsString.startswith('PDF') else 'epub'
this_book.path = self.path_template % (library_books[book].Name, this_book.path = self.path_template % (library_books[book].Name,
library_books[book].Artist) library_books[book].Artist,
format)
try: try:
this_book.datetime = parse_date(str(library_books[book].DateAdded)).timetuple() this_book.datetime = parse_date(str(library_books[book].DateAdded)).timetuple()
except: except:
pass this_book.datetime = time.gmtime()
this_book.db_id = None this_book.db_id = None
this_book.device_collections = [] this_book.device_collections = []
this_book.library_id = library_books[book] this_book.library_id = library_books[book]
@ -2700,7 +2829,7 @@ class ITUNES_ASYNC(ITUNES):
'author':library_books[book].Artist, 'author':library_books[book].Artist,
'lib_book':library_books[book], 'lib_book':library_books[book],
'uuid': library_books[book].Composer, 'uuid': library_books[book].Composer,
'format': 'pdf' if library_books[book].KindAsString.startswith('PDF') else 'epub' 'format': format
} }
if self.report_progress is not None: if self.report_progress is not None:
@ -2721,13 +2850,6 @@ class ITUNES_ASYNC(ITUNES):
else: else:
return BookList(self.log) return BookList(self.log)
def unmount_device(self):
'''
'''
if DEBUG:
self.log.info("ITUNES_ASYNC:unmount_device()")
self.connected = False
def eject(self): def eject(self):
''' '''
Un-mount / eject the device from the OS. This does not check if there Un-mount / eject the device from the OS. This does not check if there
@ -2793,6 +2915,13 @@ class ITUNES_ASYNC(ITUNES):
self.problem_msg = None self.problem_msg = None
self.update_list = [] self.update_list = []
def unmount_device(self):
'''
'''
if DEBUG:
self.log.info("ITUNES_ASYNC:unmount_device()")
self.connected = False
class BookList(list): class BookList(list):
''' '''
A list of books. Each Book object must have the fields: A list of books. Each Book object must have the fields:

View File

@ -269,11 +269,11 @@ class TouchscreenFeedTemplate(Template):
link = A(CLASS('feed_link'), link = A(CLASS('feed_link'),
trim_title(feeds[f-1].title), trim_title(feeds[f-1].title),
href = '../feed_%d/index.html' % int(f-1)) href = '../feed_%d/index.html' % int(f-1))
navbar_tr.append(TD(link, width="40%", align="center")) navbar_tr.append(TD(CLASS('feed_prev'),link))
# Up to Sections # Up to Sections
link = A(STRONG('Sections'), href="../index.html") link = A('Sections', href="../index.html")
navbar_tr.append(TD(link,width="20%",align="center")) navbar_tr.append(TD(CLASS('feed_up'),link))
# Next Section # Next Section
link = '' link = ''
@ -281,7 +281,7 @@ class TouchscreenFeedTemplate(Template):
link = A(CLASS('feed_link'), link = A(CLASS('feed_link'),
trim_title(feeds[f+1].title), trim_title(feeds[f+1].title),
href = '../feed_%d/index.html' % int(f+1)) href = '../feed_%d/index.html' % int(f+1))
navbar_tr.append(TD(link, width="40%", align="center", )) navbar_tr.append(TD(CLASS('feed_next'),link))
navbar_t.append(navbar_tr) navbar_t.append(navbar_tr)
top_navbar = navbar_t top_navbar = navbar_t
bottom_navbar = copy.copy(navbar_t) bottom_navbar = copy.copy(navbar_t)
@ -319,10 +319,9 @@ class TouchscreenFeedTemplate(Template):
continue continue
tr = TR() tr = TR()
div_td = DIV( div_td = DIV(CLASS('article_summary'),
A(article.title, CLASS('summary_headline','calibre_rescale_120', A(article.title, CLASS('summary_headline','calibre_rescale_120',
href=article.url)), href=article.url)))
style="display:inline-block")
if article.author: if article.author:
div_td.append(DIV(article.author, div_td.append(DIV(article.author,
CLASS('summary_byline', 'calibre_rescale_100'))) CLASS('summary_byline', 'calibre_rescale_100')))
@ -354,27 +353,25 @@ class TouchscreenNavBarTemplate(Template):
# | Previous # | Previous
if art > 0: if art > 0:
href = '%s../article_%d/index.html'%(prefix, art-1) link = A(CLASS('article_link'),'Previous',href='%s../article_%d/index.html'%(prefix, art-1))
navbar_tr.append(TD(A(EM('Previous'),href=href), navbar_tr.append(TD(CLASS('article_prev'),link))
width="32%"))
else: else:
navbar_tr.append(TD('', width="32%")) navbar_tr.append(TD(CLASS('article_prev'),''))
# | Articles | Sections | # | Articles | Sections |
href = '%s../index.html#article_%d'%(prefix, art) link = A(CLASS('articles_link'),'Articles', href='%s../index.html#article_%d'%(prefix, art))
navbar_tr.append(TD(A(STRONG('Articles'), href=href),width="18%")) navbar_tr.append(TD(CLASS('article_articles_list'),link))
href = '%s../../index.html#feed_%d'%(prefix, feed) link = A(CLASS('sections_link'),'Sections', href='%s../../index.html#feed_%d'%(prefix, feed))
navbar_tr.append(TD(A(STRONG('Sections'), href=href),width="18%")) navbar_tr.append(TD(CLASS('article_sections_list'),link))
# | Next # | Next
next = 'feed_%d'%(feed+1) if art == number_of_articles_in_feed - 1 \ next = 'feed_%d'%(feed+1) if art == number_of_articles_in_feed - 1 \
else 'article_%d'%(art+1) else 'article_%d'%(art+1)
up = '../..' if art == number_of_articles_in_feed - 1 else '..' up = '../..' if art == number_of_articles_in_feed - 1 else '..'
href = '%s%s/%s/index.html'%(prefix, up, next)
navbar_tr.append(TD(A(EM('Next'),href=href), link = A(CLASS('article_link'),'Next', href='%s%s/%s/index.html'%(prefix, up, next))
width="32%")) navbar_tr.append(TD(CLASS('article_next'),link))
navbar_t.append(navbar_tr) navbar_t.append(navbar_tr)
navbar.append(navbar_t) navbar.append(navbar_t)
#print "\n%s\n" % etree.tostring(navbar, pretty_print=True) #print "\n%s\n" % etree.tostring(navbar, pretty_print=True)