MTP driver: Implement sending files to device.

This commit is contained in:
Kovid Goyal 2012-08-08 10:54:16 +05:30
parent a215f62d08
commit 614fb3e14a
2 changed files with 67 additions and 3 deletions

View File

@ -11,7 +11,8 @@ import time, operator
from threading import RLock from threading import RLock
from functools import wraps from functools import wraps
from itertools import chain from itertools import chain
from collections import deque from collections import deque, OrderedDict
from io import BytesIO
from calibre import prints from calibre import prints
from calibre.devices.errors import OpenFailed from calibre.devices.errors import OpenFailed
@ -34,6 +35,7 @@ class FilesystemCache(object):
self.folder_id_map = {f['id']:f for f in self.iterfolders(set_level=0)} self.folder_id_map = {f['id']:f for f in self.iterfolders(set_level=0)}
# Set the parents of each file # Set the parents of each file
self.files_in_root = OrderedDict()
for f in files: for f in files:
parents = deque() parents = deque()
pid = f['parent_id'] pid = f['parent_id']
@ -44,6 +46,9 @@ class FilesystemCache(object):
break break
parents.appendleft(pid) parents.appendleft(pid)
pid = parent['parent_id'] pid = parent['parent_id']
f['parents'] = parents
if not parents:
self.files_in_root[f['id']] = f
# Set the files in each folder # Set the files in each folder
for f in self.iterfolders(): for f in self.iterfolders():
@ -79,6 +84,9 @@ class FilesystemCache(object):
prints(prefix, ' '*indent, '-', leaf['name'], prints(prefix, ' '*indent, '-', leaf['name'],
'id=%d'%leaf['id'], 'size=%d'%leaf['size'], 'id=%d'%leaf['id'], 'size=%d'%leaf['size'],
'modtime=%d'%leaf['modtime']) 'modtime=%d'%leaf['modtime'])
for leaf in self.files_in_root.itervalues():
prints('-', leaf['name'], 'id=%d'%leaf['id'],
'size=%d'%leaf['size'], 'modtime=%d'%leaf['modtime'])
class MTP_DEVICE(MTPDeviceBase): class MTP_DEVICE(MTPDeviceBase):
@ -138,11 +146,11 @@ class MTP_DEVICE(MTPDeviceBase):
@synchronous @synchronous
def post_yank_cleanup(self): def post_yank_cleanup(self):
self.dev = None self.dev = self.filesystem_cache = None
@synchronous @synchronous
def shutdown(self): def shutdown(self):
self.dev = None self.dev = self.filesystem_cache = None
def format_errorstack(self, errs): def format_errorstack(self, errs):
return '\n'.join(['%d:%s'%(code, msg.decode('utf-8', 'replace')) for return '\n'.join(['%d:%s'%(code, msg.decode('utf-8', 'replace')) for
@ -244,6 +252,10 @@ if __name__ == '__main__':
print ("Storage info:") print ("Storage info:")
pprint(d.storage_info) pprint(d.storage_info)
print("Free space:", dev.free_space()) print("Free space:", dev.free_space())
raw = b'test'
fname = b'moose.txt'
src = BytesIO(raw)
print (d.put_file(dev._main_id, 0, fname, src, len(raw), PR()))
# dev.filesystem_cache.dump_filesystem() # dev.filesystem_cache.dump_filesystem()
# with open('/tmp/flint.epub', 'wb') as f: # with open('/tmp/flint.epub', 'wb') as f:
# print(d.get_file(786, f, PR())) # print(d.get_file(786, f, PR()))

View File

@ -455,6 +455,54 @@ libmtp_Device_get_file(libmtp_Device *self, PyObject *args, PyObject *kwargs) {
} // }}} } // }}}
// Device.put_file {{{
static PyObject *
libmtp_Device_put_file(libmtp_Device *self, PyObject *args, PyObject *kwargs) {
PyObject *stream, *callback = NULL, *errs, *fo;
ProgressCallback cb;
uint32_t parent_id, storage_id;
uint64_t filesize;
int ret;
char *name;
LIBMTP_file_t f, *nf;
ENSURE_DEV(NULL); ENSURE_STORAGE(NULL);
if (!PyArg_ParseTuple(args, "kksOK|O", &storage_id, &parent_id, &name, &stream, &filesize, &callback)) return NULL;
errs = PyList_New(0);
if (errs == NULL) { PyErr_NoMemory(); return NULL; }
cb.obj = callback; cb.extra = stream;
f.parent_id = parent_id; f.storage_id = storage_id; f.item_id = 0; f.filename = name; f.filetype = LIBMTP_FILETYPE_UNKNOWN; f.filesize = filesize;
Py_XINCREF(callback); Py_INCREF(stream);
cb.state = PyEval_SaveThread();
ret = LIBMTP_Send_File_From_Handler(self->device, data_from_python, &cb, &f, report_progress, &cb);
PyEval_RestoreThread(cb.state);
Py_XDECREF(callback); Py_DECREF(stream);
if (ret != 0) {
dump_errorstack(self->device, errs);
fo = Py_None; Py_INCREF(fo);
} else {
nf = LIBMTP_Get_Filemetadata(self->device, f.item_id);
if (nf == NULL) {
dump_errorstack(self->device, errs);
fo = Py_None; Py_INCREF(fo);
} else {
fo = Py_BuildValue("{s:k,s:k,s:k,s:s,s:K,s:k}",
"id", nf->item_id,
"parent_id", nf->parent_id,
"storage_id", nf->storage_id,
"name", nf->filename,
"size", nf->filesize,
"modtime", nf->modificationdate
);
}
}
return Py_BuildValue("ONN", (ret == 0) ? Py_True : Py_False, fo, errs);
} // }}}
static PyMethodDef libmtp_Device_methods[] = { static PyMethodDef libmtp_Device_methods[] = {
{"update_storage_info", (PyCFunction)libmtp_Device_update_storage_info, METH_VARARGS, {"update_storage_info", (PyCFunction)libmtp_Device_update_storage_info, METH_VARARGS,
"update_storage_info() -> Reread the storage info from the device (total, space, free space, storage locations, etc.)" "update_storage_info() -> Reread the storage info from the device (total, space, free space, storage locations, etc.)"
@ -472,6 +520,10 @@ static PyMethodDef libmtp_Device_methods[] = {
"get_file(fileid, stream, callback=None) -> Get the file specified by fileid from the device. stream must be a file-like object. The file will be written to it. callback works the same as in get_filelist(). Returns ok, errs, where errs is a list of errors (if any)." "get_file(fileid, stream, callback=None) -> Get the file specified by fileid from the device. stream must be a file-like object. The file will be written to it. callback works the same as in get_filelist(). Returns ok, errs, where errs is a list of errors (if any)."
}, },
{"put_file", (PyCFunction)libmtp_Device_put_file, METH_VARARGS,
"put_file(storage_id, parent_id, filename, stream, size, callback=None) -> Put a file on the device. The file is read from stream. It is put inside the folder identified by parent_id on the storage identified by storage_id. Use parent_id=0 to put it in the root. Use storage_id=0 to put it on the primary storage. stream must be a file-like object. size is the size in bytes of the data in stream. callback works the same as in get_filelist(). Returns ok, fileinfo, errs, where errs is a list of errors (if any), and fileinfo is a file information dictionary, as returned by get_filelist()."
},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };