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'
oldest_article = 8
max_articles_per_feed = 100
no_stylesheets = False
no_stylesheets = True
use_embedded_content = False
encoding = 'utf-8'
masthead_url = 'http://www.akter.co.rs/templates/gk_thenews2/images/style2/logo.png'
@ -23,9 +23,9 @@ class Akter(BasicNewsRecipe):
publication_type = 'magazine'
remove_empty_feeds = True
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)}
.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;
border-left: 1px solid #D00000; color: #D00000}
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',
'side_tool',
'singleAd',
'subNavigation clearfix',
'subNavigation tabContent active',
'subNavigation tabContent active clearfix',
]}),
@ -460,8 +461,10 @@ class NYTimes(BasicNewsRecipe):
if mp_off >= 0:
c = c[:mp_off]
emTag.insert(0, c)
hrTag = Tag(soup, 'hr')
#hrTag['style'] = "margin-top:0em;margin-bottom:0em"
#hrTag = Tag(soup, 'hr')
#hrTag['class'] = 'caption_divider'
hrTag = Tag(soup, 'div')
hrTag['class'] = 'divider'
emTag.insert(1, hrTag)
caption.replaceWith(emTag)

View File

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

View File

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

View File

@ -265,6 +265,9 @@ class Py2App(object):
@flush
def get_local_dependencies(self, 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/',
'/opt/local/lib/',
'/Library/Frameworks/Python.framework/', SW+'/freetype/lib/'):
@ -397,7 +400,7 @@ class Py2App(object):
@flush
def add_podofo(self):
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)
@flush

View File

@ -162,9 +162,50 @@ SET(WANT_LIB64 FALSE)
SET(PODOFO_BUILD_SHARED TRUE)
SET(PODOFO_BUILD_STATIC FALSE)
cp build/podofo-0.7.0/build/src/Release/podofo.dll bin/
cp build/podofo-0.7.0/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.dll bin/
cp build/podofo/build/src/Release/podofo.lib 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
--------------

View File

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

View File

