coldfix / udiskie

Automounter for removable media
MIT License
877 stars 54 forks source link

[Feature Request] MTP support #34

Open roman-holovin opened 10 years ago

roman-holovin commented 10 years ago

Automounting mtp-enabed devices using libmtp or any other mtp backend

coldfix commented 10 years ago

Sounds promising. Do you have any experience with this? I'm afraid, I don't know anything about libmtp. Is there a dbus interface or a python binding or will it require writing a C(ython) binding manually? If you want to speed things up, you (or someone else) could start development. Otherwise, I will postpone this for possibly quite a long time, since I don't even have any mtp device.

coldfix commented 7 years ago

I opened an issue on storaged repo, hope it can be implemented there.

Edit: See here: https://github.com/storaged-project/udisks/issues/187

tsutsarin commented 7 years ago

Hello @coldfix, do you support MTP already? Or have same tips and pointers on it? Any status on issue?

coldfix commented 7 years ago

Nope, sorry. I'd rather have it implemented on the udisks side and continue to use udiskie only as udisks-client (unless you can propose a somewhat clean patch for udiskie that works on the user level). However, I currently don't have the time to read into libmtp or propose a udisks interface+patch, so don't hold back if anyone wants to step in.

alex3kov commented 5 years ago

For anyone interested: android-file-transfer-linux works very well for mounting MTP over FUSE, way better than libmtp-based programs I tried (gvfs, mtpfs).
There is an open ticket to introduce python bindings to android-file-transfer-linux, if it ever happens I can work on udiskie patches.

coldfix commented 5 years ago

That would be great!

For the record, the udisks team seems to be unwilling to add mtp to the udisks featureset itself.

set-soft commented 4 years ago

Hi!

udiskie is really nice @coldfix thanks! I'm also missing MTP support, I know nothing about D-Bus, but if I start dbus-monitor and plug my cellphone I see:

signal time=1591045012.179222 sender=:1.26 -> destination=(null destination) serial=2858 path=/org/gtk/Private/RemoteVolumeMonitor; interface=org.gtk.Private.RemoteVolumeMonitor; member=VolumeAdded

   string "org.gtk.vfs.MTPVolumeMonitor"
   string "0x7fd91c010800"
   struct {
      string "0x7fd91c010800"
      string "SAMSUNG Android"
      string "multimedia-player"
      string ". GThemedIcon multimedia-player-symbolic multimedia-symbolic multimedia-player multimedia"
      string ""
      string "mtp://SAMSUNG_SAMSUNG_Android_420329bed4df5200/"
      boolean true
      boolean true
      string ""
      string ""
      array [
         dict entry(
            string "unix-device"
            string "/dev/bus/usb/002/043"
         )
      ]
      string ""
      array [
      ]
   }

