mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
MTP drivers: Provide feedback during filesystem scan
This commit is contained in:
parent
3733d739f7
commit
dbf4a84950
@ -155,6 +155,9 @@ class MTP_DEVICE(BASE):
|
||||
# }}}
|
||||
|
||||
# Get list of books from device, with metadata {{{
|
||||
def filesystem_callback(self, msg):
|
||||
self.report_progress(0, msg)
|
||||
|
||||
def books(self, oncard=None, end_session=True):
|
||||
from calibre.devices.mtp.books import JSONCodec
|
||||
from calibre.devices.mtp.books import BookList, Book
|
||||
|
@ -212,6 +212,9 @@ class MTP_DEVICE(MTPDeviceBase):
|
||||
ans += pprint.pformat(storage)
|
||||
return ans
|
||||
|
||||
def _filesystem_callback(self, entry):
|
||||
self.filesystem_callback(_('Found object: %s')%entry.get('name', ''))
|
||||
|
||||
@property
|
||||
def filesystem_cache(self):
|
||||
if self._filesystem_cache is None:
|
||||
@ -231,7 +234,8 @@ class MTP_DEVICE(MTPDeviceBase):
|
||||
storage.append({'id':sid, 'size':capacity,
|
||||
'is_folder':True, 'name':name, 'can_delete':False,
|
||||
'is_system':True})
|
||||
items, errs = self.dev.get_filesystem(sid)
|
||||
items, errs = self.dev.get_filesystem(sid,
|
||||
self._filesystem_callback)
|
||||
all_items.extend(items), all_errs.extend(errs)
|
||||
if not all_items and all_errs:
|
||||
raise DeviceError(
|
||||
|
@ -357,7 +357,7 @@ Device_storage_info(Device *self, void *closure) {
|
||||
|
||||
// Device.get_filesystem {{{
|
||||
|
||||
static int recursive_get_files(LIBMTP_mtpdevice_t *dev, uint32_t storage_id, uint32_t parent_id, PyObject *ans, PyObject *errs) {
|
||||
static int recursive_get_files(LIBMTP_mtpdevice_t *dev, uint32_t storage_id, uint32_t parent_id, PyObject *ans, PyObject *errs, PyObject *callback) {
|
||||
LIBMTP_file_t *f, *files;
|
||||
PyObject *entry;
|
||||
int ok = 1;
|
||||
@ -372,12 +372,13 @@ static int recursive_get_files(LIBMTP_mtpdevice_t *dev, uint32_t storage_id, uin
|
||||
entry = build_file_metadata(f, storage_id);
|
||||
if (entry == NULL) { ok = 0; }
|
||||
else {
|
||||
Py_XDECREF(PyObject_CallFunctionObjArgs(callback, entry, NULL));
|
||||
if (PyList_Append(ans, entry) != 0) { ok = 0; }
|
||||
Py_DECREF(entry);
|
||||
}
|
||||
|
||||
if (ok && f->filetype == LIBMTP_FILETYPE_FOLDER) {
|
||||
if (!recursive_get_files(dev, storage_id, f->item_id, ans, errs)) {
|
||||
if (!recursive_get_files(dev, storage_id, f->item_id, ans, errs, callback)) {
|
||||
ok = 0;
|
||||
}
|
||||
}
|
||||
@ -394,19 +395,20 @@ static int recursive_get_files(LIBMTP_mtpdevice_t *dev, uint32_t storage_id, uin
|
||||
|
||||
static PyObject *
|
||||
Device_get_filesystem(Device *self, PyObject *args) {
|
||||
PyObject *ans, *errs;
|
||||
PyObject *ans, *errs, *callback;
|
||||
unsigned long storage_id;
|
||||
int ok = 0;
|
||||
|
||||
ENSURE_DEV(NULL); ENSURE_STORAGE(NULL);
|
||||
|
||||
if (!PyArg_ParseTuple(args, "k", &storage_id)) return NULL;
|
||||
if (!PyArg_ParseTuple(args, "kO", &storage_id, &callback)) return NULL;
|
||||
if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, "callback is not a callable"); return NULL; }
|
||||
ans = PyList_New(0);
|
||||
errs = PyList_New(0);
|
||||
if (errs == NULL || ans == NULL) { PyErr_NoMemory(); return NULL; }
|
||||
|
||||
LIBMTP_Clear_Errorstack(self->device);
|
||||
ok = recursive_get_files(self->device, (uint32_t)storage_id, 0, ans, errs);
|
||||
ok = recursive_get_files(self->device, (uint32_t)storage_id, 0, ans, errs, callback);
|
||||
dump_errorstack(self->device, errs);
|
||||
if (!ok) {
|
||||
Py_DECREF(ans);
|
||||
@ -535,7 +537,7 @@ static PyMethodDef Device_methods[] = {
|
||||
},
|
||||
|
||||
{"get_filesystem", (PyCFunction)Device_get_filesystem, METH_VARARGS,
|
||||
"get_filesystem(storage_id) -> Get the list of files and folders on the device in storage_id. Returns files, errors."
|
||||
"get_filesystem(storage_id, callback) -> Get the list of files and folders on the device in storage_id. Returns files, errors. callback must be a callable that accepts a single argument. It is called with every found object."
|
||||
},
|
||||
|
||||
{"get_file", (PyCFunction)Device_get_file, METH_VARARGS,
|
||||
|
@ -136,8 +136,9 @@ public:
|
||||
HANDLE complete;
|
||||
ULONG self_ref;
|
||||
PyThreadState *thread_state;
|
||||
PyObject *callback;
|
||||
|
||||
GetBulkCallback(PyObject *items_dict, HANDLE ev) : items(items_dict), complete(ev), self_ref(1), thread_state(NULL) {}
|
||||
GetBulkCallback(PyObject *items_dict, HANDLE ev, PyObject* pycallback) : items(items_dict), complete(ev), self_ref(1), thread_state(NULL), callback(pycallback) {}
|
||||
~GetBulkCallback() {}
|
||||
|
||||
HRESULT __stdcall OnStart(REFGUID Context) { return S_OK; }
|
||||
@ -195,6 +196,7 @@ public:
|
||||
Py_DECREF(temp);
|
||||
|
||||
set_properties(obj, properties);
|
||||
Py_XDECREF(PyObject_CallFunctionObjArgs(callback, obj, NULL));
|
||||
|
||||
properties->Release(); properties = NULL;
|
||||
}
|
||||
@ -207,7 +209,7 @@ public:
|
||||
|
||||
};
|
||||
|
||||
static PyObject* bulk_get_filesystem(IPortableDevice *device, IPortableDevicePropertiesBulk *bulk_properties, const wchar_t *storage_id, IPortableDevicePropVariantCollection *object_ids) {
|
||||
static PyObject* bulk_get_filesystem(IPortableDevice *device, IPortableDevicePropertiesBulk *bulk_properties, const wchar_t *storage_id, IPortableDevicePropVariantCollection *object_ids, PyObject *pycallback) {
|
||||
PyObject *folders = NULL;
|
||||
GUID guid_context = GUID_NULL;
|
||||
HANDLE ev = NULL;
|
||||
@ -227,7 +229,7 @@ static PyObject* bulk_get_filesystem(IPortableDevice *device, IPortableDevicePro
|
||||
properties = create_filesystem_properties_collection();
|
||||
if (properties == NULL) goto end;
|
||||
|
||||
callback = new (std::nothrow) GetBulkCallback(folders, ev);
|
||||
callback = new (std::nothrow) GetBulkCallback(folders, ev, pycallback);
|
||||
if (callback == NULL) { PyErr_NoMemory(); goto end; }
|
||||
|
||||
hr = bulk_properties->QueueGetValuesByObjectList(object_ids, properties, callback, &guid_context);
|
||||
@ -272,7 +274,7 @@ end:
|
||||
// }}}
|
||||
|
||||
// find_all_objects_in() {{{
|
||||
static BOOL find_all_objects_in(IPortableDeviceContent *content, IPortableDevicePropVariantCollection *object_ids, const wchar_t *parent_id) {
|
||||
static BOOL find_all_objects_in(IPortableDeviceContent *content, IPortableDevicePropVariantCollection *object_ids, const wchar_t *parent_id, PyObject *callback) {
|
||||
/*
|
||||
* Find all children of the object identified by parent_id, recursively.
|
||||
* The child ids are put into object_ids. Returns False if any errors
|
||||
@ -284,6 +286,7 @@ static BOOL find_all_objects_in(IPortableDeviceContent *content, IPortableDevice
|
||||
DWORD fetched, i;
|
||||
PROPVARIANT pv;
|
||||
BOOL ok = 1;
|
||||
PyObject *id;
|
||||
|
||||
PropVariantInit(&pv);
|
||||
pv.vt = VT_LPWSTR;
|
||||
@ -303,10 +306,15 @@ static BOOL find_all_objects_in(IPortableDeviceContent *content, IPortableDevice
|
||||
if (SUCCEEDED(hr)) {
|
||||
for(i = 0; i < fetched; i++) {
|
||||
pv.pwszVal = child_ids[i];
|
||||
id = wchar_to_unicode(pv.pwszVal);
|
||||
if (id != NULL) {
|
||||
Py_XDECREF(PyObject_CallFunctionObjArgs(callback, id, NULL));
|
||||
Py_DECREF(id);
|
||||
}
|
||||
hr2 = object_ids->Add(&pv);
|
||||
pv.pwszVal = NULL;
|
||||
if (FAILED(hr2)) { hresult_set_exc("Failed to add child ids to propvariantcollection", hr2); break; }
|
||||
ok = find_all_objects_in(content, object_ids, child_ids[i]);
|
||||
ok = find_all_objects_in(content, object_ids, child_ids[i], callback);
|
||||
if (!ok) break;
|
||||
}
|
||||
for (i = 0; i < fetched; i++) { CoTaskMemFree(child_ids[i]); child_ids[i] = NULL; }
|
||||
@ -347,7 +355,7 @@ end:
|
||||
return ans;
|
||||
}
|
||||
|
||||
static PyObject* single_get_filesystem(IPortableDeviceContent *content, const wchar_t *storage_id, IPortableDevicePropVariantCollection *object_ids) {
|
||||
static PyObject* single_get_filesystem(IPortableDeviceContent *content, const wchar_t *storage_id, IPortableDevicePropVariantCollection *object_ids, PyObject *callback) {
|
||||
DWORD num, i;
|
||||
PROPVARIANT pv;
|
||||
HRESULT hr;
|
||||
@ -375,6 +383,7 @@ static PyObject* single_get_filesystem(IPortableDeviceContent *content, const wc
|
||||
if (SUCCEEDED(hr) && pv.pwszVal != NULL) {
|
||||
item = get_object_properties(devprops, properties, pv.pwszVal);
|
||||
if (item != NULL) {
|
||||
Py_XDECREF(PyObject_CallFunctionObjArgs(callback, item, NULL));
|
||||
PyDict_SetItem(ans, PyDict_GetItemString(item, "id"), item);
|
||||
Py_DECREF(item); item = NULL;
|
||||
ok = 1;
|
||||
@ -429,7 +438,7 @@ end:
|
||||
return values;
|
||||
} // }}}
|
||||
|
||||
PyObject* wpd::get_filesystem(IPortableDevice *device, const wchar_t *storage_id, IPortableDevicePropertiesBulk *bulk_properties) { // {{{
|
||||
PyObject* wpd::get_filesystem(IPortableDevice *device, const wchar_t *storage_id, IPortableDevicePropertiesBulk *bulk_properties, PyObject *callback) { // {{{
|
||||
PyObject *folders = NULL;
|
||||
IPortableDevicePropVariantCollection *object_ids = NULL;
|
||||
IPortableDeviceContent *content = NULL;
|
||||
@ -447,11 +456,11 @@ PyObject* wpd::get_filesystem(IPortableDevice *device, const wchar_t *storage_id
|
||||
Py_END_ALLOW_THREADS;
|
||||
if (FAILED(hr)) { hresult_set_exc("Failed to create propvariantcollection", hr); goto end; }
|
||||
|
||||
ok = find_all_objects_in(content, object_ids, storage_id);
|
||||
ok = find_all_objects_in(content, object_ids, storage_id, callback);
|
||||
if (!ok) goto end;
|
||||
|
||||
if (bulk_properties != NULL) folders = bulk_get_filesystem(device, bulk_properties, storage_id, object_ids);
|
||||
else folders = single_get_filesystem(content, storage_id, object_ids);
|
||||
if (bulk_properties != NULL) folders = bulk_get_filesystem(device, bulk_properties, storage_id, object_ids, callback);
|
||||
else folders = single_get_filesystem(content, storage_id, object_ids, callback);
|
||||
|
||||
end:
|
||||
if (content != NULL) content->Release();
|
||||
|
@ -78,14 +78,15 @@ update_data(Device *self, PyObject *args) {
|
||||
// get_filesystem() {{{
|
||||
static PyObject*
|
||||
py_get_filesystem(Device *self, PyObject *args) {
|
||||
PyObject *storage_id, *ret;
|
||||
PyObject *storage_id, *ret, *callback;
|
||||
wchar_t *storage;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O", &storage_id)) return NULL;
|
||||
if (!PyArg_ParseTuple(args, "OO", &storage_id, &callback)) return NULL;
|
||||
if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, "callback is not a callable"); return NULL; }
|
||||
storage = unicode_to_wchar(storage_id);
|
||||
if (storage == NULL) return NULL;
|
||||
|
||||
ret = wpd::get_filesystem(self->device, storage, self->bulk_properties);
|
||||
ret = wpd::get_filesystem(self->device, storage, self->bulk_properties, callback);
|
||||
free(storage);
|
||||
return ret;
|
||||
} // }}}
|
||||
@ -163,7 +164,7 @@ static PyMethodDef Device_methods[] = {
|
||||
},
|
||||
|
||||
{"get_filesystem", (PyCFunction)py_get_filesystem, METH_VARARGS,
|
||||
"get_filesystem(storage_id) -> Get all files/folders on the storage identified by storage_id. Tries to use bulk operations when possible."
|
||||
"get_filesystem(storage_id, callback) -> Get all files/folders on the storage identified by storage_id. Tries to use bulk operations when possible. callback must be a callable that accepts a single argument. It is called with every found id and then with the metadata for every id."
|
||||
},
|
||||
|
||||
{"get_file", (PyCFunction)py_get_file, METH_VARARGS,
|
||||
|
@ -214,6 +214,14 @@ class MTP_DEVICE(MTPDeviceBase):
|
||||
|
||||
return True
|
||||
|
||||
def _filesystem_callback(self, obj):
|
||||
if isinstance(obj, dict):
|
||||
n = obj.get('name', '')
|
||||
msg = _('Found object: %s')%n
|
||||
else:
|
||||
msg = _('Found id: %s')%obj
|
||||
self.filesystem_callback(msg)
|
||||
|
||||
@property
|
||||
def filesystem_cache(self):
|
||||
if self._filesystem_cache is None:
|
||||
@ -233,7 +241,8 @@ class MTP_DEVICE(MTPDeviceBase):
|
||||
break
|
||||
storage = {'id':storage_id, 'size':capacity, 'name':name,
|
||||
'is_folder':True, 'can_delete':False, 'is_system':True}
|
||||
id_map = self.dev.get_filesystem(storage_id)
|
||||
id_map = self.dev.get_filesystem(storage_id,
|
||||
self._filesystem_callback)
|
||||
for x in id_map.itervalues(): x['storage_id'] = storage_id
|
||||
all_storage.append(storage)
|
||||
items.append(id_map.itervalues())
|
||||
|
@ -56,7 +56,7 @@ int pump_waiting_messages();
|
||||
extern IPortableDeviceValues* get_client_information();
|
||||
extern IPortableDevice* open_device(const wchar_t *pnp_id, IPortableDeviceValues *client_information);
|
||||
extern PyObject* get_device_information(IPortableDevice *device, IPortableDevicePropertiesBulk **bulk_properties);
|
||||
extern PyObject* get_filesystem(IPortableDevice *device, const wchar_t *storage_id, IPortableDevicePropertiesBulk *bulk_properties);
|
||||
extern PyObject* get_filesystem(IPortableDevice *device, const wchar_t *storage_id, IPortableDevicePropertiesBulk *bulk_properties, PyObject *callback);
|
||||
extern PyObject* get_file(IPortableDevice *device, const wchar_t *object_id, PyObject *dest, PyObject *callback);
|
||||
extern PyObject* create_folder(IPortableDevice *device, const wchar_t *parent_id, const wchar_t *name);
|
||||
extern PyObject* delete_object(IPortableDevice *device, const wchar_t *object_id);
|
||||
|
Loading…
x
Reference in New Issue
Block a user