This commit is contained in:
GRiker 2010-06-22 11:41:08 -06:00
parent 5d91f7515a
commit bd04312a76
7 changed files with 416 additions and 213 deletions

View File

@ -72,4 +72,9 @@ gui_pubdate_display_format = 'MMM yyyy'
# without changing anything is sufficient to change the sort. # without changing anything is sufficient to change the sort.
title_series_sorting = 'library_order' title_series_sorting = 'library_order'
# Apple iDevice
# Control whether Series name is used as Category/Genre in iTunes/iBooks
# If set to 'True', a Book's Series name will be used as the Category/Genre
# If set to 'False', the book's first tag beginning with an alpha character will
# be used as the Category/Genre
iDevice_use_series_as_category = True

View File

@ -237,9 +237,6 @@ class OutputProfile(Plugin):
# If True the MOBI renderer on the device supports MOBI indexing # If True the MOBI renderer on the device supports MOBI indexing
supports_mobi_indexing = False supports_mobi_indexing = False
# Device supports displaying a nested TOC
supports_nested_toc = True
# If True output should be optimized for a touchscreen interface # If True output should be optimized for a touchscreen interface
touchscreen = False touchscreen = False
@ -256,8 +253,82 @@ class iPadOutput(OutputProfile):
screen_size = (768, 1024) screen_size = (768, 1024)
comic_screen_size = (768, 1024) comic_screen_size = (768, 1024)
dpi = 132.0 dpi = 132.0
supports_nested_toc = False timefmt = '%A, %d %b %Y'
cssutils_addProfile = { 'name':'webkit',
'props': {
'-webkit-border-bottom-left-radius':'{length}',
'-webkit-border-bottom-right-radius':'{length}',
'-webkit-border-top-left-radius':'{length}',
'-webkit-border-top-right-radius':'{length}',
'-webkit-border-radius': r'{border-width}(\s+{border-width}){0,3}|inherit',
},
'macros': {'border-width': '{length}|medium|thick|thin'}}
touchscreen = True touchscreen = True
touchscreen_css = u'''
/* hr used in articles */
.caption_divider {
border:#ccc 1px solid;
}
.touchscreen_navbar {
background:#ccc;
border:#ccc 1px solid;
border-collapse:separate;
border-spacing:1px;
margin-left: 5%;
margin-right: 5%;
width: 90%;
-webkit-border-radius:4px;
}
.touchscreen_navbar td {
background:#fff;
font-family:Helvetica;
font-size:80%;
padding: 5px;
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 {
font-style: italic;
}
/* Feed summary formatting */
.feed_title {
text-align: center;
font-size: 160%;
}
.summary_headline {
font-weight:bold;
text-align:left;
}
.summary_byline {
text-align:left;
font-family:monospace;
}
.summary_text {
text-align:left;
}
.feed {
font-family:sans-serif;
font-weight:bold;
font-size:larger;
}
'''
class SonyReaderOutput(OutputProfile): class SonyReaderOutput(OutputProfile):

View File

