Use bulk fetch for list_folder by name

This commit is contained in:
Kovid Goyal 2025-01-20 14:45:47 +05:30
parent f19ca06b86
commit b8c92ac9ce
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 26 additions and 34 deletions

View File

@ -7,6 +7,7 @@
#include "global.h" #include "global.h"
#include <dictobject.h>
#include <new> #include <new>
namespace wpd { namespace wpd {
@ -169,12 +170,14 @@ private:
if (PyDict_SetItem(this->items, object_id.ptr(), obj.ptr()) != 0) { PyErr_Clear(); return; } if (PyDict_SetItem(this->items, object_id.ptr(), obj.ptr()) != 0) { PyErr_Clear(); return; }
} else Py_INCREF(obj.ptr()); } else Py_INCREF(obj.ptr());
set_properties(obj.ptr(), properties); set_properties(obj.ptr(), properties);
pyobject_raii r(PyObject_CallFunction(callback, "OI", obj.ptr(), this->level)); if (callback) {
if (!r) PyErr_Clear(); pyobject_raii r(PyObject_CallFunction(callback, "OI", obj.ptr(), this->level));
else if (r && PyObject_IsTrue(r.ptr())) { if (!r) PyErr_Clear();
PyObject *borrowed = PyDict_GetItemString(obj.ptr(), "id"); else if (r && PyObject_IsTrue(r.ptr())) {
if (borrowed) if (PyList_Append(this->subfolders, borrowed) != 0) PyErr_Clear(); PyObject *borrowed = PyDict_GetItemString(obj.ptr(), "id");
} if (borrowed) if (PyList_Append(this->subfolders, borrowed) != 0) PyErr_Clear();
}
}
} }
void handle_values(IPortableDeviceValuesCollection* values) { void handle_values(IPortableDeviceValuesCollection* values) {
@ -422,9 +425,11 @@ single_get_filesystem(unsigned int level, CComPtr<IPortableDeviceContent> &conte
if (SUCCEEDED(hr) && pv.pwszVal != NULL) { if (SUCCEEDED(hr) && pv.pwszVal != NULL) {
pyobject_raii item(get_object_properties(devprops, properties, pv.pwszVal)); pyobject_raii item(get_object_properties(devprops, properties, pv.pwszVal));
if (!item) return false; if (!item) return false;
pyobject_raii r(PyObject_CallFunction(callback, "OI", item.ptr(), level));
if (PyDict_SetItem(ans, PyDict_GetItemString(item.ptr(), "id"), item.ptr()) != 0) return false; if (PyDict_SetItem(ans, PyDict_GetItemString(item.ptr(), "id"), item.ptr()) != 0) return false;
if (r && PyObject_IsTrue(r.ptr())) recurse.attach(item.detach()); if (callback) {
pyobject_raii r(PyObject_CallFunction(callback, "OI", item.ptr(), level));
if (r && PyObject_IsTrue(r.ptr())) recurse.attach(item.detach());
}
} else { hresult_set_exc("Failed to get item from IPortableDevicePropVariantCollection", hr); return false; } } else { hresult_set_exc("Failed to get item from IPortableDevicePropVariantCollection", hr); return false; }
if (recurse) { if (recurse) {
@ -468,42 +473,28 @@ create_object_properties(const wchar_t *parent_id, const wchar_t *name, const GU
} // }}} } // }}}
PyObject* PyObject*
list_folder(CComPtr<IPortableDeviceContent> &content, IPortableDevicePropertiesBulk *bulk_properties, const wchar_t *folder_id) { list_folder(IPortableDevice *device, CComPtr<IPortableDeviceContent> &content, IPortableDevicePropertiesBulk *bulk_properties, const wchar_t *folder_id) {
HRESULT hr; HRESULT hr;
CComPtr<IPortableDevicePropVariantCollection> object_ids; CComPtr<IPortableDevicePropVariantCollection> object_ids;
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
hr = object_ids.CoCreateInstance(CLSID_PortableDevicePropVariantCollection, NULL, CLSCTX_INPROC_SERVER); hr = object_ids.CoCreateInstance(CLSID_PortableDevicePropVariantCollection, NULL, CLSCTX_INPROC_SERVER);
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to create propvariantcollection", hr); return NULL; } if (FAILED(hr)) { hresult_set_exc("Failed to create propvariantcollection", hr); return NULL; }
pyobject_raii ans(PyDict_New()); if (!ans) return NULL;
bool enum_failed = false; bool enum_failed = false;
if (!find_objects_in(content, object_ids, folder_id, &enum_failed)) return NULL; if (!find_objects_in(content, object_ids, folder_id, &enum_failed)) return NULL;
DWORD num;
CComPtr<IPortableDeviceProperties> devprops;
hr = content->Properties(&devprops); if (bulk_properties) {
if (FAILED(hr)) { hresult_set_exc("Failed to get IPortableDeviceProperties interface", hr); return NULL; } if (!bulk_get_filesystem(0, device, bulk_properties, object_ids, NULL, ans.ptr(), NULL)) return NULL;
} else {
CComPtr<IPortableDeviceKeyCollection> properties(create_filesystem_properties_collection()); if (!single_get_filesystem(0, content, object_ids, NULL, ans.ptr(), NULL)) return NULL;
if (!properties) return NULL;
hr = object_ids->GetCount(&num);
if (FAILED(hr)) { hresult_set_exc("Failed to get object id count", hr); return NULL; }
pyobject_raii ans(PyList_New(0)); if (!ans) return NULL;
for (DWORD i = 0; i < num; i++) {
prop_variant pv;
hr = object_ids->GetAt(i, &pv);
if (SUCCEEDED(hr) && pv.pwszVal != NULL) {
pyobject_raii item(get_object_properties(devprops, properties, pv.pwszVal));
if (!item) return NULL;
if (PyList_Append(ans.ptr(), item.ptr()) != 0) return NULL;
}
} }
return ans.detach(); return ans.detach();
} }
PyObject* PyObject*
find_in_parent(CComPtr<IPortableDeviceContent> &content, IPortableDevicePropertiesBulk *bulk_properties, const wchar_t *parent_id, PyObject *name) { find_in_parent(CComPtr<IPortableDeviceContent> &content, const wchar_t *parent_id, PyObject *name) {
HRESULT hr; HRESULT hr;
CComPtr<IPortableDevicePropVariantCollection> object_ids; CComPtr<IPortableDevicePropVariantCollection> object_ids;
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;

View File

@ -97,7 +97,7 @@ list_folder_by_name(Device *self, PyObject *args) {
PyObject *k = PyTuple_GET_ITEM(names, i); PyObject *k = PyTuple_GET_ITEM(names, i);
if (!PyUnicode_Check(k)) { PyErr_SetString(PyExc_TypeError, "names must contain only unicode strings"); return NULL; } if (!PyUnicode_Check(k)) { PyErr_SetString(PyExc_TypeError, "names must contain only unicode strings"); return NULL; }
pyobject_raii l(PyObject_CallMethod(k, "lower", NULL)); if (!l) return NULL; pyobject_raii l(PyObject_CallMethod(k, "lower", NULL)); if (!l) return NULL;
pyobject_raii object_id(wpd::find_in_parent(content, self->bulk_properties, parent_id.ptr(), l.ptr())); pyobject_raii object_id(wpd::find_in_parent(content, parent_id.ptr(), l.ptr()));
if (!object_id) { if (!object_id) {
if (PyErr_Occurred()) return NULL; if (PyErr_Occurred()) return NULL;
Py_RETURN_NONE; Py_RETURN_NONE;
@ -106,7 +106,7 @@ list_folder_by_name(Device *self, PyObject *args) {
found = true; found = true;
} }
if (!found) Py_RETURN_NONE; if (!found) Py_RETURN_NONE;
return wpd::list_folder(content, self->bulk_properties, parent_id.ptr()); return wpd::list_folder(self->device, content, self->bulk_properties, parent_id.ptr());
} // }}} } // }}}
// create_folder() {{{ // create_folder() {{{

View File

@ -398,7 +398,7 @@ class MTP_DEVICE(MTPDeviceBase):
x = self.dev.list_folder_by_name(parent.object_id, names) x = self.dev.list_folder_by_name(parent.object_id, names)
if x is None: if x is None:
raise DeviceError(f'Could not find folder named: {"/".join(names)} in {parent.full_path}') raise DeviceError(f'Could not find folder named: {"/".join(names)} in {parent.full_path}')
return x return list(x.values())
@same_thread @same_thread
def get_mtp_file(self, f, stream=None, callback=None): def get_mtp_file(self, f, stream=None, callback=None):

View File

@ -50,7 +50,8 @@ extern PyObject* create_folder(IPortableDevice *device, const wchar_t *parent_id
extern PyObject* delete_object(IPortableDevice *device, const wchar_t *object_id); extern PyObject* delete_object(IPortableDevice *device, const wchar_t *object_id);
extern PyObject* put_file(IPortableDevice *device, const wchar_t *parent_id, const wchar_t *name, PyObject *src, unsigned PY_LONG_LONG size, PyObject *callback); extern PyObject* put_file(IPortableDevice *device, const wchar_t *parent_id, const wchar_t *name, PyObject *src, unsigned PY_LONG_LONG size, PyObject *callback);
extern PyObject* find_in_parent( extern PyObject* find_in_parent(
CComPtr<IPortableDeviceContent> &content, IPortableDevicePropertiesBulk *bulk_properties, const wchar_t *parent_id, PyObject *name); CComPtr<IPortableDeviceContent> &content, const wchar_t *parent_id, PyObject *name);
extern PyObject* list_folder( extern PyObject* list_folder(
CComPtr<IPortableDeviceContent> &content, IPortableDevicePropertiesBulk *bulk_properties, const wchar_t *folder_id); IPortableDevice *device, CComPtr<IPortableDeviceContent> &content, IPortableDevicePropertiesBulk *bulk_properties,
const wchar_t *folder_id);
} }