Syncing music to Android

Published on 2024-01-27 by TomatoSoup

I've typically used MusicBee to syncronize my music from my library to my phone. However, this is a little annoying: I need to be at home, I need to plug in my phone, enable MTP, and let MusicBee sort out what files need to be moved.

And MTP sucks! It's really slow and frequently locks up. I've found it easier and faster to move pictures off my phone by starting an FTP server and connecting to it.

However, I use WireGuard to always appear at my home network. And for some reason, when I have WireGuard running, I can't reach my phone at its listed IP address. I'm not certain exactly how the networking works, I just know that if I turn it off I can reach it at the IP that the phone thinks it has. Because I wouldn't start the FTP transfer from somewhere that my phone is on a different access point than my computer, I haven't looked into this much.

Suffice to say, that's a bit of friction in the whole process. Furthermore, there's not a really good way to syncronize files over FTP. I'd love to use rsync, but that's a totally different protocol. Even if I could host an SSH server on my phone I'd still have the problem of needing to kill WireGuard before syncing.

Enter termux.

Termux gives you a shell environment on your non-rooted phone. My music lives on my NAS, which is mounted by my home server, which I already know I can access with ConnectBot via the WireGuard tunnel.

Interestingly enough, termux doesn't even ship with OpenSSH or rsync. So first things first. Install OpenSSH and generate a key.

pkg install openssh
ssh-keygen -t ed25519

I want an Ed25519 key because they're less characters, more secure, and faster than an RSA key. So I hit that handy command and-

CANNOT LINK EXECUTABLE "ssh": library "libcrypto.so.3" not found: needed by main executable

Huh. Well, Google shows other people with this problem. Apparently I needed to pkg up first to update my repositories? Oh well. I didn't even try installing again. I'm not totally certain why that error is solved by that fix but hey.

So now I've got my keys and the pubkey is short enough I can plausibly retype it instead of needing to email it to myself. I try doing so and make a typo somewhere I can't notice so I email it to myself anyway. Frankly, I expected this.

Once I can actually ssh to my server I start writing a bash script, which is to say I ask ChatGPT to write me a bash script. I don't like bash.

ssh_check_command="ssh tomato@192.168.1.4 [ -e /mnt/freenas/media/touchstone ] && echo 'File exists' || echo 'File does not exist'"

file_exists=$(eval ${ssh_check_command})

if [ "${file_exists}" == "File exists" ]; then
    rsync -avz --progress tomato@192.168.1.4:/mnt/freenas/media/Music/Music/ /mnt/sdcard/Music
    echo "Folder synced successfully."
else
    echo "File does not exist on the remote server. Skipping sync."
fi

I also pkg install rsync because, like I said, termux doesn't ship with it.

I don't yet have the --delete flag in here, but I want to verify that my NAS is properly mounted and functioning before I copy my files over, hence why I check for the existence of a file called touchstone. If it exists, I start the sync. My fear is pretty much that something dumb happens to either my server or my NAS and running this script, once I add --delete, blows away my local (and now potentially only) copy of my music.

Some of this music is really personal! My folks introduced me to a lot of classic rock. My copy of London Calling was ripped from an old CD and has some crackling at the beginning of it. But, I always thought that was intentional! It made thematic sense, given the subject matter of nuclear armageddon. I was genuinely surprised when I heard a fresh copy and it was clean.

Of course there's a gap between the time of check and the time of use, but I'm pretty willing to accept the risk here. I expect an error if the directory isn't mounted, anyway, because the folder that would empty is /mnt/freenas/media, so no Music folder would exist. I also have some paranoia about it blowing away other folders on my phone. Well, that's what --dry-run --itemize-changes is for.

The script is pretty much what I wanted, if a little verbose. ChatGPT originally omitted the trailing / on the folders, which resulted in my songs being duplicated to /mnt/sdcard/Music/Music. It didn't really understand the problem when I tried describing it, even when I used surrogate folders A and B so it didn't get confused by the intentionally nested Music/Music on my NAS. Turns out even though it's better at bash than I am (and that is not a high bar), I can debug better than it.

(why does my NAS have a Music/Music folder? Because I also have Music/Playlists and Music/MusicBee and Music/Podcasts and so on).

It also suggested a #!/bin/bash but despite bash being a valid command in termux, it doesn't live at /bin/bash. And termux, shocker, doesn't have which installed so I can't ask where it lives. Of course, as I write this, I realize I can echo $PATH and see the only thing on my path is /data/data/com.termux/usr/bin and sure enough there's a bash in there. But you know, #!/data/data/com.termux/usr/bin/bash as a hashbang just really offends my sensibilities.

Like, really offends.

At any rate, the script as presented "works". There's a better one below.


Clearly it's still a work in progress. I probably don't actually want the archive flag (it being shorthand for the mess that is rlptgoD, I only really care about recursive, not links, permissions, times, groups, owners, or Devices) or the compression flag (on account of music generally already being compressed, but maybe the entire protocol can benefit from compression?). I also need to fool about with the exact semantics of how the source and destination paths work. I'm not 100% sure on the semantics of the trailing slash.

The safest course of action would probably be to use rsync to copy the files to a temp directory, verify that I copied a sane amount, and then overwrite my existing music library. But that's using double the storage space just for a bit of paranoia.

It's also annoying to write the script on my phone. I live in perpetual fear of typing rm -rf /, intending to write more, and then brushing the enter key. So I write it on my desktop, save it to my NAS, and run an scp to copy it over! Maybe some day the first thing the script will do is copy itself over and execute itself. I foresee no recursive problems here.

I've, vaguely, been getting annoyed with MusicBee. Mostly due to bugs when syncronizing music to my phone. This should eliminate those problems and let me syncronize my music from anywhere. What took MusicBee a minute to do this completes in a second. It also eliminates my dependence on MusicBee to syncronize! If, in the future, I decide I'm done with MusicBee I can switch to any other program and, so long as I have some system for organizing my songs into folders, I can just hit this script and get sorted. I love solutions where I know I'm not stuck with something.


I went back through later and rewrote the script by hand. I don't like bash. It is finnicky and has esoteric rules about how the three types of quotes work and even vi doesn't know how to syntax highlight this next bit right despite being, to my inexperienced eyes, fairly straight forward.

if `ssh tomato@192.168.1.4 [ ! -f /mnt/freenas/media/touchstone ]`; then
    echo "NAS seemingly not connected."
    exit 1
fi

rsync -rv --progress tomato@192.168.1.4:/mnt/freenas/media/Music/Music/ /mnt/sdcard/Music/
echo "Folder synced successfully."

It also turns out that rsync's source argument is a notable exception for how trailing slashes work, so that's neat.