mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk
This commit is contained in:
commit
eb8208114e
BIN
resources/images/news/alo_novine.png
Normal file
BIN
resources/images/news/alo_novine.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 753 B |
@ -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} """
|
||||
|
65
resources/recipes/alo_novine.recipe
Normal file
65
resources/recipes/alo_novine.recipe
Normal 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/')
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
--------------
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
'''
|
||||
# }}}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user