~ubuntu-branches/ubuntu/vivid/gpodder/vivid-proposed

« back to all changes in this revision

Viewing changes to src/gpodder/gpopymtp.py

  • Committer: Bazaar Package Importer
  • Author(s): tony mancill
  • Date: 2010-12-05 17:08:02 UTC
  • mfrom: (5.3.2 experimental) (5.2.10 sid)
  • Revision ID: james.westby@ubuntu.com-20101205170802-qbsq7r331j21np1i
Tags: 2.10-1
* New upstream release
* Upload to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
#
 
4
# A Ctypes wrapper to LibMTP
 
5
# Developed by: Nick Devito (nick@nick125.com)
 
6
# (c) 2008 Nick Devito
 
7
# Released under the GPLv3 or later.
 
8
#
 
9
 
 
10
#
 
11
# This is a patched version of pymtp by Nick Devito that is specially
 
12
# tailored to work with newer releases of libmtp. Patches written by:
 
13
#
 
14
#  Jérôme Chabod <jerome.chabod@ifrance.com>
 
15
#  Justin Forest <justin.forest@gmail.com>
 
16
#
 
17
# As the upstream website (http://nick125.com/projects/pymtp) is gone as of
 
18
# September 2010, we now include this library with gPodder (renamed from
 
19
# "pymtp" to "gpopymtp" to avoid namespace clashes).
 
20
#
 
21
# See http://gpodder.org/bug/307 for details.
 
22
#
 
23
 
 
24
"""
 
25
        PyMTP is a pythonic wrapper around libmtp, making it a bit more 
 
26
        friendly to use in python
 
27
 
 
28
        Example Usage (or see examples/):
 
29
                >>> import pymtp
 
30
                >>> mtp = pymtp.MTP()
 
31
                >>> mtp.connect()
 
32
                PTP: Opening session
 
33
                >>> print mtp.get_devicename()
 
34
                Device name
 
35
                >>> mtp.disconnect()
 
36
                PTP: Closing session
 
37
                >>>
 
38
"""
 
39
 
 
40
__VERSION__ = "0.0.4"
 
41
__VERSION_MACRO__ = 4
 
42
__VERSION_MINOR__ = 0
 
43
__VERSION_MAJOR__ = 0
 
44
__VERSION_TUPLE__ = (__VERSION_MAJOR__, __VERSION_MINOR__, __VERSION_MACRO__)
 
45
__AUTHOR__ = "Nick Devito (nick@nick125.com)"
 
46
__LICENSE__ = "GPL-3"
 
47
__DEBUG__ = 1
 
48
 
 
49
import os
 
50
import ctypes
 
51
import ctypes.util
 
52
 
 
53
# NOTE: This code *may* work on windows, I don't have a win32 system to test
 
54
# this on. 
 
55
_module_path = ctypes.util.find_library("mtp") 
 
56
_libmtp = ctypes.CDLL(_module_path)
 
57
 
 
58
# ----------
 
59
# Error Definitions
 
60
# ----------
 
61
class NoDeviceConnected(Exception): 
 
62
        """
 
63
                Raised when there isn't a device connected to the USB bus 
 
64
        """
 
65
 
 
66
        pass
 
67
 
 
68
class AlreadyConnected(Exception):
 
69
        """
 
70
                Raised when we're already connected to a device and there is 
 
71
                an attempt to connect
 
72
        """
 
73
 
 
74
        pass
 
75
        
 
76
class UnsupportedCommand(Exception): 
 
77
        """
 
78
                Raised when the connected device does not support the command 
 
79
                issued
 
80
        """
 
81
 
 
82
        pass
 
83
 
 
84
class CommandFailed(Exception): 
 
85
        """
 
86
                Raised when the connected device returned an error when trying 
 
87
                to execute a command
 
88
        """
 
89
 
 
90
        pass
 
91
 
 
92
class NotConnected(Exception): 
 
93
        """
 
94
                Raised when a command is called and the device is not connected
 
95
        """
 
96
 
 
97
        pass
 
98
 
 
99
class ObjectNotFound(Exception):
 
100
        """
 
101
                Raised when a command tries to get an object that doesn't exist
 
102
        """
 
103
 
 
104
        pass
 
105
 
 
106
# ----------
 
107
# End Error Definitions
 
108
# ----------
 
109
 
 
110
# ----------
 
111
# Data Model Definitions
 
112
# ----------
 
113
 
 
114
class LIBMTP_Error(ctypes.Structure):
 
115
        """
 
116
                LIBMTP_Error
 
117
                Contains the ctypes structure for LIBMTP_error_t
 
118
        """
 
119
 
 
120
        def __repr__(self):
 
121
                return self.errornumber
 
122
 
 
123
LIBMTP_Error._fields_ = [("errornumber", ctypes.c_int),
 
124
                         ("error_text", ctypes.c_char_p),
 
125
                         ("next", ctypes.POINTER(LIBMTP_Error))]
 
126
 
 
127
class LIBMTP_DeviceStorage(ctypes.Structure):
 
128
        """
 
129
                LIBMTP_DeviceStorage
 
130
                Contains the ctypes structure for LIBMTP_devicestorage_t
 
131
        """
 
132
 
 
133
        def __repr__(self):
 
134
                return self.id
 
135
 
 
136
LIBMTP_DeviceStorage._fields_ = [("id", ctypes.c_uint32),
 
137
                                 ("StorageType", ctypes.c_uint16),
 
138
                                 ("FilesystemType", ctypes.c_uint16),
 
139
                                 ("AccessCapability", ctypes.c_uint16),
 
140
                                 ("MaxCapacity", ctypes.c_uint64),
 
141
                                 ("FreeSpaceInBytes", ctypes.c_uint64),
 
142
                                 ("FreeSpaceInObjects", ctypes.c_uint64),
 
143
                                 ("StorageDescription", ctypes.c_char_p),
 
144
                                 ("VolumeIdentifier", ctypes.c_char_p),
 
145
                                 ("next", ctypes.POINTER(LIBMTP_DeviceStorage)),
 
146
                                 ("prev", ctypes.POINTER(LIBMTP_DeviceStorage))]
 
147
 
 
148
class LIBMTP_MTPDevice(ctypes.Structure):
 
149
        """
 
150
                LIBMTP_MTPDevice
 
151
                Contains the ctypes structure for LIBMTP_mtpdevice_t
 
152
        """
 
153
 
 
154
        def __repr__(self):
 
155
                return self.interface_number
 