@ -15,7 +15,7 @@ from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
from calibre.ebooks.metadata.epub import set_metadata from calibre.ebooks.metadata.epub import set_metadata
from calibre.library.server.utils import strftime from calibre.library.server.utils import strftime
from calibre.utils.config import Config, config_dir from calibre.utils.config import Config, config_dir, tweaks
from calibre.utils.date import isoformat, now, parse_date from calibre.utils.date import isoformat, now, parse_date
from calibre.utils.logging import Log from calibre.utils.logging import Log
from calibre.utils.zipfile import ZipFile from calibre.utils.zipfile import ZipFile
@ -78,12 +78,12 @@ class ITUNES(DevicePlugin):
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,7,0) version = (0,8,0)
OPEN_FEEDBACK_MESSAGE = _( OPEN_FEEDBACK_MESSAGE = _(
'Apple device detected, launching iTunes, please wait ...') 'Apple device detected, launching iTunes, please wait ...')
FORMATS = ['epub'] FORMATS = ['epub','pdf']
# Product IDs: # Product IDs:
# 0x1292:iPhone 3G # 0x1292:iPhone 3G
@ -141,6 +141,10 @@ class ITUNES(DevicePlugin):
'SongNames', 'SongNames',
] ]
# Cover art size limits
MAX_COVER_WIDTH = 510
MAX_COVER_HEIGHT = 680
# Properties # Properties
cached_books = {} cached_books = {}
cache_dir = os.path.join(config_dir, 'caches', 'itunes') cache_dir = os.path.join(config_dir, 'caches', 'itunes')
@ -159,7 +163,7 @@ class ITUNES(DevicePlugin):
sources = None sources = None
update_msg = None update_msg = None
update_needed = False update_needed = False
use_series_data = True use_series_as_category = tweaks['iDevice_use_series_as_category']
# Public methods # Public methods
def add_books_to_metadata(self, locations, metadata, booklists): def add_books_to_metadata(self, locations, metadata, booklists):
@ -173,16 +177,17 @@ class ITUNES(DevicePlugin):
(L{books}(oncard=None), L{books}(oncard='carda'), (L{books}(oncard=None), L{books}(oncard='carda'),
L{books}(oncard='cardb')). L{books}(oncard='cardb')).
''' '''
if DEBUG:
self.log.info("ITUNES.add_books_to_metadata()")
task_count = float(len(self.update_list)) task_count = float(len(self.update_list))
# Delete any obsolete copies of the book from the booklist # Delete any obsolete copies of the book from the booklist
if self.update_list: if self.update_list:
if True: if False:
self.log.info("ITUNES.add_books_to_metadata()") self._dump_booklist(booklists[0], header='before',indent=2)
#self._dump_booklist(booklists[0], header='before',indent=2) self._dump_update_list(header='before',indent=2)
#self._dump_update_list(header='before',indent=2) self._dump_cached_books(header='before',indent=2)
#self._dump_cached_books(header='before',indent=2)
for (j,p_book) in enumerate(self.update_list): for (j,p_book) in enumerate(self.update_list):
if False: if False:
@ -230,12 +235,12 @@ class ITUNES(DevicePlugin):
# Add new books to booklists[0] # Add new books to booklists[0]
for new_book in locations[0]: for new_book in locations[0]:
if False: if DEBUG:
self.log.info(" adding '%s' by '%s' to booklists[0]" % self.log.info(" adding '%s' by '%s' to booklists[0]" %
(new_book.title, new_book.author)) (new_book.title, new_book.author))
booklists[0].append(new_book) booklists[0].append(new_book)
if False: if DEBUG:
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)
@ -329,7 +334,8 @@ class ITUNES(DevicePlugin):
'title':book.Name, 'title':book.Name,
'author':book.Artist, 'author':book.Artist,
'lib_book':library_books[this_book.path] if this_book.path in library_books else None, 'lib_book':library_books[this_book.path] if this_book.path in library_books else None,
'uuid': book.Composer 'uuid': book.Composer,
'format': 'pdf' if book.KindAsString.startswith('PDF') else 'epub'
} }
if self.report_progress is not None: if self.report_progress is not None:
@ -343,9 +349,9 @@ class ITUNES(DevicePlugin):
if self.report_progress is not None: if self.report_progress is not None:
self.report_progress(1.0, _('finished')) self.report_progress(1.0, _('finished'))
self.cached_books = cached_books self.cached_books = cached_books
# if DEBUG: if DEBUG:
# self._dump_booklist(booklist, 'returning from books():') self._dump_booklist(booklist, 'returning from books()', indent=2)
# self._dump_cached_books('returning from books():') self._dump_cached_books('returning from books()',indent=2)
return booklist return booklist
else: else:
return [] return []
@ -685,6 +691,9 @@ class ITUNES(DevicePlugin):
@param booklists: A tuple containing the result of calls to @param booklists: A tuple containing the result of calls to
(L{books}(oncard=None), L{books}(oncard='carda'), (L{books}(oncard=None), L{books}(oncard='carda'),
L{books}(oncard='cardb')). L{books}(oncard='cardb')).
NB: This will not find books that were added by a different installation of calibre
as uuids are different
''' '''
if DEBUG: if DEBUG:
self.log.info("ITUNES.remove_books_from_metadata()") self.log.info("ITUNES.remove_books_from_metadata()")
@ -750,6 +759,10 @@ class ITUNES(DevicePlugin):
(L{books}(oncard=None), L{books}(oncard='carda'), (L{books}(oncard=None), L{books}(oncard='carda'),
L{books}(oncard='cardb')). L{books}(oncard='cardb')).
''' '''
if DEBUG:
self.log.info("ITUNES.sync_booklists()")
if self.update_needed: if self.update_needed:
if DEBUG: if DEBUG:
self.log.info(' calling _update_device') self.log.info(' calling _update_device')
@ -812,30 +825,33 @@ class ITUNES(DevicePlugin):
self.problem_msg = _("Some cover art could not be converted.\n" self.problem_msg = _("Some cover art could not be converted.\n"
"Click 'Show Details' for a list.") "Click 'Show Details' for a list.")
if False: if DEBUG:
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)
if isosx: if isosx:
for (i,file) in enumerate(files): for (i,file) in enumerate(files):
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])
self._remove_existing_copy(path, metadata[i]) self._remove_existing_copy(path, metadata[i])
fpath = self._get_fpath(file, metadata[i], 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])
thumb = self._cover_to_thumb(path, metadata[i], db_added, lb_added) thumb = self._cover_to_thumb(path, metadata[i], db_added, lb_added, format)
this_book = self._create_new_book(fpath, metadata[i], path, db_added, lb_added, thumb) this_book = self._create_new_book(fpath, metadata[i], path, db_added, lb_added, thumb, format)
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_paths
self.cached_books[this_book.path] = { self.cached_books[this_book.path] = {
'title': metadata[i].title,
'author': metadata[i].author, 'author': metadata[i].author,
'lib_book': lb_added,
'dev_book': db_added, 'dev_book': db_added,
'format': format,
'lib_book': lb_added,
'title': metadata[i].title,
'uuid': metadata[i].uuid } 'uuid': metadata[i].uuid }
# Report progress # Report progress
if self.report_progress is not None: if self.report_progress is not None:
self.report_progress(i+1/file_count, _('%d of %d') % (i+1, file_count)) self.report_progress(i+1/file_count, _('%d of %d') % (i+1, file_count))
@ -846,9 +862,10 @@ class ITUNES(DevicePlugin):
self.iTunes = win32com.client.Dispatch("iTunes.Application") self.iTunes = win32com.client.Dispatch("iTunes.Application")
for (i,file) in enumerate(files): for (i,file) in enumerate(files):
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])
self._remove_existing_copy(path, metadata[i]) self._remove_existing_copy(path, metadata[i])
fpath = self._get_fpath(file, metadata[i], 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])
if self.manual_sync_mode and not db_added: if self.manual_sync_mode and not db_added:
@ -857,17 +874,18 @@ class ITUNES(DevicePlugin):
"Click 'Show Details...' for affected books.") "Click 'Show Details...' for affected books.")
self.problem_titles.append("'%s' by %s" % (metadata[i].title, metadata[i].author[0])) self.problem_titles.append("'%s' by %s" % (metadata[i].title, metadata[i].author[0]))
thumb = self._cover_to_thumb(path, metadata[i], lb_added, db_added) thumb = self._cover_to_thumb(path, metadata[i], db_added, lb_added, format)
this_book = self._create_new_book(fpath, metadata[i], path, db_added, lb_added, thumb) this_book = self._create_new_book(fpath, metadata[i], path, db_added, lb_added, thumb, format)
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_paths
self.cached_books[this_book.path] = { self.cached_books[this_book.path] = {
'title': metadata[i].title,
'author': metadata[i].author[0], 'author': metadata[i].author[0],
'lib_book': lb_added,
'dev_book': db_added, 'dev_book': db_added,
'format': format,
'lib_book': lb_added,
'title': metadata[i].title,
'uuid': metadata[i].uuid} 'uuid': metadata[i].uuid}
# Report progress # Report progress
@ -968,7 +986,8 @@ class ITUNES(DevicePlugin):
db_added = self._find_device_book( db_added = self._find_device_book(
{'title': metadata.title, {'title': metadata.title,
'author': metadata.authors[0], 'author': metadata.authors[0],
'uuid': metadata.uuid}) 'uuid': metadata.uuid,
'format': fpath.rpartition('.')[2].lower()})
return db_added return db_added
@ -1021,7 +1040,8 @@ class ITUNES(DevicePlugin):
added = self._find_library_book( added = self._find_library_book(
{ 'title': metadata.title, { 'title': metadata.title,
'author': metadata.author[0], 'author': metadata.author[0],
'uuid': metadata.uuid}) 'uuid': metadata.uuid,
'format': file.rpartition('.')[2].lower()})
return added return added
def _add_new_copy(self, fpath, metadata): def _add_new_copy(self, fpath, metadata):
@ -1047,23 +1067,50 @@ class ITUNES(DevicePlugin):
return db_added, lb_added return db_added, lb_added
def _cover_to_thumb(self, path, metadata, db_added, lb_added): def _cover_to_thumb(self, path, metadata, db_added, lb_added, format):
''' '''
assumes pythoncom wrapper for db_added assumes pythoncom wrapper for db_added
as of iTunes 9.2, iBooks 1.1, can't set artwork for PDF files via automation
''' '''
self.log.info(" ITUNES._cover_to_thumb()") self.log.info(" ITUNES._cover_to_thumb()")
thumb = None thumb = None
if metadata.cover: if metadata.cover:
if (format == 'epub'):
# Pre-shrink cover
# self.MAX_COVER_WIDTH, self.MAX_COVER_HEIGHT
try:
img = PILImage.open(metadata.cover)
width = img.size[0]
height = img.size[1]
scaled, nwidth, nheight = fit_image(width, height, self.MAX_COVER_WIDTH, self.MAX_COVER_HEIGHT)
if scaled:
if DEBUG:
self.log.info(" '%s' scaled from %sx%s to %sx%s" %
(metadata.cover,width,height,nwidth,nheight))
img = img.resize((nwidth, nheight), PILImage.ANTIALIAS)
cd = cStringIO.StringIO()
img.convert('RGB').save(cd, 'JPEG')
cover_data = cd.getvalue()
cd.close()
else:
with open(metadata.cover,'r+b') as cd:
cover_data = cd.read()
except:
self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0]))
self.log.error(" error scaling '%s' for '%s'" % (metadata.cover,metadata.title))
return thumb
if isosx: if isosx:
cover_data = open(metadata.cover,'rb')
if lb_added: if lb_added:
lb_added.artworks[1].data_.set(cover_data.read()) lb_added.artworks[1].data_.set(cover_data)
if db_added: if db_added:
# The following command generates an error, but the artwork does in fact # The following command generates an error, but the artwork does in fact
# get sent to the device. Seems like a bug in Apple's automation interface # get sent to the device. Seems like a bug in Apple's automation interface
try: try:
db_added.artworks[1].data_.set(cover_data.read()) db_added.artworks[1].data_.set(cover_data)
except: except:
if DEBUG: if DEBUG:
self.log.warning(" iTunes automation interface reported an error" self.log.warning(" iTunes automation interface reported an error"
@ -1076,17 +1123,26 @@ class ITUNES(DevicePlugin):
elif iswindows: elif iswindows:
# Write the data to a real file for Windows iTunes
tc = os.path.join(tempfile.gettempdir(), "cover.jpg")
with open(tc,'wb') as tmp_cover:
tmp_cover.write(cover_data)
if lb_added: if lb_added:
if lb_added.Artwork.Count: if lb_added.Artwork.Count:
lb_added.Artwork.Item(1).SetArtworkFromFile(metadata.cover) lb_added.Artwork.Item(1).SetArtworkFromFile(tc)
else: else:
lb_added.AddArtworkFromFile(metadata.cover) lb_added.AddArtworkFromFile(tc)
if db_added: if db_added:
if db_added.Artwork.Count: if db_added.Artwork.Count:
db_added.Artwork.Item(1).SetArtworkFromFile(metadata.cover) db_added.Artwork.Item(1).SetArtworkFromFile(tc)
else: else:
db_added.AddArtworkFromFile(metadata.cover) db_added.AddArtworkFromFile(tc)
elif format == 'pdf':
if DEBUG:
self.log.info(" unable to set PDF cover via automation interface")
try: try:
# Resize for thumb # Resize for thumb
@ -1097,6 +1153,7 @@ class ITUNES(DevicePlugin):
of = cStringIO.StringIO() of = cStringIO.StringIO()
im.convert('RGB').save(of, 'JPEG') im.convert('RGB').save(of, 'JPEG')
thumb = of.getvalue() thumb = of.getvalue()
of.close()
# Refresh the thumbnail cache # Refresh the thumbnail cache
if DEBUG: if DEBUG:
@ -1105,14 +1162,15 @@ class ITUNES(DevicePlugin):
zfw = ZipFile(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)
zfw.close()
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:
zfw.close()
return thumb return thumb
def _create_new_book(self,fpath, metadata, path, db_added, lb_added, thumb): def _create_new_book(self,fpath, metadata, path, db_added, lb_added, thumb, format):
''' '''
''' '''
if DEBUG: if DEBUG:
@ -1122,6 +1180,7 @@ class ITUNES(DevicePlugin):
this_book.db_id = None this_book.db_id = None
this_book.device_collections = [] this_book.device_collections = []
this_book.format = format
this_book.library_id = lb_added this_book.library_id = lb_added
this_book.path = path this_book.path = path
this_book.thumbnail = thumb this_book.thumbnail = thumb
@ -1319,10 +1378,11 @@ class ITUNES(DevicePlugin):
self.cached_books[cb]['uuid'])) self.cached_books[cb]['uuid']))
elif iswindows: elif iswindows:
for cb in self.cached_books.keys(): for cb in self.cached_books.keys():
self.log.info("%s%-40.40s %-30.30s %s" % self.log.info("%s%-40.40s %-30.30s %-4.4s %s" %
(' '*indent, (' '*indent,
self.cached_books[cb]['title'], self.cached_books[cb]['title'],
self.cached_books[cb]['author'], self.cached_books[cb]['author'],
self.cached_books[cb]['format'],
self.cached_books[cb]['uuid'])) self.cached_books[cb]['uuid']))
self.log.info() self.log.info()
@ -1338,8 +1398,9 @@ class ITUNES(DevicePlugin):
fnames = zf.namelist() fnames = zf.namelist()
opf = [x for x in fnames if '.opf' in x][0] opf = [x for x in fnames if '.opf' in x][0]
if opf: if opf:
opf_raw = cStringIO.StringIO(zf.read(opf)).getvalue() opf_raw = cStringIO.StringIO(zf.read(opf))
soup = BeautifulSoup(opf_raw) soup = BeautifulSoup(opf_raw.getvalue())
opf_raw.close()
title = soup.find('dc:title').renderContents() title = soup.find('dc:title').renderContents()
author = soup.find('dc:creator').renderContents() author = soup.find('dc:creator').renderContents()
ts = soup.find('meta',attrs={'name':'calibre:timestamp'}) ts = soup.find('meta',attrs={'name':'calibre:timestamp'})
@ -1440,6 +1501,22 @@ class ITUNES(DevicePlugin):
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
# PDF metadata was rewritten at export as 'safe(title) - safe(author)'
if search['format'] == 'pdf':
title = re.sub(r'[^0-9a-zA-Z ]', '_', search['title'])
author = re.sub(r'[^0-9a-zA-Z ]', '_', search['author'])
if DEBUG:
self.log.info(" searching by name: '%s - %s'" % (title,author))
hits = dev_books.Search('%s - %s' % (title,author),
self.SearchField.index('All'))
if hits:
hit = hits[0]
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
return hit
else:
if DEBUG:
self.log.info(" no PDF hits")
attempts -= 1 attempts -= 1
time.sleep(0.5) time.sleep(0.5)
if DEBUG: if DEBUG:
@ -1509,6 +1586,22 @@ class ITUNES(DevicePlugin):
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
# PDF metadata was rewritten at export as 'safe(title) - safe(author)'
if search['format'] == 'pdf':
title = re.sub(r'[^0-9a-zA-Z ]', '_', search['title'])
author = re.sub(r'[^0-9a-zA-Z ]', '_', search['author'])
if DEBUG:
self.log.info(" searching by name: %s - %s" % (title,author))
hits = lib_books.Search('%s - %s' % (title,author),
self.SearchField.index('All'))
if hits:
hit = hits[0]
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
return hit
else:
if DEBUG:
self.log.info(" no PDF hits")
attempts -= 1 attempts -= 1
time.sleep(0.5) time.sleep(0.5)
if DEBUG: if DEBUG:
@ -1523,10 +1616,12 @@ class ITUNES(DevicePlugin):
Convert iTunes artwork to thumbnail Convert iTunes artwork to thumbnail
Cache generated thumbnails Cache generated thumbnails
cache_dir = os.path.join(config_dir, 'caches', 'itunes') cache_dir = os.path.join(config_dir, 'caches', 'itunes')
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") archive_path = os.path.join(self.cache_dir, "thumbs.zip")
thumb_path = book_path.rpartition('.')[0] + '.jpg' thumb_path = book_path.rpartition('.')[0] + '.jpg'
format = book_path.rpartition('.')[2].lower()
try: try:
zfr = ZipFile(archive_path) zfr = ZipFile(archive_path)
@ -1539,22 +1634,30 @@ class ITUNES(DevicePlugin):
self.log.info(" ITUNES._generate_thumbnail()") self.log.info(" ITUNES._generate_thumbnail()")
if isosx: if isosx:
if format == 'epub':
try: try:
if False:
self.log.info(" fetching artwork from %s\n %s" % (book_path,book))
# Resize the cover # Resize the cover
data = book.artworks[1].raw_data().data data = book.artworks[1].raw_data().data
#self._dump_hex(data[:256]) #self._dump_hex(data[:256])
im = PILImage.open(cStringIO.StringIO(data)) img_data = cStringIO.StringIO(data)
im = PILImage.open(img_data)
scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80) scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80)
im = im.resize((int(width),int(height)), PILImage.ANTIALIAS) im = im.resize((int(width),int(height)), PILImage.ANTIALIAS)
img_data.close()
thumb = cStringIO.StringIO() thumb = cStringIO.StringIO()
im.convert('RGB').save(thumb,'JPEG') im.convert('RGB').save(thumb,'JPEG')
thumb_data = thumb.getvalue()
thumb.close()
# Cache the tagged thumb # Cache the tagged thumb
if DEBUG: if DEBUG:
self.log.info(" generated thumb for '%s', caching" % book.name()) self.log.info(" generated thumb for '%s', caching" % book.name())
zfw.writestr(thumb_path, thumb.getvalue()) zfw.writestr(thumb_path, thumb_data)
zfw.close() zfw.close()
return thumb.getvalue() return thumb_data
except: except:
self.log.error(" error generating thumb for '%s'" % book.name()) self.log.error(" error generating thumb for '%s'" % book.name())
try: try:
@ -1562,14 +1665,19 @@ class ITUNES(DevicePlugin):
except: except:
pass pass
return None return None
else:
if DEBUG:
self.log.info(" unable to generate PDF thumbs")
return None
elif iswindows: elif iswindows:
if not book.Artwork.Count: if not book.Artwork.Count:
if DEBUG: if DEBUG:
self.log.info(" no artwork available") self.log.info(" no artwork available for '%s'" % book.Name)
return None return None
if format == 'epub':
# Save the cover from iTunes # Save the cover from iTunes
try: try:
tmp_thumb = os.path.join(tempfile.gettempdir(), "thumb.%s" % self.ArtworkFormat[book.Artwork.Item(1).Format]) tmp_thumb = os.path.join(tempfile.gettempdir(), "thumb.%s" % self.ArtworkFormat[book.Artwork.Item(1).Format])
@ -1580,14 +1688,16 @@ class ITUNES(DevicePlugin):
im = im.resize((int(width),int(height)), PILImage.ANTIALIAS) im = im.resize((int(width),int(height)), PILImage.ANTIALIAS)
thumb = cStringIO.StringIO() thumb = cStringIO.StringIO()
im.convert('RGB').save(thumb,'JPEG') im.convert('RGB').save(thumb,'JPEG')
thumb_data = thmb.getvalue()
os.remove(tmp_thumb) os.remove(tmp_thumb)
thumb.close()
# Cache the tagged thumb # Cache the tagged thumb
if DEBUG: if DEBUG:
self.log.info(" generated thumb for '%s', caching" % book.Name) self.log.info(" generated thumb for '%s', caching" % book.Name)
zfw.writestr(thumb_path, thumb.getvalue()) zfw.writestr(thumb_path, thumb_data)
zfw.close() zfw.close()
return thumb.getvalue() return thumb_data
except: except:
self.log.error(" error generating thumb for '%s'" % book.Name) self.log.error(" error generating thumb for '%s'" % book.Name)
try: try:
@ -1595,11 +1705,18 @@ class ITUNES(DevicePlugin):
except: except:
pass pass
return None return None
else:
if DEBUG:
self.log.info(" unable to generate PDF thumbs")
return None
def _get_device_book_size(self, file, compressed_size): def _get_device_book_size(self, file, compressed_size):
''' '''
Calculate the exploded size of file Calculate the exploded size of file
''' '''
exploded_file_size = compressed_size
format = file.rpartition('.')[2].lower()
if format == 'epub':
myZip = ZipFile(file,'r') myZip = ZipFile(file,'r')
myZipList = myZip.infolist() myZipList = myZip.infolist()
exploded_file_size = 0 exploded_file_size = 0
@ -1701,7 +1818,7 @@ class ITUNES(DevicePlugin):
self.log.error(" no iPad|Books playlist found") self.log.error(" no iPad|Books playlist found")
return pl return pl
def _get_fpath(self,file, metadata, update_md=False): def _get_fpath(self,file, metadata, format, update_md=False):
''' '''
If the database copy will be deleted after upload, we have to If the database copy will be deleted after upload, we have to
use file (the PersistentTemporaryFile), which will be around until use file (the PersistentTemporaryFile), which will be around until
@ -1725,7 +1842,7 @@ class ITUNES(DevicePlugin):
if DEBUG: if DEBUG:
self.log.info(" file will be deleted after upload") self.log.info(" file will be deleted after upload")
if update_md: if format == 'epub' and update_md:
self._update_epub_metadata(fpath, metadata) self._update_epub_metadata(fpath, metadata)
return fpath return fpath
@ -1950,10 +2067,12 @@ class ITUNES(DevicePlugin):
# Read the current storage path for iTunes media from the XML file # Read the current storage path for iTunes media from the XML file
with open(self.iTunes.LibraryXMLPath, 'r') as xml: with open(self.iTunes.LibraryXMLPath, 'r') as xml:
soup = BeautifulSoup(xml.read().decode('utf-8')) for line in xml:
mf = soup.find('key',text="Music Folder").parent if line.strip().startswith('<key>Music Folder'):
string = mf.findNext('string').renderContents() soup = BeautifulSoup(line)
string = soup.find('string').renderContents()
media_dir = os.path.abspath(string[len('file://localhost/'):].replace('%20',' ')) media_dir = os.path.abspath(string[len('file://localhost/'):].replace('%20',' '))
break
if os.path.exists(media_dir): if os.path.exists(media_dir):
self.iTunes_media = media_dir self.iTunes_media = media_dir
else: else:
@ -2028,7 +2147,9 @@ class ITUNES(DevicePlugin):
# 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: if (self.cached_books[book]['uuid'] == metadata.uuid) or \
(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_iTunes(self.cached_books[book]) self._remove_from_iTunes(self.cached_books[book])
if DEBUG: if DEBUG:
@ -2036,7 +2157,7 @@ class ITUNES(DevicePlugin):
break break
else: else:
if DEBUG: if DEBUG:
self.log.info(" '%s' not 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):
''' '''
@ -2158,12 +2279,14 @@ class ITUNES(DevicePlugin):
fnames = zf_opf.namelist() fnames = zf_opf.namelist()
opf = [x for x in fnames if '.opf' in x][0] opf = [x for x in fnames if '.opf' in x][0]
if opf: if opf:
opf_raw = cStringIO.StringIO(zf_opf.read(opf)).getvalue() opf_raw = cStringIO.StringIO(zf_opf.read(opf))
soup = BeautifulSoup(opf_raw) soup = BeautifulSoup(opf_raw.getvalue())
opf_raw.close()
# Touch existing calibre timestamp
md = soup.find('metadata') md = soup.find('metadata')
ts = md.find('meta',attrs={'name':'calibre:timestamp'}) ts = md.find('meta',attrs={'name':'calibre:timestamp'})
if ts: if ts:
# Touch existing calibre timestamp
timestamp = ts['content'] timestamp = ts['content']
old_ts = parse_date(timestamp) old_ts = parse_date(timestamp)
metadata.timestamp = datetime.datetime(old_ts.year, old_ts.month, old_ts.day, old_ts.hour, metadata.timestamp = datetime.datetime(old_ts.year, old_ts.month, old_ts.day, old_ts.hour,
@ -2172,6 +2295,15 @@ class ITUNES(DevicePlugin):
metadata.timestamp = isoformat(now()) metadata.timestamp = isoformat(now())
if DEBUG: if DEBUG:
self.log.info(" add timestamp: %s" % metadata.timestamp) self.log.info(" add timestamp: %s" % metadata.timestamp)
# Fix the language declaration for iBooks 1.1
patched_language = 'en-US'
language = md.find('dc:language')
if language:
self.log.info(" changing <dc:language> from '%s' to '%s'" %
(language.renderContents(),patched_language))
metadata.language = patched_language
zf_opf.close() zf_opf.close()
# If 'News' in tags, tweak the title/author for friendlier display in iBooks # If 'News' in tags, tweak the title/author for friendlier display in iBooks
@ -2257,6 +2389,9 @@ class ITUNES(DevicePlugin):
lb_added.enabled.set(True) lb_added.enabled.set(True)
lb_added.sort_artist.set(metadata.author_sort.title()) lb_added.sort_artist.set(metadata.author_sort.title())
lb_added.sort_name.set(this_book.title_sorter) lb_added.sort_name.set(this_book.title_sorter)
if this_book.format == 'pdf':
lb_added.artist.set(metadata.authors[0])
lb_added.name.set(metadata.title)
if db_added: if db_added:
db_added.album.set(metadata.title) db_added.album.set(metadata.title)
@ -2265,6 +2400,9 @@ class ITUNES(DevicePlugin):
db_added.enabled.set(True) db_added.enabled.set(True)
db_added.sort_artist.set(metadata.author_sort.title()) db_added.sort_artist.set(metadata.author_sort.title())
db_added.sort_name.set(this_book.title_sorter) db_added.sort_name.set(this_book.title_sorter)
if this_book.format == 'pdf':
db_added.artist.set(metadata.authors[0])
db_added.name.set(metadata.title)
if metadata.comments: if metadata.comments:
if lb_added: if lb_added:
@ -2284,7 +2422,9 @@ class ITUNES(DevicePlugin):
# Set genre from series if available, else first alpha tag # Set genre from series if available, else first alpha tag
# Otherwise iTunes grabs the first dc:subject from the opf metadata # Otherwise iTunes grabs the first dc:subject from the opf metadata
if self.use_series_data and metadata.series: if self.use_series_as_category and metadata.series:
if DEBUG:
self.log.info(" using Series name as Genre")
if lb_added: if lb_added:
lb_added.sort_name.set("%s %03d" % (metadata.series, metadata.series_index)) lb_added.sort_name.set("%s %03d" % (metadata.series, metadata.series_index))
lb_added.genre.set(metadata.series) lb_added.genre.set(metadata.series)
@ -2298,6 +2438,8 @@ class ITUNES(DevicePlugin):
db_added.episode_number.set(metadata.series_index) db_added.episode_number.set(metadata.series_index)
elif metadata.tags: elif metadata.tags:
if DEBUG:
self.log.info(" using Tag as Genre")
for tag in metadata.tags: for tag in metadata.tags:
if self._is_alpha(tag[0]): if self._is_alpha(tag[0]):
if lb_added: if lb_added:
@ -2314,6 +2456,9 @@ class ITUNES(DevicePlugin):
lb_added.Enabled = True lb_added.Enabled = True
lb_added.SortArtist = (metadata.author_sort.title()) lb_added.SortArtist = (metadata.author_sort.title())
lb_added.SortName = (this_book.title_sorter) lb_added.SortName = (this_book.title_sorter)
if this_book.format == 'pdf':
lb_added.Artist = metadata.authors[0]
lb_added.Name = metadata.title
if db_added: if db_added:
db_added.Album = metadata.title db_added.Album = metadata.title
@ -2322,6 +2467,9 @@ class ITUNES(DevicePlugin):
db_added.Enabled = True db_added.Enabled = True
db_added.SortArtist = (metadata.author_sort.title()) db_added.SortArtist = (metadata.author_sort.title())
db_added.SortName = (this_book.title_sorter) db_added.SortName = (this_book.title_sorter)
if this_book.format == 'pdf':
db_added.Artist = metadata.authors[0]
db_added.Name = metadata.title
if metadata.comments: if metadata.comments:
if lb_added: if lb_added:
@ -2345,7 +2493,9 @@ class ITUNES(DevicePlugin):
# Otherwise iBooks uses first <dc:subject> from opf # Otherwise iBooks uses first <dc:subject> from opf
# iTunes balks on setting EpisodeNumber, but it sticks (9.1.1.12) # iTunes balks on setting EpisodeNumber, but it sticks (9.1.1.12)
if self.use_series_data and metadata.series: if self.use_series_as_category and metadata.series:
if DEBUG:
self.log.info(" using Series name as Genre")
if lb_added: if lb_added:
lb_added.SortName = "%s %03d" % (metadata.series, metadata.series_index) lb_added.SortName = "%s %03d" % (metadata.series, metadata.series_index)
lb_added.Genre = metadata.series lb_added.Genre = metadata.series
@ -2365,6 +2515,8 @@ class ITUNES(DevicePlugin):
self.log.warning(" iTunes automation interface reported an error" self.log.warning(" iTunes automation interface reported an error"
" setting EpisodeNumber on iDevice") " setting EpisodeNumber on iDevice")
elif metadata.tags: elif metadata.tags:
if DEBUG:
self.log.info(" using Tag as Genre")
for tag in metadata.tags: for tag in metadata.tags:
if self._is_alpha(tag[0]): if self._is_alpha(tag[0]):
if lb_added: if lb_added:

View File

@ -127,15 +127,12 @@ class Stylizer(object):
else: else:
head = [] head = []
# GwR : Add webkit profile to cssutils before validating # Add optional cssutils parsing profile from output_profile
if True: if hasattr(self.opts.output_profile, 'cssutils_addProfile'):
wk_macros = { profile = self.opts.output_profile.cssutils_addProfile
'border-width': '{length}|thin|medium|thick' cssutils.profile.addProfile(profile['name'],
} profile['props'],
wk_props = { profile['macros'])
'-webkit-border-radius': r'{border-width}(\s+{border-width}){0,3}|inherit'
}
cssutils.profile.addProfile('webkit', wk_props, wk_macros)
parser = cssutils.CSSParser(fetcher=self._fetch_css_file, parser = cssutils.CSSParser(fetcher=self._fetch_css_file,
log=logging.getLogger('calibre.css')) log=logging.getLogger('calibre.css'))

View File

@ -176,6 +176,7 @@ class AnnotationsAction(object): # {{{
def mark_book_as_read(self,id): def mark_book_as_read(self,id):
read_tag = gprefs.get('catalog_epub_mobi_read_tag') read_tag = gprefs.get('catalog_epub_mobi_read_tag')
if read_tag:
self.db.set_tags(id, [read_tag], append=True) self.db.set_tags(id, [read_tag], append=True)
def canceled(self): def canceled(self):

View File

@ -280,46 +280,6 @@ class BasicNewsRecipe(Recipe):
} }
''' '''
#: The CSS that is used to style the touchscreen elements, i.e., the navigation bars and
#: the Feed summaries.
touchscreen_css = u'''
.article_navbar {
-webkit-border-radius:4px;
background-color:#eee;
border:1px solid #888;
margin-left: 5%;
margin-right: 5%;
width: 90%;
}
.feed_navbar {
-webkit-border-radius:4px;
background-color:#eee;
border:1px solid #888;
margin-left: 5%;
margin-right: 5%;
width: 90%;
}
.summary_headline {
font-weight:bold; text-align:left;
}
.summary_byline {
text-align:left;
font-family:monospace;
}
.summary_text {
text-align:left;
}
.feed {
font-family:sans-serif; font-weight:bold; font-size:larger;
}
'''
#: By default, calibre will use a default image for the masthead (Kindle only). #: By default, calibre will use a default image for the masthead (Kindle only).
#: Override this in your recipe to provide a url to use as a masthead. #: Override this in your recipe to provide a url to use as a masthead.
masthead_url = None masthead_url = None
@ -625,6 +585,8 @@ class BasicNewsRecipe(Recipe):
self.lrf = options.lrf self.lrf = options.lrf
self.output_profile = options.output_profile self.output_profile = options.output_profile
self.touchscreen = getattr(self.output_profile, 'touchscreen', False) self.touchscreen = getattr(self.output_profile, 'touchscreen', False)
if self.touchscreen and getattr(self.output_profile, 'touchscreen_css',False):
self.extra_css += self.output_profile.touchscreen_css
self.output_dir = os.path.abspath(self.output_dir) self.output_dir = os.path.abspath(self.output_dir)
if options.test: if options.test:
@ -678,10 +640,8 @@ class BasicNewsRecipe(Recipe):
if self.delay > 0: if self.delay > 0:
self.simultaneous_downloads = 1 self.simultaneous_downloads = 1
if self.touchscreen: self.navbar = templates.TouchscreenNavBarTemplate() if self.touchscreen else \
self.extra_css += self.touchscreen_css templates.NavBarTemplate()
self.navbar = templates.TouchscreenNavBarTemplate() if self.touchscreen else templates.NavBarTemplate()
self.failed_downloads = [] self.failed_downloads = []
self.partial_failures = [] self.partial_failures = []
@ -768,7 +728,8 @@ class BasicNewsRecipe(Recipe):
timefmt = self.timefmt timefmt = self.timefmt
if self.touchscreen: if self.touchscreen:
templ = templates.TouchscreenIndexTemplate() templ = templates.TouchscreenIndexTemplate()
timefmt = '%A, %d %b %Y' if getattr(self.output_profile,'timefmt',False):
timefmt = self.output_profile.timefmt
return templ.generate(self.title, "mastheadImage.jpg", timefmt, feeds, return templ.generate(self.title, "mastheadImage.jpg", timefmt, feeds,
extra_css=css).render(doctype='xhtml') extra_css=css).render(doctype='xhtml')

View File

@ -3,6 +3,9 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import copy
from lxml import html, etree from lxml import html, etree
from lxml.html.builder import HTML, HEAD, TITLE, STYLE, DIV, BODY, \ from lxml.html.builder import HTML, HEAD, TITLE, STYLE, DIV, BODY, \
STRONG, EM, BR, SPAN, A, HR, UL, LI, H2, IMG, P as PT, \ STRONG, EM, BR, SPAN, A, HR, UL, LI, H2, IMG, P as PT, \
@ -73,6 +76,7 @@ class EmbeddedContent(Template):
class IndexTemplate(Template): class IndexTemplate(Template):
def _generate(self, title, masthead, datefmt, feeds, extra_css=None, style=None): def _generate(self, title, masthead, datefmt, feeds, extra_css=None, style=None):
self.IS_HTML = False
if isinstance(datefmt, unicode): if isinstance(datefmt, unicode):
datefmt = datefmt.encode(preferred_encoding) datefmt = datefmt.encode(preferred_encoding)
date = strftime(datefmt) date = strftime(datefmt)
@ -198,6 +202,9 @@ class NavBarTemplate(Template):
class TouchscreenIndexTemplate(Template): class TouchscreenIndexTemplate(Template):
def _generate(self, title, masthead, datefmt, feeds, extra_css=None, style=None): def _generate(self, title, masthead, datefmt, feeds, extra_css=None, style=None):
self.IS_HTML = False
if isinstance(datefmt, unicode): if isinstance(datefmt, unicode):
datefmt = datefmt.encode(preferred_encoding) datefmt = datefmt.encode(preferred_encoding)
date = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y')) date = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y'))
@ -238,6 +245,8 @@ class TouchscreenFeedTemplate(Template):
tokens = title.split(' ') tokens = title.split(' ')
new_title_tokens = [] new_title_tokens = []
new_title_len = 0 new_title_len = 0
if len(tokens[0]) > clip:
return tokens[0][:clip] + '...'
for token in tokens: for token in tokens:
if len(token) + new_title_len < clip: if len(token) + new_title_len < clip:
new_title_tokens.append(token) new_title_tokens.append(token)
@ -248,29 +257,37 @@ class TouchscreenFeedTemplate(Template):
break break
return title return title
self.IS_HTML = False
feed = feeds[f] feed = feeds[f]
# Construct the navbar # Construct the navbar
navbar_t = TABLE(CLASS('feed_navbar')) navbar_t = TABLE(CLASS('touchscreen_navbar'))
navbar_tr = TR() navbar_tr = TR()
# Previous Section
link = '' link = ''
if f > 0: if f > 0:
link = A(EM( '< ' + trim_title(feeds[f-1].title)), link = A(CLASS('feed_link'),
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(link, width="40%", align="center"))
# Up to Sections
link = A(STRONG('Sections'), href="../index.html") link = A(STRONG('Sections'), href="../index.html")
navbar_tr.append(TD(link,width="20%",align="center")) navbar_tr.append(TD(link,width="20%",align="center"))
# Next Section
link = '' link = ''
if f < len(feeds)-1: if f < len(feeds)-1:
link = A(EM(trim_title(feeds[f+1].title) + ' >'), link = A(CLASS('feed_link'),
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(link, width="40%", align="center", ))
navbar_t.append(navbar_tr) navbar_t.append(navbar_tr)
navbar = navbar_t top_navbar = navbar_t
bottom_navbar = copy.copy(navbar_t)
#print "\n%s\n" % etree.tostring(navbar_t, pretty_print=True)
# Build the page # Build the page
head = HEAD(TITLE(feed.title)) head = HEAD(TITLE(feed.title))
@ -280,8 +297,8 @@ class TouchscreenFeedTemplate(Template):
head.append(STYLE(extra_css, type='text/css')) head.append(STYLE(extra_css, type='text/css'))
body = BODY(style='page-break-before:always') body = BODY(style='page-break-before:always')
div = DIV( div = DIV(
H2(feed.title, CLASS('calibre_feed_title', 'calibre_rescale_160')), top_navbar,
DIV(style="border-top:1px solid gray;border-bottom:1em solid white") H2(feed.title, CLASS('feed_title'))
) )
body.append(div) body.append(div)
@ -317,9 +334,8 @@ class TouchscreenFeedTemplate(Template):
toc.append(tr) toc.append(tr)
div.append(toc) div.append(toc)
#div.append(DIV(style="border-top:1px solid gray;border-bottom:1em solid white"))
div.append(BR()) div.append(BR())
div.append(navbar) div.append(bottom_navbar)
self.root = HTML(head, body) self.root = HTML(head, body)
class TouchscreenNavBarTemplate(Template): class TouchscreenNavBarTemplate(Template):
@ -334,24 +350,23 @@ class TouchscreenNavBarTemplate(Template):
head.append(STYLE(extra_css, type='text/css')) head.append(STYLE(extra_css, type='text/css'))
navbar = DIV() navbar = DIV()
if bottom: navbar_t = TABLE(CLASS('touchscreen_navbar'))
navbar.append(DIV(style="border-top:1px solid gray;border-bottom:1em solid white"))
navbar_t = TABLE(CLASS('article_navbar'))
navbar_tr = TR() navbar_tr = TR()
# | Previous # | Previous
if art > 0: if art > 0:
href = '%s../article_%d/index.html'%(prefix, art-1) href = '%s../article_%d/index.html'%(prefix, art-1)
navbar_tr.append(TD(A(EM('< Previous'), href=href), width="32%", align="center")) navbar_tr.append(TD(A(EM('Previous'),href=href),
width="32%"))
else: else:
navbar_tr.append(TD('', width="25%")) navbar_tr.append(TD('', width="32%"))
# | Articles | Sections | # | Articles | Sections |
href = '%s../index.html#article_%d'%(prefix, art) href = '%s../index.html#article_%d'%(prefix, art)
navbar_tr.append(TD(A(STRONG('Articles'), href=href),width="18%", align="center")) navbar_tr.append(TD(A(STRONG('Articles'), href=href),width="18%"))
href = '%s../../index.html#feed_%d'%(prefix, feed) href = '%s../../index.html#feed_%d'%(prefix, feed)
navbar_tr.append(TD(A(STRONG('Sections'), href=href),width="18%", align="center")) navbar_tr.append(TD(A(STRONG('Sections'), href=href),width="18%"))
# | 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 \
@ -359,7 +374,8 @@ class TouchscreenNavBarTemplate(Template):
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) href = '%s%s/%s/index.html'%(prefix, up, next)
navbar_tr.append(TD(A(EM('Next >'), href=href),width="32%", align="center")) navbar_tr.append(TD(A(EM('Next'),href=href),
width="32%"))
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)