Also pass path to original files to device drivers when uploading so that device drivers that need to can usethe originals instead of the tem files with updated metadata

This commit is contained in:
Kovid Goyal 2010-06-07 20:22:53 -06:00
parent 142d6a532e
commit 2fcba45013
7 changed files with 66 additions and 42 deletions

View File

@ -16,7 +16,7 @@ class NYTimes(BasicNewsRecipe):
title = 'New York Times Top Stories'
__author__ = 'GRiker'
language = _('English')
language = 'en'
description = 'Top Stories from the New York Times'
# List of sections typically included in Top Stories. Use a keyword from the

View File

@ -14,7 +14,6 @@ from calibre.devices.interface import DevicePlugin
from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.ebooks.metadata import MetaInformation
from calibre.library.server.utils import strftime
from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.config import Config, config_dir
from calibre.utils.date import parse_date
from calibre.utils.logging import Log
@ -775,10 +774,12 @@ class ITUNES(DevicePlugin):
self._remove_from_iTunes(self.cached_books[path])
# Add to iTunes Library|Books
if isinstance(file,PersistentTemporaryFile):
added = self.iTunes.add(appscript.mactypes.File(file._name))
else:
added = self.iTunes.add(appscript.mactypes.File(file))
fpath = file
if getattr(file, 'orig_file_path', None) is not None:
fpath = file.orig_file_path
elif getattr(file, 'name', None) is not None:
fpath = file.name
added = self.iTunes.add(appscript.mactypes.File(fpath))
thumb = None
if metadata[i].cover:
@ -815,7 +816,7 @@ class ITUNES(DevicePlugin):
this_book.device_collections = []
this_book.library_id = added
this_book.path = path
this_book.size = self._get_device_book_size(file, added.size())
this_book.size = self._get_device_book_size(fpath, added.size())
this_book.thumbnail = thumb
this_book.iTunes_id = added
@ -922,12 +923,15 @@ class ITUNES(DevicePlugin):
self.log.info(" '%s' not in cached_books" % metadata[i].title)
# Add to iTunes Library|Books
if isinstance(file,PersistentTemporaryFile):
op_status = lib_books.AddFile(file._name)
self.log.info("ITUNES.upload_books():\n iTunes adding '%s'" % file._name)
else:
op_status = lib_books.AddFile(file)
self.log.info(" iTunes adding '%s'" % file)
fpath = file
if getattr(file, 'orig_file_path', None) is not None:
fpath = file.orig_file_path
elif getattr(file, 'name', None) is not None:
fpath = file.name
op_status = lib_books.AddFile(fpath)
self.log.info("ITUNES.upload_books():\n iTunes adding '%s'"
% fpath)
if DEBUG:
sys.stdout.write(" iTunes copying '%s' ..." % metadata[i].title)
@ -1464,7 +1468,7 @@ class ITUNES(DevicePlugin):
# Read the current storage path for iTunes media
cmd = "defaults read com.apple.itunes NSNavLastRootDirectory"
proc = subprocess.Popen( cmd, shell=True, cwd=os.curdir, stdout=subprocess.PIPE)
retcode = proc.wait()
proc.wait()
media_dir = os.path.abspath(proc.communicate()[0].strip())
if os.path.exists(media_dir):
self.iTunes_media = media_dir

View File

@ -287,7 +287,9 @@ class DevicePlugin(Plugin):
This method should raise a L{FreeSpaceError} if there is not enough
free space on the device. The text of the FreeSpaceError must contain the
word "card" if C{on_card} is not None otherwise it must contain the word "memory".
:files: A list of paths and/or file-like objects.
:files: A list of paths and/or file-like objects. If they are paths and
the paths point to temporary files, they may have an additional
attribute, original_file_path pointing to the originals.
:names: A list of file names that the books should have
once uploaded to the device. len(names) == len(files)
:return: A list of 3-element tuples. The list is meant to be passed

View File

@ -108,7 +108,7 @@ class BookList(_BookList):
def add_book(self, book, replace_metadata):
try:
b = self.index(book)
except ValueError, IndexError:
except (ValueError, IndexError):
b = None
if b is None:
self.append(book)

