mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
GwR revisions
This commit is contained in:
parent
c1293b5a94
commit
e17b085c9f
@ -237,8 +237,6 @@ class ITUNES(DevicePlugin):
|
||||
(new_book.title, new_book.author))
|
||||
booklists[0].append(new_book)
|
||||
|
||||
self.update_list = []
|
||||
|
||||
if DEBUG:
|
||||
self._dump_booklist(booklists[0],header='after',indent=2)
|
||||
self._dump_cached_books(header='after',indent=2)
|
||||
@ -414,7 +412,7 @@ class ITUNES(DevicePlugin):
|
||||
self.ejected = True
|
||||
return False
|
||||
|
||||
self._discover_manual_sync_mode()
|
||||
self._discover_manual_sync_mode(wait = 2 if self.initial_status == 'launched' else 0)
|
||||
return True
|
||||
|
||||
def can_handle_windows(self, device_id, debug=False):
|
||||
@ -676,25 +674,18 @@ class ITUNES(DevicePlugin):
|
||||
self.log.info("ITUNES.remove_books_from_metadata()")
|
||||
for path in paths:
|
||||
self._dump_cached_book(self.cached_books[path], indent=2)
|
||||
if self.cached_books[path]['lib_book']:
|
||||
# Remove from the booklist
|
||||
for i,book in enumerate(booklists[0]):
|
||||
print "book.uuid: %s" % book.uuid
|
||||
print "self.cached_books[path]['uuid']: %s" % self.cached_books[path]['uuid']
|
||||
if book.uuid == self.cached_books[path]['uuid']:
|
||||
booklists[0].pop(i)
|
||||
break
|
||||
else:
|
||||
self.log.error(" '%s' not found in self.cached_book" % path)
|
||||
|
||||
# Remove from cached_books
|
||||
self.cached_books.pop(path)
|
||||
if DEBUG:
|
||||
self.log.info(" removing '%s' from self.cached_books" % path)
|
||||
self._dump_cached_books(header='remove_books_from_metadata()',indent=2)
|
||||
self._dump_booklist(booklists[0], header='remove_books_from_metadata()',indent=2)
|
||||
else:
|
||||
self.log.warning(" skipping purchased book, can't remove via automation interface")
|
||||
# Purge the booklist, self.cached_books
|
||||
for i,bl_book in enumerate(booklists[0]):
|
||||
if bl_book.uuid == self.cached_books[path]['uuid']:
|
||||
# Remove from booklists[0]
|
||||
booklists[0].pop(i)
|
||||
|
||||
for cb in self.cached_books:
|
||||
if self.cached_books[cb]['uuid'] == self.cached_books[path]['uuid']:
|
||||
self.cached_books.pop(cb)
|
||||
break
|
||||
break
|
||||
|
||||
def reset(self, key='-1', log_packets=False, report_progress=None,
|
||||
detected_device=None) :
|
||||
@ -749,6 +740,7 @@ class ITUNES(DevicePlugin):
|
||||
details='\n'.join(self.problem_titles), level=UserFeedback.WARN)
|
||||
self.problem_titles = []
|
||||
self.problem_msg = None
|
||||
self.update_list = []
|
||||
|
||||
def total_space(self, end_session=True):
|
||||
"""
|
||||
@ -1152,6 +1144,8 @@ class ITUNES(DevicePlugin):
|
||||
'''
|
||||
if DEBUG:
|
||||
self.log.info(" ITUNES._discover_manual_sync_mode()")
|
||||
if wait:
|
||||
time.sleep(wait)
|
||||
if isosx:
|
||||
connected_device = self.sources['iPod']
|
||||
dev_books = None
|
||||
@ -1165,8 +1159,8 @@ class ITUNES(DevicePlugin):
|
||||
|
||||
if len(dev_books):
|
||||
first_book = dev_books[0]
|
||||
#if DEBUG:
|
||||
#self.log.info(" determing manual mode by modifying '%s' by %s" % (first_book.name(), first_book.artist()))
|
||||
if DEBUG:
|
||||
self.log.info(" determing manual mode by modifying '%s' by %s" % (first_book.name(), first_book.artist()))
|
||||
try:
|
||||
first_book.bpm.set(0)
|
||||
self.manual_sync_mode = True
|
||||
@ -1184,8 +1178,6 @@ class ITUNES(DevicePlugin):
|
||||
self.manual_sync_mode = False
|
||||
|
||||
elif iswindows:
|
||||
if wait:
|
||||
time.sleep(wait)
|
||||
connected_device = self.sources['iPod']
|
||||
device = self.iTunes.sources.ItemByName(connected_device)
|
||||
|
||||
@ -1294,18 +1286,35 @@ class ITUNES(DevicePlugin):
|
||||
|
||||
self.log.info()
|
||||
|
||||
def _dump_hex(self, src, length=16):
|
||||
def _dump_epub_metadata(self, fpath):
|
||||
'''
|
||||
'''
|
||||
FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)])
|
||||
N=0; result=''
|
||||
while src:
|
||||
s,src = src[:length],src[length:]
|
||||
hexa = ' '.join(["%02X"%ord(x) for x in s])
|
||||
s = s.translate(FILTER)
|
||||
result += "%04X %-*s %s\n" % (N, length*3, hexa, s)
|
||||
N+=length
|
||||
print result
|
||||
self.log.info(" ITUNES.__get_epub_metadata()")
|
||||
title = None
|
||||
author = None
|
||||
timestamp = None
|
||||
zf = ZipFile(fpath,'r')
|
||||
fnames = zf.namelist()
|
||||
opf = [x for x in fnames if '.opf' in x][0]
|
||||
if opf:
|
||||
opf_raw = cStringIO.StringIO(zf.read(opf)).getvalue()
|
||||
soup = BeautifulSoup(opf_raw)
|
||||
title = soup.find('dc:title').renderContents()
|
||||
author = soup.find('dc:creator').renderContents()
|
||||
ts = soup.find('meta',attrs={'name':'calibre:timestamp'})
|
||||
if ts:
|
||||
# Touch existing calibre timestamp
|
||||
timestamp = ts['content']
|
||||
|
||||
if not title or not author:
|
||||
if DEBUG:
|
||||
self.log.error(" couldn't extract title/author from %s in %s" % (opf,fpath))
|
||||
self.log.error(" title: %s author: %s timestamp: %s" % (title, author, timestamp))
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.error(" can't find .opf in %s" % fpath)
|
||||
zf.close()
|
||||
return (title, author, timestamp)
|
||||
|
||||
def _dump_files(self, files, header=None,indent=0):
|
||||
if header:
|
||||
@ -1319,6 +1328,19 @@ class ITUNES(DevicePlugin):
|
||||
self.log.info(" %s%s" % (' '*indent,file.name))
|
||||
self.log.info()
|
||||
|
||||
def _dump_hex(self, src, length=16):
|
||||
'''
|
||||
'''
|
||||
FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)])
|
||||
N=0; result=''
|
||||
while src:
|
||||
s,src = src[:length],src[length:]
|
||||
hexa = ' '.join(["%02X"%ord(x) for x in s])
|
||||
s = s.translate(FILTER)
|
||||
result += "%04X %-*s %s\n" % (N, length*3, hexa, s)
|
||||
N+=length
|
||||
print result
|
||||
|
||||
def _dump_library_books(self, library_books):
|
||||
'''
|
||||
'''
|
||||
@ -1615,30 +1637,6 @@ class ITUNES(DevicePlugin):
|
||||
self.log.error(" no iPad|Books playlist found")
|
||||
return pl
|
||||
|
||||
def _get_epub_metadata(self, fpath):
|
||||
'''
|
||||
Return the original title and author from the .OPF file in the epub bundle
|
||||
'''
|
||||
self.log.info(" ITUNES.__get_epub_metadata()")
|
||||
title = None
|
||||
author = None
|
||||
zf = ZipFile(fpath,'r')
|
||||
fnames = zf.namelist()
|
||||
opf = [x for x in fnames if '.opf' in x][0]
|
||||
if opf:
|
||||
opf_raw = cStringIO.StringIO(zf.read(opf)).getvalue()
|
||||
soup = BeautifulSoup(opf_raw)
|
||||
title = soup.find('dc:title').renderContents()
|
||||
author = soup.find('dc:creator').renderContents()
|
||||
if not title or not author:
|
||||
if DEBUG:
|
||||
self.log.error(" couldn't extract title/author from %s in %s" % (opf,fpath))
|
||||
self.log.error(" title: %s author: %s" % (title, author))
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.error(" can't find .opf in %s" % fpath)
|
||||
return title, author
|
||||
|
||||
def _get_fpath(self,file, metadata, update_md=False):
|
||||
'''
|
||||
If the database copy will be deleted after upload, we have to
|
||||
@ -1652,13 +1650,17 @@ class ITUNES(DevicePlugin):
|
||||
if not getattr(fpath, 'deleted_after_upload', False):
|
||||
if getattr(file, 'orig_file_path', None) is not None:
|
||||
fpath = file.orig_file_path
|
||||
if update_md:
|
||||
self._update_epub_metadata(fpath, metadata)
|
||||
elif getattr(file, 'name', None) is not None:
|
||||
fpath = file.name
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.info(" file will be deleted after upload")
|
||||
if update_md:
|
||||
if DEBUG:
|
||||
self.log.info(" metadata before rewrite: '{0[0]}' '{0[1]}' '{0[2]}'".format(self._dump_epub_metadata(fpath)))
|
||||
self._update_epub_metadata(fpath, metadata)
|
||||
if DEBUG:
|
||||
self.log.info(" metadata after rewrite: '{0[0]}' '{0[1]}' '{0[2]}'".format(self._dump_epub_metadata(fpath)))
|
||||
return fpath
|
||||
|
||||
def _get_library_books(self):
|
||||
@ -1843,10 +1845,10 @@ class ITUNES(DevicePlugin):
|
||||
self.log.info( "ITUNES:open(): Launching iTunes" )
|
||||
self.iTunes = iTunes= appscript.app('iTunes', hide=True)
|
||||
iTunes.run()
|
||||
initial_status = 'launched'
|
||||
self.initial_status = 'launched'
|
||||
else:
|
||||
self.iTunes = appscript.app('iTunes')
|
||||
initial_status = 'already running'
|
||||
self.initial_status = 'already running'
|
||||
|
||||
# Read the current storage path for iTunes media
|
||||
cmd = "defaults read com.apple.itunes NSNavLastRootDirectory"
|
||||
@ -1860,7 +1862,7 @@ class ITUNES(DevicePlugin):
|
||||
self.log.error(" media_dir: %s" % media_dir)
|
||||
if DEBUG:
|
||||
self.log.info(" [OSX %s - %s (%s), driver version %d.%d.%d]" %
|
||||
(self.iTunes.name(), self.iTunes.version(), initial_status,
|
||||
(self.iTunes.name(), self.iTunes.version(), self.initial_status,
|
||||
self.version[0],self.version[1],self.version[2]))
|
||||
self.log.info(" iTunes_media: %s" % self.iTunes_media)
|
||||
if iswindows:
|
||||
@ -1872,7 +1874,7 @@ class ITUNES(DevicePlugin):
|
||||
self.iTunes = win32com.client.Dispatch("iTunes.Application")
|
||||
if not DEBUG:
|
||||
self.iTunes.Windows[0].Minimized = True
|
||||
initial_status = 'launched'
|
||||
self.initial_status = 'launched'
|
||||
|
||||
# Read the current storage path for iTunes media from the XML file
|
||||
with open(self.iTunes.LibraryXMLPath, 'r') as xml:
|
||||
@ -1889,7 +1891,7 @@ class ITUNES(DevicePlugin):
|
||||
|
||||
if DEBUG:
|
||||
self.log.info(" [Windows %s - %s (%s), driver version %d.%d.%d]" %
|
||||
(self.iTunes.Windows[0].name, self.iTunes.Version, initial_status,
|
||||
(self.iTunes.Windows[0].name, self.iTunes.Version, self.initial_status,
|
||||
self.version[0],self.version[1],self.version[2]))
|
||||
self.log.info(" iTunes_media: %s" % self.iTunes_media)
|
||||
|
||||
@ -2061,61 +2063,31 @@ class ITUNES(DevicePlugin):
|
||||
|
||||
def _update_epub_metadata(self, fpath, metadata):
|
||||
'''
|
||||
Refresh metadata in database epub to force iBooks to recache
|
||||
'''
|
||||
self.log.info(" ITUNES._update_epub_metadata()")
|
||||
|
||||
# Refresh epub metadata
|
||||
with open(fpath,'r+b') as zfo:
|
||||
|
||||
# Touch the timestamp to force a recache
|
||||
if metadata.timestamp:
|
||||
#old_ts = strptime(metadata.timestamp,"%Y-%m-%dT%H:%M:%S.%f+00:00")
|
||||
old_ts = metadata.timestamp
|
||||
metadata.timestamp = datetime.datetime(old_ts.year, old_ts.month, old_ts.day, old_ts.hour,
|
||||
old_ts.minute, old_ts.second, old_ts.microsecond+1)
|
||||
old_ts.minute, old_ts.second, old_ts.microsecond+1, old_ts.tzinfo)
|
||||
else:
|
||||
metadata.timestamp = isoformat(now())
|
||||
|
||||
if iswindows:
|
||||
# This hack compensates for the Windows automation interface not allowing
|
||||
# us to update Category after the fact. By removing the tags, we should be
|
||||
# able to set the Category after the upload.
|
||||
if metadata.series:
|
||||
metadata.tags = None
|
||||
# Tweak the author if 'News' in tags for friendlier display in iBooks
|
||||
if _('News') in metadata.tags:
|
||||
if metadata.title.find('[') > 0:
|
||||
metadata.title = metadata.title[:metadata.title.find('[')-1]
|
||||
date_as_author = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y'))
|
||||
metadata.author = metadata.authors = [date_as_author]
|
||||
sort_author = re.sub('^\s*A\s+|^\s*The\s+|^\s*An\s+', '', metadata.title).rstrip()
|
||||
metadata.author_sort = '%s %s' % (sort_author, strftime('%Y-%m-%d'))
|
||||
|
||||
if DEBUG:
|
||||
self.log.info(" updating to '%s' by %s" % (metadata.title, metadata.authors[0]))
|
||||
set_metadata(zfo,metadata)
|
||||
|
||||
if False:
|
||||
# This code operates directly on the OPF file, obsolete since we're rewriting md
|
||||
zf = ZipFile(fpath,'r')
|
||||
fnames = zf.namelist()
|
||||
opf = [x for x in fnames if '.opf' in x][0]
|
||||
if opf:
|
||||
opf_raw = cStringIO.StringIO(zf.read(opf)).getvalue()
|
||||
soup = BeautifulSoup(opf_raw)
|
||||
md = soup.find('metadata')
|
||||
ts = md.find('meta',attrs={'name':'calibre:timestamp'})
|
||||
if ts:
|
||||
# Touch existing calibre timestamp
|
||||
timestamp = ts['content']
|
||||
old_ts = strptime(timestamp,"%Y-%m-%dT%H:%M:%S.%f+00:00")
|
||||
new_ts = datetime.datetime(old_ts.year, old_ts.month, old_ts.day, old_ts.hour,
|
||||
old_ts.minute, old_ts.second, old_ts.microsecond+1)
|
||||
ts['content'] = new_ts.strftime("%Y-%m-%dT%H:%M:%S.%f+00:00")
|
||||
else:
|
||||
# Create new calibre timestamp
|
||||
ts = Tag(soup,'meta')
|
||||
ts['name'] = 'calibre:timestamp'
|
||||
ts['content'] = isoformat(now())
|
||||
md.insert(len(md),ts)
|
||||
zfo = open(fpath,'r+b')
|
||||
safe_replace(zfo, opf, cStringIO.StringIO(soup.renderContents()))
|
||||
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.error(" can't find .opf in %s" % fpath)
|
||||
|
||||
def _update_device(self, msg='', wait=True):
|
||||
'''
|
||||
Trigger a sync, wait for completion
|
||||
|
@ -256,7 +256,7 @@ class MetaInformation(object):
|
||||
setattr(self, x, getattr(mi, x, None))
|
||||
|
||||
def print_all_attributes(self):
|
||||
for x in ('author', 'author_sort', 'title_sort', 'comments', 'category', 'publisher',
|
||||
for x in ('title','author', 'author_sort', 'title_sort', 'comments', 'category', 'publisher',
|
||||
'series', 'series_index', 'tags', 'rating', 'isbn', 'language',
|
||||
'application_id', 'manifest', 'toc', 'spine', 'guide', 'cover',
|
||||
'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc', 'pubdate',
|
||||
|
@ -918,7 +918,7 @@ class OPF(object):
|
||||
for attr in ('title', 'authors', 'author_sort', 'title_sort',
|
||||
'publisher', 'series', 'series_index', 'rating',
|
||||
'isbn', 'language', 'tags', 'category', 'comments',
|
||||
'pubdate'):
|
||||
'pubdate','timestamp'):
|
||||
val = getattr(mi, attr, None)
|
||||
if val is not None and val != [] and val != (None, None):
|
||||
setattr(self, attr, val)
|
||||
|
@ -1140,12 +1140,6 @@ class BasicNewsRecipe(Recipe):
|
||||
mi = MetaInformation(self.short_title() + strftime(self.timefmt), [__appname__])
|
||||
mi.publisher = __appname__
|
||||
mi.author_sort = __appname__
|
||||
if self.output_profile.name == 'iPad':
|
||||
date_as_author = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y'))
|
||||
mi = MetaInformation(self.short_title(), [date_as_author])
|
||||
mi.publisher = __appname__
|
||||
sort_author = re.sub('^\s*A\s+|^\s*The\s+|^\s*An\s+', '', self.title).rstrip()
|
||||
mi.author_sort = '%s %s' % (sort_author, strftime('%Y-%m-%d'))
|
||||
mi.publication_type = 'periodical:'+self.publication_type
|
||||
mi.timestamp = nowf()
|
||||
mi.comments = self.description
|
||||
|
Loading…
x
Reference in New Issue
Block a user