And it contains all the needed data:

  1. It says a volume was added (member=VolumeAdded)
  2. It says this is an MTP device (mtp://...)
  3. It provides a name to identify it ("SAMSUNG Android")
  4. It says the bus and device (/dev/bus/usb/002/043)

So you just need to:

  1. Create a mount point
  2. Invoke jmtpfs tool:
    jmtpfs -device=BUS,DEVICE MOUNT_POINT
  3. When finished just:
    fusermount -u MOUNT_POINT

It works really well for me, and you could make it configurable allowing the user to replace jmtpfs by another tool.

I don't know who is sending this message, but I'm not using Gnome nor KDE. Seems to be some very basic functionality. And doesn't look too complex to implement. What do you think?

set-soft commented 4 years ago

Oh, BTW, here is an example using dbus-python:

from gi.repository import GLib

import dbus
import dbus.mainloop.glib
import re

def mountDetected(sender, mount_id, data):
    name=data[1]
    uri=data[5]
    device=data[10]['unix-device']
    if uri[:6] == 'mtp://':
       m = re.search(r'\/dev\/bus\/usb\/(\d+)/(\d+)', device)
       if m:
          bus = m.group(1)
          dev = m.group(2)
          print "To mount "+name+" use:"
          print "jmtpfs -device={},{} MOUNT_POINT".format(bus, dev)

dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

sessionbus = dbus.SessionBus()
sessionbus.add_signal_receiver(signal_name="VolumeAdded",
                               dbus_interface="org.gtk.Private.RemoteVolumeMonitor",
                               path="/org/gtk/Private/RemoteVolumeMonitor",
                               bus_name=None,
                               handler_function=mountDetected)

loop = GLib.MainLoop()
loop.run()
coldfix commented 4 years ago

Hi!

udiskie is really nice @coldfix thanks! I'm also missing MTP support, I know nothing about D-Bus, but if I start dbus-monitor and plug my cellphone I see:

signal time=1591045012.179222 sender=:1.26 -> destination=(null destination) serial=2858 path=/org/gtk/Private/RemoteVolumeMonitor; interface=org.gtk.Private.RemoteVolumeMonitor; member=VolumeAdded

   string "org.gtk.vfs.MTPVolumeMonitor"
   string "0x7fd91c010800"
   struct {
      string "0x7fd91c010800"
      string "SAMSUNG Android"
      string "multimedia-player"
      string ". GThemedIcon multimedia-player-symbolic multimedia-symbolic multimedia-player multimedia"
      string ""
      string "mtp://SAMSUNG_SAMSUNG_Android_420329bed4df5200/"
      boolean true
      boolean true
      string ""
      string ""
      array [
         dict entry(
            string "unix-device"
            string "/dev/bus/usb/002/043"
         )
      ]
      string ""
      array [
      ]
   }

And it contains all the needed data:

1. It says a volume was added (member=VolumeAdded)

2. It says this is an MTP device (mtp://...)

3. It provides a name to identify it ("SAMSUNG Android")

4. It says the bus and device (/dev/bus/usb/002/043)

Hi,

many thanks, I didn't know about this dbus interface. With such a binding it should be relatively easy to add support in udiskie. I guess this depends on gvfs(?)

So you just need to:

1. Create a mount point

2. Invoke jmtpfs tool:

I would hope this dbus interface also has methods for mounting, but I haven't checked yet.

jmtpfs -device=BUS,DEVICE MOUNT_POINT
1. When finished just:
fusermount -u MOUNT_POINT

It works really well for me, and you could make it configurable allowing the user to replace jmtpfs by another tool.

I don't know who is sending this message, but I'm not using Gnome nor KDE. Seems to be some very basic functionality. And doesn't look too complex to implement. What do you think?

Looks like it's gvfs. It's not that basic, but one could give it a try as optional dependency. (Actually I think udiskie probably better fits the niche of users who are willing to install udisks but don't necessarily want gvfs).

set-soft commented 4 years ago

Looks like it's gvfs. It's not that basic, but one could give it a try as optional dependency. (Actually I think udiskie probably better fits the niche of users who are willing to install udisks but don't necessarily want gvfs).

I can confirm gvfs is running on my system:

$ systemctl | grep gvfs
run-user-1000-gvfs.mount         loaded active mounted   /run/user/1000/gvfs

The good thing is that this is all optional, no extra Python packages are needed.

At low level libmtp seems to be hooking udev. GIO MTP backend uses libmtp. Various Python bindings for libmtp seems to be available. Not sure if any of them can inform about a new device plugged without needing to poll.

coldfix commented 4 years ago

So I took a quick look. It seems this is an undocumented private interface of gvfs-mtp (org.gtk.Private.RemoteVolumeMonitor) for use by other gvfs tools. So this will probably take some reading the source gvfs source code. It has a method VolumeMount that could be useful, but I'm actually not even sure whether this will mount an actual fuse filesystem or make the device just visible within some gvfs scope.

In any case, it doesn't seem to know about mount points created by jmtpfs (execute its List method and observe that the mountpoint isn't listed). So its only use for us seems to be listing available devices.

You mention

GIO MTP backend uses libmtp. Various Python bindings for libmtp seems to be available.

What gio mtp backend and python bindings? Can you paste some links?

set-soft commented 4 years ago

but I'm actually not even sure whether this will mount an actual fuse filesystem or make the device just visible within some gvfs scope.

When I used gnome-shell something that annoyed me was that in order to do anything with an MTP device I was forced to use nautilus (well, at least worked, KDE+dolphin didn't). So I think this is only visible inside gvfs, not really mounted.

So its only use for us seems to be listing available devices.

Yes, list and report the connection.

What gio mtp backend

gvfs: "GIO - GLib Input, Output and Streaming Library MTP Backend" https://github.com/gicmo/gvfs/blob/master/daemon/gvfsbackendmtp.c

and python bindings?

Just search for "python libmtp" on Google (PyMTP, mtpy, python-mtp, etc.)

set-soft commented 4 years ago

Ok, this is really low level, at least for Python ;-)

import pyudev

mounted = {}

def log_event(action, device):
    if action == 'add' and device.get('ID_MTP_DEVICE'):  # remove, bind, unbind
       path = str(device)
       print('\nAdding: '+path)
       bus = device.get('BUSNUM')
       dev = device.get('DEVNUM')
       #  for a in device.properties:
       #      print(a+' -> '+device.get(a))
       name = device.get('ID_SERIAL')
       print("To mount "+name+" use:")
       print("jmtpfs -device={},{} MOUNT_POINT".format(bus, dev))
       mounted[path] = 1
    elif action == 'remove':  # remove, bind, unbind
       name = str(device)
       try:
           if mounted[name]:
              print('\nRemoving: '+name)
              mounted[name] = 0
       except KeyError:
           pass

context = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(context)
monitor.filter_by('usb')
observer = pyudev.MonitorObserver(monitor, log_event)
observer.start()
input("Press Enter to stop ...\n")
observer.stop()

This is even lower level than what gvfs-mtp-volume-monitor does (uses gudev on top of udev).

coldfix commented 4 years ago

Nice! You seem to know more about this than me :) A pyudev based solution might also be a good option.

By the way: mounting via gvfs should also provide a fuse filesystem in /run/user/UID/gvfs, see here.

set-soft commented 4 years ago

Nice! You seem to know more about this than me :) A pyudev based solution might also be a good option.

:-) (Google "knows" more than us ;-)

By the way: mounting via gvfs should also provide a fuse filesystem in /run/user/UID/gvfs, see here.

Thanks for the tip!

ghost commented 1 year ago

Uhm any news on this?

android-file-transfer now also has python bindings

spikespaz commented 1 year ago

We would all appreciate a pull-request from @set-soft!

set-soft commented 1 year ago

We would all appreciate a pull-request from @set-soft!

The problem is that I no longer use it ... I gave up trying to use my old nothebook for remote work during the COVID19 pandemic, in august 2020 I brought a new PC with 16 GiB (4 times my notebook RAM) and just installed a full GNOME as desktop.