changeset 1:0ca90a90e723

Complete rework to make MP3 CDs from iTunes plist (XML) playlists. Use ffmpeg as it doesn't barf on long file names or some m4as (like faad) nor does it display unwanted album images and hang wasting CPU (mplayer). Shuffle now creates a directory and fills it with hardlinks in random order.
author Daniel O'Connor <darius@dons.net.au>
date Thu, 13 Jun 2013 00:16:23 +0930
parents 7ca49dff763a
children 1681a628ceb4
files itunes2mp3.py
diffstat 1 files changed, 53 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/itunes2mp3.py	Wed Jun 12 22:36:44 2013 +0930
+++ b/itunes2mp3.py	Thu Jun 13 00:16:23 2013 +0930
@@ -3,51 +3,77 @@
 import errno
 import glob
 import os
+import plistlib
 import random
 import shutil
 import subprocess
+import sys
+import urllib2
 
 def compress(srcfile, destdir):
-    f = file(srcfile, 'rU')
-    for line in f:
-        s = line.strip().split('\t')[-1]
-        path = reduce(lambda a, b: a + '/' + b, s.split(':')[1:], '')
+    plist = plistlib.readPlist(srcfile)
+    playlist = plist['Playlists'][0]
+    
+    failures = []
+    for item in playlist['Playlist Items']:
+        track = plist['Tracks'][str(item['Track ID'])]
+        if 'Location' not in track:
+            print "No location for " + track['Name']
+            continue
+
+        location = track['Location']
+
+        path = urllib2.unquote(urllib2.urlparse.urlparse(location).path)
         print "Path is " + path
+
         destmp3 = destdir + '/' + path.split('/')[-1][:-3] + 'mp3'
         if os.path.isfile(destmp3):
-            print "Skipping"
+            print "Already done"
             continue
         
+        devnull = file('/dev/null', 'w')
         if path.lower().endswith('.mp3'):
             shutil.copy(path, destmp3)
         elif path.lower().endswith('.m4a'):
-            faad = subprocess.Popen(['faad', '-qw', path], stdout = subprocess.PIPE)
-            lame = subprocess.Popen(['lame', '--abr', '160', '-', destmp3], stdin = faad.stdout)
-            faad.stdout.close() # So lame will get SIGPIPE
-            rtn = lame.communicate()
-            print "Returned " + str(rtn)
+            ffmpeg = subprocess.Popen(['ffmpeg', '-y', '-v', 'quiet', '-b', '160', '-i', path, destmp3])
+            rtn = ffmpeg.wait()
+
+            if rtn != 0:
+                print "Failed to convert %s, return code %d" % (path, rtn)
+                failures.append(path)
+                try:
+                    os.unlink(destmp3)
+                except OSError, e:
+                    if e.errno != 2:
+                        raise e
         else:
             print "Don't know how to convert " + path
+    if len(failures) > 0:
+        print
+        print "Failures:"
+    for f in failures:
+        print f
 
 def shuffle(srcdir):
+    try:
+        os.mkdir(srcdir + '/burn')
+    except OSError, e:
+        if e.errno != 17:
+            raise e
     files = glob.glob(srcdir + '/*.mp3')
     random.shuffle(files)
-    for i in range(6):
-        destdir = srcdir + '/CD%02d' % (i + 1)
-        try:
-            os.mkdir(destdir)
-        except OSError, e:
-            if e.errno == errno.EEXIST:
-                pass
-        for j in range(99):
-            try:
-                shutil.move(files.pop(), destdir)
-            except IndexError, e:
-                break
-    print "Ran out of space, %d left" % (len(files))
+
+    i = 0
+    for f in files:
+        dest = srcdir + '/burn/%03d - %s' % (i, f.split('/')[-1])
+        i += 1
+        os.link(f, dest)
     
 if __name__ == "__main__":
-    srcfile = 'Stuff I like.txt'
-    destdir = '/tmp/music'
-    compress(srcfile, destdir)
-    shuffle(destdir)
+    if len(sys.argv) != 2:
+        print "Bad usage"
+        print "%s playlist.xml destdir"
+        sys.exit(1)
+
+    compress(sys.argv[1], sys.argv[2])
+    shuffle(sys.argv[2])