Merge from trunk

This commit is contained in:
Charles Haley 2010-06-30 18:04:50 +01:00
commit eb8208114e
14 changed files with 569 additions and 276 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 753 B

View File

@ -15,7 +15,7 @@ class Akter(BasicNewsRecipe):
category = 'vesti, online vesti, najnovije vesti, politika, sport, ekonomija, biznis, finansije, berza, kultura, zivot, putovanja, auto, automobili, tehnologija, politicki magazin, dogadjaji, desavanja, lifestyle, zdravlje, zdravstvo, vest, novine, nedeljnik, srbija, novi sad, vojvodina, svet, drustvo, zabava, republika srpska, beograd, intervju, komentar, reportaza, arhiva vesti, news, serbia, politics' category = 'vesti, online vesti, najnovije vesti, politika, sport, ekonomija, biznis, finansije, berza, kultura, zivot, putovanja, auto, automobili, tehnologija, politicki magazin, dogadjaji, desavanja, lifestyle, zdravlje, zdravstvo, vest, novine, nedeljnik, srbija, novi sad, vojvodina, svet, drustvo, zabava, republika srpska, beograd, intervju, komentar, reportaza, arhiva vesti, news, serbia, politics'
oldest_article = 8 oldest_article = 8
max_articles_per_feed = 100 max_articles_per_feed = 100
no_stylesheets = False no_stylesheets = True
use_embedded_content = False use_embedded_content = False
encoding = 'utf-8' encoding = 'utf-8'
masthead_url = 'http://www.akter.co.rs/templates/gk_thenews2/images/style2/logo.png' masthead_url = 'http://www.akter.co.rs/templates/gk_thenews2/images/style2/logo.png'
@ -23,9 +23,9 @@ class Akter(BasicNewsRecipe):
publication_type = 'magazine' publication_type = 'magazine'
remove_empty_feeds = True remove_empty_feeds = True
PREFIX = 'http://www.akter.co.rs' PREFIX = 'http://www.akter.co.rs'
extra_css = """ @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} extra_css = """
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
.article_description,body,.lokacija{font-family: Arial,Helvetica,sans1,sans-serif} .article_description,body{font-family: Arial,Helvetica,sans1,sans-serif}
.color-2{display:block; margin-bottom: 10px; padding: 5px, 10px; .color-2{display:block; margin-bottom: 10px; padding: 5px, 10px;
border-left: 1px solid #D00000; color: #D00000} border-left: 1px solid #D00000; color: #D00000}
img{margin-bottom: 0.8em} """ img{margin-bottom: 0.8em} """

View File

@ -0,0 +1,65 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
'''
www.alo.rs
'''
import re
from calibre.web.feeds.recipes import BasicNewsRecipe
class Alo_Novine(BasicNewsRecipe):
title = 'Alo!'
__author__ = 'Darko Miletic'
description = "News Portal from Serbia"
publisher = 'Alo novine d.o.o.'
category = 'news, politics, Serbia'
oldest_article = 2
max_articles_per_feed = 100
delay = 4
no_stylesheets = True
encoding = 'utf-8'
use_embedded_content = False
language = 'sr'
extra_css = """
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
.article_description,body{font-family: Arial,Helvetica,sans1,sans-serif}
.lead {font-size: 1.3em}
h1{color: #DB0700}
.article_uvod{font-style: italic; font-size: 1.2em}
img{margin-bottom: 0.8em} """
conversion_options = {
'comment' : description
, 'tags' : category
, 'publisher': publisher
, 'language' : language
}
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
remove_tags = [dict(name=['object','link','embed'])]
remove_attributes = ['height','width']
feeds = [
(u'Najnovije Vijesti', u'http://www.alo.rs/rss/danasnje_vesti')
,(u'Politika' , u'http://www.alo.rs/rss/politika')
,(u'Vesti' , u'http://www.alo.rs/rss/vesti')
,(u'Sport' , u'http://www.alo.rs/rss/sport')
,(u'Ljudi' , u'http://www.alo.rs/rss/ljudi')
,(u'Saveti' , u'http://www.alo.rs/rss/saveti')
]
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
return soup
def print_version(self, url):
artl = url.rpartition('/')[0]
artid = artl.rpartition('/')[2]
return 'http://www.alo.rs/resources/templates/tools/print.php?id=' + artid
def image_url_processor(self, baseurl, url):
return url.replace('alo.rs//','alo.rs/')

View File

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

View File

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

View File

@ -46,7 +46,7 @@ class LinuxFreeze(Command):
'/usr/lib/libsqlite3.so.0', '/usr/lib/libsqlite3.so.0',
'/usr/lib/libsqlite3.so.0', '/usr/lib/libsqlite3.so.0',
'/usr/lib/libmng.so.1', '/usr/lib/libmng.so.1',
'/usr/lib/libpodofo.so.0.6.99', '/usr/lib/libpodofo.so.0.8.1',
'/lib/libz.so.1', '/lib/libz.so.1',
'/lib/libuuid.so.1', '/lib/libuuid.so.1',
'/usr/lib/libtiff.so.3', '/usr/lib/libtiff.so.3',

View File

@ -265,6 +265,9 @@ class Py2App(object):
@flush @flush
def get_local_dependencies(self, path_to_lib): def get_local_dependencies(self, path_to_lib):
for x in self.get_dependencies(path_to_lib): for x in self.get_dependencies(path_to_lib):
if x.startswith('libpodofo'):
yield x, x
continue
for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/', for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/',
'/opt/local/lib/', '/opt/local/lib/',
'/Library/Frameworks/Python.framework/', SW+'/freetype/lib/'): '/Library/Frameworks/Python.framework/', SW+'/freetype/lib/'):
@ -397,7 +400,7 @@ class Py2App(object):
@flush @flush
def add_podofo(self): def add_podofo(self):
info('\nAdding PoDoFo') info('\nAdding PoDoFo')
pdf = join(SW, 'lib', 'libpodofo.0.6.99.dylib') pdf = join(SW, 'lib', 'libpodofo.0.8.1.dylib')
self.install_dylib(pdf) self.install_dylib(pdf)
@flush @flush

