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'
|
category = 'vesti, online vesti, najnovije vesti, politika, sport, ekonomija, biznis, finansije, berza, kultura, zivot, putovanja, auto, automobili, tehnologija, politicki magazin, dogadjaji, desavanja, lifestyle, zdravlje, zdravstvo, vest, novine, nedeljnik, srbija, novi sad, vojvodina, svet, drustvo, zabava, republika srpska, beograd, intervju, komentar, reportaza, arhiva vesti, news, serbia, politics'
|
||||||
oldest_article = 8
|
oldest_article = 8
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
no_stylesheets = False
|
no_stylesheets = True
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
masthead_url = 'http://www.akter.co.rs/templates/gk_thenews2/images/style2/logo.png'
|
masthead_url = 'http://www.akter.co.rs/templates/gk_thenews2/images/style2/logo.png'
|
||||||
@ -23,9 +23,9 @@ class Akter(BasicNewsRecipe):
|
|||||||
publication_type = 'magazine'
|
publication_type = 'magazine'
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
PREFIX = 'http://www.akter.co.rs'
|
PREFIX = 'http://www.akter.co.rs'
|
||||||
extra_css = """ @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)}
|
extra_css = """
|
||||||
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
|
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
|
||||||
.article_description,body,.lokacija{font-family: Arial,Helvetica,sans1,sans-serif}
|
.article_description,body{font-family: Arial,Helvetica,sans1,sans-serif}
|
||||||
.color-2{display:block; margin-bottom: 10px; padding: 5px, 10px;
|
.color-2{display:block; margin-bottom: 10px; padding: 5px, 10px;
|
||||||
border-left: 1px solid #D00000; color: #D00000}
|
border-left: 1px solid #D00000; color: #D00000}
|
||||||
img{margin-bottom: 0.8em} """
|
img{margin-bottom: 0.8em} """
|
||||||
|
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',
|
'relatedSearchesModule',
|
||||||
'side_tool',
|
'side_tool',
|
||||||
'singleAd',
|
'singleAd',
|
||||||
|
'subNavigation clearfix',
|
||||||
'subNavigation tabContent active',
|
'subNavigation tabContent active',
|
||||||
'subNavigation tabContent active clearfix',
|
'subNavigation tabContent active clearfix',
|
||||||
]}),
|
]}),
|
||||||
@ -460,8 +461,10 @@ class NYTimes(BasicNewsRecipe):
|
|||||||
if mp_off >= 0:
|
if mp_off >= 0:
|
||||||
c = c[:mp_off]
|
c = c[:mp_off]
|
||||||
emTag.insert(0, c)
|
emTag.insert(0, c)
|
||||||
hrTag = Tag(soup, 'hr')
|
#hrTag = Tag(soup, 'hr')
|
||||||
#hrTag['style'] = "margin-top:0em;margin-bottom:0em"
|
#hrTag['class'] = 'caption_divider'
|
||||||
|
hrTag = Tag(soup, 'div')
|
||||||
|
hrTag['class'] = 'divider'
|
||||||
emTag.insert(1, hrTag)
|
emTag.insert(1, hrTag)
|
||||||
caption.replaceWith(emTag)
|
caption.replaceWith(emTag)
|
||||||
|
|
||||||
|
@ -76,6 +76,7 @@ class NYTimes(BasicNewsRecipe):
|
|||||||
'relatedSearchesModule',
|
'relatedSearchesModule',
|
||||||
'side_tool',
|
'side_tool',
|
||||||
'singleAd',
|
'singleAd',
|
||||||
|
'subNavigation clearfix',
|
||||||
'subNavigation tabContent active',
|
'subNavigation tabContent active',
|
||||||
'subNavigation tabContent active clearfix',
|
'subNavigation tabContent active clearfix',
|
||||||
]}),
|
]}),
|
||||||
@ -350,8 +351,10 @@ class NYTimes(BasicNewsRecipe):
|
|||||||
if mp_off >= 0:
|
if mp_off >= 0:
|
||||||
c = c[:mp_off]
|
c = c[:mp_off]
|
||||||
emTag.insert(0, c)
|
emTag.insert(0, c)
|
||||||
hrTag = Tag(soup, 'hr')
|
#hrTag = Tag(soup, 'hr')
|
||||||
#hrTag['style'] = "margin-top:0em;margin-bottom:0em"
|
#hrTag['class'] = 'caption_divider'
|
||||||
|
hrTag = Tag(soup, 'div')
|
||||||
|
hrTag['class'] = 'divider'
|
||||||
emTag.insert(1, hrTag)
|
emTag.insert(1, hrTag)
|
||||||
caption.replaceWith(emTag)
|
caption.replaceWith(emTag)
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ class LinuxFreeze(Command):
|
|||||||
'/usr/lib/libsqlite3.so.0',
|
'/usr/lib/libsqlite3.so.0',
|
||||||
'/usr/lib/libsqlite3.so.0',
|
'/usr/lib/libsqlite3.so.0',
|
||||||
'/usr/lib/libmng.so.1',
|
'/usr/lib/libmng.so.1',
|
||||||
'/usr/lib/libpodofo.so.0.6.99',
|
'/usr/lib/libpodofo.so.0.8.1',
|
||||||
'/lib/libz.so.1',
|
'/lib/libz.so.1',
|
||||||
'/lib/libuuid.so.1',
|
'/lib/libuuid.so.1',
|
||||||
'/usr/lib/libtiff.so.3',
|
'/usr/lib/libtiff.so.3',
|
||||||
|
@ -265,6 +265,9 @@ class Py2App(object):
|
|||||||
@flush
|
@flush
|
||||||
def get_local_dependencies(self, path_to_lib):
|
def get_local_dependencies(self, path_to_lib):
|
||||||
for x in self.get_dependencies(path_to_lib):
|
for x in self.get_dependencies(path_to_lib):
|
||||||
|
if x.startswith('libpodofo'):
|
||||||
|
yield x, x
|
||||||
|
continue
|
||||||
for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/',
|
for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/',
|
||||||
'/opt/local/lib/',
|
'/opt/local/lib/',
|
||||||
'/Library/Frameworks/Python.framework/', SW+'/freetype/lib/'):
|
'/Library/Frameworks/Python.framework/', SW+'/freetype/lib/'):
|
||||||
@ -397,7 +400,7 @@ class Py2App(object):
|
|||||||
@flush
|
@flush
|
||||||
def add_podofo(self):
|
def add_podofo(self):
|
||||||
info('\nAdding PoDoFo')
|
info('\nAdding PoDoFo')
|
||||||
pdf = join(SW, 'lib', 'libpodofo.0.6.99.dylib')
|
pdf = join(SW, 'lib', 'libpodofo.0.8.1.dylib')
|
||||||
self.install_dylib(pdf)
|
self.install_dylib(pdf)
|
||||||
|
|
||||||
@flush
|
@flush
|
||||||
|
@ -162,9 +162,50 @@ SET(WANT_LIB64 FALSE)
|
|||||||
SET(PODOFO_BUILD_SHARED TRUE)
|
SET(PODOFO_BUILD_SHARED TRUE)
|
||||||
SET(PODOFO_BUILD_STATIC FALSE)
|
SET(PODOFO_BUILD_STATIC FALSE)
|
||||||
|
|
||||||
cp build/podofo-0.7.0/build/src/Release/podofo.dll bin/
|
cp build/podofo/build/src/Release/podofo.dll bin/
|
||||||
cp build/podofo-0.7.0/build/src/Release/podofo.lib lib/
|
cp build/podofo/build/src/Release/podofo.lib lib/
|
||||||
cp build/podofo-0.7.0/build/src/Release/podofo.exp lib/
|
cp build/podofo/build/src/Release/podofo.exp lib/
|
||||||
|
|
||||||
|
cp build/podofo/build/podofo_config.h include/podofo/
|
||||||
|
cp -r build/podofo/src/* include/podofo/
|
||||||
|
|
||||||
|
The following patch was required to get it to compile:
|
||||||
|
|
||||||
|
Index: src/PdfImage.cpp
|
||||||
|
===================================================================
|
||||||
|
--- src/PdfImage.cpp (revision 1261)
|
||||||
|
+++ src/PdfImage.cpp (working copy)
|
||||||
|
@@ -627,7 +627,7 @@
|
||||||
|
|
||||||
|
long lLen = static_cast<long>(pInfo->rowbytes * height);
|
||||||
|
char* pBuffer = static_cast<char*>(malloc(sizeof(char) * lLen));
|
||||||
|
- png_bytep pRows[height];
|
||||||
|
+ png_bytepp pRows = static_cast<png_bytepp>(malloc(sizeof(png_bytep)*height));
|
||||||
|
for(int y=0; y<height; y++)
|
||||||
|
{
|
||||||
|
pRows[y] = reinterpret_cast<png_bytep>(pBuffer + (y * pInfo->rowbytes));
|
||||||
|
@@ -672,6 +672,7 @@
|
||||||
|
this->SetImageData( width, height, pInfo->bit_depth, &stream );
|
||||||
|
|
||||||
|
free(pBuffer);
|
||||||
|
+ free(pRows);
|
||||||
|
}
|
||||||
|
#endif // PODOFO_HAVE_PNG_LIB
|
||||||
|
|
||||||
|
Index: src/PdfFiltersPrivate.cpp
|
||||||
|
===================================================================
|
||||||
|
--- src/PdfFiltersPrivate.cpp (revision 1261)
|
||||||
|
+++ src/PdfFiltersPrivate.cpp (working copy)
|
||||||
|
@@ -1019,7 +1019,7 @@
|
||||||
|
/*
|
||||||
|
* Prepare for input from a memory buffer.
|
||||||
|
*/
|
||||||
|
-GLOBAL(void)
|
||||||
|
+void
|
||||||
|
jpeg_memory_src (j_decompress_ptr cinfo, const JOCTET * buffer, size_t bufsize)
|
||||||
|
{
|
||||||
|
my_src_ptr src;
|
||||||
|
|
||||||
|
|
||||||
ImageMagick
|
ImageMagick
|
||||||
--------------
|
--------------
|
||||||
|
@ -153,18 +153,10 @@
|
|||||||
<Property Id="WixShellExecTarget" Value="[#{exe_map[calibre]}]" />
|
<Property Id="WixShellExecTarget" Value="[#{exe_map[calibre]}]" />
|
||||||
<CustomAction Id="LaunchApplication" BinaryKey="WixCA"
|
<CustomAction Id="LaunchApplication" BinaryKey="WixCA"
|
||||||
DllEntry="WixShellExec" Impersonate="yes"/>
|
DllEntry="WixShellExec" Impersonate="yes"/>
|
||||||
<InstallExecuteSequence>
|
|
||||||
<FileCost Suppress="yes" />
|
|
||||||
</InstallExecuteSequence>
|
|
||||||
<InstallUISequence>
|
<InstallUISequence>
|
||||||
<FileCost Suppress="yes" />
|
<FileCost Suppress="yes" />
|
||||||
</InstallUISequence>
|
</InstallUISequence>
|
||||||
<AdminExecuteSequence>
|
|
||||||
<FileCost Suppress="yes" />
|
|
||||||
</AdminExecuteSequence>
|
|
||||||
<AdminUISequence>
|
|
||||||
<FileCost Suppress="yes" />
|
|
||||||
</AdminUISequence>
|
|
||||||
|
|
||||||
</Product>
|
</Product>
|
||||||
</Wix>
|
</Wix>
|
||||||
|
@ -275,13 +275,44 @@ class iPadOutput(OutputProfile):
|
|||||||
# touchscreen_news_css {{{
|
# touchscreen_news_css {{{
|
||||||
touchscreen_news_css = u'''
|
touchscreen_news_css = u'''
|
||||||
/* hr used in articles */
|
/* hr used in articles */
|
||||||
|
.article_articles_list {
|
||||||
|
width:18%;
|
||||||
|
}
|
||||||
|
.article_link {
|
||||||
|
color: #593f29;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.article_next {
|
||||||
|
-webkit-border-top-right-radius:4px;
|
||||||
|
-webkit-border-bottom-right-radius:4px;
|
||||||
|
font-style: italic;
|
||||||
|
width:32%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article_prev {
|
||||||
|
-webkit-border-top-left-radius:4px;
|
||||||
|
-webkit-border-bottom-left-radius:4px;
|
||||||
|
font-style: italic;
|
||||||
|
width:32%;
|
||||||
|
}
|
||||||
|
.article_sections_list {
|
||||||
|
width:18%;
|
||||||
|
}
|
||||||
|
.articles_link {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.sections_link {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.caption_divider {
|
.caption_divider {
|
||||||
border:#ccc 1px solid;
|
border:#ccc 1px solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
.touchscreen_navbar {
|
.touchscreen_navbar {
|
||||||
background:#ccc;
|
background:#c3bab2;
|
||||||
border:#ccc 1px solid;
|
border:#ccc 0px solid;
|
||||||
border-collapse:separate;
|
border-collapse:separate;
|
||||||
border-spacing:1px;
|
border-spacing:1px;
|
||||||
margin-left: 5%;
|
margin-left: 5%;
|
||||||
@ -292,22 +323,16 @@ class iPadOutput(OutputProfile):
|
|||||||
.touchscreen_navbar td {
|
.touchscreen_navbar td {
|
||||||
background:#fff;
|
background:#fff;
|
||||||
font-family:Helvetica;
|
font-family:Helvetica;
|
||||||
font-size:90%;
|
font-size:80%;
|
||||||
padding: 5px;
|
/* UI touchboxes use 8px padding */
|
||||||
|
padding: 6px;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
}
|
||||||
.touchscreen_navbar td:first-child {
|
|
||||||
-webkit-border-top-left-radius:4px;
|
|
||||||
-webkit-border-bottom-left-radius:4px;
|
|
||||||
}
|
|
||||||
.touchscreen_navbar td:last-child {
|
|
||||||
-webkit-border-top-right-radius:4px;
|
|
||||||
-webkit-border-bottom-right-radius:4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feed_link {
|
.touchscreen_navbar td a:link {
|
||||||
font-style: italic;
|
color: #593f29;
|
||||||
}
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Index formatting */
|
/* Index formatting */
|
||||||
.publish_date {
|
.publish_date {
|
||||||
@ -318,12 +343,50 @@ class iPadOutput(OutputProfile):
|
|||||||
border-top:1px solid gray;
|
border-top:1px solid gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr.caption_divider {
|
||||||
|
border-color:black;
|
||||||
|
border-style:solid;
|
||||||
|
border-width:1px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Feed summary formatting */
|
/* Feed summary formatting */
|
||||||
|
.article_summary {
|
||||||
|
display:inline-block;
|
||||||
|
}
|
||||||
|
.feed {
|
||||||
|
font-family:sans-serif;
|
||||||
|
font-weight:bold;
|
||||||
|
font-size:larger;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feed_link {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feed_next {
|
||||||
|
-webkit-border-top-right-radius:4px;
|
||||||
|
-webkit-border-bottom-right-radius:4px;
|
||||||
|
font-style: italic;
|
||||||
|
width:40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feed_prev {
|
||||||
|
-webkit-border-top-left-radius:4px;
|
||||||
|
-webkit-border-bottom-left-radius:4px;
|
||||||
|
font-style: italic;
|
||||||
|
width:40%;
|
||||||
|
}
|
||||||
|
|
||||||
.feed_title {
|
.feed_title {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 160%;
|
font-size: 160%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.feed_up {
|
||||||
|
font-weight: bold;
|
||||||
|
width:20%;
|
||||||
|
}
|
||||||
|
|
||||||
.summary_headline {
|
.summary_headline {
|
||||||
font-weight:bold;
|
font-weight:bold;
|
||||||
text-align:left;
|
text-align:left;
|
||||||
@ -338,12 +401,6 @@ class iPadOutput(OutputProfile):
|
|||||||
text-align:left;
|
text-align:left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.feed {
|
|
||||||
font-family:sans-serif;
|
|
||||||
font-weight:bold;
|
|
||||||
font-size:larger;
|
|
||||||
}
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ from calibre.utils.config import config_dir
|
|||||||
from calibre.utils.date import isoformat, now, parse_date
|
from calibre.utils.date import isoformat, now, parse_date
|
||||||
from calibre.utils.localization import get_lang
|
from calibre.utils.localization import get_lang
|
||||||
from calibre.utils.logging import Log
|
from calibre.utils.logging import Log
|
||||||
from calibre.utils.zipfile import ZipFile
|
from calibre.utils.zipfile import ZipFile, safe_replace
|
||||||
|
|
||||||
from PIL import Image as PILImage
|
from PIL import Image as PILImage
|
||||||
|
|
||||||
@ -38,6 +38,7 @@ if iswindows:
|
|||||||
class DriverBase(DeviceConfig, DevicePlugin):
|
class DriverBase(DeviceConfig, DevicePlugin):
|
||||||
# Needed for config_widget to work
|
# Needed for config_widget to work
|
||||||
FORMATS = ['epub', 'pdf']
|
FORMATS = ['epub', 'pdf']
|
||||||
|
SUPPORTS_SUB_DIRS = True # To enable second checkbox in customize widget
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _config_base_name(cls):
|
def _config_base_name(cls):
|
||||||
@ -87,7 +88,7 @@ class ITUNES(DriverBase):
|
|||||||
supported_platforms = ['osx','windows']
|
supported_platforms = ['osx','windows']
|
||||||
author = 'GRiker'
|
author = 'GRiker'
|
||||||
#: The version of this plugin as a 3-tuple (major, minor, revision)
|
#: The version of this plugin as a 3-tuple (major, minor, revision)
|
||||||
version = (0,8,0)
|
version = (0,9,0)
|
||||||
|
|
||||||
OPEN_FEEDBACK_MESSAGE = _(
|
OPEN_FEEDBACK_MESSAGE = _(
|
||||||
'Apple device detected, launching iTunes, please wait ...')
|
'Apple device detected, launching iTunes, please wait ...')
|
||||||
@ -106,53 +107,55 @@ class ITUNES(DriverBase):
|
|||||||
BCD = [0x01]
|
BCD = [0x01]
|
||||||
|
|
||||||
# iTunes enumerations
|
# iTunes enumerations
|
||||||
Sources = [
|
Audiobooks = [
|
||||||
'Unknown',
|
'Audible file',
|
||||||
'Library',
|
'MPEG audio file',
|
||||||
'iPod',
|
'Protected AAC audio file'
|
||||||
'AudioCD',
|
]
|
||||||
'MP3CD',
|
|
||||||
'Device',
|
|
||||||
'RadioTuner',
|
|
||||||
'SharedLibrary']
|
|
||||||
|
|
||||||
ArtworkFormat = [
|
ArtworkFormat = [
|
||||||
'Unknown',
|
'Unknown',
|
||||||
'JPEG',
|
'JPEG',
|
||||||
'PNG',
|
'PNG',
|
||||||
'BMP'
|
'BMP'
|
||||||
]
|
]
|
||||||
|
|
||||||
PlaylistKind = [
|
PlaylistKind = [
|
||||||
'Unknown',
|
'Unknown',
|
||||||
'Library',
|
'Library',
|
||||||
'User',
|
'User',
|
||||||
'CD',
|
'CD',
|
||||||
'Device',
|
'Device',
|
||||||
'Radio Tuner'
|
'Radio Tuner'
|
||||||
]
|
]
|
||||||
|
|
||||||
PlaylistSpecialKind = [
|
PlaylistSpecialKind = [
|
||||||
'Unknown',
|
'Unknown',
|
||||||
'Purchased Music',
|
'Purchased Music',
|
||||||
'Party Shuffle',
|
'Party Shuffle',
|
||||||
'Podcasts',
|
'Podcasts',
|
||||||
'Folder',
|
'Folder',
|
||||||
'Video',
|
'Video',
|
||||||
'Music',
|
'Music',
|
||||||
'Movies',
|
'Movies',
|
||||||
'TV Shows',
|
'TV Shows',
|
||||||
'Books',
|
'Books',
|
||||||
]
|
]
|
||||||
|
|
||||||
SearchField = [
|
SearchField = [
|
||||||
'All',
|
'All',
|
||||||
'Visible',
|
'Visible',
|
||||||
'Artists',
|
'Artists',
|
||||||
'Albums',
|
'Albums',
|
||||||
'Composers',
|
'Composers',
|
||||||
'SongNames',
|
'SongNames',
|
||||||
]
|
]
|
||||||
|
Sources = [
|
||||||
|
'Unknown',
|
||||||
|
'Library',
|
||||||
|
'iPod',
|
||||||
|
'AudioCD',
|
||||||
|
'MP3CD',
|
||||||
|
'Device',
|
||||||
|
'RadioTuner',
|
||||||
|
'SharedLibrary'
|
||||||
|
]
|
||||||
|
|
||||||
# Cover art size limits
|
# Cover art size limits
|
||||||
MAX_COVER_WIDTH = 510
|
MAX_COVER_WIDTH = 510
|
||||||
@ -161,6 +164,7 @@ class ITUNES(DriverBase):
|
|||||||
# Properties
|
# Properties
|
||||||
cached_books = {}
|
cached_books = {}
|
||||||
cache_dir = os.path.join(config_dir, 'caches', 'itunes')
|
cache_dir = os.path.join(config_dir, 'caches', 'itunes')
|
||||||
|
archive_path = os.path.join(cache_dir, "thumbs.zip")
|
||||||
description_prefix = "added by calibre"
|
description_prefix = "added by calibre"
|
||||||
ejected = False
|
ejected = False
|
||||||
iTunes= None
|
iTunes= None
|
||||||
@ -168,7 +172,7 @@ class ITUNES(DriverBase):
|
|||||||
library_orphans = None
|
library_orphans = None
|
||||||
log = Log()
|
log = Log()
|
||||||
manual_sync_mode = False
|
manual_sync_mode = False
|
||||||
path_template = 'iTunes/%s - %s.epub'
|
path_template = 'iTunes/%s - %s.%s'
|
||||||
problem_titles = []
|
problem_titles = []
|
||||||
problem_msg = None
|
problem_msg = None
|
||||||
report_progress = None
|
report_progress = None
|
||||||
@ -252,7 +256,7 @@ class ITUNES(DriverBase):
|
|||||||
(new_book.title, new_book.author))
|
(new_book.title, new_book.author))
|
||||||
booklists[0].append(new_book)
|
booklists[0].append(new_book)
|
||||||
|
|
||||||
if DEBUG:
|
if False:
|
||||||
self._dump_booklist(booklists[0],header='after',indent=2)
|
self._dump_booklist(booklists[0],header='after',indent=2)
|
||||||
self._dump_cached_books(header='after',indent=2)
|
self._dump_cached_books(header='after',indent=2)
|
||||||
|
|
||||||
@ -273,10 +277,13 @@ class ITUNES(DriverBase):
|
|||||||
"""
|
"""
|
||||||
if not oncard:
|
if not oncard:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info("ITUNES:books(oncard=%s)" % oncard)
|
self.log.info("ITUNES:books():")
|
||||||
|
if self.settings().use_subdirs:
|
||||||
|
self.log.info(" Cover fetching/caching enabled")
|
||||||
|
else:
|
||||||
|
self.log.info(" Cover fetching/caching disabled")
|
||||||
|
|
||||||
# Fetch a list of books from iPod device connected to iTunes
|
# Fetch a list of books from iPod device connected to iTunes
|
||||||
|
|
||||||
if 'iPod' in self.sources:
|
if 'iPod' in self.sources:
|
||||||
booklist = BookList(self.log)
|
booklist = BookList(self.log)
|
||||||
cached_books = {}
|
cached_books = {}
|
||||||
@ -287,11 +294,12 @@ class ITUNES(DriverBase):
|
|||||||
book_count = float(len(device_books))
|
book_count = float(len(device_books))
|
||||||
for (i,book) in enumerate(device_books):
|
for (i,book) in enumerate(device_books):
|
||||||
this_book = Book(book.name(), book.artist())
|
this_book = Book(book.name(), book.artist())
|
||||||
this_book.path = self.path_template % (book.name(), book.artist())
|
format = 'pdf' if book.kind().startswith('PDF') else 'epub'
|
||||||
|
this_book.path = self.path_template % (book.name(), book.artist(),format)
|
||||||
try:
|
try:
|
||||||
this_book.datetime = parse_date(str(book.date_added())).timetuple()
|
this_book.datetime = parse_date(str(book.date_added())).timetuple()
|
||||||
except:
|
except:
|
||||||
pass
|
this_book.datetime = time.gmtime()
|
||||||
this_book.db_id = None
|
this_book.db_id = None
|
||||||
this_book.device_collections = []
|
this_book.device_collections = []
|
||||||
this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None
|
this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None
|
||||||
@ -325,11 +333,12 @@ class ITUNES(DriverBase):
|
|||||||
book_count = float(len(device_books))
|
book_count = float(len(device_books))
|
||||||
for (i,book) in enumerate(device_books):
|
for (i,book) in enumerate(device_books):
|
||||||
this_book = Book(book.Name, book.Artist)
|
this_book = Book(book.Name, book.Artist)
|
||||||
this_book.path = self.path_template % (book.Name, book.Artist)
|
format = 'pdf' if book.KindAsString.startswith('PDF') else 'epub'
|
||||||
|
this_book.path = self.path_template % (book.Name, book.Artist,format)
|
||||||
try:
|
try:
|
||||||
this_book.datetime = parse_date(str(book.DateAdded)).timetuple()
|
this_book.datetime = parse_date(str(book.DateAdded)).timetuple()
|
||||||
except:
|
except:
|
||||||
pass
|
this_book.datetime = time.gmtime()
|
||||||
this_book.db_id = None
|
this_book.db_id = None
|
||||||
this_book.device_collections = []
|
this_book.device_collections = []
|
||||||
this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None
|
this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None
|
||||||
@ -532,8 +541,10 @@ class ITUNES(DriverBase):
|
|||||||
# Turn off the Save template
|
# Turn off the Save template
|
||||||
cw.opt_save_template.setVisible(False)
|
cw.opt_save_template.setVisible(False)
|
||||||
cw.label.setVisible(False)
|
cw.label.setVisible(False)
|
||||||
# Repurpose the checkbox
|
# Repurpose the metadata checkbox
|
||||||
cw.opt_read_metadata.setText(_("Use Series as Category in iTunes/iBooks"))
|
cw.opt_read_metadata.setText(_("Use Series as Category in iTunes/iBooks"))
|
||||||
|
# Repurpose the use_subdirs checkbox
|
||||||
|
cw.opt_use_subdirs.setText(_("Cache covers from iTunes/iBooks"))
|
||||||
return cw
|
return cw
|
||||||
|
|
||||||
def delete_books(self, paths, end_session=True):
|
def delete_books(self, paths, end_session=True):
|
||||||
@ -691,21 +702,19 @@ class ITUNES(DriverBase):
|
|||||||
self.log.info("ITUNES.open()")
|
self.log.info("ITUNES.open()")
|
||||||
|
|
||||||
# Confirm/create thumbs archive
|
# Confirm/create thumbs archive
|
||||||
archive_path = os.path.join(self.cache_dir, "thumbs.zip")
|
|
||||||
|
|
||||||
if not os.path.exists(self.cache_dir):
|
if not os.path.exists(self.cache_dir):
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" creating thumb cache '%s'" % self.cache_dir)
|
self.log.info(" creating thumb cache '%s'" % self.cache_dir)
|
||||||
os.makedirs(self.cache_dir)
|
os.makedirs(self.cache_dir)
|
||||||
|
|
||||||
if not os.path.exists(archive_path):
|
if not os.path.exists(self.archive_path):
|
||||||
self.log.info(" creating zip archive")
|
self.log.info(" creating zip archive")
|
||||||
zfw = ZipFile(archive_path, mode='w')
|
zfw = ZipFile(self.archive_path, mode='w')
|
||||||
zfw.writestr("iTunes Thumbs Archive",'')
|
zfw.writestr("iTunes Thumbs Archive",'')
|
||||||
zfw.close()
|
zfw.close()
|
||||||
else:
|
else:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" existing thumb cache at '%s'" % archive_path)
|
self.log.info(" existing thumb cache at '%s'" % self.archive_path)
|
||||||
|
|
||||||
def remove_books_from_metadata(self, paths, booklists):
|
def remove_books_from_metadata(self, paths, booklists):
|
||||||
'''
|
'''
|
||||||
@ -722,21 +731,61 @@ class ITUNES(DriverBase):
|
|||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info("ITUNES.remove_books_from_metadata()")
|
self.log.info("ITUNES.remove_books_from_metadata()")
|
||||||
for path in paths:
|
for path in paths:
|
||||||
self._dump_cached_book(self.cached_books[path], indent=2)
|
if DEBUG:
|
||||||
|
self._dump_cached_book(self.cached_books[path], indent=2)
|
||||||
|
self.log.info(" looking for '%s' by '%s' (%s)" %
|
||||||
|
(self.cached_books[path]['title'],
|
||||||
|
self.cached_books[path]['author'],
|
||||||
|
self.cached_books[path]['uuid']))
|
||||||
|
|
||||||
# Purge the booklist, self.cached_books
|
# Purge the booklist, self.cached_books, thumb cache
|
||||||
for i,bl_book in enumerate(booklists[0]):
|
for i,bl_book in enumerate(booklists[0]):
|
||||||
if False:
|
if False:
|
||||||
self.log.info(" evaluating '%s'" % bl_book.uuid)
|
self.log.info(" evaluating '%s' by '%s' (%s)" %
|
||||||
if bl_book.uuid == self.cached_books[path]['uuid']:
|
(bl_book.title, bl_book.author,bl_book.uuid))
|
||||||
# Remove from booklists[0]
|
|
||||||
booklists[0].pop(i)
|
|
||||||
|
|
||||||
|
found = False
|
||||||
|
if bl_book.uuid == self.cached_books[path]['uuid']:
|
||||||
|
if False:
|
||||||
|
self.log.info(" matched with uuid")
|
||||||
|
booklists[0].pop(i)
|
||||||
|
found = True
|
||||||
|
elif bl_book.title == self.cached_books[path]['title'] and \
|
||||||
|
bl_book.author[0] == self.cached_books[path]['author']:
|
||||||
|
if False:
|
||||||
|
self.log.info(" matched with title + author")
|
||||||
|
booklists[0].pop(i)
|
||||||
|
found = True
|
||||||
|
|
||||||
|
if found:
|
||||||
|
# Remove from self.cached_books
|
||||||
for cb in self.cached_books:
|
for cb in self.cached_books:
|
||||||
if self.cached_books[cb]['uuid'] == self.cached_books[path]['uuid']:
|
if self.cached_books[cb]['uuid'] == self.cached_books[path]['uuid']:
|
||||||
self.cached_books.pop(cb)
|
self.cached_books.pop(cb)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Remove from thumb from thumb cache
|
||||||
|
thumb_path = path.rpartition('.')[0] + '.jpg'
|
||||||
|
zf = ZipFile(self.archive_path,'a')
|
||||||
|
fnames = zf.namelist()
|
||||||
|
try:
|
||||||
|
thumb = [x for x in fnames if thumb_path in x][0]
|
||||||
|
except:
|
||||||
|
thumb = None
|
||||||
|
if thumb:
|
||||||
|
if DEBUG:
|
||||||
|
self.log.info(" deleting '%s' from cover cache" % (thumb_path))
|
||||||
|
zf.delete(thumb_path)
|
||||||
|
else:
|
||||||
|
if DEBUG:
|
||||||
|
self.log.info(" '%s' not found in cover cache" % thumb_path)
|
||||||
|
zf.close()
|
||||||
|
|
||||||
break
|
break
|
||||||
|
# else:
|
||||||
|
# if DEBUG:
|
||||||
|
# self.log.error(" unable to find '%s' by '%s' (%s)" %
|
||||||
|
# (bl_book.title, bl_book.author,bl_book.uuid))
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
self._dump_booklist(booklists[0], indent = 2)
|
self._dump_booklist(booklists[0], indent = 2)
|
||||||
@ -842,12 +891,11 @@ class ITUNES(DriverBase):
|
|||||||
self.log.info("ITUNES.upload_books()")
|
self.log.info("ITUNES.upload_books()")
|
||||||
self._dump_files(files, header='upload_books()',indent=2)
|
self._dump_files(files, header='upload_books()',indent=2)
|
||||||
self._dump_update_list(header='upload_books()',indent=2)
|
self._dump_update_list(header='upload_books()',indent=2)
|
||||||
#self.log.info(" self.settings().format_map: %s" % self.settings().format_map)
|
|
||||||
|
|
||||||
if isosx:
|
if isosx:
|
||||||
for (i,file) in enumerate(files):
|
for (i,file) in enumerate(files):
|
||||||
format = file.rpartition('.')[2].lower()
|
format = file.rpartition('.')[2].lower()
|
||||||
path = self.path_template % (metadata[i].title, metadata[i].author[0])
|
path = self.path_template % (metadata[i].title, metadata[i].author[0],format)
|
||||||
self._remove_existing_copy(path, metadata[i])
|
self._remove_existing_copy(path, metadata[i])
|
||||||
fpath = self._get_fpath(file, metadata[i], format, update_md=True)
|
fpath = self._get_fpath(file, metadata[i], format, update_md=True)
|
||||||
db_added, lb_added = self._add_new_copy(fpath, metadata[i])
|
db_added, lb_added = self._add_new_copy(fpath, metadata[i])
|
||||||
@ -856,7 +904,10 @@ class ITUNES(DriverBase):
|
|||||||
new_booklist.append(this_book)
|
new_booklist.append(this_book)
|
||||||
self._update_iTunes_metadata(metadata[i], db_added, lb_added, this_book)
|
self._update_iTunes_metadata(metadata[i], db_added, lb_added, this_book)
|
||||||
|
|
||||||
# Add new_book to self.cached_paths
|
# Add new_book to self.cached_books
|
||||||
|
if DEBUG:
|
||||||
|
self.log.info(" adding '%s' by '%s' ['%s'] to self.cached_books" %
|
||||||
|
( metadata[i].title, metadata[i].author, metadata[i].uuid))
|
||||||
self.cached_books[this_book.path] = {
|
self.cached_books[this_book.path] = {
|
||||||
'author': metadata[i].author,
|
'author': metadata[i].author,
|
||||||
'dev_book': db_added,
|
'dev_book': db_added,
|
||||||
@ -877,7 +928,7 @@ class ITUNES(DriverBase):
|
|||||||
|
|
||||||
for (i,file) in enumerate(files):
|
for (i,file) in enumerate(files):
|
||||||
format = file.rpartition('.')[2].lower()
|
format = file.rpartition('.')[2].lower()
|
||||||
path = self.path_template % (metadata[i].title, metadata[i].author[0])
|
path = self.path_template % (metadata[i].title, metadata[i].author[0],format)
|
||||||
self._remove_existing_copy(path, metadata[i])
|
self._remove_existing_copy(path, metadata[i])
|
||||||
fpath = self._get_fpath(file, metadata[i],format, update_md=True)
|
fpath = self._get_fpath(file, metadata[i],format, update_md=True)
|
||||||
db_added, lb_added = self._add_new_copy(fpath, metadata[i])
|
db_added, lb_added = self._add_new_copy(fpath, metadata[i])
|
||||||
@ -1091,7 +1142,7 @@ class ITUNES(DriverBase):
|
|||||||
thumb = None
|
thumb = None
|
||||||
if metadata.cover:
|
if metadata.cover:
|
||||||
|
|
||||||
if (format == 'epub'):
|
if format == 'epub':
|
||||||
# Pre-shrink cover
|
# Pre-shrink cover
|
||||||
# self.MAX_COVER_WIDTH, self.MAX_COVER_HEIGHT
|
# self.MAX_COVER_WIDTH, self.MAX_COVER_HEIGHT
|
||||||
try:
|
try:
|
||||||
@ -1171,17 +1222,18 @@ class ITUNES(DriverBase):
|
|||||||
|
|
||||||
# Refresh the thumbnail cache
|
# Refresh the thumbnail cache
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info( " refreshing cached thumb for '%s'" % metadata.title)
|
self.log.info( " refreshing cached thumb for '%s'" % metadata.title)
|
||||||
archive_path = os.path.join(self.cache_dir, "thumbs.zip")
|
zfw = ZipFile(self.archive_path, mode='a')
|
||||||
zfw = ZipFile(archive_path, mode='a')
|
|
||||||
thumb_path = path.rpartition('.')[0] + '.jpg'
|
thumb_path = path.rpartition('.')[0] + '.jpg'
|
||||||
zfw.writestr(thumb_path, thumb)
|
zfw.writestr(thumb_path, thumb)
|
||||||
except:
|
except:
|
||||||
self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0]))
|
self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0]))
|
||||||
self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title))
|
self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title))
|
||||||
finally:
|
finally:
|
||||||
zfw.close()
|
zfw.close()
|
||||||
|
else:
|
||||||
|
if DEBUG:
|
||||||
|
self.log.info(" no cover defined in metadata for '%s'" % metadata.title)
|
||||||
return thumb
|
return thumb
|
||||||
|
|
||||||
def _create_new_book(self,fpath, metadata, path, db_added, lb_added, thumb, format):
|
def _create_new_book(self,fpath, metadata, path, db_added, lb_added, thumb, format):
|
||||||
@ -1190,8 +1242,9 @@ class ITUNES(DriverBase):
|
|||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" ITUNES._create_new_book()")
|
self.log.info(" ITUNES._create_new_book()")
|
||||||
|
|
||||||
this_book = Book(metadata.title, metadata.author[0])
|
#this_book = Book(metadata.title, metadata.author[0])
|
||||||
|
this_book = Book(metadata.title, ' & '.join(metadata.author))
|
||||||
|
this_book.datetime = time.gmtime()
|
||||||
this_book.db_id = None
|
this_book.db_id = None
|
||||||
this_book.device_collections = []
|
this_book.device_collections = []
|
||||||
this_book.format = format
|
this_book.format = format
|
||||||
@ -1207,13 +1260,13 @@ class ITUNES(DriverBase):
|
|||||||
try:
|
try:
|
||||||
this_book.datetime = parse_date(str(lb_added.date_added())).timetuple()
|
this_book.datetime = parse_date(str(lb_added.date_added())).timetuple()
|
||||||
except:
|
except:
|
||||||
this_book.datetime = time.gmtime()
|
pass
|
||||||
elif db_added:
|
elif db_added:
|
||||||
this_book.size = self._get_device_book_size(fpath, db_added.size())
|
this_book.size = self._get_device_book_size(fpath, db_added.size())
|
||||||
try:
|
try:
|
||||||
this_book.datetime = parse_date(str(db_added.date_added())).timetuple()
|
this_book.datetime = parse_date(str(db_added.date_added())).timetuple()
|
||||||
except:
|
except:
|
||||||
this_book.datetime = time.gmtime()
|
pass
|
||||||
|
|
||||||
elif iswindows:
|
elif iswindows:
|
||||||
if lb_added:
|
if lb_added:
|
||||||
@ -1221,13 +1274,13 @@ class ITUNES(DriverBase):
|
|||||||
try:
|
try:
|
||||||
this_book.datetime = parse_date(str(lb_added.DateAdded)).timetuple()
|
this_book.datetime = parse_date(str(lb_added.DateAdded)).timetuple()
|
||||||
except:
|
except:
|
||||||
this_book.datetime = time.gmtime()
|
pass
|
||||||
elif db_added:
|
elif db_added:
|
||||||
this_book.size = self._get_device_book_size(fpath, db_added.Size)
|
this_book.size = self._get_device_book_size(fpath, db_added.Size)
|
||||||
try:
|
try:
|
||||||
this_book.datetime = parse_date(str(db_added.DateAdded)).timetuple()
|
this_book.datetime = parse_date(str(db_added.DateAdded)).timetuple()
|
||||||
except:
|
except:
|
||||||
this_book.datetime = time.gmtime()
|
pass
|
||||||
|
|
||||||
return this_book
|
return this_book
|
||||||
|
|
||||||
@ -1244,7 +1297,8 @@ class ITUNES(DriverBase):
|
|||||||
plist = None
|
plist = None
|
||||||
if plist:
|
if plist:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" deleting %s from %s" % (pl_name,fpath))
|
self.log.info(" _delete_iTunesMetadata_plist():")
|
||||||
|
self.log.info(" deleting '%s'\n from '%s'" % (pl_name,fpath))
|
||||||
zf.delete(pl_name)
|
zf.delete(pl_name)
|
||||||
zf.close()
|
zf.close()
|
||||||
|
|
||||||
@ -1494,27 +1548,45 @@ class ITUNES(DriverBase):
|
|||||||
if iswindows:
|
if iswindows:
|
||||||
dev_books = self._get_device_books_playlist()
|
dev_books = self._get_device_books_playlist()
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" ITUNES._find_device_book(uuid)")
|
self.log.info(" ITUNES._find_device_book()")
|
||||||
self.log.info(" searching for %s ('%s' by %s)" %
|
self.log.info(" searching for '%s' by '%s' (%s)" %
|
||||||
(search['uuid'], search['title'], search['author']))
|
(search['title'], search['author'],search['uuid']))
|
||||||
attempts = 9
|
attempts = 9
|
||||||
while attempts:
|
while attempts:
|
||||||
# Try by uuid - only one hit
|
# Try by uuid - only one hit
|
||||||
hits = dev_books.Search(search['uuid'],self.SearchField.index('All'))
|
if 'uuid' in search and search['uuid']:
|
||||||
if hits:
|
if DEBUG:
|
||||||
hit = hits[0]
|
self.log.info(" searching by uuid '%s' ..." % search['uuid'])
|
||||||
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
|
hits = dev_books.Search(search['uuid'],self.SearchField.index('All'))
|
||||||
return hit
|
if hits:
|
||||||
|
hit = hits[0]
|
||||||
|
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
|
||||||
|
return hit
|
||||||
|
|
||||||
# Try by author - there could be multiple hits
|
# Try by author - there could be multiple hits
|
||||||
hits = dev_books.Search(search['author'],self.SearchField.index('Artists'))
|
if search['author']:
|
||||||
|
if DEBUG:
|
||||||
|
self.log.info(" searching by author '%s' ..." % search['author'])
|
||||||
|
hits = dev_books.Search(search['author'],self.SearchField.index('Artists'))
|
||||||
|
if hits:
|
||||||
|
for hit in hits:
|
||||||
|
if hit.Name == search['title']:
|
||||||
|
if DEBUG:
|
||||||
|
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
|
||||||
|
return hit
|
||||||
|
|
||||||
|
# Search by title if no author available
|
||||||
|
if DEBUG:
|
||||||
|
self.log.info(" searching by title '%s' ..." % search['title'])
|
||||||
|
hits = dev_books.Search(search['title'],self.SearchField.index('All'))
|
||||||
if hits:
|
if hits:
|
||||||
for hit in hits:
|
for hit in hits:
|
||||||
if hit.Name == search['title']:
|
if hit.Name == search['title']:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
|
self.log.info(" found '%s'" % (hit.Name))
|
||||||
return hit
|
return hit
|
||||||
|
|
||||||
|
# PDF just sent, title not updated yet, look for export pattern
|
||||||
# PDF metadata was rewritten at export as 'safe(title) - safe(author)'
|
# PDF metadata was rewritten at export as 'safe(title) - safe(author)'
|
||||||
if search['format'] == 'pdf':
|
if search['format'] == 'pdf':
|
||||||
title = re.sub(r'[^0-9a-zA-Z ]', '_', search['title'])
|
title = re.sub(r'[^0-9a-zA-Z ]', '_', search['title'])
|
||||||
@ -1547,12 +1619,14 @@ class ITUNES(DriverBase):
|
|||||||
if iswindows:
|
if iswindows:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" ITUNES._find_library_book()")
|
self.log.info(" ITUNES._find_library_book()")
|
||||||
|
'''
|
||||||
if 'uuid' in search:
|
if 'uuid' in search:
|
||||||
self.log.info(" looking for '%s' by %s (%s)" %
|
self.log.info(" looking for '%s' by %s (%s)" %
|
||||||
(search['title'], search['author'], search['uuid']))
|
(search['title'], search['author'], search['uuid']))
|
||||||
else:
|
else:
|
||||||
self.log.info(" looking for '%s' by %s" %
|
self.log.info(" looking for '%s' by %s" %
|
||||||
(search['title'], search['author']))
|
(search['title'], search['author']))
|
||||||
|
'''
|
||||||
|
|
||||||
for source in self.iTunes.sources:
|
for source in self.iTunes.sources:
|
||||||
if source.Kind == self.Sources.index('Library'):
|
if source.Kind == self.Sources.index('Library'):
|
||||||
@ -1577,10 +1651,11 @@ class ITUNES(DriverBase):
|
|||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.error(" no Books playlist found")
|
self.log.error(" no Books playlist found")
|
||||||
|
|
||||||
|
|
||||||
attempts = 9
|
attempts = 9
|
||||||
while attempts:
|
while attempts:
|
||||||
# Find book whose Album field = search['uuid']
|
# Find book whose Album field = search['uuid']
|
||||||
if 'uuid' in search:
|
if 'uuid' in search and search['uuid']:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" searching by uuid '%s' ..." % search['uuid'])
|
self.log.info(" searching by uuid '%s' ..." % search['uuid'])
|
||||||
hits = lib_books.Search(search['uuid'],self.SearchField.index('All'))
|
hits = lib_books.Search(search['uuid'],self.SearchField.index('All'))
|
||||||
@ -1590,16 +1665,30 @@ class ITUNES(DriverBase):
|
|||||||
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
|
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
|
||||||
return hit
|
return hit
|
||||||
|
|
||||||
|
# Search by author if known
|
||||||
|
if search['author']:
|
||||||
|
if DEBUG:
|
||||||
|
self.log.info(" searching by author '%s' ..." % search['author'])
|
||||||
|
hits = lib_books.Search(search['author'],self.SearchField.index('Artists'))
|
||||||
|
if hits:
|
||||||
|
for hit in hits:
|
||||||
|
if hit.Name == search['title']:
|
||||||
|
if DEBUG:
|
||||||
|
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
|
||||||
|
return hit
|
||||||
|
|
||||||
|
# Search by title if no author available
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" searching by author '%s' ..." % search['author'])
|
self.log.info(" searching by title '%s' ..." % search['title'])
|
||||||
hits = lib_books.Search(search['author'],self.SearchField.index('Artists'))
|
hits = lib_books.Search(search['title'],self.SearchField.index('All'))
|
||||||
if hits:
|
if hits:
|
||||||
for hit in hits:
|
for hit in hits:
|
||||||
if hit.Name == search['title']:
|
if hit.Name == search['title']:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer))
|
self.log.info(" found '%s'" % (hit.Name))
|
||||||
return hit
|
return hit
|
||||||
|
|
||||||
|
# PDF just sent, title not updated yet, look for export pattern
|
||||||
# PDF metadata was rewritten at export as 'safe(title) - safe(author)'
|
# PDF metadata was rewritten at export as 'safe(title) - safe(author)'
|
||||||
if search['format'] == 'pdf':
|
if search['format'] == 'pdf':
|
||||||
title = re.sub(r'[^0-9a-zA-Z ]', '_', search['title'])
|
title = re.sub(r'[^0-9a-zA-Z ]', '_', search['title'])
|
||||||
@ -1633,96 +1722,116 @@ class ITUNES(DriverBase):
|
|||||||
as of iTunes 9.2, iBooks 1.1, can't set artwork for PDF files via automation
|
as of iTunes 9.2, iBooks 1.1, can't set artwork for PDF files via automation
|
||||||
'''
|
'''
|
||||||
|
|
||||||
archive_path = os.path.join(self.cache_dir, "thumbs.zip")
|
# self.settings().use_subdirs is a repurposed DeviceConfig field
|
||||||
thumb_path = book_path.rpartition('.')[0] + '.jpg'
|
# We're using it to skip fetching/caching covers to speed things up
|
||||||
format = book_path.rpartition('.')[2].lower()
|
if not self.settings().use_subdirs:
|
||||||
|
thumb_data = None
|
||||||
try:
|
|
||||||
zfr = ZipFile(archive_path)
|
|
||||||
thumb_data = zfr.read(thumb_path)
|
|
||||||
zfr.close()
|
|
||||||
except:
|
|
||||||
zfw = ZipFile(archive_path, mode='a')
|
|
||||||
else:
|
|
||||||
return thumb_data
|
return thumb_data
|
||||||
|
|
||||||
self.log.info(" ITUNES._generate_thumbnail()")
|
thumb_path = book_path.rpartition('.')[0] + '.jpg'
|
||||||
|
format = book_path.rpartition('.')[2].lower()
|
||||||
if isosx:
|
if isosx:
|
||||||
if format == 'epub':
|
title = book.name()
|
||||||
try:
|
elif iswindows:
|
||||||
if False:
|
title = book.Name
|
||||||
self.log.info(" fetching artwork from %s\n %s" % (book_path,book))
|
|
||||||
# Resize the cover
|
|
||||||
data = book.artworks[1].raw_data().data
|
|
||||||
#self._dump_hex(data[:256])
|
|
||||||
img_data = cStringIO.StringIO(data)
|
|
||||||
im = PILImage.open(img_data)
|
|
||||||
scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80)
|
|
||||||
im = im.resize((int(width),int(height)), PILImage.ANTIALIAS)
|
|
||||||
img_data.close()
|
|
||||||
|
|
||||||
thumb = cStringIO.StringIO()
|
try:
|
||||||
im.convert('RGB').save(thumb,'JPEG')
|
zfr = ZipFile(self.archive_path)
|
||||||
thumb_data = thumb.getvalue()
|
thumb_data = zfr.read(thumb_path)
|
||||||
thumb.close()
|
if thumb_data == 'None':
|
||||||
|
if False:
|
||||||
|
self.log.info(" ITUNES._generate_thumbnail()\n returning None from cover cache for '%s'" % title)
|
||||||
|
zfr.close()
|
||||||
|
return None
|
||||||
|
except:
|
||||||
|
zfw = ZipFile(self.archive_path, mode='a')
|
||||||
|
else:
|
||||||
|
if False:
|
||||||
|
self.log.info(" returning thumb from cache for '%s'" % title)
|
||||||
|
return thumb_data
|
||||||
|
|
||||||
# Cache the tagged thumb
|
if DEBUG:
|
||||||
if DEBUG:
|
self.log.info(" ITUNES._generate_thumbnail():")
|
||||||
self.log.info(" generated thumb for '%s', caching" % book.name())
|
if isosx:
|
||||||
zfw.writestr(thumb_path, thumb_data)
|
|
||||||
zfw.close()
|
# Fetch the artwork from iTunes
|
||||||
return thumb_data
|
try:
|
||||||
except:
|
data = book.artworks[1].raw_data().data
|
||||||
self.log.error(" error generating thumb for '%s'" % book.name())
|
except:
|
||||||
try:
|
# If no artwork, write an empty marker to cache
|
||||||
zfw.close()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" unable to generate PDF thumbs")
|
self.log.error(" error fetching iTunes artwork for '%s'" % title)
|
||||||
|
zfw.writestr(thumb_path, 'None')
|
||||||
|
zfw.close()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
elif iswindows:
|
# Generate a thumb
|
||||||
|
try:
|
||||||
|
img_data = cStringIO.StringIO(data)
|
||||||
|
im = PILImage.open(img_data)
|
||||||
|
scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80)
|
||||||
|
im = im.resize((int(width),int(height)), PILImage.ANTIALIAS)
|
||||||
|
|
||||||
|
thumb = cStringIO.StringIO()
|
||||||
|
im.convert('RGB').save(thumb,'JPEG')
|
||||||
|
thumb_data = thumb.getvalue()
|
||||||
|
thumb.close()
|
||||||
|
if False:
|
||||||
|
self.log.info(" generated thumb for '%s', caching" % title)
|
||||||
|
# Cache the tagged thumb
|
||||||
|
zfw.writestr(thumb_path, thumb_data)
|
||||||
|
except:
|
||||||
|
if DEBUG:
|
||||||
|
self.log.error(" error generating thumb for '%s', caching empty marker" % book.name())
|
||||||
|
self._dump_hex(data[:32])
|
||||||
|
thumb_data = None
|
||||||
|
# Cache the empty cover
|
||||||
|
zfw.writestr(thumb_path, 'None')
|
||||||
|
finally:
|
||||||
|
img_data.close()
|
||||||
|
zfw.close()
|
||||||
|
|
||||||
|
return thumb_data
|
||||||
|
|
||||||
|
|
||||||
|
elif iswindows:
|
||||||
if not book.Artwork.Count:
|
if not book.Artwork.Count:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" no artwork available for '%s'" % book.Name)
|
self.log.info(" no artwork available for '%s'" % book.Name)
|
||||||
|
zfw.writestr(thumb_path, 'None')
|
||||||
|
zfw.close()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if format == 'epub':
|
# Fetch the artwork from iTunes
|
||||||
# Save the cover from iTunes
|
|
||||||
try:
|
|
||||||
tmp_thumb = os.path.join(tempfile.gettempdir(), "thumb.%s" % self.ArtworkFormat[book.Artwork.Item(1).Format])
|
|
||||||
book.Artwork.Item(1).SaveArtworkToFile(tmp_thumb)
|
|
||||||
# Resize the cover
|
|
||||||
im = PILImage.open(tmp_thumb)
|
|
||||||
scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80)
|
|
||||||
im = im.resize((int(width),int(height)), PILImage.ANTIALIAS)
|
|
||||||
thumb = cStringIO.StringIO()
|
|
||||||
im.convert('RGB').save(thumb,'JPEG')
|
|
||||||
thumb_data = thumb.getvalue()
|
|
||||||
os.remove(tmp_thumb)
|
|
||||||
thumb.close()
|
|
||||||
|
|
||||||
# Cache the tagged thumb
|
try:
|
||||||
if DEBUG:
|
tmp_thumb = os.path.join(tempfile.gettempdir(), "thumb.%s" % self.ArtworkFormat[book.Artwork.Item(1).Format])
|
||||||
self.log.info(" generated thumb for '%s', caching" % book.Name)
|
book.Artwork.Item(1).SaveArtworkToFile(tmp_thumb)
|
||||||
zfw.writestr(thumb_path, thumb_data)
|
# Resize the cover
|
||||||
zfw.close()
|
im = PILImage.open(tmp_thumb)
|
||||||
return thumb_data
|
scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80)
|
||||||
except:
|
im = im.resize((int(width),int(height)), PILImage.ANTIALIAS)
|
||||||
self.log.error(" error generating thumb for '%s'" % book.Name)
|
thumb = cStringIO.StringIO()
|
||||||
try:
|
im.convert('RGB').save(thumb,'JPEG')
|
||||||
zfw.close()
|
thumb_data = thumb.getvalue()
|
||||||
except:
|
os.remove(tmp_thumb)
|
||||||
pass
|
thumb.close()
|
||||||
return None
|
if False:
|
||||||
else:
|
self.log.info(" generated thumb for '%s', caching" % book.Name)
|
||||||
|
# Cache the tagged thumb
|
||||||
|
zfw.writestr(thumb_path, thumb_data)
|
||||||
|
except:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" unable to generate PDF thumbs")
|
self.log.error(" error generating thumb for '%s', caching empty marker" % book.Name)
|
||||||
return None
|
self._dump_hex(data[:32])
|
||||||
|
thumb_data = None
|
||||||
|
# Cache the empty cover
|
||||||
|
zfw.writestr(thumb_path,'None')
|
||||||
|
|
||||||
|
finally:
|
||||||
|
zfw.close()
|
||||||
|
|
||||||
|
return thumb_data
|
||||||
|
|
||||||
def _get_device_book_size(self, file, compressed_size):
|
def _get_device_book_size(self, file, compressed_size):
|
||||||
'''
|
'''
|
||||||
@ -1766,7 +1875,7 @@ class ITUNES(DriverBase):
|
|||||||
|
|
||||||
for book in books:
|
for book in books:
|
||||||
# This may need additional entries for international iTunes users
|
# This may need additional entries for international iTunes users
|
||||||
if book.kind() in ['MPEG audio file']:
|
if book.kind() in self.Audiobooks:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind()))
|
self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind()))
|
||||||
else:
|
else:
|
||||||
@ -1798,7 +1907,7 @@ class ITUNES(DriverBase):
|
|||||||
|
|
||||||
for book in dev_books:
|
for book in dev_books:
|
||||||
# This may need additional entries for international iTunes users
|
# This may need additional entries for international iTunes users
|
||||||
if book.KindAsString in ['MPEG audio file']:
|
if book.KindAsString in self.Audiobooks:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" ignoring '%s' of type '%s'" % (book.Name, book.KindAsString))
|
self.log.info(" ignoring '%s' of type '%s'" % (book.Name, book.KindAsString))
|
||||||
else:
|
else:
|
||||||
@ -1899,12 +2008,13 @@ class ITUNES(DriverBase):
|
|||||||
lib_books = pl.file_tracks()
|
lib_books = pl.file_tracks()
|
||||||
for book in lib_books:
|
for book in lib_books:
|
||||||
# This may need additional entries for international iTunes users
|
# This may need additional entries for international iTunes users
|
||||||
if book.kind() in ['MPEG audio file']:
|
if book.kind() in self.Audiobooks:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind()))
|
self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind()))
|
||||||
else:
|
else:
|
||||||
# Collect calibre orphans - remnants of recipe uploads
|
# Collect calibre orphans - remnants of recipe uploads
|
||||||
path = self.path_template % (book.name(), book.artist())
|
format = 'pdf' if book.kind().startswith('PDF') else 'epub'
|
||||||
|
path = self.path_template % (book.name(), book.artist(),format)
|
||||||
if str(book.description()).startswith(self.description_prefix):
|
if str(book.description()).startswith(self.description_prefix):
|
||||||
try:
|
try:
|
||||||
if book.location() == appscript.k.missing_value:
|
if book.location() == appscript.k.missing_value:
|
||||||
@ -1917,7 +2027,8 @@ class ITUNES(DriverBase):
|
|||||||
|
|
||||||
library_books[path] = book
|
library_books[path] = book
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" %-30.30s %-30.30s %-40.40s [%s]" % (book.name(), book.artist(), book.album(), book.kind()))
|
self.log.info(" %-30.30s %-30.30s %-40.40s [%s]" %
|
||||||
|
(book.name(), book.artist(), book.album(), book.kind()))
|
||||||
else:
|
else:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(' no Library playlists')
|
self.log.info(' no Library playlists')
|
||||||
@ -1955,11 +2066,12 @@ class ITUNES(DriverBase):
|
|||||||
try:
|
try:
|
||||||
for book in lib_books:
|
for book in lib_books:
|
||||||
# This may need additional entries for international iTunes users
|
# This may need additional entries for international iTunes users
|
||||||
if book.KindAsString in ['MPEG audio file']:
|
if book.KindAsString in self.Audiobooks:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" ignoring %-30.30s of type '%s'" % (book.Name, book.KindAsString))
|
self.log.info(" ignoring %-30.30s of type '%s'" % (book.Name, book.KindAsString))
|
||||||
else:
|
else:
|
||||||
path = self.path_template % (book.Name, book.Artist)
|
format = 'pdf' if book.KindAsString.startswith('PDF') else 'epub'
|
||||||
|
path = self.path_template % (book.Name, book.Artist,format)
|
||||||
|
|
||||||
# Collect calibre orphans
|
# Collect calibre orphans
|
||||||
if book.Description.startswith(self.description_prefix):
|
if book.Description.startswith(self.description_prefix):
|
||||||
@ -2174,7 +2286,9 @@ class ITUNES(DriverBase):
|
|||||||
# Delete existing from Device|Books, add to self.update_list
|
# Delete existing from Device|Books, add to self.update_list
|
||||||
# for deletion from booklist[0] during add_books_to_metadata
|
# for deletion from booklist[0] during add_books_to_metadata
|
||||||
for book in self.cached_books:
|
for book in self.cached_books:
|
||||||
if self.cached_books[book]['uuid'] == metadata.uuid:
|
if self.cached_books[book]['uuid'] == metadata.uuid and \
|
||||||
|
self.cached_books[book]['title'] == metadata.title and \
|
||||||
|
self.cached_books[book]['author'] == metadata.authors[0]:
|
||||||
self.update_list.append(self.cached_books[book])
|
self.update_list.append(self.cached_books[book])
|
||||||
self._remove_from_device(self.cached_books[book])
|
self._remove_from_device(self.cached_books[book])
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
@ -2191,17 +2305,17 @@ class ITUNES(DriverBase):
|
|||||||
# Delete existing from Library|Books, add to self.update_list
|
# Delete existing from Library|Books, add to self.update_list
|
||||||
# for deletion from booklist[0] during add_books_to_metadata
|
# for deletion from booklist[0] during add_books_to_metadata
|
||||||
for book in self.cached_books:
|
for book in self.cached_books:
|
||||||
if (self.cached_books[book]['uuid'] == metadata.uuid) or \
|
if self.cached_books[book]['uuid'] == metadata.uuid and \
|
||||||
(self.cached_books[book]['title'] == metadata.title and \
|
self.cached_books[book]['title'] == metadata.title and \
|
||||||
self.cached_books[book]['author'] == metadata.authors[0]):
|
self.cached_books[book]['author'] == metadata.authors[0]:
|
||||||
self.update_list.append(self.cached_books[book])
|
self.update_list.append(self.cached_books[book])
|
||||||
self._remove_from_iTunes(self.cached_books[book])
|
self._remove_from_iTunes(self.cached_books[book])
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info( " deleting library book '%s'" % metadata.title)
|
self.log.info( " deleting library book '%s'" % metadata.title)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" '%s' not found in cached_books" % metadata.title)
|
self.log.info(" '%s' not found in cached_books" % metadata.title)
|
||||||
|
|
||||||
def _remove_from_device(self, cached_book):
|
def _remove_from_device(self, cached_book):
|
||||||
'''
|
'''
|
||||||
@ -2209,18 +2323,20 @@ class ITUNES(DriverBase):
|
|||||||
'''
|
'''
|
||||||
self.log.info(" ITUNES._remove_from_device()")
|
self.log.info(" ITUNES._remove_from_device()")
|
||||||
if isosx:
|
if isosx:
|
||||||
if False:
|
if DEBUG:
|
||||||
self.log.info(" deleting %s" % cached_book['dev_book'])
|
self.log.info(" deleting '%s' from iDevice" % cached_book['title'])
|
||||||
cached_book['dev_book'].delete()
|
cached_book['dev_book'].delete()
|
||||||
|
|
||||||
elif iswindows:
|
elif iswindows:
|
||||||
dev_pl = self._get_device_books_playlist()
|
hit = self._find_device_book(cached_book)
|
||||||
hits = dev_pl.Search(cached_book['uuid'],self.SearchField.index('All'))
|
if hit:
|
||||||
if hits:
|
if DEBUG:
|
||||||
hit = hits[0]
|
self.log.info(" deleting '%s' from iDevice" % cached_book['title'])
|
||||||
if False:
|
|
||||||
self.log.info(" deleting '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Album))
|
|
||||||
hit.Delete()
|
hit.Delete()
|
||||||
|
else:
|
||||||
|
if DEBUG:
|
||||||
|
self.log.warning(" unable to remove '%s' by '%s' (%s) from device" %
|
||||||
|
(cached_book['title'],cached_book['author'],cached_book['uuid']))
|
||||||
|
|
||||||
def _remove_from_iTunes(self, cached_book):
|
def _remove_from_iTunes(self, cached_book):
|
||||||
'''
|
'''
|
||||||
@ -2263,7 +2379,7 @@ class ITUNES(DriverBase):
|
|||||||
except:
|
except:
|
||||||
# We get here if there was an error with .location().path
|
# We get here if there was an error with .location().path
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info(" '%s' not found in iTunes" % cached_book['title'])
|
self.log.info(" '%s' not in iTunes storage" % cached_book['title'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.iTunes.delete(cached_book['lib_book'])
|
self.iTunes.delete(cached_book['lib_book'])
|
||||||
@ -2281,7 +2397,8 @@ class ITUNES(DriverBase):
|
|||||||
path = book.Location
|
path = book.Location
|
||||||
except:
|
except:
|
||||||
book = self._find_library_book(cached_book)
|
book = self._find_library_book(cached_book)
|
||||||
path = book.Location
|
if book:
|
||||||
|
path = book.Location
|
||||||
|
|
||||||
if book:
|
if book:
|
||||||
if self.iTunes_media and path.startswith(self.iTunes_media):
|
if self.iTunes_media and path.startswith(self.iTunes_media):
|
||||||
@ -2292,7 +2409,7 @@ class ITUNES(DriverBase):
|
|||||||
try:
|
try:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
except:
|
except:
|
||||||
self.log.warning(" could not find '%s' in iTunes storage" % path)
|
self.log.warning(" '%s' not in iTunes storage" % path)
|
||||||
try:
|
try:
|
||||||
os.rmdir(storage_path[0])
|
os.rmdir(storage_path[0])
|
||||||
self.log.info(" removed folder '%s'" % storage_path[0])
|
self.log.info(" removed folder '%s'" % storage_path[0])
|
||||||
@ -2431,7 +2548,8 @@ class ITUNES(DriverBase):
|
|||||||
if isosx:
|
if isosx:
|
||||||
if lb_added:
|
if lb_added:
|
||||||
lb_added.album.set(metadata.title)
|
lb_added.album.set(metadata.title)
|
||||||
lb_added.artist.set(metadata.authors[0])
|
#lb_added.artist.set(metadata.authors[0])
|
||||||
|
lb_added.artist.set(' & '.join(metadata.authors))
|
||||||
lb_added.composer.set(metadata.uuid)
|
lb_added.composer.set(metadata.uuid)
|
||||||
lb_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
|
lb_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
|
||||||
lb_added.enabled.set(True)
|
lb_added.enabled.set(True)
|
||||||
@ -2442,7 +2560,8 @@ class ITUNES(DriverBase):
|
|||||||
|
|
||||||
if db_added:
|
if db_added:
|
||||||
db_added.album.set(metadata.title)
|
db_added.album.set(metadata.title)
|
||||||
db_added.artist.set(metadata.authors[0])
|
#db_added.artist.set(metadata.authors[0])
|
||||||
|
db_added.artist.set(' & '.join(metadata.authors))
|
||||||
db_added.composer.set(metadata.uuid)
|
db_added.composer.set(metadata.uuid)
|
||||||
db_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
|
db_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
|
||||||
db_added.enabled.set(True)
|
db_added.enabled.set(True)
|
||||||
@ -2499,7 +2618,8 @@ class ITUNES(DriverBase):
|
|||||||
elif iswindows:
|
elif iswindows:
|
||||||
if lb_added:
|
if lb_added:
|
||||||
lb_added.Album = metadata.title
|
lb_added.Album = metadata.title
|
||||||
lb_added.Artist = metadata.authors[0]
|
#lb_added.Artist = metadata.authors[0]
|
||||||
|
lb_added.Artist = ' & '.join(metadata.authors)
|
||||||
lb_added.Composer = metadata.uuid
|
lb_added.Composer = metadata.uuid
|
||||||
lb_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
|
lb_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
|
||||||
lb_added.Enabled = True
|
lb_added.Enabled = True
|
||||||
@ -2510,7 +2630,8 @@ class ITUNES(DriverBase):
|
|||||||
|
|
||||||
if db_added:
|
if db_added:
|
||||||
db_added.Album = metadata.title
|
db_added.Album = metadata.title
|
||||||
db_added.Artist = metadata.authors[0]
|
#db_added.Artist = metadata.authors[0]
|
||||||
|
db_added.Artist = ' & '.join(metadata.authors)
|
||||||
db_added.Composer = metadata.uuid
|
db_added.Composer = metadata.uuid
|
||||||
db_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
|
db_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S')))
|
||||||
db_added.Enabled = True
|
db_added.Enabled = True
|
||||||
@ -2627,7 +2748,11 @@ class ITUNES_ASYNC(ITUNES):
|
|||||||
"""
|
"""
|
||||||
if not oncard:
|
if not oncard:
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
self.log.info("ITUNES_ASYNC:books(oncard=%s)" % oncard)
|
self.log.info("ITUNES_ASYNC:books()")
|
||||||
|
if self.settings().use_subdirs:
|
||||||
|
self.log.info(" Cover fetching/caching enabled")
|
||||||
|
else:
|
||||||
|
self.log.info(" Cover fetching/caching disabled")
|
||||||
|
|
||||||
# Fetch a list of books from iTunes
|
# Fetch a list of books from iTunes
|
||||||
|
|
||||||
@ -2638,13 +2763,15 @@ class ITUNES_ASYNC(ITUNES):
|
|||||||
library_books = self._get_library_books()
|
library_books = self._get_library_books()
|
||||||
book_count = float(len(library_books))
|
book_count = float(len(library_books))
|
||||||
for (i,book) in enumerate(library_books):
|
for (i,book) in enumerate(library_books):
|
||||||
|
format = 'pdf' if library_books[book].kind().startswith('PDF') else 'epub'
|
||||||
this_book = Book(library_books[book].name(), library_books[book].artist())
|
this_book = Book(library_books[book].name(), library_books[book].artist())
|
||||||
this_book.path = self.path_template % (library_books[book].name(),
|
this_book.path = self.path_template % (library_books[book].name(),
|
||||||
library_books[book].artist())
|
library_books[book].artist(),
|
||||||
|
format)
|
||||||
try:
|
try:
|
||||||
this_book.datetime = parse_date(str(library_books[book].date_added())).timetuple()
|
this_book.datetime = parse_date(str(library_books[book].date_added())).timetuple()
|
||||||
except:
|
except:
|
||||||
pass
|
this_book.datetime = time.gmtime()
|
||||||
this_book.db_id = None
|
this_book.db_id = None
|
||||||
this_book.device_collections = []
|
this_book.device_collections = []
|
||||||
#this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None
|
#this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None
|
||||||
@ -2664,7 +2791,7 @@ class ITUNES_ASYNC(ITUNES):
|
|||||||
'lib_book':library_books[book],
|
'lib_book':library_books[book],
|
||||||
'dev_book':None,
|
'dev_book':None,
|
||||||
'uuid': library_books[book].composer(),
|
'uuid': library_books[book].composer(),
|
||||||
#'format': 'pdf' if book.KindAsString.startswith('PDF') else 'epub'
|
'format': format
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.report_progress is not None:
|
if self.report_progress is not None:
|
||||||
@ -2678,12 +2805,14 @@ class ITUNES_ASYNC(ITUNES):
|
|||||||
book_count = float(len(library_books))
|
book_count = float(len(library_books))
|
||||||
for (i,book) in enumerate(library_books):
|
for (i,book) in enumerate(library_books):
|
||||||
this_book = Book(library_books[book].Name, library_books[book].Artist)
|
this_book = Book(library_books[book].Name, library_books[book].Artist)
|
||||||
|
format = 'pdf' if library_books[book].KindAsString.startswith('PDF') else 'epub'
|
||||||
this_book.path = self.path_template % (library_books[book].Name,
|
this_book.path = self.path_template % (library_books[book].Name,
|
||||||
library_books[book].Artist)
|
library_books[book].Artist,
|
||||||
|
format)
|
||||||
try:
|
try:
|
||||||
this_book.datetime = parse_date(str(library_books[book].DateAdded)).timetuple()
|
this_book.datetime = parse_date(str(library_books[book].DateAdded)).timetuple()
|
||||||
except:
|
except:
|
||||||
pass
|
this_book.datetime = time.gmtime()
|
||||||
this_book.db_id = None
|
this_book.db_id = None
|
||||||
this_book.device_collections = []
|
this_book.device_collections = []
|
||||||
this_book.library_id = library_books[book]
|
this_book.library_id = library_books[book]
|
||||||
@ -2700,7 +2829,7 @@ class ITUNES_ASYNC(ITUNES):
|
|||||||
'author':library_books[book].Artist,
|
'author':library_books[book].Artist,
|
||||||
'lib_book':library_books[book],
|
'lib_book':library_books[book],
|
||||||
'uuid': library_books[book].Composer,
|
'uuid': library_books[book].Composer,
|
||||||
'format': 'pdf' if library_books[book].KindAsString.startswith('PDF') else 'epub'
|
'format': format
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.report_progress is not None:
|
if self.report_progress is not None:
|
||||||
@ -2721,13 +2850,6 @@ class ITUNES_ASYNC(ITUNES):
|
|||||||
else:
|
else:
|
||||||
return BookList(self.log)
|
return BookList(self.log)
|
||||||
|
|
||||||
def unmount_device(self):
|
|
||||||
'''
|
|
||||||
'''
|
|
||||||
if DEBUG:
|
|
||||||
self.log.info("ITUNES_ASYNC:unmount_device()")
|
|
||||||
self.connected = False
|
|
||||||
|
|
||||||
def eject(self):
|
def eject(self):
|
||||||
'''
|
'''
|
||||||
Un-mount / eject the device from the OS. This does not check if there
|
Un-mount / eject the device from the OS. This does not check if there
|
||||||
@ -2793,6 +2915,13 @@ class ITUNES_ASYNC(ITUNES):
|
|||||||
self.problem_msg = None
|
self.problem_msg = None
|
||||||
self.update_list = []
|
self.update_list = []
|
||||||
|
|
||||||
|
def unmount_device(self):
|
||||||
|
'''
|
||||||
|
'''
|
||||||
|
if DEBUG:
|
||||||
|
self.log.info("ITUNES_ASYNC:unmount_device()")
|
||||||
|
self.connected = False
|
||||||
|
|
||||||
class BookList(list):
|
class BookList(list):
|
||||||
'''
|
'''
|
||||||
A list of books. Each Book object must have the fields:
|
A list of books. Each Book object must have the fields:
|
||||||
|
@ -495,7 +495,7 @@ class DeviceMenu(QMenu): # {{{
|
|||||||
self.connect_to_folder_action = mitem
|
self.connect_to_folder_action = mitem
|
||||||
|
|
||||||
mitem = self.addAction(QIcon(I('devices/itunes.png')),
|
mitem = self.addAction(QIcon(I('devices/itunes.png')),
|
||||||
_('Connect to iTunes (EXPERIMENTAL)'))
|
_('Connect to iTunes'))
|
||||||
mitem.setEnabled(True)
|
mitem.setEnabled(True)
|
||||||
mitem.triggered.connect(lambda x : self.connect_to_itunes.emit())
|
mitem.triggered.connect(lambda x : self.connect_to_itunes.emit())
|
||||||
self.connect_to_itunes_action = mitem
|
self.connect_to_itunes_action = mitem
|
||||||
|
@ -164,7 +164,7 @@ podofo_convert_pystring(PyObject *py) {
|
|||||||
Py_UNICODE* u = PyUnicode_AS_UNICODE(py);
|
Py_UNICODE* u = PyUnicode_AS_UNICODE(py);
|
||||||
PyObject *u8 = PyUnicode_EncodeUTF8(u, PyUnicode_GET_SIZE(py), "replace");
|
PyObject *u8 = PyUnicode_EncodeUTF8(u, PyUnicode_GET_SIZE(py), "replace");
|
||||||
if (u8 == NULL) { PyErr_NoMemory(); return NULL; }
|
if (u8 == NULL) { PyErr_NoMemory(); return NULL; }
|
||||||
pdf_utf8 *s8 = (pdf_utf8 *)PyString_AS_STRING(u8);
|
pdf_utf8 *s8 = reinterpret_cast<pdf_utf8 *>(PyString_AS_STRING(u8));
|
||||||
PdfString *ans = new PdfString(s8);
|
PdfString *ans = new PdfString(s8);
|
||||||
Py_DECREF(u8);
|
Py_DECREF(u8);
|
||||||
if (ans == NULL) PyErr_NoMemory();
|
if (ans == NULL) PyErr_NoMemory();
|
||||||
@ -219,9 +219,10 @@ podofo_PDFDoc_setter(podofo_PDFDoc *self, PyObject *val, int field) {
|
|||||||
PyErr_SetString(PyExc_Exception, "You must first load a PDF Document");
|
PyErr_SetString(PyExc_Exception, "You must first load a PDF Document");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
PdfString *s = podofo_convert_pystring(val);
|
PdfString *s = podofo_convert_pystring(val);
|
||||||
if (s == NULL) return -1;
|
if (s == NULL) return -1;
|
||||||
|
|
||||||
|
|
||||||
switch (field) {
|
switch (field) {
|
||||||
case 0:
|
case 0:
|
||||||
info->SetTitle(*s); break;
|
info->SetTitle(*s); break;
|
||||||
@ -240,7 +241,9 @@ podofo_PDFDoc_setter(podofo_PDFDoc *self, PyObject *val, int field) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
self->doc->set_info(info);
|
self->doc->set_info(info);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,11 +269,11 @@ class TouchscreenFeedTemplate(Template):
|
|||||||
link = A(CLASS('feed_link'),
|
link = A(CLASS('feed_link'),
|
||||||
trim_title(feeds[f-1].title),
|
trim_title(feeds[f-1].title),
|
||||||
href = '../feed_%d/index.html' % int(f-1))
|
href = '../feed_%d/index.html' % int(f-1))
|
||||||
navbar_tr.append(TD(link, width="40%", align="center"))
|
navbar_tr.append(TD(CLASS('feed_prev'),link))
|
||||||
|
|
||||||
# Up to Sections
|
# Up to Sections
|
||||||
link = A(STRONG('Sections'), href="../index.html")
|
link = A('Sections', href="../index.html")
|
||||||
navbar_tr.append(TD(link,width="20%",align="center"))
|
navbar_tr.append(TD(CLASS('feed_up'),link))
|
||||||
|
|
||||||
# Next Section
|
# Next Section
|
||||||
link = ''
|
link = ''
|
||||||
@ -281,7 +281,7 @@ class TouchscreenFeedTemplate(Template):
|
|||||||
link = A(CLASS('feed_link'),
|
link = A(CLASS('feed_link'),
|
||||||
trim_title(feeds[f+1].title),
|
trim_title(feeds[f+1].title),
|
||||||
href = '../feed_%d/index.html' % int(f+1))
|
href = '../feed_%d/index.html' % int(f+1))
|
||||||
navbar_tr.append(TD(link, width="40%", align="center", ))
|
navbar_tr.append(TD(CLASS('feed_next'),link))
|
||||||
navbar_t.append(navbar_tr)
|
navbar_t.append(navbar_tr)
|
||||||
top_navbar = navbar_t
|
top_navbar = navbar_t
|
||||||
bottom_navbar = copy.copy(navbar_t)
|
bottom_navbar = copy.copy(navbar_t)
|
||||||
@ -319,10 +319,9 @@ class TouchscreenFeedTemplate(Template):
|
|||||||
continue
|
continue
|
||||||
tr = TR()
|
tr = TR()
|
||||||
|
|
||||||
div_td = DIV(
|
div_td = DIV(CLASS('article_summary'),
|
||||||
A(article.title, CLASS('summary_headline','calibre_rescale_120',
|
A(article.title, CLASS('summary_headline','calibre_rescale_120',
|
||||||
href=article.url)),
|
href=article.url)))
|
||||||
style="display:inline-block")
|
|
||||||
if article.author:
|
if article.author:
|
||||||
div_td.append(DIV(article.author,
|
div_td.append(DIV(article.author,
|
||||||
CLASS('summary_byline', 'calibre_rescale_100')))
|
CLASS('summary_byline', 'calibre_rescale_100')))
|
||||||
@ -354,27 +353,25 @@ class TouchscreenNavBarTemplate(Template):
|
|||||||
|
|
||||||
# | Previous
|
# | Previous
|
||||||
if art > 0:
|
if art > 0:
|
||||||
href = '%s../article_%d/index.html'%(prefix, art-1)
|
link = A(CLASS('article_link'),'Previous',href='%s../article_%d/index.html'%(prefix, art-1))
|
||||||
navbar_tr.append(TD(A(EM('Previous'),href=href),
|
navbar_tr.append(TD(CLASS('article_prev'),link))
|
||||||
width="32%"))
|
|
||||||
else:
|
else:
|
||||||
navbar_tr.append(TD('', width="32%"))
|
navbar_tr.append(TD(CLASS('article_prev'),''))
|
||||||
|
|
||||||
# | Articles | Sections |
|
# | Articles | Sections |
|
||||||
href = '%s../index.html#article_%d'%(prefix, art)
|
link = A(CLASS('articles_link'),'Articles', href='%s../index.html#article_%d'%(prefix, art))
|
||||||
navbar_tr.append(TD(A(STRONG('Articles'), href=href),width="18%"))
|
navbar_tr.append(TD(CLASS('article_articles_list'),link))
|
||||||
|
|
||||||
href = '%s../../index.html#feed_%d'%(prefix, feed)
|
link = A(CLASS('sections_link'),'Sections', href='%s../../index.html#feed_%d'%(prefix, feed))
|
||||||
navbar_tr.append(TD(A(STRONG('Sections'), href=href),width="18%"))
|
navbar_tr.append(TD(CLASS('article_sections_list'),link))
|
||||||
|
|
||||||
# | Next
|
# | Next
|
||||||
next = 'feed_%d'%(feed+1) if art == number_of_articles_in_feed - 1 \
|
next = 'feed_%d'%(feed+1) if art == number_of_articles_in_feed - 1 \
|
||||||
else 'article_%d'%(art+1)
|
else 'article_%d'%(art+1)
|
||||||
up = '../..' if art == number_of_articles_in_feed - 1 else '..'
|
up = '../..' if art == number_of_articles_in_feed - 1 else '..'
|
||||||
href = '%s%s/%s/index.html'%(prefix, up, next)
|
|
||||||
|
|
||||||
navbar_tr.append(TD(A(EM('Next'),href=href),
|
link = A(CLASS('article_link'),'Next', href='%s%s/%s/index.html'%(prefix, up, next))
|
||||||
width="32%"))
|
navbar_tr.append(TD(CLASS('article_next'),link))
|
||||||
navbar_t.append(navbar_tr)
|
navbar_t.append(navbar_tr)
|
||||||
navbar.append(navbar_t)
|
navbar.append(navbar_t)
|
||||||
#print "\n%s\n" % etree.tostring(navbar, pretty_print=True)
|
#print "\n%s\n" % etree.tostring(navbar, pretty_print=True)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user