156
 
 
157
LIBMTP_MTPDevice._fields_ = [("interface_number", ctypes.c_uint8),
 
158
                             ("params", ctypes.c_void_p),
 
159
                             ("usbinfo", ctypes.c_void_p),
 
160
                             ("storage", ctypes.POINTER(LIBMTP_DeviceStorage)),
 
161
                             ("errorstack", ctypes.POINTER(LIBMTP_Error)),
 
162
                             ("maximum_battery_level", ctypes.c_uint8),
 
163
                             ("default_music_folder", ctypes.c_uint32),
 
164
                             ("default_playlist_folder", ctypes.c_uint32),
 
165
                             ("default_picture_folder", ctypes.c_uint32),
 
166
                             ("default_video_folder", ctypes.c_uint32),
 
167
                             ("default_organizer_folder", ctypes.c_uint32),
 
168
                             ("default_zencast_folder", ctypes.c_uint32),
 
169
                             ("default_album_folder", ctypes.c_uint32),
 
170
                             ("default_text_folder", ctypes.c_uint32),
 
171
                             ("cd", ctypes.c_void_p),
 
172
                             ("next", ctypes.POINTER(LIBMTP_MTPDevice))]
 
173
 
 
174
class LIBMTP_File(ctypes.Structure):
 
175
        """
 
176
                LIBMTP_File
 
177
                Contains the ctypes structure for LIBMTP_file_t
 
178
        """
 
179
 
 
180
        def __repr__(self):
 
181
                return "%s (%s)" % (self.filename, self.item_id)
 
182
 
 
183
LIBMTP_File._fields_ = [("item_id", ctypes.c_uint32),
 
184
                        ("parent_id", ctypes.c_uint32),
 
185
                        ("storage_id", ctypes.c_uint32),
 
186
                        ("filename", ctypes.c_char_p),
 
187
                        ("filesize", ctypes.c_uint64),
 
188
                        ("filetype", ctypes.c_int),
 
189
                        ("next", ctypes.POINTER(LIBMTP_File))]
 
190
 
 
191
class LIBMTP_Track(ctypes.Structure):
 
192
        """
 
193
                LIBMTP_Track
 
194
                Contains the ctypes structure for LIBMTP_track_t
 
195
        """
 
196
 
 
197
        def __repr__(self):
 
198
                return "%s - %s (%s)" % (self.artist, self.title, self.item_id)
 
199
                
 
200
LIBMTP_Track._fields_ = [("item_id", ctypes.c_uint32),
 
201
                        ("parent_id", ctypes.c_uint32),
 
202
                        ("storage_id", ctypes.c_uint32),
 
203
                        ("title", ctypes.c_char_p),
 
204
                        ("artist", ctypes.c_char_p),
 
205
                        ("composer", ctypes.c_char_p),
 
206
                        ("genre", ctypes.c_char_p),
 
207
                        ("album", ctypes.c_char_p),
 
208
                        ("date", ctypes.c_char_p),
 
209
                        ("filename", ctypes.c_char_p),
 
210
                        ("tracknumber", ctypes.c_uint16),
 
211
                        ("duration", ctypes.c_uint32),
 
212
                        ("samplerate", ctypes.c_uint32),
 
213
                        ("nochannels", ctypes.c_uint16),
 
214
                        ("wavecodec", ctypes.c_uint32),
 
215
                        ("bitrate", ctypes.c_uint32),
 
216
                        ("bitratetype", ctypes.c_uint16),
 
217
                        ("rating", ctypes.c_uint16),
 
218
                        ("usecount", ctypes.c_uint32),
 
219
                        ("filesize", ctypes.c_uint64),
 
220
                        ("modificationdate", ctypes.c_long),
 
221
                        ("filetype", ctypes.c_int),
 
222
                        ("next", ctypes.POINTER(LIBMTP_Track))]
 
223
 
 
224
class LIBMTP_Playlist(ctypes.Structure):
 
225
        """
 
226
                LIBMTP_Playlist
 
227
                Contains the ctypes structure for LIBMTP_playlist_t
 
228
        """
 
229
 
 
230
        def __init__(self):
 
231
                self.tracks = ctypes.pointer(ctypes.c_uint32(0))
 
232
                self.no_tracks = ctypes.c_uint32(0)
 
233
        def __repr__(self):
 
234
                return "%s (%s)" % (self.name, self.playlist_id)
 
235
 
 
236
        def __iter__(self):
 
237
                """
 
238
                        This allows the playlist object to act like a list with
 
239
                        a generator.
 
240
                """
 
241
                for track in xrange(self.no_tracks):
 
242
                        yield self.tracks[track]
 
243
 
 
244
        def __getitem__(self, key):
 
245
                """
 
246
                        This allows the playlist to return tracks like a list
 
247
                """
 
248
 
 
249
                if (key > (self.no_tracks - 1)):
 
250
                        raise IndexError
 
251
 
 
252
                return self.tracks[key]
 
253
 
 
254
        def __setitem__(self, key, value):
 
255
                """
 
256
                        This allows the user to manipulate the playlist like a 
 
257
                        list. However, this will only modify existing objects, 
 
258
                        you can't try to set a key outside of the current size.
 
259
                """
 
260
 
 
261
                if (key > (self.no_tracks - 1)):
 
262
                        raise IndexError
 
263
 
 
264
                self.tracks[key] = value
 
265
 
 
266
        def __delitem__(self, key):
 
267
                """
 
268
                        This allows the user to delete an object
 
269
                        from the playlist
 
270
                """
 
271
 
 
272
                if (key > (self.no_tracks - 1)):
 
273
                        raise IndexError
 
274
 
 
275
                for i in range(key, (self.no_tracks - 1)):
 
276
                        self.tracks[i] = self.tracks[i + 1]
 
277
 
 
278
                self.no_tracks -= 1
 
279
        
 
280
        def append(self, value):
 
281
                """
 
282
                        This function appends a track to the end of the tracks
 
283
                        list.
 
284
                """
 
285
                if (self.tracks == None):
 
286
                        self.tracks = ctypes.pointer(ctypes.c_uint32(0))
 
287
 
 
288
                self.no_tracks += 1
 
289
                self.tracks[(self.no_tracks - 1)] = value
 
290
 
 
291
        def __len__(self):
 
292
                """
 
293
                        This returns the number of tracks in the playlist
 
294
                """
 
295
 
 
296
                return self.no_tracks
 
297
 
 
298
LIBMTP_Playlist._fields_ = [("playlist_id", ctypes.c_uint32),
 
299
                            ("parent_id", ctypes.c_uint32),
 
300
                            ("storage_id", ctypes.c_uint32),
 
301
                            ("name", ctypes.c_char_p),
 
302
                            ("tracks", ctypes.POINTER(ctypes.c_uint32)),
 
303
                            ("no_tracks", ctypes.c_uint32),
 
304
                            ("next", ctypes.POINTER(LIBMTP_Playlist))]
 
305
 
 
306
class LIBMTP_Folder(ctypes.Structure):
 
307
        """
 
308
                LIBMTP_Folder
 
309
                Contains the ctypes structure for LIBMTP_folder_t
 
310
        """
 
311
 
 
312
        def __repr__(self):
 
313
                return "%s (%s)" % (self.name, self.folder_id)
 
314
 
 
315
LIBMTP_Folder._fields_ = [("folder_id", ctypes.c_uint32),
 
316
                          ("parent_id", ctypes.c_uint32),
 
317
                          ("storage_id", ctypes.c_uint32),
 
318
                          ("name", ctypes.c_char_p),
 
319
                          ("sibling", ctypes.POINTER(LIBMTP_Folder)),
 
320
                          ("child", ctypes.POINTER(LIBMTP_Folder))]
 
321
 
 
322
# Abstracted from libmtp 0.2.6.1's libmtp.h. This must be kept in sync.
 
323
LIBMTP_Filetype = {
 
324
        "WAV":                  ctypes.c_int(0),
 
325
        "MP3":                  ctypes.c_int(1),
 
326
        "WMA":                  ctypes.c_int(2),
 
327
        "OGG":                  ctypes.c_int(3),
 
328
        "AUDIBLE":              ctypes.c_int(4),
 
329
        "MP4":                  ctypes.c_int(5),
 
330
        "UNDEF_AUDIO":          ctypes.c_int(6),        
 
331
        "WMV":                  ctypes.c_int(7),
 
332
        "AVI":                  ctypes.c_int(8),
 
333
        "MPEG":                 ctypes.c_int(9),
 
334
        "ASF":                  ctypes.c_int(10),
 
335
        "QT":                   ctypes.c_int(11),
 
336
        "UNDEF_VIDEO":          ctypes.c_int(12),
 
337
        "JPEG":                 ctypes.c_int(13),
 
338
        "JFIF":                 ctypes.c_int(14),
 
339
        "TIFF":                 ctypes.c_int(15),
 
340
        "BMP":                  ctypes.c_int(16),
 
341
        "GIF":                  ctypes.c_int(17),
 
342
        "PICT":                 ctypes.c_int(18),
 
343
        "PNG":                  ctypes.c_int(19),
 
344
        "VCALENDAR1":           ctypes.c_int(20),
 
345
        "VCALENDAR2":           ctypes.c_int(21),
 
346
        "VCARD2":               ctypes.c_int(22),
 
347
        "VCARD3":               ctypes.c_int(23),
 
348
        "WINDOWSIMAGEFORMAT":   ctypes.c_int(24),
 
349
        "WINEXEC":              ctypes.c_int(25),
 
350
        "TEXT":                 ctypes.c_int(26),
 
351
        "HTML":                 ctypes.c_int(27),
 
352
        "FIRMWARE":             ctypes.c_int(28),
 
353
        "AAC":                  ctypes.c_int(29),
 
354
        "MEDIACARD":            ctypes.c_int(30),
 
355
        "FLAC":                 ctypes.c_int(31),
 
356
        "MP2":                  ctypes.c_int(32),
 
357
        "M4A":                  ctypes.c_int(33),
 
358
        "DOC":                  ctypes.c_int(34),
 
359
        "XML":                  ctypes.c_int(35),
 
360
        "XLS":                  ctypes.c_int(36),
 
361
        "PPT":                  ctypes.c_int(37),
 
362
        "MHT":                  ctypes.c_int(38),
 
363
        "JP2":                  ctypes.c_int(39),
 
364
        "JPX":                  ctypes.c_int(40),
 
365
        "UNKNOWN":              ctypes.c_int(41),
 
366
}
 
367
 
 
368
# Synced from libmtp 0.2.6.1's libmtp.h. Must be kept in sync.
 
369
LIBMTP_Error_Number = {
 
370
        "NONE":                 ctypes.c_int(0),
 
371
        "GENERAL":              ctypes.c_int(1),
 
372
        "PTP_LAYER":            ctypes.c_int(2),
 
373
        "USB_LAYER":            ctypes.c_int(3),
 
374
        "MEMORY_ALLOCATION":    ctypes.c_int(4),
 
375
        "NO_DEVICE_ATTACHED":   ctypes.c_int(5),
 
376
        "STORAGE_FULL":         ctypes.c_int(6),
 
377
        "CONNECTING":           ctypes.c_int(7),
 
378
        "CANCELLED":            ctypes.c_int(8),
 
379
}
 
380
 
 
381
# ----------
 
382
# End Data Model Definitions
 
383
# ----------
 
384
 
 
385
# ----------
 
386
# Type Definitions
 
387
# ----------
 
388
 
 
389
_libmtp.LIBMTP_Get_Friendlyname.restype = ctypes.c_char_p
 
390
_libmtp.LIBMTP_Get_Serialnumber.restype = ctypes.c_char_p
 
391
_libmtp.LIBMTP_Get_Modelname.restype = ctypes.c_char_p
 
392
_libmtp.LIBMTP_Get_Manufacturername.restype = ctypes.c_char_p
 
393
_libmtp.LIBMTP_Get_Deviceversion.restype = ctypes.c_char_p
 
394
_libmtp.LIBMTP_Get_Filelisting_With_Callback.restype = ctypes.POINTER(LIBMTP_File)
 
395
_libmtp.LIBMTP_Get_Tracklisting_With_Callback.restype = ctypes.POINTER(LIBMTP_Track)
 
396
_libmtp.LIBMTP_Get_Filetype_Description.restype = ctypes.c_char_p
 
397
_libmtp.LIBMTP_Get_Filemetadata.restype = ctypes.POINTER(LIBMTP_File)
 
398
_libmtp.LIBMTP_Get_Trackmetadata.restype = ctypes.POINTER(LIBMTP_Track)
 
399
_libmtp.LIBMTP_Get_First_Device.restype = ctypes.POINTER(LIBMTP_MTPDevice)
 
400
_libmtp.LIBMTP_Get_Playlist_List.restype = ctypes.POINTER(LIBMTP_Playlist)
 
401
_libmtp.LIBMTP_Get_Playlist.restype = ctypes.POINTER(LIBMTP_Playlist)
 
402
_libmtp.LIBMTP_Get_Folder_List.restype = ctypes.POINTER(LIBMTP_Folder)
 
403
_libmtp.LIBMTP_Find_Folder.restype = ctypes.POINTER(LIBMTP_Folder)
 
404
_libmtp.LIBMTP_Get_Errorstack.restype = ctypes.POINTER(LIBMTP_Error)
 
405
# This is for callbacks with the type of LIBMTP_progressfunc_t
 
406
Progressfunc = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64)
 
407
 
 
408
# ----------
 
409
# End Type Definitions
 
410
# ----------
 
411
 
 
412
class MTP:
 
413
        """
 
414
                The MTP object 
 
415
                This is the main wrapper around libmtp
 
416
        """
 
417
 
 
418
        def __init__(self):
 
419
                """ 
 
420
                        Initializes the MTP object
 
421
 
 
422
                        @rtype: None
 
423
                        @return: None
 
424
                """
 
425
 
 
426
                self.mtp = _libmtp
 
427
                self.mtp.LIBMTP_Init()
 
428
                self.device = None
 
429
 
 
430
        def debug_stack(self):
 
431
                """
 
432
                        Checks if __DEBUG__ is set, if so, prints and clears the
 
433
                        errorstack.
 
434
 
 
435
                        @rtype: None
 
436
                        @return: None
 
437
                """
 
438
 
 
439
                if __DEBUG__:
 
440
                        self.mtp.LIBMTP_Dump_Errorstack()
 
441
                        #self.mtp.LIBMTP_Clear_Errorstack()
 
442
 
 
443
        def connect(self):
 
444
                """
 
445
                        Initializes the MTP connection to the device 
 
446
 
 
447
                        @rtype: None
 
448
                        @return: None
 
449
 
 
450
                """
 
451
 
 
452
                if (self.device != None):
 
453
                        raise AlreadyConnected
 
454
 
 
455
                self.device = self.mtp.LIBMTP_Get_First_Device()
 
456
 
 
457
                if not self.device:
 
458
                        self.device = None
 
459
                        raise NoDeviceConnected
 
460
                        
 
461
        def disconnect(self):
 
462
                """
 
463
                        Disconnects the MTP device and deletes the self.device object
 
464
 
 
465
                        @rtype: None
 
466
                        @return: None
 
467
                """
 
468
 
 
469
                if (self.device == None):
 
470
                        raise NotConnected
 
471
 
 
472
                self.mtp.LIBMTP_Release_Device(self.device)
 
473
                del self.device
 
474
                self.device = None
 
475
 
 
476
        def get_devicename(self):
 
477
                """
 
478
                        Returns the connected device's 'friendly name' (or 
 
479
                        known as the owner name)
 
480
 
 
481
                        @rtype: string
 
482
                        @return: The connected device's 'friendly name'
 
483
                """
 
484
 
 
485
                if (self.device == None): 
 
486
                        raise NotConnected
 
487
 
 
488
                return self.mtp.LIBMTP_Get_Friendlyname(self.device)
 
489
                
 
490
        def set_devicename(self, name):
 
491
                """
 
492
                        Changes the connected device's 'friendly name' to name
 
493
 
 
494
                        @type name: string
 
495
                        @param name: The name to change the connected device's
 
496
                         'friendly name' to
 
497
                        @rtype: None
 
498
                        @return: None
 
499
                """
 
500
 
 
501
                if (self.device == None):
 
502
                        raise NotConnected
 
503
 
 
504
                ret = self.mtp.LIBMTP_Set_Friendlyname(self.device, name)
 
505
                if (ret != 0):
 
506
                        self.debug_stack()
 
507
                        raise CommandFailed
 
508
 
 
509
        def get_serialnumber(self):
 
510
                """
 
511
                        Returns the connected device's serial number
 
512
 
 
513
                        @rtype: string
 
514
                        @return: The connected device's serial number
 
515
                """
 
516
 
 
517
                if (self.device == None):
 
518
                        raise NotConnected
 
519
 
 
520
                return self.mtp.LIBMTP_Get_Serialnumber(self.device)
 
521
 
 
522
        def get_manufacturer(self):
 
523
                """
 
524
                        Return the connected device's manufacturer
 
525
 
 
526
                        @rtype: string
 
527
                        @return: The connected device's manufacturer
 
528
                """
 
529
                if (self.device == None):
 
530
                        raise NotConnected
 
531
 
 
532
                return self.mtp.LIBMTP_Get_Manufacturername(self.device)
 
533
 
 
534
        def get_batterylevel(self):
 
535
                """
 
536
                        Returns the connected device's maximum and current 
 
537
                        battery levels
 
538
 
 
539
                        @rtype: tuple
 
540
                        @return: The connected device's maximum and current 
 
541
                         battery levels ([0] is maximum, [1] is current)
 
542
                """
 
543
 
 
544
                if (self.device == None):
 
545
                        raise NotConnected
 
546
 
 
547
                maximum_level = ctypes.c_uint8()
 
548
                current_level = ctypes.c_uint8()
 
549
 
 
550
                ret = self.mtp.LIBMTP_Get_Batterylevel(self.device, \
 
551
                  ctypes.byref(maximum_level), ctypes.byref(current_level))
 
552
 
 
553
                if (ret != 0):
 
554
                        raise CommandFailed
 
555
                        
 
556
                return (maximum_level.value, current_level.value)
 
557
 
 
558
        def get_modelname(self):
 
559
                """
 
560
                        Returns the connected device's model name (such 
 
561
                        as "Zen V Plus")
 
562
 
 
563
                        @rtype: string
 
564
                        @return: The connected device's model name
 
565
                """
 
566
 
 
567
                if (self.device == None):
 
568
                        raise NotConnected
 
569
 
 
570
                return self.mtp.LIBMTP_Get_Modelname(self.device)
 
571
 
 
572
        def get_deviceversion(self):
 
573
                """
 
574
                        Returns the connected device's version (such as 
 
575
                        firmware/hardware version)
 
576
 
 
577
                        @rtype: string
 
578
                        @return: Returns the connect device's version 
 
579
                         information
 
580
                """
 
581
 
 
582
                if (self.device == None):
 
583
                        raise NotConnected
 
584
 
 
585
                return self.mtp.LIBMTP_Get_Deviceversion(self.device)
 
586
 
 
587
        def get_filelisting(self, callback=None):
 
588
                """
 
589
                        Returns the connected device's file listing as a tuple, 
 
590
                        containing L{LIBMTP_File} objects.
 
591
 
 
592
                        @type callback: function or None
 
593
                        @param callback: The function provided to libmtp to
 
594
                         receive callbacks from ptp. Callback must take two
 
595
                         arguments, total and sent (in bytes)
 
596
                        @rtype: tuple
 
597
                        @return: Returns the connect device file listing tuple
 
598
                """
 
599
 
 
600
                if (self.device == None):
 
601
                        raise NotConnected
 
602
 
 
603
                if (callback != None):
 
604
                        callback = Progressfunc(callback)
 
605
 
 
606
                files = self.mtp.LIBMTP_Get_Filelisting_With_Callback(self.device, callback, None)
 
607
                ret = []
 
608
                next = files
 
609
 
 
610
                while next:
 
611
                        ret.append(next.contents)
 
612
                        if (next.contents.next == None):
 
613
                                break
 
614
                        next = next.contents.next
 
615
 
 
616
                return ret
 
617
        
 
618
        def get_filetype_description(self, filetype):
 
619
                """
 
620
                        Returns the description of the filetype
 
621
 
 
622
                        @type filetype: int
 
623
                        @param filetype: The MTP filetype integer
 
624
                        @rtype: string
 
625
                        @return: The file type information
 
626
                """
 
627
 
 
628
                if (self.device == None):
 
629
                        raise NotConnected
 
630
 
 
631
                return self.mtp.LIBMTP_Get_Filetype_Description(filetype)
 
632
 
 
633
        def get_file_metadata(self, file_id):
 
634
                """
 
635
                        Returns the file metadata from the connected device
 
636
 
 
637
                        As per the libmtp documentation, calling this function 
 
638
                        repeatly is not recommended, as it is slow and creates 
 
639
                        a large amount of USB traffic.
 
640
 
 
641
                        @type file_id: int
 
642
                        @param file_id: The unique numeric file id
 
643
                        @rtype: LIBMTP_File
 
644
                        @return: The file metadata
 
645
                """
 
646
 
 
647
                if (self.device == None):
 
648
                        raise NotConnected
 
649
 
 
650
                ret = self.mtp.LIBMTP_Get_Filemetadata(self.device, file_id)
 
651
 
 
652
                if (not hasattr(ret, 'contents')):
 
653
                        raise ObjectNotFound
 
654
 
 
655
                return ret.contents
 
656
        
 
657
        def get_tracklisting(self, callback=None):
 
658
                """
 
659
                        Returns tracks from the connected device
 
660
 
 
661
                        @type callback: function or None
 
662
                        @param callback: The function provided to libmtp to
 
663
                         receive callbacks from ptp. Callback must take two
 
664
                         arguments, total and sent (in bytes)
 
665
                        @rtype: tuple
 
666
                        @return: Returns a tuple full of L{LIBMTP_Track} objects
 
667
                """
 
668
 
 
669
                if (self.device == None):
 
670
                        raise NotConnected
 
671
 
 
672
                if (callback != None):
 
673
                        callback = Progressfunc(callback)
 
674
 
 
675
                tracks = self.mtp.LIBMTP_Get_Tracklisting_With_Callback(self.device, callback, None)
 
676
                ret = []
 
677
                next = tracks
 
678
 
 
679
                while next:
 
680
                        ret.append(next.contents)
 
681
                        next = next.contents.next
 
682
                return ret
 
683
 
 
684
        def get_track_metadata(self, track_id):
 
685
                """
 
686
                        Returns the track metadata
 
687
 
 
688
                        As per the libmtp documentation, calling this function repeatly is not
 
689
                        recommended, as it is slow and creates a large amount of USB traffic.
 
690
 
 
691
                        @type track_id: int
 
692
                        @param track_id: The unique numeric track id
 
693
                        @rtype: L{LIBMTP_Track}
 
694
                        @return: The track metadata
 
695
                """
 
696
 
 
697
                if (self.device == None):
 
698
                        raise NotConnected
 
699
 
 
700
                ret = self.mtp.LIBMTP_Get_Trackmetadata(self.device, track_id)
 
701
 
 
702
                if (not hasattr(ret, 'contents')):
 
703
                        raise ObjectNotFound
 
704
 
 
705
                return ret.contents
 
706
 
 
707
        def get_file_to_file(self, file_id, target, callback=None):
 
708
                """
 
709
                        Downloads the file from the connected device and stores it at the 
 
710
                        target location
 
711
 
 
712
                        @type file_id: int
 
713
                        @param file_id: The unique numeric file id
 
714
                        @type target: str
 
715
                        @param target: The location to place the file
 
716
                        @type callback: function or None
 
717
                        @param callback: The function provided to libmtp to 
 
718
                         receive callbacks from ptp. Callback must take two 
 
719
                         arguments, total and sent (in bytes)
 
720
                """
 
721
 
 
722
                if (self.device == None):
 
723
                        raise NotConnected
 
724
                        
 
725
                if (callback != None):
 
726
                        callback = Progressfunc(callback)
 
727
                        
 
728
                ret = self.mtp.LIBMTP_Get_File_To_File(self.device, file_id, target, callback, None)
 
729
 
 
730
                if (ret != 0):
 
731
                        self.debug_stack()
 
732
                        raise CommandFailed
 
733
 
 
734
        def get_track_to_file(self, track_id, target, callback=None):
 
735
                """
 
736
                        Downloads the track from the connected device and stores it at 
 
737
                        the target location
 
738
                        
 
739
                        @type track_id: int
 
740
                        @param track_id: The unique numeric track id
 
741
                        @type target: str
 
742
                        @param target: The location to place the track
 
743
                        @type callback: function or None
 
744
                        @param callback: The function provided to libmtp to
 
745
                         receive callbacks from ptp. Callback must take two
 
746
                         arguments, total and sent (in bytes)
 
747
                """
 
748
 
 
749
                if (self.device == None):
 
750
                        raise NotConnected
 
751
                        
 
752
                if (callback != None):
 
753
                        callback = Progressfunc(callback)
 
754
 
 
755
                ret = self.mtp.LIBMTP_Get_Track_To_File(self.device, track_id, target, callback, None)
 
756
 
 
757
                if (ret != 0):
 
758
                        self.debug_stack()
 
759
                        raise CommandFailed
 
760
 
 
761
        def find_filetype(self, filename):
 
