Transfering files to Android devices on linux via USB/MTP

I’ve had a Samsung Android phone for a few years now, and since Apple decided to stop supporting my old iPod Touch, I’ve lost interest in using it for music, switching over to my phone. After making the switch to Linux, I slowly moved my music library to Banshee. The problem I quickly encountered was my inability to transfer media directly from the application to my phone, particularly my beloved playlists. After poking around the interwebs, I came across several tutorials describing data transfers via SSH connection (using apps such as SSHDroid ). Though I had success with this, I found it to be a bit of a pain, since I would have to manually enable the SSH server on my phone, mount an sshfs to my computer to start the slow transfer. This was satisfactory for a while, but I recently wondered if it was possible to transfer over USB. After some more poking around, I found my solution and I much prefer it. The following is bash code I use to transfer media via MTP over a USB connection.

The first part of my script sets up a few variables:


#!/bin/bash

#ID for Samsung phone
Id="04e8:6860"
#Music directory
Music="Card/Music"

The ID for the device comes from running the lsusb command, which will give output similar to:

Bus 001 Device 009: ID 04e8:6860 Samsung Electronics Co., Ltd GT-I9100 Phone [Galaxy S II], GT-I9300 Phone [Galaxy S III], GT-P7500 [Galaxy Tab 10.1]

The Music variable stores the location of music on my device. Since I have an SD card installed, the Card/Music path is used from the root directory of the device.

After this, I run a quick check to make sure banshee is not running. The process seems to disable filesystem mounting, possibly via mtp_switch.


#Check if banshee process is open
ps -C banshee > /dev/null
if [ $? -eq 0 ]; then
	echo "Banshee is currently open, and will conflict with the MTP mountpoint."
	exit
fi

It’s only after this point that things got tricky. In order to transfer files to the device, I had to identify it’s mountpoint on the filesystem. Nautilus lists it as being mtp://[usb:001,009]/, which is not useful in this case. Thankfully I found my answer on an AskUbuntu.com forum post. This indicated that the filesystem was mounted on:

/run/user/$UID/gvfs/mtp:host=%5Busb%3A001%2C009%5D

Note the repetition of the Bus id 001 and Device id 009. As these are not static, the output from lsusb can be used here:


#Setup MTP directory
if [ -d "$XDG_RUNTIME_DIR" ]; then
	PathStart="$XDG_RUNTIME_DIR"
else
	PathStart="/run/user/$USER"
fi
DeviceInfo=`lsusb -d $Id | sed 's/:.*//'`
Bus=`echo $DeviceInfo | awk '{print $2}'`
Device=`echo $DeviceInfo | awk '{print $4}'`
MTPPath="$PathStart/gvfs/mtp:host=%5Busb%3A${Bus}%2C${Device}%5D"
MTP="mtp://[usb:${Bus},${Device}]"
if [ ! -d $MTPPath ]; then
	echo "MTP directory not found."
	exit
fi

Since there is a chance the XDG_RUNTIME_DIR system variable does not exist, the USER will be used in the path instead of the UID. I also stored the mtp:// location for later usage. With all this said and done, the rest should have been easy: copy the music files onto the device the same way as via SSH. Seems this was easier said then done, as I was immediately bombarded with Operation Not Supported errors, and such. Apparently the type of filesystem used (gvfs) does not support “low level” operations such as standard copy (cp) commands. Some sources indicate this is strictly an issue with non-Google devices, though I don’t have means of testing this. Knowing there had to be a way of copying the files onto it, I stumbled upon the gvfs commands, such as gvfs-copy. It doesn’t have as many options as a typical *nix cp command, but it suffices. One feature that it does have, which cp does not, is the ability to copy directly onto mtp:// locations. Upon discovering this, I first thought that I had wasted my time finding the mountpoint on the filesystem, since I no longer need it. I instead found a way to take advantage of it.

One flaw of the gvfs-copy command is that it cannot overwrite an existing file, else it will spit out “Location already exists” errors, or similar. To avoid this, I decided to check if the file was already present, before copying. Since there is no gvfs operation to test for files, I use the test command, utilizing the filesystem path instead. The resulting copy operations are as follows:


#Transfer Music
songs=`grep -v "^#EXT.*" $1 | sed 's/^.*Music\///'`
IFS=$'\n'
for track in $songs
do
	test -d $MTPPath/${Music}/`dirname $track` || gvfs-mkdir -p $MTP/${Music}/`dirname $track`
	test -e $MTPPath/${Music}/$track || gvfs-copy ~/Music/$track $MTP/${Music}/$track
done

The script takes in a .m3u playlist file, exported from Banshee, as an input argument. The highlighted line is used to clean up the file, and extract the paths to the songs. After this, directories are created (if needed) and the songs are transferred (if not already present). The last operation I needed was to add the playlist to the device. Since the formatting between the .m3u files generated by Banshee are not the same as what is accepted by Android (at least not version 4.0.4), a bit of cleanup was needed:


#Save playlist
grep -v "^#EXT.*" $1 | sed 's/^.*Music\//\//' > tmplist
test -e $MTPPath/${Music}/$1 || gvfs-rm $MTP/${Music}/$1
gvfs-move tmplist $MTP/${Music}/$1

With this, I had no errors copying the device, and the transfer was faster. The only thing I may add, which is lacking from my previous transfers over SSH, is checking for file versions via the cp -u command. I often update the meta-data of my music files, though it’s not a big loss if this is not reflected on the files on the device.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s