MTP: Sending books to device and deleting books from device implemented

This commit is contained in:
Kovid Goyal 2012-09-03 17:36:45 +05:30
parent 4d7ed1b4c6
commit 92c46d2ded
5 changed files with 141 additions and 4 deletions

View File

@ -22,6 +22,22 @@ class BookList(BL):
def supports_collections(self): def supports_collections(self):
return False return False
def add_book(self, book, replace_metadata=True):
try:
b = self.index(book)
except (ValueError, IndexError):
b = None
if b is None:
self.append(book)
return book
if replace_metadata:
self[b].smart_update(book, replace_metadata=True)
return self[b]
return None
def remove_book(self, book):
self.remove(book)
class Book(Metadata): class Book(Metadata):
def __init__(self, storage_id, lpath, other=None): def __init__(self, storage_id, lpath, other=None):
@ -36,6 +52,17 @@ class Book(Metadata):
return (self.storage_id == mtp_file.storage_id and return (self.storage_id == mtp_file.storage_id and
self.mtp_relpath == mtp_file.mtp_relpath) self.mtp_relpath == mtp_file.mtp_relpath)
def __eq__(self, other):
return (isinstance(other, self.__class__) and (self.storage_id ==
other.storage_id and self.mtp_relpath == other.mtp_relpath))
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
return hash((self.storage_id, self.mtp_relpath))
class JSONCodec(JsonCodec): class JSONCodec(JsonCodec):
pass pass

View File

@ -191,6 +191,7 @@ class MTP_DEVICE(BASE):
self.put_file(storage, self.METADATA_CACHE, stream, size) self.put_file(storage, self.METADATA_CACHE, stream, size)
def sync_booklists(self, booklists, end_session=True): def sync_booklists(self, booklists, end_session=True):
debug('sync_booklists() called')
for bl in booklists: for bl in booklists:
if getattr(bl, 'storage_id', None) is None: if getattr(bl, 'storage_id', None) is None:
continue continue
@ -198,6 +199,7 @@ class MTP_DEVICE(BASE):
if storage is None: if storage is None:
continue continue
self.write_metadata_cache(storage, bl) self.write_metadata_cache(storage, bl)
debug('sync_booklists() ended')
# }}} # }}}
@ -249,15 +251,117 @@ class MTP_DEVICE(BASE):
# TODO: Implement this # TODO: Implement this
return 'calibre' return 'calibre'
def ensure_parent(self, storage, path):
parent = storage
pos = list(path)[:-1]
while pos:
name = pos[0]
pos = pos[1:]
parent = self.create_folder(parent, name)
return parent
def upload_books(self, files, names, on_card=None, end_session=True, def upload_books(self, files, names, on_card=None, end_session=True,
metadata=None): metadata=None):
debug('upload_books() called')
from calibre.devices.utils import sanity_check from calibre.devices.utils import sanity_check
sanity_check(on_card, files, self.card_prefix(), self.free_space()) sanity_check(on_card, files, self.card_prefix(), self.free_space())
prefix = self.prefix_for_location(on_card) prefix = self.prefix_for_location(on_card)
sid = {'carda':self._carda_id, 'cardb':self._cardb_id}.get(on_card,
self._main_id)
bl_idx = {'carda':1, 'cardb':2}.get(on_card, 0)
storage = self.filesystem_cache.storage(sid)
ans = []
self.report_progress(0, _('Transferring books to device...'))
i, total = 0, len(files)
for infile, fname, mi in izip(files, names, metadata): for infile, fname, mi in izip(files, names, metadata):
path = self.create_upload_path(prefix, mi, fname) path = self.create_upload_path(prefix, mi, fname)
print (1111111, path) parent = self.ensure_parent(storage, path)
raise NotImplementedError() if hasattr(infile, 'read'):
pos = infile.tell()
infile.seek(0, 2)
sz = infile.tell()
infile.seek(pos)
stream = infile
close = False
else:
sz = os.path.getsize(infile)
stream = lopen(infile, 'rb')
close = True
try:
mtp_file = self.put_file(parent, path[-1], stream, sz)
finally:
if close:
stream.close()
ans.append((mtp_file, bl_idx))
i += 1
self.report_progress(i/total, _('Transferred %s to device')%mi.title)
self.report_progress(1, _('Transfer to device finished...'))
debug('upload_books() ended')
return ans
def add_books_to_metadata(self, mtp_files, metadata, booklists):
debug('add_books_to_metadata() called')
from calibre.devices.mtp.books import Book
i, total = 0, len(mtp_files)
self.report_progress(0, _('Adding books to device metadata listing...'))
for x, mi in izip(mtp_files, metadata):
mtp_file, bl_idx = x
bl = booklists[bl_idx]
book = Book(mtp_file.storage_id, '/'.join(mtp_file.mtp_relpath),
other=mi)
book = bl.add_book(book, replace_metadata=True)
if book is not None:
book.size = mtp_file.size
book.datetime = mtp_file.last_modified.timetuple()
book.path = mtp_file.mtp_id_path
i += 1
self.report_progress(i/total, _('Added %s')%mi.title)
self.report_progress(1, _('Adding complete'))
debug('add_books_to_metadata() ended')
# }}}
# Removing books from the device {{{
def recursive_delete(self, obj):
parent = self.delete_file_or_folder(obj)
if parent.empty and parent.can_delete and not parent.is_system:
try:
self.recursive_delete(parent)
except:
prints('Failed to delete parent: %s, ignoring'%(
'/'.join(parent.full_path)))
def delete_books(self, paths, end_session=True):
self.report_progress(0, _('Deleting books from device...'))
for i, path in enumerate(paths):
f = self.filesystem_cache.resolve_mtp_id_path(path)
self.recursive_delete(f)
self.report_progress((i+1) / float(len(paths)),
_('Deleted %s')%path)
self.report_progress(1, _('All books deleted'))
def remove_books_from_metadata(self, paths, booklists):
self.report_progress(0, _('Removing books from metadata'))
class NextPath(Exception): pass
for i, path in enumerate(paths):
try:
for bl in booklists:
for book in bl:
if book.path == path:
bl.remove_book(book)
raise NextPath('')
except NextPath:
pass
self.report_progress((i+1)/len(paths), _('Removed %s')%path)
self.report_progress(1, _('All books removed'))
# }}} # }}}