762
                """
 
763
                        Attempts to guess the filetype off the filename. Kind of 
 
764
                        inaccurate and should be trusted with a grain of salt. It
 
765
                        works in most situations, though.
 
766
                        
 
767
                        @type filename: str
 
768
                        @param filename: The filename to attempt to guess from
 
769
                        @rtype: int
 
770
                        @return: The integer of the Filetype
 
771
                """
 
772
 
 
773
                fileext = filename.lower().split(".")[-1]
 
774
 
 
775
                if (fileext == "wav" or fileext == "wave"):
 
776
                        return LIBMTP_Filetype["WAV"]
 
777
                elif (fileext == "mp3"):
 
778
                        return LIBMTP_Filetype["MP3"]
 
779
                elif (fileext == "wma"):
 
780
                        return LIBMTP_Filetype["WMA"]
 
781
                elif (fileext == "ogg"):
 
782
                        return LIBMTP_Filetype["OGG"]
 
783
                elif (fileext == "mp4"):
 
784
                        return LIBMTP_Filetype["MP4"]
 
785
                elif (fileext == "wmv"):
 
786
                        return LIBMTP_Filetype["WMV"]
 
787
                elif (fileext == "avi"):
 
788
                        return LIBMTP_Filetype["AVI"]
 
789
                elif (fileext == "mpeg" or fileext == "mpg"):
 
790
                        return LIBMTP_Filetype["MPEG"]
 
791
                elif (fileext == "asf"):
 
792
                        return LIBMTP_Filetype["ASF"]
 
793
                elif (fileext == "qt" or fileext == "mov"):
 
794
                        return LIBMTP_Filetype["QT"]
 
795
                elif (fileext == "jpeg" or fileext == "jpg"):
 
796
                        return LIBMTP_Filetype["JPEG"]
 
797
                elif (fileext == "jfif"):
 
798
                        return LIBMTP_Filetype["JFIF"]
 
799
                elif (fileext == "tif" or fileext == "tiff"):
 
800
                        return LIBMTP_Filetype["TIFF"]
 
801
                elif (fileext == "bmp"):
 
802
                        return LIBMTP_Filetype["BMP"]
 
803
                elif (fileext == "gif"):
 
804
                        return LIBMTP_Filetype["GIF"]
 
805
                elif (fileext == "pic" or fileext == "pict"):
 
806
                        return LIBMTP_Filetype["PICT"]
 
807
                elif (fileext == "png"):
 
808
                        return LIBMTP_Filetype["PNG"]
 
809
                elif (fileext == "wmf"):
 
810
                        return LIBMTP_Filetype["WINDOWSIMAGEFORMAT"]
 
811
                elif (fileext == "ics"):
 
812
                        return LIBMTP_Filetype["VCALENDAR2"]
 
813
                elif (fileext == "exe" or fileext == "com" or fileext == "bat"\
 
814
                      or fileext == "dll" or fileext == "sys"):
 
815
                        return LIBMTP_Filetype["WINEXEC"]
 
816
                elif (fileext == "aac"):
 
817
                        return LIBMTP_Filetype["AAC"]
 
818
                elif (fileext == "mp2"):
 
819
                        return LIBMTP_Filetype["MP2"]
 
820
                elif (fileext == "flac"):
 
821
                        return LIBMTP_Filetype["FLAC"]
 
822
                elif (fileext == "m4a"):
 
823
                        return LIBMTP_Filetype["M4A"]
 
824
                elif (fileext == "doc"):
 
825
                        return LIBMTP_Filetype["DOC"]
 
826
                elif (fileext == "xml"):
 
827
                        return LIBMTP_Filetype["XML"]
 
828
                elif (fileext == "xls"):
 
829
                        return LIBMTP_Filetype["XLS"]
 
830
                elif (fileext == "ppt"):
 
831
                        return LIBMTP_Filetype["PPT"]
 
832
                elif (fileext == "mht"):
 
833
                        return LIBMTP_Filetype["MHT"]
 
834
                elif (fileext == "jp2"):
 
835
                        return LIBMTP_Filetype["JP2"]
 
836
                elif (fileext == "jpx"):
 
837
                        return LIBMTP_Filetype["JPX"]
 
838
                else:
 
839
                        return LIBMTP_Filetype["UNKNOWN"]
 
840
 
 
841
        def send_file_from_file(self, source, target, parent=0, callback=None):
 
842
                """
 
843
                        Sends a file from the filesystem to the connected device
 
844
                        and stores it at the target filename inside the parent.
 
845
                
 
846
                        This will attempt to "guess" the filetype with 
 
847
                        find_filetype()
 
848
                
 
849
                        @type source: str
 
850
                        @param source: The path on the filesystem where the file resides
 
851
                        @type target: str
 
852
                        @param target: The target filename on the device
 
853
                        @type parent: int or 0
 
854
                        @param parent: The parent directory for the file to go
 
855
                         into; If 0, the file goes into main directory
 
856
                        @type callback: function or None
 
857
                        @param callback: The function provided to libmtp to 
 
858
                         receive callbacks from ptp. Callback function must
 
859
                         take two arguments, sent and total (in bytes)
 
860
                        @rtype: int
 
861
                        @return: The object ID of the new file
 
862
                """
 
863
 
 
864
                if (self.device == None):
 
865
                        raise NotConnected
 
866
 
 
867
                if (os.path.isfile(source) == False):
 
868
                        raise IOError
 
869
                        
 
870
                if (callback != None):
 
871
                        callback = Progressfunc(callback)
 
872
 
 
873
                metadata = LIBMTP_File(filename=target, \
 
874
                  filetype=self.find_filetype(source), \
 
875
                  filesize=os.stat(source).st_size)
 
876
 
 
877
                ret = self.mtp.LIBMTP_Send_File_From_File(self.device, source, \
 
878
                  ctypes.pointer(metadata), callback, None, parent)
 
879
 
 
880
                if (ret != 0):
 
881
                        self.debug_stack()
 
882
                        raise CommandFailed
 
883
 
 
884
                return metadata.item_id
 
885
                
 
886
        def send_track_from_file(self, source, target, metadata, parent=0, callback=None):
 
887
                """
 
888
                        Sends a track from the filesystem to the connected 
 
889
                        device
 
890
                        
 
891
                        @type source: str
 
892
                        @param source: The path where the track resides
 
893
                        @type target: str
 
894
                        @param target: The target filename on the device
 
895
                        @type metadata: LIBMTP_Track
 
896
                        @param metadata: The track metadata
 
897
                        @type parent: int or 0
 
898
                        @param parent: The parent directory for the track;
 
899
                         if 0, the track will be placed in the base dir.
 
900
                        @type callback: function or None
 
901
                        @param callback: The function provided to libmtp to
 
902
                         receive callbacks from ptp. Callback function must
 
903
                         take two arguments, sent and total (in bytes)
 
904
                        @rtype: int
 
905
                        @return: The object ID of the new track
 
906
                """
 
