Implement function to get metadata by name

This commit is contained in:
Kovid Goyal 2025-01-20 16:53:41 +05:30
parent b8c92ac9ce
commit 2a36fd3fce
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 55 additions and 0 deletions

View File

@ -493,6 +493,16 @@ list_folder(IPortableDevice *device, CComPtr<IPortableDeviceContent> &content, I
return ans.detach();
}
PyObject*
get_metadata(CComPtr<IPortableDeviceContent> &content, const wchar_t *object_id) {
CComPtr<IPortableDeviceKeyCollection> properties(create_filesystem_properties_collection());
if (!properties) return NULL;
CComPtr<IPortableDeviceProperties> devprops;
HRESULT hr = content->Properties(&devprops);
if (FAILED(hr)) { hresult_set_exc("Failed to get IPortableDeviceProperties interface", hr); return NULL; }
return get_object_properties(devprops, properties, object_id);
}
PyObject*
find_in_parent(CComPtr<IPortableDeviceContent> &content, const wchar_t *parent_id, PyObject *name) {
HRESULT hr;

View File

@ -109,6 +109,37 @@ list_folder_by_name(Device *self, PyObject *args) {
return wpd::list_folder(self->device, content, self->bulk_properties, parent_id.ptr());
} // }}}
// get_metadata_by_name() {{{
static PyObject*
get_metadata_by_name(Device *self, PyObject *args) {
wchar_raii parent_id; PyObject *names;
CComPtr<IPortableDeviceContent> content;
HRESULT hr; bool found = false;
Py_BEGIN_ALLOW_THREADS;
hr = self->device->Content(&content);
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to create content interface", hr); return NULL; }
if (!PyArg_ParseTuple(args, "O&O!", py_to_wchar, &parent_id, &PyTuple_Type, &names)) return NULL;
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(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; }
pyobject_raii l(PyObject_CallMethod(k, "lower", NULL)); if (!l) return NULL;
pyobject_raii object_id(wpd::find_in_parent(content, parent_id.ptr(), l.ptr()));
if (!object_id) {
if (PyErr_Occurred()) return NULL;
Py_RETURN_NONE;
}
if (!py_to_wchar_(object_id.ptr(), &parent_id)) return NULL;
found = true;
}
if (!found) Py_RETURN_NONE;
return wpd::get_metadata(content, parent_id.ptr());
} // }}}
// create_folder() {{{
static PyObject*
py_create_folder(Device *self, PyObject *args) {
@ -151,6 +182,10 @@ static PyMethodDef Device_methods[] = {
"list_folder_by_name(parent_id, names) -> List the folder specified by names (a tuple of name components) relative to parent_id from the device. Return None or a list of entries."
},
{"get_metadata_by_name", (PyCFunction)get_metadata_by_name, METH_VARARGS,
"get_metadata_by_name(parent_id, names) -> get metadata for the file or folder folder specified by names (a tuple of name components) relative to parent_id from the device. Return None or metadata."
},
{"get_file", (PyCFunction)py_get_file, METH_VARARGS,
"get_file(object_id, stream, callback=None) -> Get the file identified by object_id from the device. The file is written to the stream object, which must be a file like object. If callback is not None, it must be a callable that accepts two arguments: (bytes_read, total_size). It will be called after each chunk is read from the device. Note that it can be called multiple times with the same values."
},

View File

@ -400,6 +400,15 @@ class MTP_DEVICE(MTPDeviceBase):
raise DeviceError(f'Could not find folder named: {"/".join(names)} in {parent.full_path}')
return list(x.values())
@same_thread
def get_mtp_metadata_by_name(self, parent, *names: str):
if not parent.is_folder:
raise ValueError(f'{parent.full_path} is not a folder')
x = self.dev.get_metadata_by_name(parent.object_id, names)
if x is None:
raise DeviceError(f'Could not find folder named: {"/".join(names)} in {parent.full_path}')
return x
@same_thread
def get_mtp_file(self, f, stream=None, callback=None):
if f.is_folder:

View File

@ -54,4 +54,5 @@ extern PyObject* find_in_parent(
extern PyObject* list_folder(
IPortableDevice *device, CComPtr<IPortableDeviceContent> &content, IPortableDevicePropertiesBulk *bulk_properties,
const wchar_t *folder_id);
extern PyObject* get_metadata(CComPtr<IPortableDeviceContent> &content, const wchar_t *object_id);
}