View File

@ -765,12 +765,8 @@ class Device(DeviceConfig, DevicePlugin):
path = existing[0]
def get_size(obj):
if hasattr(obj, 'seek'):
obj.seek(0, os.SEEK_END)
size = obj.tell()
obj.seek(0)
return size
return os.path.getsize(obj)
path = getattr(obj, 'name', obj)
return os.path.getsize(path)
sizes = [get_size(f) for f in files]
size = sum(sizes)

View File

@ -1,6 +1,8 @@
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
# Imports {{{
import os, traceback, Queue, time, socket, cStringIO, re
from threading import Thread, RLock
from itertools import repeat
@ -27,7 +29,9 @@ from calibre.utils.smtp import compose_mail, sendmail, extract_email_address, \
config as email_config
from calibre.devices.folder_device.driver import FOLDER_DEVICE
class DeviceJob(BaseJob):
# }}}
class DeviceJob(BaseJob): # {{{
def __init__(self, func, done, job_manager, args=[], kwargs={},
description=''):
@ -78,8 +82,9 @@ class DeviceJob(BaseJob):
def log_file(self):
return cStringIO.StringIO(self._details.encode('utf-8'))
# }}}
class DeviceManager(Thread):
class DeviceManager(Thread): # {{{
def __init__(self, connected_slot, job_manager, open_feedback_slot, sleep_time=2):
'''
@ -338,8 +343,9 @@ class DeviceManager(Thread):
return self.create_job(self._view_book, done, args=[path, target],
description=_('View book on device'))
# }}}
class DeviceAction(QAction):
class DeviceAction(QAction): # {{{
a_s = pyqtSignal(object)
@ -356,9 +362,9 @@ class DeviceAction(QAction):
def __repr__(self):
return self.__class__.__name__ + ':%s:%s:%s'%(self.dest, self.delete,
self.specific)
# }}}
class DeviceMenu(QMenu):
class DeviceMenu(QMenu): # {{{
fetch_annotations = pyqtSignal()
connect_to_folder = pyqtSignal()
@ -532,8 +538,9 @@ class DeviceMenu(QMenu):
annot_enable = enable and getattr(device, 'SUPPORTS_ANNOTATIONS', False)
self.annotation_action.setEnabled(annot_enable)
# }}}
class Emailer(Thread):
class Emailer(Thread): # {{{
def __init__(self, timeout=60):
Thread.__init__(self)
@ -590,6 +597,7 @@ class Emailer(Thread):
results.append([jobname, e, traceback.format_exc()])
callback(results)
# }}}
class DeviceGUI(object):
@ -637,7 +645,7 @@ class DeviceGUI(object):
if not ids or len(ids) == 0:
return
files, _auto_ids = self.library_view.model().get_preferred_formats_from_ids(ids,
fmts, paths=True, set_metadata=True,
fmts, set_metadata=True,
specific_format=specific_format,
exclude_auto=do_auto_convert)
if do_auto_convert:
@ -647,7 +655,6 @@ class DeviceGUI(object):
_auto_ids = []
full_metadata = self.library_view.model().metadata_for(ids)
files = [getattr(f, 'name', None) for f in files]
bad, remove_ids, jobnames = [], [], []
texts, subjects, attachments, attachment_names = [], [], [], []
@ -760,7 +767,7 @@ class DeviceGUI(object):
for account, fmts in accounts:
files, auto = self.library_view.model().\
get_preferred_formats_from_ids([id], fmts)
files = [f.name for f in files if f is not None]
files = [f for f in files if f is not None]
if not files:
continue
attachment = files[0]
@ -824,7 +831,7 @@ class DeviceGUI(object):
prefix = prefix.decode(preferred_encoding, 'replace')
prefix = ascii_filename(prefix)
names.append('%s_%d%s'%(prefix, id,
os.path.splitext(f.name)[1]))
os.path.splitext(f)[1]))
if mi.cover and os.access(mi.cover, os.R_OK):
mi.thumbnail = self.cover_to_thumbnail(open(mi.cover,
'rb').read())
@ -837,7 +844,7 @@ class DeviceGUI(object):
on_card = space.get(sorted(space.keys(), reverse=True)[0], None)
self.upload_books(files, names, metadata,
on_card=on_card,
memory=[[f.name for f in files], remove])
memory=[files, remove])
self.status_bar.showMessage(_('Sending catalogs to device.'), 5000)
@ -884,7 +891,7 @@ class DeviceGUI(object):
prefix = prefix.decode(preferred_encoding, 'replace')
prefix = ascii_filename(prefix)
names.append('%s_%d%s'%(prefix, id,
os.path.splitext(f.name)[1]))
os.path.splitext(f)[1]))
if mi.cover and os.access(mi.cover, os.R_OK):
mi.thumbnail = self.cover_to_thumbnail(open(mi.cover,
'rb').read())
@ -898,7 +905,7 @@ class DeviceGUI(object):
on_card = space.get(sorted(space.keys(), reverse=True)[0], None)
self.upload_books(files, names, metadata,
on_card=on_card,
memory=[[f.name for f in files], remove])
memory=[files, remove])
self.status_bar.showMessage(_('Sending news to device.'), 5000)
@ -914,7 +921,7 @@ class DeviceGUI(object):
_files, _auto_ids = self.library_view.model().get_preferred_formats_from_ids(ids,
settings.format_map,
paths=True, set_metadata=True,
set_metadata=True,
specific_format=specific_format,
exclude_auto=do_auto_convert)
if do_auto_convert:
@ -930,9 +937,8 @@ class DeviceGUI(object):
mi.thumbnail = self.cover_to_thumbnail(open(mi.cover, 'rb').read())
imetadata = iter(metadata)
files = [getattr(f, 'name', None) for f in _files]
bad, good, gf, names, remove_ids = [], [], [], [], []
for f in files:
for f in _files:
mi = imetadata.next()
id = ids.next()
if f is None:

View File

@ -21,7 +21,8 @@ from calibre.utils.date import dt_factory, qt_to_dt, isoformat
from calibre.ebooks.metadata.meta import set_metadata as _set_metadata
from calibre.utils.search_query_parser import SearchQueryParser
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, REGEXP_MATCH
from calibre import strftime
from calibre import strftime, isbytestring
from calibre.constants import filesystem_encoding
from calibre.gui2.library import DEFAULT_SORT
def human_readable(size, precision=1):
@ -33,6 +34,13 @@ TIME_FMT = '%d %b %Y'
ALIGNMENT_MAP = {'left': Qt.AlignLeft, 'right': Qt.AlignRight, 'center':
Qt.AlignHCenter}
class FormatPath(unicode):
def __new__(cls, path, orig_file_path):
ans = unicode.__new__(cls, path)
ans.orig_file_path = orig_file_path
return ans
class BooksModel(QAbstractTableModel): # {{{
about_to_be_sorted = pyqtSignal(object, name='aboutToBeSorted')
@ -379,7 +387,7 @@ class BooksModel(QAbstractTableModel): # {{{
else:
return metadata
def get_preferred_formats_from_ids(self, ids, formats, paths=False,
def get_preferred_formats_from_ids(self, ids, formats,
set_metadata=False, specific_format=None,
exclude_auto=False, mode='r+b'):
ans = []
@ -404,12 +412,20 @@ class BooksModel(QAbstractTableModel): # {{{
as_file=True)) as src:
shutil.copyfileobj(src, pt)
pt.flush()
if getattr(src, 'name', None):
pt.orig_file_path = os.path.abspath(src.name)
pt.seek(0)
if set_metadata:
_set_metadata(pt, self.db.get_metadata(id, get_cover=True, index_is_id=True),
format)
pt.close() if paths else pt.seek(0)
ans.append(pt)
pt.close()
def to_uni(x):
if isbytestring(x):
x = x.decode(filesystem_encoding)
return x
name, op = map(to_uni, map(os.path.abspath, (pt.name,
pt.orig_file_path)))
ans.append(FormatPath(name, op))
else:
need_auto.append(id)
if not exclude_auto: