HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

iTunes playlist copier

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
copierplaylistitunes

Problem

When you own an Android device and want your iTunes playlist to be copied over onto it, what do you do?

What if your playlist hits something in the region of 20GB+ ? Well, I am sure I could've paid for a tool, to do this for me and keep things in sync, but I wanted to challenge myself into writing something in Python to resolve it.

I tried dragging and dropping the files on the playlist onto the phone via Android Device Manager and often found it would just hang. This is what I ended up with:

from xml.etree.ElementTree import ElementTree
import os
from shutil import copyfile
import urllib

srcfile = "/path/to/playlist.xml"
dst = '/path/to/destination';
overwrite = 0

tree = ElementTree()
tree.parse(srcfile)
songs = tree.findall('/dict/dict/dict/string')

# Run through all the found elements
for song in songs:
    # Is this element a file?
    if song.text[0:7] == "file://":

        # Grab everything after file://
        myfile = urllib.unquote(song.text[7:])

        # Grab the filename without the path
        basefile = os.path.basename(myfile)

        # Grab the last dirname
        dirname = myfile.split('/')[-2]

        # If the dir does not exist on the destination, create it
        if not os.path.exists(dst+dirname):
            os.mkdir(dst+dirname)

        # Create the final filename
        dstFile = dst + dirname + '/' + basefile

        # If the file exists and we have chosen to not overwrite it, dont copy the file
        if os.path.exists(dstFile) and not overwrite:
            print "Skipping " + myfile
        else:
            copyfile(myfile, dst + dirname + '/' + basefile)
            print "Copying " + myfile

print "Finished...."


I have found a bug in it when copying over bands or songs with UTF-8 names in it e.g. Fantômas. Because the UTF-8 elements are encoded in the Playlist XML the names do not match the directory so I need to fix that. Have kept a copy on Gist, so if you want a copy go grab/comment/whatever. I'm new to Py

Solution

overwrite would be better as True or False.

Since each file is treated independently, you should define a function to make the independence obvious.

To split and join path components, use os.path.split() and os.path.join() instead of doing your own string analysis. The interface is slightly nicer, and it also ensures that there won't be missing or extra / separators.

If you reorder the conditionals, you could reduce the number of os.path.exists() tests in the don't-overwrite case, where the destination file already exists.

Print 'Copying filename' before copying the file, so that if the copying fails, you'll see which file it failed on. Print "Finished." instead of "Finished....", because ellipses imply that the job is incomplete.

import os
from shutil import copyfile
import urllib
from xml.etree.ElementTree import ElementTree

def copy_song(url, dest, overwrite=False):
    if not url.startswith('file://'):
        return

    orig_path = urllib.unquote(url[len('file://'):])
    orig_dirname, basename = os.path.split(orig_path)
    dest_dirname = os.path.join(dest, orig_dirname)
    dest_path = os.path.join(dest_dirname, basename)

    if not overwrite and os.path.exists(dest_path):
        print 'Skipping ' + orig_path
    else:
        if not os.path.exists(dest_dirname):
            os.mkdir(dest_dirname)
        print 'Copying ' + orig_path
        copyfile(orig_path, dest_path)

playlist = "/path/to/playlist.xml"
dest = '/path/to/destination';

playlist_xml = ElementTree.parse(playlist)
for song in playlist_xml.findall('/dict/dict/dict/string'):
    copy_song(song.text, dest)
print 'Finished.'

Code Snippets

import os
from shutil import copyfile
import urllib
from xml.etree.ElementTree import ElementTree

def copy_song(url, dest, overwrite=False):
    if not url.startswith('file://'):
        return

    orig_path = urllib.unquote(url[len('file://'):])
    orig_dirname, basename = os.path.split(orig_path)
    dest_dirname = os.path.join(dest, orig_dirname)
    dest_path = os.path.join(dest_dirname, basename)

    if not overwrite and os.path.exists(dest_path):
        print 'Skipping ' + orig_path
    else:
        if not os.path.exists(dest_dirname):
            os.mkdir(dest_dirname)
        print 'Copying ' + orig_path
        copyfile(orig_path, dest_path)


playlist = "/path/to/playlist.xml"
dest = '/path/to/destination';

playlist_xml = ElementTree.parse(playlist)
for song in playlist_xml.findall('/dict/dict/dict/string'):
    copy_song(song.text, dest)
print 'Finished.'

Context

StackExchange Code Review Q#94445, answer score: 6

Revisions (0)

No revisions yet.