mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
MTP: Sending books to device and deleting books from device implemented
This commit is contained in:
parent
4d7ed1b4c6
commit
92c46d2ded
@ -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
|
||||||
|
|
||||||
|
@ -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'))
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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]:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user