Crappy Python Script to Copy iTunes Data into GoneMAD Stats
Jan 2, 2015 23:00:35 GMT -5
GoneMAD likes this
Post by diastelo on Jan 2, 2015 23:00:35 GMT -5
If you don't know what Python is, the rest of this post isn't going to make a lot of sense. However, if you're on a Mac, you can probably wing it and hope for the best, because Python comes preinstalled.
I went ahead about bought GoneMAD because of the Smart Playlist functionality (for which Last Played and Rating is generally sufficient for my needs), but I had to find a way to copy this data into the Stats file that GoneMAD generates (thanks for the Backup/Restore stats feature, it's terrific). I hacked together a crappy Python script to do this for me. It assumes that iTunes manages the folder locations of music, so that the music on the phone and the music on the computer have the same folder structure and file names, and it uses the 'date added to iTunes' field if the file has never been played in iTunes. I only tested this on Mac and on a Linux computer. It's possible to get Python working on a Windows machine, but I've never bothered. I also don't use iTunes on Windows, so maybe it behaves a little differently.
1. Open up iTunes, make sure all music is visible (i.e., not in a Playlist, but in the top level Music Library level). Then go to File -> Library -> Export Playlist. It'll default to Music.txt.
2. Rename Music.txt to Music.csv. That's because it's actually a tab separated CSV formatted file and the script will need it to be called that.
3. From inside GoneMAD, go to Preferences -> Backup -> Backup Stats. Find the file on the local phone filesystem. I found it in gmmp/stats.xml using ES File Explorer and used the app to email a copy to myself.
4. Copy the script.py (attached to this post), Music.csv, and stats.xml into the same folder. It doesn't matter where, it can be on a totally different computer, as long as Python is installed.
5. Assuming you have Python installed (and a few modules, which I honestly can't remember if they came installed by default : csv and xml.etree.ElementTree), run 'python script.py' from a command line (on Macs, /Applications/Utilities/Terminal). It takes a while because my coding is bad and I'm not great at optimizing loops for string searches. It will either spit out gibberish looking errors or take a while and then give you back your command prompt.
6. Open up the new file, stats-new.xml, and see if it missed anything (Google Chrome does a great job of rendering XML). I started with a clean database file in which LastPlayed hadn't been initialized for any song, so searching for <LastPlayed>0</LastPlayed> showed me some tagging problems that the script couldn't match (capitalization problems, missing files, folder mismatches, etc.). I chose to fix these manually in iTunes and on the phone and then reran the script with new copies of Music.csv and stats.xml. You could also open up the file in an editor and fix up the odds and ends misses yourself.
7. When you're done, reverse the process of copying stats-new.xml overtop of gmmp/stats.xml (I emailed it to myself, downloaded it on the phone, and used ES File Explorer to copy it from Downloads to gmmp), and from inside GoneMAD, go to Preferences -> Backup -> Restore Stats.
This won't work for everyone, sorry. But I wanted to share it with you all because it's waaaay faster to fix a few tagging problems than it is to input the ratings for every single song (I have over 5000). Assuming you feel like converting your m/d/y date fields into 'milliseconds since the Epoch'.
Copy and paste code follows, but I have an attachment that also contains comments.
I went ahead about bought GoneMAD because of the Smart Playlist functionality (for which Last Played and Rating is generally sufficient for my needs), but I had to find a way to copy this data into the Stats file that GoneMAD generates (thanks for the Backup/Restore stats feature, it's terrific). I hacked together a crappy Python script to do this for me. It assumes that iTunes manages the folder locations of music, so that the music on the phone and the music on the computer have the same folder structure and file names, and it uses the 'date added to iTunes' field if the file has never been played in iTunes. I only tested this on Mac and on a Linux computer. It's possible to get Python working on a Windows machine, but I've never bothered. I also don't use iTunes on Windows, so maybe it behaves a little differently.
1. Open up iTunes, make sure all music is visible (i.e., not in a Playlist, but in the top level Music Library level). Then go to File -> Library -> Export Playlist. It'll default to Music.txt.
2. Rename Music.txt to Music.csv. That's because it's actually a tab separated CSV formatted file and the script will need it to be called that.
3. From inside GoneMAD, go to Preferences -> Backup -> Backup Stats. Find the file on the local phone filesystem. I found it in gmmp/stats.xml using ES File Explorer and used the app to email a copy to myself.
4. Copy the script.py (attached to this post), Music.csv, and stats.xml into the same folder. It doesn't matter where, it can be on a totally different computer, as long as Python is installed.
5. Assuming you have Python installed (and a few modules, which I honestly can't remember if they came installed by default : csv and xml.etree.ElementTree), run 'python script.py' from a command line (on Macs, /Applications/Utilities/Terminal). It takes a while because my coding is bad and I'm not great at optimizing loops for string searches. It will either spit out gibberish looking errors or take a while and then give you back your command prompt.
6. Open up the new file, stats-new.xml, and see if it missed anything (Google Chrome does a great job of rendering XML). I started with a clean database file in which LastPlayed hadn't been initialized for any song, so searching for <LastPlayed>0</LastPlayed> showed me some tagging problems that the script couldn't match (capitalization problems, missing files, folder mismatches, etc.). I chose to fix these manually in iTunes and on the phone and then reran the script with new copies of Music.csv and stats.xml. You could also open up the file in an editor and fix up the odds and ends misses yourself.
7. When you're done, reverse the process of copying stats-new.xml overtop of gmmp/stats.xml (I emailed it to myself, downloaded it on the phone, and used ES File Explorer to copy it from Downloads to gmmp), and from inside GoneMAD, go to Preferences -> Backup -> Restore Stats.
This won't work for everyone, sorry. But I wanted to share it with you all because it's waaaay faster to fix a few tagging problems than it is to input the ratings for every single song (I have over 5000). Assuming you feel like converting your m/d/y date fields into 'milliseconds since the Epoch'.
Copy and paste code follows, but I have an attachment that also contains comments.
import csv,string,time
import xml.etree.ElementTree as ET
sourceEncoding = "utf-16"
targetEncoding = "utf-8"
source = open("Music.csv")
target = open("Musicdecoded.csv", "w")
target.write(unicode(source.read(), sourceEncoding).encode(targetEncoding))
source.close()
target.close()
DATA = csv.DictReader(open("Musicdecoded.csv", 'rU'), delimiter='\t', quoting=csv.QUOTE_NONE)
compressed = []
for row in DATA:
# The following is a debug statement to catch if you've somehow got duplicate songs in duplicate folders
#for x in compressed:
# if string.split(row['Location'],':').pop(-3) + '/' + string.split(row['Location'],':').pop(-2) + '/' + string.split(row['Location'],':').pop() in x:
# print string.split(row['Location'],':').pop(-3) + '/' + string.split(row['Location'],':').pop(-2) + '/' + string.split(row['Location'],':').pop(), "is a dupe"
if (row['Last Played'] == ''):
compressed += [[string.split(row['Location'],':').pop(-3) + '/' + string.split(row['Location'],':').pop(-2) + '/' + string.split(row['Location'],':').pop(), float(int(row['My Rating'] or 0)/20), row['Plays'] or 0, int(time.mktime(time.strptime(row['Date Added'], '%m/%d/%y, %I:%M %p'))) * 1000]]
else:
compressed += [[string.split(row['Location'],':').pop(-3) + '/' + string.split(row['Location'],':').pop(-2) + '/' + string.split(row['Location'],':').pop(), float(int(row['My Rating'] or 0)/20), row['Plays'] or 0, int(time.mktime(time.strptime(row['Last Played'], '%m/%d/%y, %I:%M %p'))) * 1000]]
tree = ET.parse('stats.xml')
root = tree.getroot()
# This loop is really slow. Sorry.
for files in root.iter('File'):
# Uncomment the following to see a "before" value
#print files[0].text, files[1].text, files[2].text, files[3].text
for x in compressed:
if (string.split(files[0].text, u'/').pop(-3) + '/' + string.split(files[0].text, '/').pop(-2) + '/' + string.split(files[0].text, '/').pop()).encode('utf-8') in x:
files[1].text = str(x[1])
files[2].text = str(x[2])
files[3].text = str(x[3])
# Uncomment the following to see an "after" value
#print files[0].text, files[1].text, files[2].text, files[3].text
tree.write('stats-new.xml')