pyMSync: M3U Sync
I depend on the music on my phone to get throughout my day, and I still like syncing my audio files from my computer to my Android phone. In the past, I’d been doing these syncs with DoubleTwist because it also allowed me to sync my playlists (making it easier to select the music I want to transfer); but the newest update to Catalina broke compatibility for me, so I had to look for alternatives. On top of this, I’ve recently shifted most of my day-to-day work to an Ubuntu system, so I wanted to keep my syncs compatible to both: iTunes and Amarok (or any other player that can export M3U playlists).
Given these constraints, and that I am a bit tired of depending on increasingly scarce apps that do what I need, I started coding a Python script that allowed me to take an M3U playlist, copy all the referenced files from my library to another folder, and create a second M3U file so that it pointed towards the copied files. This would allow me to copy music from my computer to my phone seamlessly.
Development
In essence, an M3U file is a text file that contains the song references in the order they are to be reproduced by the music player. There are two types of M3U files, the extended ones:
which hold the information about the duration, name, and artist of the song; and non-extended ones:
which contain only the paths to the songs in the order they are to be played. Almost any media player can export absolute-paths M3U files, so we’ll be using these standards as the basis for the music transfer.
The script works by following the next steps:
- Get the required elements from the user.
- M3U playlist path.
- Path to which we want to copy the files into.
- [optional] If the M3U is not located at the root of the music library, path pointing towards the root of the library. This is necessary to convert the absolute path reference to a relative one in the output playlist.
- [optional] Flag to decide if we want to overwrite existing files (defaults to False).
- [optional] Flag to decide if we want to print the process to the terminal (verbose).
- [optional] Flag to decide if we want a txt log to be written in the output folder.
- Read the contents of the playlist.
- Decide if the playlist is extended or not by reading the first line (should match:
#EXTM3U
)- If it is extended, create two lists of the same length: one containing the songs information, and another one with the paths.
- If it is not extended, the first list contains
None
, and the second one the full paths to the songs.
- Decide if the playlist is extended or not by reading the first line (should match:
- Create a new M3U file in the output folder, and start iterating through the lists.
- For each element, we verify if the path and the file exist.
- If they do exist, we create the same folder structure (starting from the library root) into the output folder, and we copy the audio file. Additionally, we add a relative reference to it in the output M3U.
- If they don’t exist, we skip the song and report it to the log file.
- For each element, we verify if the path and the file exist.
That is pretty much it!
In the cycle defined in step 2 there are steps added to remove the absolute reference to the file (coming from the original playlist), additional tags such as the file://
added by some players, to overwrite files if desired, and to manage the log/verbose operations.
The log file/terminal output can contain three kinds of tags:
- Copy: The indicated song was copied correctly into the desired directory.
- Skip: The song was already present and there’s no overwrite flag.
- Error: Something is wrong with the reference to the song (either the song doesn’t exist, or there is a problem with the path to it).
To use the script, simply run the following command on the terminal:
Again, the -lRT is optional in case the playlist is not stored at the root of the music library. For example, if my playlist is stored at /chipdelmal/Mixtape127_TonightTonight.m3u, but my library lives in /media/hdd/Music/, I would need to add the latter as the -lRT argument; if the playlist was stored in the root (/media/hdd/Music/Mixtape127_TonightTonight.m3u), I can leave the -lRt blank.
An example of use follows. We are gonna take the Mixtape127_TonightTonight.m3u stored in my library’s root, and copy its contents to the Mixtapes folder to be synced to my second desktop. The playlist’s contents look like this:
And we want to move the contents from the folder on the top left, to the one on the top right:
so we run the following command:
and all the files get moved across:
With the contents of the new M3U playlist looking like this:
Which leaves us with a folder that we can sync over to whichever device we need to!
Further thoughts
One thing that is still causing problems in the script, is that some of the paths coming from my MacOS library are case-insensitive, so it causes problems when parsing them in my current Ubuntu-based system (which is case-sensitive). I need to add support for this special case, along with ratings and playcount conversions.
Documentation and Code
- Repository: Github repo
- Dependencies: unidecode