mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
iTunes driver: Various bug fixes. Add option to disable cover fetching for speedup with large book collections in iTunes.
This commit is contained in:
commit
47479e630d
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user