@ -275,13 +275,44 @@ class iPadOutput(OutputProfile):
# touchscreen_news_css {{{
touchscreen_news_css = u'''
/* 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 {
border:#ccc 1px solid;
}
.touchscreen_navbar {
background:#ccc;
border:#ccc 1px solid;
background:#c3bab2;
border:#ccc 0px solid;
border-collapse:separate;
border-spacing:1px;
margin-left: 5%;
@ -292,21 +323,15 @@ class iPadOutput(OutputProfile):
.touchscreen_navbar td {
background:#fff;
font-family:Helvetica;
font-size:90%;
padding: 5px;
font-size:80%;
/* UI touchboxes use 8px padding */
padding: 6px;
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;
.touchscreen_navbar td a:link {
color: #593f29;
text-decoration: none;
}
/* Index formatting */
@ -318,12 +343,50 @@ class iPadOutput(OutputProfile):
border-top:1px solid gray;
}
hr.caption_divider {
border-color:black;
border-style:solid;
border-width:1px;
}
/* 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 {
text-align: center;
font-size: 160%;
}
.feed_up {
font-weight: bold;
width:20%;
}
.summary_headline {
font-weight:bold;
text-align:left;
@ -338,12 +401,6 @@ class iPadOutput(OutputProfile):
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.localization import get_lang
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
@ -38,6 +38,7 @@ if iswindows:
class DriverBase(DeviceConfig, DevicePlugin):
# Needed for config_widget to work
FORMATS = ['epub', 'pdf']
SUPPORTS_SUB_DIRS = True # To enable second checkbox in customize widget
@classmethod
def _config_base_name(cls):
@ -87,7 +88,7 @@ class ITUNES(DriverBase):
supported_platforms = ['osx','windows']
author = 'GRiker'
#: The version of this plugin as a 3-tuple (major, minor, revision)
version = (0,8,0)
version = (0,9,0)
OPEN_FEEDBACK_MESSAGE = _(
'Apple device detected, launching iTunes, please wait ...')
@ -106,23 +107,17 @@ class ITUNES(DriverBase):
BCD = [0x01]
# iTunes enumerations
Sources = [
'Unknown',
'Library',
'iPod',
'AudioCD',
'MP3CD',
'Device',
'RadioTuner',
'SharedLibrary']
Audiobooks = [
'Audible file',
'MPEG audio file',
'Protected AAC audio file'
]
ArtworkFormat = [
'Unknown',
'JPEG',
'PNG',
'BMP'
]
PlaylistKind = [
'Unknown',
'Library',
@ -131,7 +126,6 @@ class ITUNES(DriverBase):
'Device',
'Radio Tuner'
]
PlaylistSpecialKind = [
'Unknown',
'Purchased Music',
@ -144,7 +138,6 @@ class ITUNES(DriverBase):
'TV Shows',
'Books',
]
SearchField = [
'All',
'Visible',
@ -153,6 +146,16 @@ class ITUNES(DriverBase):
'Composers',
'SongNames',
]
Sources = [
'Unknown',
'Library',
'iPod',
'AudioCD',
'MP3CD',
'Device',
'RadioTuner',
'SharedLibrary'
]
# Cover art size limits
MAX_COVER_WIDTH = 510
@ -161,6 +164,7 @@ class ITUNES(DriverBase):
# Properties
cached_books = {}
cache_dir = os.path.join(config_dir, 'caches', 'itunes')
archive_path = os.path.join(cache_dir, "thumbs.zip")
description_prefix = "added by calibre"
ejected = False
iTunes= None
@ -168,7 +172,7 @@ class ITUNES(DriverBase):
library_orphans = None
log = Log()
manual_sync_mode = False
path_template = 'iTunes/%s - %s.epub'
path_template = 'iTunes/%s - %s.%s'
problem_titles = []
problem_msg = None
report_progress = None
@ -252,7 +256,7 @@ class ITUNES(DriverBase):
(new_book.title, new_book.author))
booklists[0].append(new_book)
if DEBUG:
if False:
self._dump_booklist(booklists[0],header='after',indent=2)
self._dump_cached_books(header='after',indent=2)
@ -273,10 +277,13 @@ class ITUNES(DriverBase):
"""
if not oncard:
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
if 'iPod' in self.sources:
booklist = BookList(self.log)
cached_books = {}
@ -287,11 +294,12 @@ class ITUNES(DriverBase):
book_count = float(len(device_books))
for (i,book) in enumerate(device_books):
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:
this_book.datetime = parse_date(str(book.date_added())).timetuple()
except:
pass
this_book.datetime = time.gmtime()
this_book.db_id = None
this_book.device_collections = []
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))
for (i,book) in enumerate(device_books):
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:
this_book.datetime = parse_date(str(book.DateAdded)).timetuple()
except:
pass
this_book.datetime = time.gmtime()
this_book.db_id = None
this_book.device_collections = []
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
cw.opt_save_template.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"))
# Repurpose the use_subdirs checkbox
cw.opt_use_subdirs.setText(_("Cache covers from iTunes/iBooks"))
return cw
def delete_books(self, paths, end_session=True):
@ -691,21 +702,19 @@ class ITUNES(DriverBase):
self.log.info("ITUNES.open()")
# Confirm/create thumbs archive
archive_path = os.path.join(self.cache_dir, "thumbs.zip")
if not os.path.exists(self.cache_dir):
if DEBUG:
self.log.info(" creating thumb cache '%s'" % 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")
zfw = ZipFile(archive_path, mode='w')
zfw = ZipFile(self.archive_path, mode='w')
zfw.writestr("iTunes Thumbs Archive",'')
zfw.close()
else:
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):
'''
@ -722,21 +731,61 @@ class ITUNES(DriverBase):
if DEBUG:
self.log.info("ITUNES.remove_books_from_metadata()")
for path in paths:
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]):
if False:
self.log.info(" evaluating '%s'" % bl_book.uuid)
if bl_book.uuid == self.cached_books[path]['uuid']:
# Remove from booklists[0]
booklists[0].pop(i)
self.log.info(" evaluating '%s' by '%s' (%s)" %
(bl_book.title, bl_book.author,bl_book.uuid))
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:
if self.cached_books[cb]['uuid'] == self.cached_books[path]['uuid']:
self.cached_books.pop(cb)
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
# else:
# if DEBUG:
# self.log.error(" unable to find '%s' by '%s' (%s)" %
# (bl_book.title, bl_book.author,bl_book.uuid))
if False:
self._dump_booklist(booklists[0], indent = 2)
@ -842,12 +891,11 @@ class ITUNES(DriverBase):
self.log.info("ITUNES.upload_books()")
self._dump_files(files, 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:
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],format)
self._remove_existing_copy(path, metadata[i])
fpath = self._get_fpath(file, metadata[i], format, update_md=True)
db_added, lb_added = self._add_new_copy(fpath, metadata[i])
@ -856,7 +904,10 @@ class ITUNES(DriverBase):
new_booklist.append(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] = {
'author': metadata[i].author,
'dev_book': db_added,
@ -877,7 +928,7 @@ class ITUNES(DriverBase):
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],format)
self._remove_existing_copy(path, metadata[i])
fpath = self._get_fpath(file, metadata[i],format, update_md=True)
db_added, lb_added = self._add_new_copy(fpath, metadata[i])
@ -1091,7 +1142,7 @@ class ITUNES(DriverBase):
thumb = None
if metadata.cover:
if (format == 'epub'):
if format == 'epub':
# Pre-shrink cover
# self.MAX_COVER_WIDTH, self.MAX_COVER_HEIGHT
try:
@ -1172,8 +1223,7 @@ class ITUNES(DriverBase):
# Refresh the thumbnail cache
if DEBUG:
self.log.info( " refreshing cached thumb for '%s'" % metadata.title)
archive_path = os.path.join(self.cache_dir, "thumbs.zip")
zfw = ZipFile(archive_path, mode='a')
zfw = ZipFile(self.archive_path, mode='a')
thumb_path = path.rpartition('.')[0] + '.jpg'
zfw.writestr(thumb_path, thumb)
except:
@ -1181,7 +1231,9 @@ class ITUNES(DriverBase):
self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title))
finally:
zfw.close()
else:
if DEBUG:
self.log.info(" no cover defined in metadata for '%s'" % metadata.title)
return thumb
def _create_new_book(self,fpath, metadata, path, db_added, lb_added, thumb, format):
@ -1190,8 +1242,9 @@ class ITUNES(DriverBase):
if DEBUG:
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.device_collections = []
this_book.format = format
@ -1207,13 +1260,13 @@ class ITUNES(DriverBase):
try:
this_book.datetime = parse_date(str(lb_added.date_added())).timetuple()
except:
this_book.datetime = time.gmtime()
pass
elif db_added:
this_book.size = self._get_device_book_size(fpath, db_added.size())
try:
this_book.datetime = parse_date(str(db_added.date_added())).timetuple()
except:
this_book.datetime = time.gmtime()
pass
elif iswindows:
if lb_added:
@ -1221,13 +1274,13 @@ class ITUNES(DriverBase):
try:
this_book.datetime = parse_date(str(lb_added.DateAdded)).timetuple()
except:
this_book.datetime = time.gmtime()
pass
elif db_added:
this_book.size = self._get_device_book_size(fpath, db_added.Size)
try:
this_book.datetime = parse_date(str(db_added.DateAdded)).timetuple()
except:
this_book.datetime = time.gmtime()
pass
return this_book
@ -1244,7 +1297,8 @@ class ITUNES(DriverBase):
plist = None
if plist:
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.close()
@ -1494,12 +1548,15 @@ class ITUNES(DriverBase):
if iswindows:
dev_books = self._get_device_books_playlist()
if DEBUG:
self.log.info(" ITUNES._find_device_book(uuid)")
self.log.info(" searching for %s ('%s' by %s)" %
(search['uuid'], search['title'], search['author']))
self.log.info(" ITUNES._find_device_book()")
self.log.info(" searching for '%s' by '%s' (%s)" %
(search['title'], search['author'],search['uuid']))
attempts = 9
while attempts:
# Try by uuid - only one hit
if 'uuid' in search and search['uuid']:
if DEBUG:
self.log.info(" searching by uuid '%s' ..." % search['uuid'])
hits = dev_books.Search(search['uuid'],self.SearchField.index('All'))
if hits:
hit = hits[0]
@ -1507,6 +1564,9 @@ class ITUNES(DriverBase):
return hit
# Try by author - there could be multiple hits
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:
@ -1515,6 +1575,18 @@ class ITUNES(DriverBase):
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:
for hit in hits:
if hit.Name == search['title']:
if DEBUG:
self.log.info(" found '%s'" % (hit.Name))
return hit
# PDF just sent, title not updated yet, look for export pattern
# 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'])
@ -1547,12 +1619,14 @@ class ITUNES(DriverBase):
if iswindows:
if DEBUG:
self.log.info(" ITUNES._find_library_book()")
'''
if 'uuid' in search:
self.log.info(" looking for '%s' by %s (%s)" %
(search['title'], search['author'], search['uuid']))
else:
self.log.info(" looking for '%s' by %s" %
(search['title'], search['author']))
'''
for source in self.iTunes.sources:
if source.Kind == self.Sources.index('Library'):
@ -1577,10 +1651,11 @@ class ITUNES(DriverBase):
if DEBUG:
self.log.error(" no Books playlist found")
attempts = 9
while attempts:
# Find book whose Album field = search['uuid']
if 'uuid' in search:
if 'uuid' in search and search['uuid']:
if DEBUG:
self.log.info(" searching by uuid '%s' ..." % search['uuid'])
hits = lib_books.Search(search['uuid'],self.SearchField.index('All'))
@ -1590,6 +1665,8 @@ class ITUNES(DriverBase):
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
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'))
@ -1600,6 +1677,18 @@ class ITUNES(DriverBase):
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 = lib_books.Search(search['title'],self.SearchField.index('All'))
if hits:
for hit in hits:
if hit.Name == search['title']:
if DEBUG:
self.log.info(" found '%s'" % (hit.Name))
return hit
# PDF just sent, title not updated yet, look for export pattern
# 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'])
@ -1633,66 +1722,88 @@ class ITUNES(DriverBase):
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")
thumb_path = book_path.rpartition('.')[0] + '.jpg'
format = book_path.rpartition('.')[2].lower()
try:
zfr = ZipFile(archive_path)
thumb_data = zfr.read(thumb_path)
zfr.close()
except:
zfw = ZipFile(archive_path, mode='a')
else:
# self.settings().use_subdirs is a repurposed DeviceConfig field
# We're using it to skip fetching/caching covers to speed things up
if not self.settings().use_subdirs:
thumb_data = None
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 format == 'epub':
title = book.name()
elif iswindows:
title = book.Name
try:
zfr = ZipFile(self.archive_path)
thumb_data = zfr.read(thumb_path)
if thumb_data == 'None':
if False:
self.log.info(" fetching artwork from %s\n %s" % (book_path,book))
# Resize the cover
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
if DEBUG:
self.log.info(" ITUNES._generate_thumbnail():")
if isosx:
# Fetch the artwork from iTunes
try:
data = book.artworks[1].raw_data().data
#self._dump_hex(data[:256])
except:
# If no artwork, write an empty marker to cache
if DEBUG:
self.log.error(" error fetching iTunes artwork for '%s'" % title)
zfw.writestr(thumb_path, 'None')
zfw.close()
return None
# 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)
img_data.close()
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
if DEBUG:
self.log.info(" generated thumb for '%s', caching" % book.name())
zfw.writestr(thumb_path, thumb_data)
zfw.close()
return thumb_data
except:
self.log.error(" error generating thumb for '%s'" % book.name())
try:
zfw.close()
except:
pass
return None
else:
if DEBUG:
self.log.info(" unable to generate PDF thumbs")
return None
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 DEBUG:
self.log.info(" no artwork available for '%s'" % book.Name)
zfw.writestr(thumb_path, 'None')
zfw.close()
return None
if format == 'epub':
# Save the cover from iTunes
# Fetch the artwork 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)
@ -1705,24 +1816,22 @@ class ITUNES(DriverBase):
thumb_data = thumb.getvalue()
os.remove(tmp_thumb)
thumb.close()
# Cache the tagged thumb
if DEBUG:
if False:
self.log.info(" generated thumb for '%s', caching" % book.Name)
# Cache the tagged thumb
zfw.writestr(thumb_path, thumb_data)
zfw.close()
return thumb_data
except:
self.log.error(" error generating thumb for '%s'" % book.Name)
try:
zfw.close()
except:
pass
return None
else:
if DEBUG:
self.log.info(" unable to generate PDF thumbs")
return None
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:
zfw.close()
return thumb_data
def _get_device_book_size(self, file, compressed_size):
'''
@ -1766,7 +1875,7 @@ class ITUNES(DriverBase):
for book in books:
# This may need additional entries for international iTunes users
if book.kind() in ['MPEG audio file']:
if book.kind() in self.Audiobooks:
if DEBUG:
self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind()))
else:
@ -1798,7 +1907,7 @@ class ITUNES(DriverBase):
for book in dev_books:
# This may need additional entries for international iTunes users
if book.KindAsString in ['MPEG audio file']:
if book.KindAsString in self.Audiobooks:
if DEBUG:
self.log.info(" ignoring '%s' of type '%s'" % (book.Name, book.KindAsString))
else:
@ -1899,12 +2008,13 @@ class ITUNES(DriverBase):
lib_books = pl.file_tracks()
for book in lib_books:
# This may need additional entries for international iTunes users
if book.kind() in ['MPEG audio file']:
if book.kind() in self.Audiobooks:
if DEBUG:
self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind()))
else:
# 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):
try:
if book.location() == appscript.k.missing_value:
@ -1917,7 +2027,8 @@ class ITUNES(DriverBase):
library_books[path] = book
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:
if DEBUG:
self.log.info(' no Library playlists')
@ -1955,11 +2066,12 @@ class ITUNES(DriverBase):
try:
for book in lib_books:
# This may need additional entries for international iTunes users
if book.KindAsString in ['MPEG audio file']:
if book.KindAsString in self.Audiobooks:
if DEBUG:
self.log.info(" ignoring %-30.30s of type '%s'" % (book.Name, book.KindAsString))
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
if book.Description.startswith(self.description_prefix):
@ -2174,7 +2286,9 @@ class ITUNES(DriverBase):
# Delete existing from Device|Books, add to self.update_list
# for deletion from booklist[0] during add_books_to_metadata
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._remove_from_device(self.cached_books[book])
if DEBUG:
@ -2191,9 +2305,9 @@ class ITUNES(DriverBase):
# Delete existing from Library|Books, add to self.update_list
# for deletion from booklist[0] during add_books_to_metadata
for book in self.cached_books:
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]):
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._remove_from_iTunes(self.cached_books[book])
if DEBUG:
@ -2209,18 +2323,20 @@ class ITUNES(DriverBase):
'''
self.log.info(" ITUNES._remove_from_device()")
if isosx:
if False:
self.log.info(" deleting %s" % cached_book['dev_book'])
if DEBUG:
self.log.info(" deleting '%s' from iDevice" % cached_book['title'])
cached_book['dev_book'].delete()
elif iswindows:
dev_pl = self._get_device_books_playlist()
hits = dev_pl.Search(cached_book['uuid'],self.SearchField.index('All'))
if hits:
hit = hits[0]
if False:
self.log.info(" deleting '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Album))
hit = self._find_device_book(cached_book)
if hit:
if DEBUG:
self.log.info(" deleting '%s' from iDevice" % cached_book['title'])
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):
'''
@ -2263,7 +2379,7 @@ class ITUNES(DriverBase):
except:
# We get here if there was an error with .location().path
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:
self.iTunes.delete(cached_book['lib_book'])
@ -2281,6 +2397,7 @@ class ITUNES(DriverBase):
path = book.Location
except:
book = self._find_library_book(cached_book)
if book:
path = book.Location
if book:
@ -2292,7 +2409,7 @@ class ITUNES(DriverBase):
try:
os.remove(path)
except:
self.log.warning(" could not find '%s' in iTunes storage" % path)
self.log.warning(" '%s' not in iTunes storage" % path)
try:
os.rmdir(storage_path[0])
self.log.info(" removed folder '%s'" % storage_path[0])
@ -2431,7 +2548,8 @@ class ITUNES(DriverBase):
if isosx:
if lb_added:
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.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
lb_added.enabled.set(True)
@ -2442,7 +2560,8 @@ class ITUNES(DriverBase):
if db_added:
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.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
db_added.enabled.set(True)
@ -2499,7 +2618,8 @@ class ITUNES(DriverBase):
elif iswindows:
if lb_added:
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.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
lb_added.Enabled = True
@ -2510,7 +2630,8 @@ class ITUNES(DriverBase):
if db_added:
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.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
db_added.Enabled = True
@ -2627,7 +2748,11 @@ class ITUNES_ASYNC(ITUNES):
"""
if not oncard:
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
@ -2638,13 +2763,15 @@ class ITUNES_ASYNC(ITUNES):
library_books = self._get_library_books()
book_count = float(len(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.path = self.path_template % (library_books[book].name(),
library_books[book].artist())
library_books[book].artist(),
format)
try:
this_book.datetime = parse_date(str(library_books[book].date_added())).timetuple()
except:
pass
this_book.datetime = time.gmtime()
this_book.db_id = None
this_book.device_collections = []
#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],
'dev_book':None,
'uuid': library_books[book].composer(),
#'format': 'pdf' if book.KindAsString.startswith('PDF') else 'epub'
'format': format
}
if self.report_progress is not None:
@ -2678,12 +2805,14 @@ class ITUNES_ASYNC(ITUNES):
book_count = float(len(library_books))
for (i,book) in enumerate(library_books):
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,
library_books[book].Artist)
library_books[book].Artist,
format)
try:
this_book.datetime = parse_date(str(library_books[book].DateAdded)).timetuple()
except:
pass
this_book.datetime = time.gmtime()
this_book.db_id = None
this_book.device_collections = []
this_book.library_id = library_books[book]
@ -2700,7 +2829,7 @@ class ITUNES_ASYNC(ITUNES):
'author':library_books[book].Artist,
'lib_book':library_books[book],
'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:
@ -2721,13 +2850,6 @@ class ITUNES_ASYNC(ITUNES):
else:
return BookList(self.log)
def unmount_device(self):
'''
'''
if DEBUG:
self.log.info("ITUNES_ASYNC:unmount_device()")
self.connected = False
def eject(self):
'''
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.update_list = []
def unmount_device(self):
'''
'''
if DEBUG:
self.log.info("ITUNES_ASYNC:unmount_device()")
self.connected = False
class BookList(list):
'''
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
mitem = self.addAction(QIcon(I('devices/itunes.png')),
_('Connect to iTunes (EXPERIMENTAL)'))
_('Connect to iTunes'))
mitem.setEnabled(True)
mitem.triggered.connect(lambda x : self.connect_to_itunes.emit())
self.connect_to_itunes_action = mitem

View File

@ -164,7 +164,7 @@ podofo_convert_pystring(PyObject *py) {
Py_UNICODE* u = PyUnicode_AS_UNICODE(py);
PyObject *u8 = PyUnicode_EncodeUTF8(u, PyUnicode_GET_SIZE(py), "replace");
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);
Py_DECREF(u8);
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");
return -1;
}
PdfString *s = podofo_convert_pystring(val);
if (s == NULL) return -1;
switch (field) {
case 0:
info->SetTitle(*s); break;
@ -240,7 +241,9 @@ podofo_PDFDoc_setter(podofo_PDFDoc *self, PyObject *val, int field) {
return -1;
}
self->doc->set_info(info);
return 0;
}

View File

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