View File

@ -306,6 +306,7 @@ class MTP_DEVICE(MTPDeviceBase):
raise DeviceError('Failed to delete %s with error: %s'% raise DeviceError('Failed to delete %s with error: %s'%
(obj.full_path, self.format_errorstack(errs))) (obj.full_path, self.format_errorstack(errs)))
parent.remove_child(obj) parent.remove_child(obj)
return parent
def develop(): def develop():
from calibre.devices.scanner import DeviceScanner from calibre.devices.scanner import DeviceScanner

View File

@ -338,6 +338,7 @@ class MTP_DEVICE(MTPDeviceBase):
parent = obj.parent parent = obj.parent
self.dev.delete_object(obj.object_id) self.dev.delete_object(obj.object_id)
parent.remove_child(obj) parent.remove_child(obj)
return parent
@same_thread @same_thread
def put_file(self, parent, name, stream, size, callback=None, replace=True): def put_file(self, parent, name, stream, size, callback=None, replace=True):

View File

@ -1495,8 +1495,12 @@ class DeviceMixin(object): # {{{
self.device_job_exception(job) self.device_job_exception(job)
return return
try:
self.device_manager.add_books_to_metadata(job.result, self.device_manager.add_books_to_metadata(job.result,
metadata, self.booklists()) metadata, self.booklists())
except:
traceback.print_exc()
raise
books_to_be_deleted = [] books_to_be_deleted = []
if memory and memory[1]: if memory and memory[1]: