From 179d4781f03d71e69ee51b7489267b8135f4ad7d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 22 Aug 2012 16:22:09 +0530 Subject: [PATCH] WPD: Create folders and use the actual file name instead of the nominal_name --- .../mtp/windows/content_enumeration.cpp | 74 ++++++++++++++++++- src/calibre/devices/mtp/windows/device.cpp | 18 +++++ src/calibre/devices/mtp/windows/global.h | 1 + src/calibre/devices/mtp/windows/remote.py | 4 + 4 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/calibre/devices/mtp/windows/content_enumeration.cpp b/src/calibre/devices/mtp/windows/content_enumeration.cpp index 70cead4893..84dd1d0c14 100644 --- a/src/calibre/devices/mtp/windows/content_enumeration.cpp +++ b/src/calibre/devices/mtp/windows/content_enumeration.cpp @@ -28,6 +28,7 @@ static IPortableDeviceKeyCollection* create_filesystem_properties_collection() { ADDPROP(WPD_OBJECT_PARENT_ID); ADDPROP(WPD_OBJECT_PERSISTENT_UNIQUE_ID); ADDPROP(WPD_OBJECT_NAME); + ADDPROP(WPD_OBJECT_ORIGINAL_FILE_NAME); // ADDPROP(WPD_OBJECT_SYNC_ID); ADDPROP(WPD_OBJECT_ISSYSTEM); ADDPROP(WPD_OBJECT_ISHIDDEN); @@ -92,8 +93,9 @@ static void set_properties(PyObject *obj, IPortableDeviceValues *values) { set_content_type_property(obj, values); set_string_property(obj, WPD_OBJECT_PARENT_ID, "parent_id", values); - set_string_property(obj, WPD_OBJECT_NAME, "name", values); + set_string_property(obj, WPD_OBJECT_NAME, "nominal_name", values); // set_string_property(obj, WPD_OBJECT_SYNC_ID, "sync_id", values); + set_string_property(obj, WPD_OBJECT_ORIGINAL_FILE_NAME, "name", values); set_string_property(obj, WPD_OBJECT_PERSISTENT_UNIQUE_ID, "persistent_id", values); set_bool_property(obj, WPD_OBJECT_ISHIDDEN, "is_hidden", values); @@ -370,6 +372,37 @@ end: } // }}} +static IPortableDeviceValues* create_object_properties(const wchar_t *parent_id, const wchar_t *name, const GUID content_type) { // {{{ + IPortableDeviceValues *values = NULL; + HRESULT hr; + BOOL ok = FALSE; + + hr = CoCreateInstance(CLSID_PortableDeviceValues, NULL, + CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&values)); + if (FAILED(hr)) { hresult_set_exc("Failed to create values interface", hr); goto end; } + + hr = values->SetStringValue(WPD_OBJECT_PARENT_ID, parent_id); + if (FAILED(hr)) { hresult_set_exc("Failed to set parent_id value", hr); goto end; } + + hr = values->SetStringValue(WPD_OBJECT_NAME, name); + if (FAILED(hr)) { hresult_set_exc("Failed to set name value", hr); goto end; } + + hr = values->SetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, name); + if (FAILED(hr)) { hresult_set_exc("Failed to set original_file_name value", hr); goto end; } + + hr = values->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_UNSPECIFIED); + if (FAILED(hr)) { hresult_set_exc("Failed to set object_format value", hr); goto end; } + + hr = values->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, content_type); + if (FAILED(hr)) { hresult_set_exc("Failed to set content_type value", hr); goto end; } + + ok = TRUE; + +end: + if (!ok && values != NULL) { values->Release(); values = NULL; } + return values; +} // }}} + PyObject* wpd::get_filesystem(IPortableDevice *device, const wchar_t *storage_id, IPortableDevicePropertiesBulk *bulk_properties) { // {{{ PyObject *folders = NULL; IPortableDevicePropVariantCollection *object_ids = NULL; @@ -500,4 +533,43 @@ end: Py_RETURN_NONE; } // }}} +PyObject* wpd::create_folder(IPortableDevice *device, const wchar_t *parent_id, const wchar_t *name) { // {{{ + IPortableDeviceContent *content = NULL; + IPortableDeviceValues *values = NULL; + IPortableDeviceProperties *devprops = NULL; + IPortableDeviceKeyCollection *properties = NULL; + PyObject *ans = NULL; + HRESULT hr; + wchar_t *newid = NULL; + + values = create_object_properties(parent_id, name, WPD_CONTENT_TYPE_FOLDER); + if (values == NULL) goto end; + + Py_BEGIN_ALLOW_THREADS; + hr = device->Content(&content); + Py_END_ALLOW_THREADS; + if (FAILED(hr)) { hresult_set_exc("Failed to create content interface", hr); goto end; } + + hr = content->Properties(&devprops); + if (FAILED(hr)) { hresult_set_exc("Failed to get IPortableDeviceProperties interface", hr); goto end; } + + properties = create_filesystem_properties_collection(); + if (properties == NULL) goto end; + + Py_BEGIN_ALLOW_THREADS; + hr = content->CreateObjectWithPropertiesOnly(values, &newid); + Py_END_ALLOW_THREADS; + if (FAILED(hr) || newid == NULL) { hresult_set_exc("Failed to create folder", hr); goto end; } + + ans = get_object_properties(devprops, properties, newid); +end: + if (content != NULL) content->Release(); + if (values != NULL) values->Release(); + if (devprops != NULL) devprops->Release(); + if (properties != NULL) properties->Release(); + if (newid != NULL) CoTaskMemFree(newid); + return ans; + +} // }}} + } // namespace wpd diff --git a/src/calibre/devices/mtp/windows/device.cpp b/src/calibre/devices/mtp/windows/device.cpp index d79db0a2d3..7ae1c00216 100644 --- a/src/calibre/devices/mtp/windows/device.cpp +++ b/src/calibre/devices/mtp/windows/device.cpp @@ -103,6 +103,20 @@ py_get_file(Device *self, PyObject *args, PyObject *kwargs) { return wpd::get_file(self->device, object, stream, callback); } // }}} +// create_folder() {{{ +static PyObject* +py_create_folder(Device *self, PyObject *args, PyObject *kwargs) { + PyObject *pparent_id, *pname; + wchar_t *parent_id, *name; + + if (!PyArg_ParseTuple(args, "OO", &pparent_id, &pname)) return NULL; + parent_id = unicode_to_wchar(pparent_id); + name = unicode_to_wchar(pname); + if (parent_id == NULL || name == NULL) return NULL; + + return wpd::create_folder(self->device, parent_id, name); +} // }}} + static PyMethodDef Device_methods[] = { {"update_data", (PyCFunction)update_data, METH_VARARGS, "update_data() -> Reread the basic device data from the device (total, space, free space, storage locations, etc.)" @@ -116,6 +130,10 @@ static PyMethodDef Device_methods[] = { "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." }, + {"create_folder", (PyCFunction)py_create_folder, METH_VARARGS, + "create_folder(parent_id, name) -> Create a folder. Returns the folder metadata." + }, + {NULL} }; diff --git a/src/calibre/devices/mtp/windows/global.h b/src/calibre/devices/mtp/windows/global.h index 47f0786249..0acd1c7e9c 100644 --- a/src/calibre/devices/mtp/windows/global.h +++ b/src/calibre/devices/mtp/windows/global.h @@ -58,6 +58,7 @@ extern IPortableDevice* open_device(const wchar_t *pnp_id, IPortableDeviceValues 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_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); } diff --git a/src/calibre/devices/mtp/windows/remote.py b/src/calibre/devices/mtp/windows/remote.py index a3686ce88c..22e186c32d 100644 --- a/src/calibre/devices/mtp/windows/remote.py +++ b/src/calibre/devices/mtp/windows/remote.py @@ -71,12 +71,16 @@ def main(): print ('Total space', dev.total_space()) print ('Free space', dev.free_space()) dev.filesystem_cache.dump() + # pprint.pprint(dev.dev.create_folder(dev.filesystem_cache.entries[0].object_id, + # 'zzz')) # print ('Fetching file: oFF (198214 bytes)') # stream = dev.get_file('oFF') # print ("Fetched size: ", stream.tell()) finally: dev.shutdown() + print ('Device connection shutdown') + if __name__ == '__main__': main()