|
Hi Jonas, Nice work. Transfering file works perfectly on my Samsung device with you patch. As you already noticed, I took another direction to implement this feature with pylibmtp. It´s not an easy way and it could take me some time to have something working correctly (have a look at http://bugs.gpodder.org/show_bug.cgi?id=9 for more detail). I also thought at the beginning about the alternative of calling mtp-tools directly like you do, but I conclude I would have problem to implement deletion (it is difficult to get the file listing back into gPodder) and to have a nice error handling (althought pylibmt is not better in this domain for the moment). However, I encourage you to go further in this direction. I really don't know if I'll success one day and when, and it would be nice if we could have an altenative. Keep us inform of your progress. Don't hesitate to ask me for advice or for testing your feature. In attachment, you'll find my patches for gPodder and pylibmtp in their actual status (without warranty ;)). I would be glade if you could try to test with your device (you'll find instruction on how to install on the wiki -> mtp-support). Regards, Jérôme Jonas Nilsson a écrit : TO Jérôme Chabod, |
=== modified file 'mtpTest.py'
--- mtpTest.py 2007-01-17 22:20:24 +0000
+++ mtpTest.py 2008-04-09 20:26:42 +0000
@@ -6,5 +6,12 @@
percent = (sent / total) * 100
print "Sent:" + str(sent) + " Total:" + str(total) + " (" + str(percent) + "%)"
-dev = pylibmtp.Device()
-fld = dev.getFileListing()
+dev = pylibmtp.Device(connect=False, autoload=False)
+print("created")
+dev.openSession()
+print("connected")
+print dev.getModelName()
+fld = dev.getTrackListing(callback=_callback)
+print fld
+dev.sendTrack("test.mp3", callback=_callback)
+dev.closeSession()
=== modified file 'pylibmtp.pxd'
--- pylibmtp.pxd 2007-02-27 21:22:07 +0000
+++ pylibmtp.pxd 2008-04-09 16:34:53 +0000
@@ -35,6 +35,17 @@
LIBMTP_FILETYPE_TEXT,
LIBMTP_FILETYPE_HTML,
LIBMTP_FILETYPE_UNKNOWN
+
+ ctypedef enum LIBMTP_error_number_t:
+ LIBMTP_ERROR_NONE,
+ LIBMTP_ERROR_GENERAL,
+ LIBMTP_ERROR_PTP_LAYER,
+ LIBMTP_ERROR_USB_LAYER,
+ LIBMTP_ERROR_MEMORY_ALLOCATION,
+ LIBMTP_ERROR_NO_DEVICE_ATTACHED,
+ LIBMTP_ERROR_STORAGE_FULL,
+ LIBMTP_ERROR_CONNECTING,
+ LIBMTP_ERROR_CANCELLED
# We do some of our structs first, because typdefs rely on them
ctypedef struct LIBMTP_device_entry_struct:
@@ -136,6 +147,7 @@
void LIBMTP_Release_Device(LIBMTP_mtpdevice_t*)
void LIBMTP_Dump_Device_Info(LIBMTP_mtpdevice_t*)
LIBMTP_mtpdevice_t *LIBMTP_Get_First_Device()
+ LIBMTP_error_number_t LIBMTP_Get_Connected_Devices(LIBMTP_mtpdevice_t **)
int LIBMTP_Detect_Descriptor(uint16_t*,uint16_t*)
## Device data accessors ##
@@ -145,12 +157,9 @@
char *LIBMTP_Get_Deviceversion(LIBMTP_mtpdevice_t*)
char *LIBMTP_Get_Friendlyname(LIBMTP_mtpdevice_t*)
int LIBMTP_Get_Batterylevel(LIBMTP_mtpdevice_t*, uint8_t*, uint8_t*)
- int LIBMTP_Get_Storageinfo(
- LIBMTP_mtpdevice_t*,
- uint64_t*,
- uint64_t*,
- char**,
- char**)
+ int LIBMTP_Get_Storage(
+ LIBMTP_mtpdevice_t*,
+ int)
## Device data mutators
int LIBMTP_Set_Friendlyname(LIBMTP_mtpdevice_t*, char*)
=== modified file 'pylibmtp.pyx'
--- pylibmtp.pyx 2007-03-22 23:11:36 +0000
+++ pylibmtp.pyx 2008-04-09 17:08:00 +0000
@@ -388,6 +388,8 @@
cdef object fileNames # Maps filenames to itemIds
cdef object trackFolders # Ditto, but for files
cdef object trackAlbums # Maps tracks to albums
+
+ cdef LIBMTP_error_number_t ERROR_NUMBER
## General class / object methods {{{
@@ -466,8 +468,15 @@
"""
Connects to the device
"""
+ cdef LIBMTP_mtpdevice_t **devs
if self.dev == NULL:
self.dev = LIBMTP_Get_First_Device()
+ #ret = LIBMTP_Get_Connected_Devices(devs)
+ #if ret != self.ERROR_NUMBER.LIBMTP_ERROR_NONE:
+ #if ret != 0:
+ # raise MTPError("Can't connect! Error %i", ret )
+ #self.dev = devs[0]
+ print("OK")
def closeSession(self):
"""
@@ -531,7 +540,7 @@
cdef uint64_t total, free
cdef char *desc, *label
- if LIBMTP_Get_Storageinfo(self.dev, &total, &free, &desc, &label) != 0:
+ if LIBMTP_Get_Storage(self.dev, LIBMTP_STORAGE_SORTBY_NOTSORTED) != 0:
raise MTPError("An error of some sort occurred")
storage = {
=== added file 'setup.py'
--- setup.py 1970-01-01 00:00:00 +0000
+++ setup.py 2008-04-09 16:34:53 +0000
@@ -0,0 +1,12 @@
+#!/usr/bin/python
+
+from distutils.core import setup
+from distutils.extension import Extension
+from Pyrex.Distutils import build_ext
+setup(
+ name = "PyrexGuide",
+ ext_modules=[
+ Extension("pylibmtp", ["pylibmtp.pyx"], libraries = ["mtp"])
+ ],
+ cmdclass = {'build_ext': build_ext}
+)
Index: src/gpodder/libgpodder.py
===================================================================
--- src/gpodder/libgpodder.py (révision 660)
+++ src/gpodder/libgpodder.py (copie de travail)
@@ -125,6 +125,8 @@
return _('iPod')
elif self.config.device_type == 'filesystem':
return _('MP3 player')
+ elif self.config.device_type == 'mtp':
+ return _('MP3 player')
else:
log( 'Warning: Called get_device_name() when no device was selected.', sender = self)
return '(unknown device)'
@@ -368,3 +370,4 @@
# Global, singleton gPodderLib object
gl = gPodderLib()
+
Index: src/gpodder/sync.py
===================================================================
--- src/gpodder/sync.py (révision 660)
+++ src/gpodder/sync.py (copie de travail)
@@ -31,7 +31,14 @@
from gpodder.liblogger import log
from gpodder.libgpodder import gl
+import gtk
+
try:
+ import pylibmtp
+except:
+ log('(gpodder.sync) Could not find pylibmtp. Consult gPodder documentation to find out how to install it.')
+
+try:
import gpod
except:
log('(gpodder.sync) Could not find gpod')
@@ -64,6 +71,8 @@
return iPodDevice()
elif device_type == 'filesystem':
return MP3PlayerDevice()
+ elif device_type == 'mtp':
+ return mtpDevice()
else:
return None
@@ -610,3 +619,121 @@
log('Error while parsing "%s".', log_file, sender=self)
return True
+
+class mtpDevice(Device):
+
+ def __init__(self):
+ Device.__init__(self)
+ self.__model_name = None
+
+ # create the device without connecting. Nothing wrong can append
+ # since no communication is done with the device now.
+ self.__MTPDevice = pylibmtp.Device(connect=False, autoload=False)
+
+ def __callback(self, sent, total):
+ info = '%.2f done' % (float(sent)/float(total)*100)
+ #self.notify('progress', sent ,total)
+ log(info)
+
+ def get_name(self):
+ """
+ this function try to find a nice name for the device.
+ First, it tries to find a friendly (user assigned) name
+ (this name can be set by other application and is stored on the device).
+ if no friendly name was assign, it tries to get the model (vendor) name.
+ If no name as all is found, a genreic one is returned.
+
+ Once found, the name is cached internaly to prevent reading again the device
+
+ return
+ the name of the device
+ """
+
+ if self.__model_name:
+ return self.__model_name
+
+ self.__model_name = self.__MTPDevice.getFriendlyName()
+ if not self.__model_name or self.__model_name == "?????":
+ self.__model_name = self.__MTPDevice.getModelName()
+ if not self.__model_name:
+ self.__model_name = "MTP device"
+
+ return self.__model_name
+
+ def open(self):
+ log("opening the MTP device", sender=self)
+ self.notify('status', _('Opening the MTP device'), )
+
+ #FIXME: mofify libmtp to use libmtp method LIBMTP_Get_Connected_Devices instead
+ # it delivers an error id which can be used to identify the problem
+ # like device not connected (maybe add a method to map errornum to exception in pylibmtp)
+ try:
+ self.__MTPDevice.openSession()
+ except Exception, exc:
+ log('unable to find an MTP device (%s)', exc, sender=self)
+ return False
+
+ self.notify('status', _('%s opened') % self.get_name())
+ return True
+
+ def close(self):
+ log("closing %s", self.get_name(), sender=self)
+ self.notify('status', _('Closing %s') % self.get_name())
+
+ try:
+ self.__MTPDevice.closeSession()
+ except Exception, exc:
+ log('unable to close %s (%s)', self.get_name(), exc, sender=self)
+ return False
+
+ self.notify('status', _('%s closed') % self.get_name())
+ self.notify('done')
+ return True
+
+ def add_track(self, episode):
+ self.notify('status', _('Adding %s') % episode.title)
+ log("sending " + episode.local_filename() + '(' + episode.title + ')', sender=self)
+
+ # FIXME: modify pylibmtp to raise an Exception according to the error
+ # returned by LIBMTP_Send_File_From_File
+ try:
+ self.__MTPDevice.sendFile( episode.local_filename(), callback=self.__callback)
+ #util.idle_add(self.__MTPDevice.sendFile, filename, callback= None)
+ except Exception, exc:
+ log('unable add episode %s (%s)', episode.title, exc, sender=self)
+ return False
+
+ return True
+
+ def remove_track(self, sync_track):
+ self.notify('status', _('Removing %s') % sync_track.mtptrack.filename)
+ log("removing " + sync_track.mtptrack.filename, sender=self)
+
+ # FIXME: modify pylibmtp to raise an Exception according to the error
+ # returned by LIBMTP_Send_File_From_File
+ try:
+ self.__MTPDevice._deleteObject(sync_track.mtptrack.itemId)
+ except Exception, exc:
+ log('unable remove file %s (%s)', sync_track.mtptrack.filename, exc, sender=self)
+
+ log(sync_track.mtptrack.filename + ' removed', sender=self)
+
+ def get_all_tracks(self):
+ try:
+ listing = self.__MTPDevice.getTrackListing(reload=False, callback=self.__callback)
+ except Exception, exc:
+ log('unable to get file listing %s (%s)', exc, sender=self)
+
+ tracks = []
+ for track in listing.values():
+ title = track.title
+ if track.artist and track.artist <> "" : title += ' (' + track.artist + ')'
+ if not title or title=="": title=track.filename
+ if len(title) > 50: title = title[0:49] + '...'
+ length = track.filesize
+ #FIXME: age_in_days = util.file_age_in_days(filename)
+ age_in_days = 0
+ modified = util.file_age_to_string(age_in_days)
+ t = SyncTrack(title, length, modified, mtptrack=track, playcount=0)
+ tracks.append(t)
+ return tracks
_______________________________________________ gpodder-devel mailing list [email protected] https://lists.berlios.de/mailman/listinfo/gpodder-devel
