Mercurial > ~darius > hgwebdir.cgi > itunes2mp3
comparison itunes2mp3.py @ 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 |
comparison
equal
deleted
inserted
replaced
0:7ca49dff763a | 1:0ca90a90e723 |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 | 2 |
3 import errno | 3 import errno |
4 import glob | 4 import glob |
5 import os | 5 import os |
6 import plistlib | |
6 import random | 7 import random |
7 import shutil | 8 import shutil |
8 import subprocess | 9 import subprocess |
10 import sys | |
11 import urllib2 | |
9 | 12 |
10 def compress(srcfile, destdir): | 13 def compress(srcfile, destdir): |
11 f = file(srcfile, 'rU') | 14 plist = plistlib.readPlist(srcfile) |
12 for line in f: | 15 playlist = plist['Playlists'][0] |
13 s = line.strip().split('\t')[-1] | 16 |
14 path = reduce(lambda a, b: a + '/' + b, s.split(':')[1:], '') | 17 failures = [] |
18 for item in playlist['Playlist Items']: | |
19 track = plist['Tracks'][str(item['Track ID'])] | |
20 if 'Location' not in track: | |
21 print "No location for " + track['Name'] | |
22 continue | |
23 | |
24 location = track['Location'] | |
25 | |
26 path = urllib2.unquote(urllib2.urlparse.urlparse(location).path) | |
15 print "Path is " + path | 27 print "Path is " + path |
28 | |
16 destmp3 = destdir + '/' + path.split('/')[-1][:-3] + 'mp3' | 29 destmp3 = destdir + '/' + path.split('/')[-1][:-3] + 'mp3' |
17 if os.path.isfile(destmp3): | 30 if os.path.isfile(destmp3): |
18 print "Skipping" | 31 print "Already done" |
19 continue | 32 continue |
20 | 33 |
34 devnull = file('/dev/null', 'w') | |
21 if path.lower().endswith('.mp3'): | 35 if path.lower().endswith('.mp3'): |
22 shutil.copy(path, destmp3) | 36 shutil.copy(path, destmp3) |
23 elif path.lower().endswith('.m4a'): | 37 elif path.lower().endswith('.m4a'): |
24 faad = subprocess.Popen(['faad', '-qw', path], stdout = subprocess.PIPE) | 38 ffmpeg = subprocess.Popen(['ffmpeg', '-y', '-v', 'quiet', '-b', '160', '-i', path, destmp3]) |
25 lame = subprocess.Popen(['lame', '--abr', '160', '-', destmp3], stdin = faad.stdout) | 39 rtn = ffmpeg.wait() |
26 faad.stdout.close() # So lame will get SIGPIPE | 40 |
27 rtn = lame.communicate() | 41 if rtn != 0: |
28 print "Returned " + str(rtn) | 42 print "Failed to convert %s, return code %d" % (path, rtn) |
43 failures.append(path) | |
44 try: | |
45 os.unlink(destmp3) | |
46 except OSError, e: | |
47 if e.errno != 2: | |
48 raise e | |
29 else: | 49 else: |
30 print "Don't know how to convert " + path | 50 print "Don't know how to convert " + path |
51 if len(failures) > 0: | |
52 print | |
53 print "Failures:" | |
54 for f in failures: | |
55 print f | |
31 | 56 |
32 def shuffle(srcdir): | 57 def shuffle(srcdir): |
58 try: | |
59 os.mkdir(srcdir + '/burn') | |
60 except OSError, e: | |
61 if e.errno != 17: | |
62 raise e | |
33 files = glob.glob(srcdir + '/*.mp3') | 63 files = glob.glob(srcdir + '/*.mp3') |
34 random.shuffle(files) | 64 random.shuffle(files) |
35 for i in range(6): | 65 |
36 destdir = srcdir + '/CD%02d' % (i + 1) | 66 i = 0 |
37 try: | 67 for f in files: |
38 os.mkdir(destdir) | 68 dest = srcdir + '/burn/%03d - %s' % (i, f.split('/')[-1]) |
39 except OSError, e: | 69 i += 1 |
40 if e.errno == errno.EEXIST: | 70 os.link(f, dest) |
41 pass | |
42 for j in range(99): | |
43 try: | |
44 shutil.move(files.pop(), destdir) | |
45 except IndexError, e: | |
46 break | |
47 print "Ran out of space, %d left" % (len(files)) | |
48 | 71 |
49 if __name__ == "__main__": | 72 if __name__ == "__main__": |
50 srcfile = 'Stuff I like.txt' | 73 if len(sys.argv) != 2: |
51 destdir = '/tmp/music' | 74 print "Bad usage" |
52 compress(srcfile, destdir) | 75 print "%s playlist.xml destdir" |
53 shuffle(destdir) | 76 sys.exit(1) |
77 | |
78 compress(sys.argv[1], sys.argv[2]) | |
79 shuffle(sys.argv[2]) |