View File

@ -162,9 +162,50 @@ SET(WANT_LIB64 FALSE)
SET(PODOFO_BUILD_SHARED TRUE) SET(PODOFO_BUILD_SHARED TRUE)
SET(PODOFO_BUILD_STATIC FALSE) SET(PODOFO_BUILD_STATIC FALSE)
cp build/podofo-0.7.0/build/src/Release/podofo.dll bin/ cp build/podofo/build/src/Release/podofo.dll bin/
cp build/podofo-0.7.0/build/src/Release/podofo.lib lib/ cp build/podofo/build/src/Release/podofo.lib lib/
cp build/podofo-0.7.0/build/src/Release/podofo.exp lib/ cp build/podofo/build/src/Release/podofo.exp lib/
cp build/podofo/build/podofo_config.h include/podofo/
cp -r build/podofo/src/* include/podofo/
The following patch was required to get it to compile:
Index: src/PdfImage.cpp
===================================================================
--- src/PdfImage.cpp (revision 1261)
+++ src/PdfImage.cpp (working copy)
@@ -627,7 +627,7 @@
long lLen = static_cast<long>(pInfo->rowbytes * height);
char* pBuffer = static_cast<char*>(malloc(sizeof(char) * lLen));
- png_bytep pRows[height];
+ png_bytepp pRows = static_cast<png_bytepp>(malloc(sizeof(png_bytep)*height));
for(int y=0; y<height; y++)
{
pRows[y] = reinterpret_cast<png_bytep>(pBuffer + (y * pInfo->rowbytes));
@@ -672,6 +672,7 @@
this->SetImageData( width, height, pInfo->bit_depth, &stream );
free(pBuffer);
+ free(pRows);
}
#endif // PODOFO_HAVE_PNG_LIB
Index: src/PdfFiltersPrivate.cpp
===================================================================
--- src/PdfFiltersPrivate.cpp (revision 1261)
+++ src/PdfFiltersPrivate.cpp (working copy)
@@ -1019,7 +1019,7 @@
/*
* Prepare for input from a memory buffer.
*/
-GLOBAL(void)
+void
jpeg_memory_src (j_decompress_ptr cinfo, const JOCTET * buffer, size_t bufsize)
{
my_src_ptr src;
ImageMagick ImageMagick
-------------- --------------

View File

@ -153,18 +153,10 @@
<Property Id="WixShellExecTarget" Value="[#{exe_map[calibre]}]" /> <Property Id="WixShellExecTarget" Value="[#{exe_map[calibre]}]" />
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" <CustomAction Id="LaunchApplication" BinaryKey="WixCA"
DllEntry="WixShellExec" Impersonate="yes"/> DllEntry="WixShellExec" Impersonate="yes"/>
<InstallExecuteSequence>
<FileCost Suppress="yes" />
</InstallExecuteSequence>
<InstallUISequence> <InstallUISequence>
<FileCost Suppress="yes" /> <FileCost Suppress="yes" />
</InstallUISequence> </InstallUISequence>
<AdminExecuteSequence>
<FileCost Suppress="yes" />
</AdminExecuteSequence>
<AdminUISequence>
<FileCost Suppress="yes" />
</AdminUISequence>
</Product> </Product>
</Wix> </Wix>

View File

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

View File

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

View File

@ -495,7 +495,7 @@ class DeviceMenu(QMenu): # {{{
self.connect_to_folder_action = mitem self.connect_to_folder_action = mitem
mitem = self.addAction(QIcon(I('devices/itunes.png')), mitem = self.addAction(QIcon(I('devices/itunes.png')),
_('Connect to iTunes (EXPERIMENTAL)')) _('Connect to iTunes'))
mitem.setEnabled(True) mitem.setEnabled(True)
mitem.triggered.connect(lambda x : self.connect_to_itunes.emit()) mitem.triggered.connect(lambda x : self.connect_to_itunes.emit())
self.connect_to_itunes_action = mitem self.connect_to_itunes_action = mitem

View File

@ -164,7 +164,7 @@ podofo_convert_pystring(PyObject *py) {
Py_UNICODE* u = PyUnicode_AS_UNICODE(py); Py_UNICODE* u = PyUnicode_AS_UNICODE(py);
PyObject *u8 = PyUnicode_EncodeUTF8(u, PyUnicode_GET_SIZE(py), "replace"); PyObject *u8 = PyUnicode_EncodeUTF8(u, PyUnicode_GET_SIZE(py), "replace");
if (u8 == NULL) { PyErr_NoMemory(); return NULL; } if (u8 == NULL) { PyErr_NoMemory(); return NULL; }
pdf_utf8 *s8 = (pdf_utf8 *)PyString_AS_STRING(u8); pdf_utf8 *s8 = reinterpret_cast<pdf_utf8 *>(PyString_AS_STRING(u8));
PdfString *ans = new PdfString(s8); PdfString *ans = new PdfString(s8);
Py_DECREF(u8); Py_DECREF(u8);
if (ans == NULL) PyErr_NoMemory(); if (ans == NULL) PyErr_NoMemory();
@ -219,9 +219,10 @@ podofo_PDFDoc_setter(podofo_PDFDoc *self, PyObject *val, int field) {
PyErr_SetString(PyExc_Exception, "You must first load a PDF Document"); PyErr_SetString(PyExc_Exception, "You must first load a PDF Document");
return -1; return -1;
} }
PdfString *s = podofo_convert_pystring(val); PdfString *s = podofo_convert_pystring(val);
if (s == NULL) return -1; if (s == NULL) return -1;
switch (field) { switch (field) {
case 0: case 0:
info->SetTitle(*s); break; info->SetTitle(*s); break;
@ -240,7 +241,9 @@ podofo_PDFDoc_setter(podofo_PDFDoc *self, PyObject *val, int field) {
return -1; return -1;
} }
self->doc->set_info(info); self->doc->set_info(info);
return 0; return 0;
} }

View File

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