907
 
 
908
                if (self.device == None):
 
909
                        raise NotConnected
 
910
 
 
911
                if (os.path.exists(source) == None):
 
912
                        raise IOError
 
913
                
 
914
                if callback:
 
915
                        callback = Progressfunc(callback)
 
916
                
 
917
                metadata.filename = target
 
918
                metadata.parent_id = parent
 
919
                metadata.filetype = self.find_filetype(source)
 
920
                metadata.filesize = os.stat(source).st_size
 
921
 
 
922
                ret = self.mtp.LIBMTP_Send_Track_From_File(self.device, source, \
 
923
                  ctypes.pointer(metadata), callback, None, parent)
 
924
 
 
925
                if (ret != 0):
 
926
                        self.debug_stack()
 
927
                        raise CommandFailed
 
928
 
 
929
                return metadata.item_id
 
930
                                        
 
931
        def get_freespace(self):
 
932
                """
 
933
                        Returns the amount of free space on the connected device
 
934
                        @rtype: long
 
935
                        @return: The amount of free storage in bytes
 
936
                """
 
937
 
 
938
                if (self.device == None):
 
939
                        raise NotConnected
 
940
 
 
941
                self.mtp.LIBMTP_Get_Storage(self.device, 0)
 
942
                return self.device.contents.storage.contents.FreeSpaceInBytes
 
943
 
 
944
        def get_totalspace(self):
 
945
                """
 
946
                        Returns the total space on the connected device
 
947
                        @rtype: long
 
948
                        @return: The amount of total storage in bytes
 
949
                """
 
950
 
 
951
                if (self.device == None):
 
952
                        raise NotConnected
 
953
 
 
954
                self.mtp.LIBMTP_Get_Storage(self.device, 0)
 
955
                return self.device.contents.storage.contents.MaxCapacity
 
956
                
 
957
        def get_usedspace(self):
 
958
                """
 
959
                        Returns the amount of used space on the connected device
 
960
 
 
961
                        @rtype: long
 
962
                        @return: The amount of used storage in bytes
 
963
                """
 
964
 
 
965
                if (self.device == None):
 
966
                        raise NotConnected
 
967
 
 
968
                self.mtp.LIBMTP_Get_Storage(self.device, 0)
 
969
                storage = self.device.contents.storage.contents
 
970
                return (storage.MaxCapacity - storage.FreeSpaceInBytes)
 
971
 
 
972
        def get_usedspace_percent(self):
 
973
                """
 
974
                        Returns the amount of used space as a percentage
 
975
 
 
976
                        @rtype: float
 
977
                        @return: The percentage of used storage
 
978
                """
 
979
 
 
980
                if (self.device == None):
 
981
                        raise NotConnected
 
982
 
 
983
                self.mtp.LIBMTP_Get_Storage(self.device, 0)
 
984
                storage = self.device.contents.storage.contents
 
985
 
 
986
                # Why don't we call self.get_totalspace/self.get_usedspace 
 
987
                # here? That would require 3 *more* calls to 
 
988
                # LIBMTP_Get_Storage
 
989
                usedspace = storage.MaxCapacity - storage.FreeSpaceInBytes
 
990
                return ((float(usedspace) / float(storage.MaxCapacity)) * 100)
 
991
 
 
992
        def delete_object(self, object_id):
 
993
                """
 
994
                        Deletes the object off the connected device.
 
995
 
 
996
                        @type object_id: int
 
997
                        @param object_id: The unique object identifier
 
998
                """
 
999
 
 
1000
                if (self.device == None):
 
1001
                        raise NotConnected
 
1002
 
 
1003
                ret = self.mtp.LIBMTP_Delete_Object(self.device, object_id)
 
1004
 
 
1005
                if (ret != 0):
 
1006
                        self.debug_stack()
 
1007
                        raise CommandFailed
 
1008
 
 
1009
        def get_playlists(self):
 
1010
                """
 
1011
                        Returns a tuple filled with L{LIBMTP_Playlist} objects
 
1012
                        from the connected device.
 
1013
 
 
1014
                        The main gotcha of this function is that the tracks 
 
1015
                        variable of LIBMTP_Playlist isn't iterable (without
 
1016
                        segfaults), so, you have to iterate over the no_tracks
 
1017
                        (through range or xrange) and access it that way (i.e.
 
1018
                        tracks[track_id]). Kind of sucks.
 
1019
 
 
1020
                        @rtype: tuple
 
1021
                        @return: Tuple filled with LIBMTP_Playlist objects
 
1022
                """
 
1023
 
 
1024
                if (self.device == None):
 
1025
                        raise NotConnected
 
1026
 
 
1027
                playlists = self.mtp.LIBMTP_Get_Playlist_List(self.device)
 
1028
                ret = []
 
1029
                next = playlists
 
1030
 
 
1031
                while next:
 
1032
                        ret.append(next.contents)
 
1033
                        if (next.contents.next == None):
 
1034
                                break
 
1035
                        next = next.contents.next
 
1036
 
 
1037
                return ret
 
1038
 
 
1039
        def get_playlist(self, playlist_id):
 
1040
                """
 
1041
                        Returns a L{LIBMTP_Playlist} object of the requested
 
1042
                        playlist_id from the connected device
 
1043
 
 
1044
                        @type playlist_id: int
 
1045
                        @param playlist_id: The unique playlist identifier
 
1046
                        @rtype: LIBMTP_Playlist
 
1047
                        @return: The playlist object
 
1048
                """
 
1049
 
 
1050
                if (self.device == None):
 
1051
                        raise NotConnected
 
1052
 
 
1053
                try:
 
1054
                        ret = self.mtp.LIBMTP_Get_Playlist(self.device, playlist_id).contents
 
1055
                except ValueError:
 
1056
                        raise ObjectNotFound
 
1057
 
 
1058
                return ret
 
1059
 
 
1060
        def create_new_playlist(self, metadata, parent=0):
 
1061
                """
 
1062
                        Creates a new playlist based on the metadata object
 
1063
                        passed.
 
1064
 
 
1065
                        @type metadata: LIBMTP_Playlist
 
1066
                        @param metadata: A LIBMTP_Playlist object describing 
 
1067
                         the playlist
 
1068
                        @type parent: int or 0
 
1069
                        @param parent: The parent ID or 0 for base
 
1070
                        @rtype: int
 
1071
                        @return: The object ID of the new playlist
 
1072
                """
 
