1
# -*- coding: utf-8 -*-
4
# A Ctypes wrapper to LibMTP
5
# Developed by: Nick Devito (nick@nick125.com)
7
# Released under the GPLv3 or later.
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:
14
# Jérôme Chabod <jerome.chabod@ifrance.com>
15
# Justin Forest <justin.forest@gmail.com>
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).
21
# See http://gpodder.org/bug/307 for details.
25
PyMTP is a pythonic wrapper around libmtp, making it a bit more
26
friendly to use in python
28
Example Usage (or see examples/):
33
>>> print mtp.get_devicename()
44
__VERSION_TUPLE__ = (__VERSION_MAJOR__, __VERSION_MINOR__, __VERSION_MACRO__)
45
__AUTHOR__ = "Nick Devito (nick@nick125.com)"
53
# NOTE: This code *may* work on windows, I don't have a win32 system to test
55
_module_path = ctypes.util.find_library("mtp")
56
_libmtp = ctypes.CDLL(_module_path)
61
class NoDeviceConnected(Exception):
63
Raised when there isn't a device connected to the USB bus
68
class AlreadyConnected(Exception):
70
Raised when we're already connected to a device and there is
76
class UnsupportedCommand(Exception):
78
Raised when the connected device does not support the command
84
class CommandFailed(Exception):
86
Raised when the connected device returned an error when trying
92
class NotConnected(Exception):
94
Raised when a command is called and the device is not connected
99
class ObjectNotFound(Exception):
101
Raised when a command tries to get an object that doesn't exist
107
# End Error Definitions
111
# Data Model Definitions
114
class LIBMTP_Error(ctypes.Structure):
117
Contains the ctypes structure for LIBMTP_error_t
121
return self.errornumber
123
LIBMTP_Error._fields_ = [("errornumber", ctypes.c_int),
124
("error_text", ctypes.c_char_p),
125
("next", ctypes.POINTER(LIBMTP_Error))]
127
class LIBMTP_DeviceStorage(ctypes.Structure):
130
Contains the ctypes structure for LIBMTP_devicestorage_t
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))]
148
class LIBMTP_MTPDevice(ctypes.Structure):
151
Contains the ctypes structure for LIBMTP_mtpdevice_t
155
return self.interface_number
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))]
174
class LIBMTP_File(ctypes.Structure):
177
Contains the ctypes structure for LIBMTP_file_t
181
return "%s (%s)" % (self.filename, self.item_id)
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))]
191
class LIBMTP_Track(ctypes.Structure):
194
Contains the ctypes structure for LIBMTP_track_t
198
return "%s - %s (%s)" % (self.artist, self.title, self.item_id)
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))]
224
class LIBMTP_Playlist(ctypes.Structure):
227
Contains the ctypes structure for LIBMTP_playlist_t
231
self.tracks = ctypes.pointer(ctypes.c_uint32(0))
232
self.no_tracks = ctypes.c_uint32(0)
234
return "%s (%s)" % (self.name, self.playlist_id)
238
This allows the playlist object to act like a list with
241
for track in xrange(self.no_tracks):
242
yield self.tracks[track]
244
def __getitem__(self, key):
246
This allows the playlist to return tracks like a list
249
if (key > (self.no_tracks - 1)):
252
return self.tracks[key]
254
def __setitem__(self, key, value):
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.
261
if (key > (self.no_tracks - 1)):
264
self.tracks[key] = value
266
def __delitem__(self, key):
268
This allows the user to delete an object
272
if (key > (self.no_tracks - 1)):
275
for i in range(key, (self.no_tracks - 1)):
276
self.tracks[i] = self.tracks[i + 1]
280
def append(self, value):
282
This function appends a track to the end of the tracks
285
if (self.tracks == None):
286
self.tracks = ctypes.pointer(ctypes.c_uint32(0))
289
self.tracks[(self.no_tracks - 1)] = value
293
This returns the number of tracks in the playlist
296
return self.no_tracks
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))]
306
class LIBMTP_Folder(ctypes.Structure):
309
Contains the ctypes structure for LIBMTP_folder_t
313
return "%s (%s)" % (self.name, self.folder_id)
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))]
322
# Abstracted from libmtp 0.2.6.1's libmtp.h. This must be kept in sync.
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),
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),
382
# End Data Model Definitions
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)
409
# End Type Definitions
415
This is the main wrapper around libmtp
420
Initializes the MTP object
427
self.mtp.LIBMTP_Init()
430
def debug_stack(self):
432
Checks if __DEBUG__ is set, if so, prints and clears the
440
self.mtp.LIBMTP_Dump_Errorstack()
441
#self.mtp.LIBMTP_Clear_Errorstack()
445
Initializes the MTP connection to the device
452
if (self.device != None):
453
raise AlreadyConnected
455
self.device = self.mtp.LIBMTP_Get_First_Device()
459
raise NoDeviceConnected
461
def disconnect(self):
463
Disconnects the MTP device and deletes the self.device object
469
if (self.device == None):
472
self.mtp.LIBMTP_Release_Device(self.device)
476
def get_devicename(self):
478
Returns the connected device's 'friendly name' (or
479
known as the owner name)
482
@return: The connected device's 'friendly name'
485
if (self.device == None):
488
return self.mtp.LIBMTP_Get_Friendlyname(self.device)
490
def set_devicename(self, name):
492
Changes the connected device's 'friendly name' to name
495
@param name: The name to change the connected device's
501
if (self.device == None):
504
ret = self.mtp.LIBMTP_Set_Friendlyname(self.device, name)
509
def get_serialnumber(self):
511
Returns the connected device's serial number
514
@return: The connected device's serial number
517
if (self.device == None):
520
return self.mtp.LIBMTP_Get_Serialnumber(self.device)
522
def get_manufacturer(self):
524
Return the connected device's manufacturer
527
@return: The connected device's manufacturer
529
if (self.device == None):
532
return self.mtp.LIBMTP_Get_Manufacturername(self.device)
534
def get_batterylevel(self):
536
Returns the connected device's maximum and current
540
@return: The connected device's maximum and current
541
battery levels ([0] is maximum, [1] is current)
544
if (self.device == None):
547
maximum_level = ctypes.c_uint8()
548
current_level = ctypes.c_uint8()
550
ret = self.mtp.LIBMTP_Get_Batterylevel(self.device, \
551
ctypes.byref(maximum_level), ctypes.byref(current_level))
556
return (maximum_level.value, current_level.value)
558
def get_modelname(self):
560
Returns the connected device's model name (such
564
@return: The connected device's model name
567
if (self.device == None):
570
return self.mtp.LIBMTP_Get_Modelname(self.device)
572
def get_deviceversion(self):
574
Returns the connected device's version (such as
575
firmware/hardware version)
578
@return: Returns the connect device's version
582
if (self.device == None):
585
return self.mtp.LIBMTP_Get_Deviceversion(self.device)
587
def get_filelisting(self, callback=None):
589
Returns the connected device's file listing as a tuple,
590
containing L{LIBMTP_File} objects.
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)
597
@return: Returns the connect device file listing tuple
600
if (self.device == None):
603
if (callback != None):
604
callback = Progressfunc(callback)
606
files = self.mtp.LIBMTP_Get_Filelisting_With_Callback(self.device, callback, None)
611
ret.append(next.contents)
612
if (next.contents.next == None):
614
next = next.contents.next
618
def get_filetype_description(self, filetype):
620
Returns the description of the filetype
623
@param filetype: The MTP filetype integer
625
@return: The file type information
628
if (self.device == None):
631
return self.mtp.LIBMTP_Get_Filetype_Description(filetype)
633
def get_file_metadata(self, file_id):
635
Returns the file metadata from the connected device
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.
642
@param file_id: The unique numeric file id
644
@return: The file metadata
647
if (self.device == None):
650
ret = self.mtp.LIBMTP_Get_Filemetadata(self.device, file_id)
652
if (not hasattr(ret, 'contents')):
657
def get_tracklisting(self, callback=None):
659
Returns tracks from the connected device
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)
666
@return: Returns a tuple full of L{LIBMTP_Track} objects
669
if (self.device == None):
672
if (callback != None):
673
callback = Progressfunc(callback)
675
tracks = self.mtp.LIBMTP_Get_Tracklisting_With_Callback(self.device, callback, None)
680
ret.append(next.contents)
681
next = next.contents.next
684
def get_track_metadata(self, track_id):
686
Returns the track metadata
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.
692
@param track_id: The unique numeric track id
693
@rtype: L{LIBMTP_Track}
694
@return: The track metadata
697
if (self.device == None):
700
ret = self.mtp.LIBMTP_Get_Trackmetadata(self.device, track_id)
702
if (not hasattr(ret, 'contents')):
707
def get_file_to_file(self, file_id, target, callback=None):
709
Downloads the file from the connected device and stores it at the
713
@param file_id: The unique numeric file id
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)
722
if (self.device == None):
725
if (callback != None):
726
callback = Progressfunc(callback)
728
ret = self.mtp.LIBMTP_Get_File_To_File(self.device, file_id, target, callback, None)
734
def get_track_to_file(self, track_id, target, callback=None):
736
Downloads the track from the connected device and stores it at
740
@param track_id: The unique numeric track id
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)
749
if (self.device == None):
752
if (callback != None):
753
callback = Progressfunc(callback)
755
ret = self.mtp.LIBMTP_Get_Track_To_File(self.device, track_id, target, callback, None)
761
def find_filetype(self, filename):
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.
768
@param filename: The filename to attempt to guess from
770
@return: The integer of the Filetype
773
fileext = filename.lower().split(".")[-1]
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"]
839
return LIBMTP_Filetype["UNKNOWN"]
841
def send_file_from_file(self, source, target, parent=0, callback=None):
843
Sends a file from the filesystem to the connected device
844
and stores it at the target filename inside the parent.
846
This will attempt to "guess" the filetype with
850
@param source: The path on the filesystem where the file resides
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)
861
@return: The object ID of the new file
864
if (self.device == None):
867
if (os.path.isfile(source) == False):
870
if (callback != None):
871
callback = Progressfunc(callback)
873
metadata = LIBMTP_File(filename=target, \
874
filetype=self.find_filetype(source), \
875
filesize=os.stat(source).st_size)
877
ret = self.mtp.LIBMTP_Send_File_From_File(self.device, source, \
878
ctypes.pointer(metadata), callback, None, parent)
884
return metadata.item_id
886
def send_track_from_file(self, source, target, metadata, parent=0, callback=None):
888
Sends a track from the filesystem to the connected
892
@param source: The path where the track resides
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)
905
@return: The object ID of the new track
908
if (self.device == None):
911
if (os.path.exists(source) == None):
915
callback = Progressfunc(callback)
917
metadata.filename = target
918
metadata.parent_id = parent
919
metadata.filetype = self.find_filetype(source)
920
metadata.filesize = os.stat(source).st_size
922
ret = self.mtp.LIBMTP_Send_Track_From_File(self.device, source, \
923
ctypes.pointer(metadata), callback, None, parent)
929
return metadata.item_id
931
def get_freespace(self):
933
Returns the amount of free space on the connected device
935
@return: The amount of free storage in bytes
938
if (self.device == None):
941
self.mtp.LIBMTP_Get_Storage(self.device, 0)
942
return self.device.contents.storage.contents.FreeSpaceInBytes
944
def get_totalspace(self):
946
Returns the total space on the connected device
948
@return: The amount of total storage in bytes
951
if (self.device == None):
954
self.mtp.LIBMTP_Get_Storage(self.device, 0)
955
return self.device.contents.storage.contents.MaxCapacity
957
def get_usedspace(self):
959
Returns the amount of used space on the connected device
962
@return: The amount of used storage in bytes
965
if (self.device == None):
968
self.mtp.LIBMTP_Get_Storage(self.device, 0)
969
storage = self.device.contents.storage.contents
970
return (storage.MaxCapacity - storage.FreeSpaceInBytes)
972
def get_usedspace_percent(self):
974
Returns the amount of used space as a percentage
977
@return: The percentage of used storage
980
if (self.device == None):
983
self.mtp.LIBMTP_Get_Storage(self.device, 0)
984
storage = self.device.contents.storage.contents
986
# Why don't we call self.get_totalspace/self.get_usedspace
987
# here? That would require 3 *more* calls to
989
usedspace = storage.MaxCapacity - storage.FreeSpaceInBytes
990
return ((float(usedspace) / float(storage.MaxCapacity)) * 100)
992
def delete_object(self, object_id):
994
Deletes the object off the connected device.
997
@param object_id: The unique object identifier
1000
if (self.device == None):
1003
ret = self.mtp.LIBMTP_Delete_Object(self.device, object_id)
1009
def get_playlists(self):
1011
Returns a tuple filled with L{LIBMTP_Playlist} objects
1012
from the connected device.
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.
1021
@return: Tuple filled with LIBMTP_Playlist objects
1024
if (self.device == None):
1027
playlists = self.mtp.LIBMTP_Get_Playlist_List(self.device)
1032
ret.append(next.contents)
1033
if (next.contents.next == None):
1035
next = next.contents.next
1039
def get_playlist(self, playlist_id):
1041
Returns a L{LIBMTP_Playlist} object of the requested
1042
playlist_id from the connected device
1044
@type playlist_id: int
1045
@param playlist_id: The unique playlist identifier
1046
@rtype: LIBMTP_Playlist
1047
@return: The playlist object
1050
if (self.device == None):
1054
ret = self.mtp.LIBMTP_Get_Playlist(self.device, playlist_id).contents
1056
raise ObjectNotFound
1060
def create_new_playlist(self, metadata, parent=0):
1062
Creates a new playlist based on the metadata object
1065
@type metadata: LIBMTP_Playlist
1066
@param metadata: A LIBMTP_Playlist object describing
1068
@type parent: int or 0
1069
@param parent: The parent ID or 0 for base
1071
@return: The object ID of the new playlist
1074
if (self.device == None):
1077
ret = self.mtp.LIBMTP_Create_New_Playlist(self.device, ctypes.pointer(metadata), parent)
1083
return metadata.playlist_id
1085
def update_playlist(self, metadata):
1087
Updates a playlist based on the supplied metadata.
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.
1095
@type metadata: LIBMTP_Playlist
1096
@param metadata: A LIBMTP_Playlist object describing
1097
the updates to the playlist.
1100
if (self.device == None):
1103
ret = self.mtp.LIBMTP_Update_Playlist(self.device, ctypes.pointer(metadata))
1109
def get_folder_list(self):
1111
Returns a pythonic dict of the folders on the
1115
@return: A dict of the folders on the device where
1116
the folder ID is the key.
1119
if (self.device == None):
1122
folders = self.mtp.LIBMTP_Get_Folder_List(self.device)
1124
# List of folders, key being the folder ID
1126
# Iterate over the folders to grab the first-level parents
1128
next = next.contents
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
1137
if ((scanned == False) and (next.child)):
1138
## Scan the children
1141
elif (next.sibling):
1142
## Scan the siblings
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
1149
next = self.mtp.LIBMTP_Find_Folder(folders, int(next.parent_id))
1152
## We have scanned everything, let's go home.
1157
def get_parent_folders(self):
1159
Returns a list of only the parent folders.
1161
@return: Returns a list of the parent folders
1164
if (self.device == None):
1167
folders = self.mtp.LIBMTP_Get_Folder_List(self.device)
1169
# A temporary holding space, this makes checking folder
1174
next = next.contents
1176
## Check if this folder is in the dict
1177
if not (tmp.has_key(next.folder_id)):
1178
tmp[next.folder_id] = next
1180
# Check for siblings
1188
## convert the dict into a list
1191
ret.append(tmp[key])
1195
def create_folder(self, name, parent=0, storage=0):
1197
This creates a new folder in the parent. If the parent
1198
is 0, it will go in the main directory.
1201
@param name: The name for the folder
1203
@param parent: The parent ID or 0 for main directory
1205
@param storage: The storage id or 0 to create the new folder
1206
on the primary storage
1208
@return: Returns the object ID of the new folder
1211
if (self.device == None):
1214
ret = self.mtp.LIBMTP_Create_Folder(self.device, name, parent, storage)
1222
def get_errorstack(self):
1224
Returns the connected device's errorstack from
1226
@rtype: L{LIBMTP_Error}
1227
@return: An array of LIBMTP_Errors.
1230
if (self.device == None):
1233
ret = self.mtp.LIBMTP_Get_Errorstack(self.device)