1073
 
 
1074
                if (self.device == None):
 
1075
                        raise NotConnected
 
1076
                
 
1077
                ret = self.mtp.LIBMTP_Create_New_Playlist(self.device, ctypes.pointer(metadata), parent)
 
1078
 
 
1079
                if (ret != 0):
 
1080
                        self.debug_stack()
 
1081
                        raise CommandFailed
 
1082
 
 
1083
                return metadata.playlist_id
 
1084
 
 
1085
        def update_playlist(self, metadata):
 
1086
                """
 
1087
                        Updates a playlist based on the supplied metadata. 
 
1088
                        
 
1089
                        When updating the tracks field in a playlist, this
 
1090
                        function will replace the playlist's tracks with 
 
1091
                        the tracks supplied in the metadata object. This
 
1092
                        means that the previous tracks in the playlist
 
1093
                        will be overwritten.
 
1094
 
 
1095
                        @type metadata: LIBMTP_Playlist
 
1096
                        @param metadata: A LIBMTP_Playlist object describing
 
1097
                         the updates to the playlist.
 
1098
                """
 
1099
                
 
1100
                if (self.device == None):
 
1101
                        raise NotConnected
 
1102
 
 
1103
                ret = self.mtp.LIBMTP_Update_Playlist(self.device, ctypes.pointer(metadata))
 
1104
 
 
1105
                if (ret != 0):
 
1106
                        self.debug_stack()
 
1107
                        raise CommandFailed
 
1108
 
 
1109
        def get_folder_list(self):
 
1110
                """
 
1111
                        Returns a pythonic dict of the folders on the
 
1112
                        device.
 
1113
 
 
1114
                        @rtype: dict
 
1115
                        @return: A dict of the folders on the device where
 
1116
                         the folder ID is the key.
 
1117
                """
 
1118
                
 
1119
                if (self.device == None):
 
1120
                        raise NotConnected
 
1121
 
 
1122
                folders = self.mtp.LIBMTP_Get_Folder_List(self.device)
 
1123
                next = folders
 
1124
                # List of folders, key being the folder ID
 
1125
                ret = {}                
 
1126
                # Iterate over the folders to grab the first-level parents
 
1127
                while True:
 
1128
                        next = next.contents
 
1129
                        scanned = True
 
1130
 
 
1131
                        # Check if this ID exists, if not, add it
 
1132
                        # and trigger a scan of the children
 
1133
                        if not (ret.has_key(next.folder_id)):
 
1134
                                ret[next.folder_id] = next
 
1135
                                scanned = False
 
1136
 
 
1137
                        if ((scanned == False) and (next.child)):
 
1138
                                ## Scan the children
 
1139
                                next = next.child
 
1140
 
 
1141
                        elif (next.sibling):
 
1142
                                ## Scan the siblings
 
1143
                                next = next.sibling
 
1144
 
 
1145
                        elif (next.parent_id != 0):
 
1146
                                ## If we have no children/siblings to visit,
 
1147
                                ## and we aren't at the parent, go back to
 
1148
                                ## the parent.
 
1149
                                next = self.mtp.LIBMTP_Find_Folder(folders, int(next.parent_id))
 
1150
                
 
1151
                        else:
 
1152
                                ## We have scanned everything, let's go home.
 
1153
                                break
 
1154
                
 
1155
                return ret
 
1156
 
 
1157
        def get_parent_folders(self):
 
1158
                """
 
1159
                        Returns a list of only the parent folders.
 
1160
                        @rtype: list
 
1161
                        @return: Returns a list of the parent folders
 
1162
                """
 
1163
                                
 
1164
                if (self.device == None):
 
1165
                        raise NotConnected
 
1166
 
 
1167
                folders = self.mtp.LIBMTP_Get_Folder_List(self.device)
 
1168
                next = folders
 
1169
                # A temporary holding space, this makes checking folder
 
1170
                # IDs easier
 
1171
                tmp = {}
 
1172
 
 
1173
                while True:
 
1174
                        next = next.contents
 
1175
 
 
1176
                        ## Check if this folder is in the dict
 
1177
                        if not (tmp.has_key(next.folder_id)):
 
1178
                                tmp[next.folder_id] = next
 
1179
 
 
1180
                        # Check for siblings
 
1181
                        if (next.sibling):
 
1182
                                ## Scan the sibling
 
1183
                                next = next.sibling
 
1184
                        else:
 
1185
                                ## We're done here.
 
1186
                                break
 
1187
 
 
1188
                ## convert the dict into a list
 
1189
                ret = []
 
1190
                for key in tmp:
 
1191
                        ret.append(tmp[key])
 
1192
 
 
1193
                return ret
 
1194
 
 
1195
        def create_folder(self, name, parent=0, storage=0):
 
1196
                """
 
1197
                        This creates a new folder in the parent. If the parent 
 
1198
                        is 0, it will go in the main directory.
 
1199
                        
 
1200
                        @type name: str
 
1201
                        @param name: The name for the folder
 
1202
                        @type parent: int
 
1203
                        @param parent: The parent ID or 0 for main directory
 
1204
                        @type storage: int
 
1205
                        @param storage: The storage id or 0 to create the new folder
 
1206
                                        on the primary storage
 
1207
                        @rtype: int
 
1208
                        @return: Returns the object ID of the new folder
 
1209
                """
 
1210
 
 
1211
                if (self.device == None):
 
1212
                        raise NotConnected
 
1213
                        
 
1214
                ret = self.mtp.LIBMTP_Create_Folder(self.device, name, parent, storage)
 
1215
 
 
1216
                if (ret == 0):
 
1217
                        self.debug_stack()
 
1218
                        raise CommandFailed 
 
1219
 
 
1220
                return ret
 
1221
 
 
1222
        def get_errorstack(self):
 
1223
                """
 
1224
                        Returns the connected device's errorstack from
 
1225
                        LIBMTP.
 
1226
                        @rtype: L{LIBMTP_Error}
 
1227
                        @return: An array of LIBMTP_Errors.
 
1228
                """
 
1229
 
 
1230
                if (self.device == None):
 
1231
                        raise NotConnected
 
1232
                
 
1233
                ret = self.mtp.LIBMTP_Get_Errorstack(self.device)
 
1234
 
 
1235
                if (ret != 0):
 
1236
                        raise CommandFailed
 
1237
 
 
1238
                return ret