Files
iTunes/iTunesAnalyzer.py
2017-04-12 00:13:19 +02:00

178 lines
5.1 KiB
Python

#!/usr/bin/env python
"""
--> Analyze library and found problems
iTunes Graph Parser
Parses an iTunes library XML file and generates a JSON file
for use in the D3.js JavaScript library.
Example Track info:
{
'Album': 'Nirvana',
'Persistent ID': 'A50FE1436726815C',
'Track Number': 4,
'Location': 'file://localhost/Users/foo/Music/iTunes/iTunes%20Music/Nirvana/Nirvana/04%20Sliver.mp3',
'File Folder Count': 4,
'Album Rating Computed': True,
'Total Time': 134295,
'Sample Rate': 44100,
'Genre': 'Rock/Alternative',
'Bit Rate': 236,
'Kind': 'MPEG audio file',
'Name': 'Sliver',
'Artist': 'Nirvana',
'Date Added': datetime.datetime(2006, 10, 11, 4, 31, 38),
'Album Rating': 60,
'Rating': 40,
'Date Modified': datetime.datetime(2009, 7, 18, 4, 57, 41),
'Library Folder Count': 1,
'Year': 2002,
'Track ID': 7459,
'Size': 3972838,
'Track Type': 'File',
'Play Count': 2,
'Play Date UTC': datetime.datetime(2009, 7, 18, 5, 00, 00)
}
"""
from __future__ import division
from optparse import OptionParser
import os
import io
import plistlib
import json
import datetime
class ITunesAnalyzer:
def __init__(self, libraryFile):
self._albums = {}
self._artists = {}
self.libraryFile = libraryFile
def _readTracks(self):
pl = plistlib.readPlist(self.libraryFile)
return pl['Tracks']
def analyzeGenres(self):
self._processSongs()
for a in self._artists:
genres = self._artists[a]['genres']
if len(genres) > 1:
print("Problem with " + a)
print(("Genre: " + str(genres)))
def _processSongs(self):
tracks = self._readTracks()
for k in tracks:
track = tracks[k]
# Filter out any non-music
if track['Track Type'] != 'File':
continue
if 'Podcast' in track or 'Has Video' in track:
continue
# Retrieve for each track artist information
self._processArtist(track)
# Retrieve for each track album information
# self._processAlbum(track)
def _processArtist(self, track):
if 'Artist' not in track and 'Album Artist' not in track:
return
#
if 'Album Artist' not in track:
akey = track['Artist']
else:
akey = track['Album Artist']
# else:
# akey = track['Artist']
if akey not in self._artists:
self._artists[akey] = {
'id': len(self._artists),
'name': akey,
'genres': set()
}
if 'Genre' not in track:
return
# Split up the Genres
genreParts = track['Genre'].split('/')
if 'Soundtrack' in genreParts:
genreParts.remove('Soundtrack')
self._artists[akey]['genres'] |= set(genreParts)
return
def _processAlbum(self, track):
if 'Album' not in track:
return
akey = track['Album']
if akey not in self._albums:
self._albums[akey] = {
'id': len(self._albums),
'name': akey,
'count': 0, 'plays': 0, 'rating': 0,
'genres': set(),
'artist': set()
}
# Compute information
rating = (track['Rating'] // 20) if 'Rating' in track else 0
plays = track['Play Count'] if 'Play Count' in track else 0
self._albums[akey]['count'] += 1
self._albums[akey]['rating'] += rating
self._albums[akey]['plays'] += plays
if 'Genre' not in track:
return
# Split up the Genres
genreParts = track['Genre'].split('/')
self._albums[akey]['genres'] |= set(genreParts)
## Add different artists
if 'Artist' not in track:
return
self._albums[akey]['artist'].add(track['Artist'])
return
#### main block ####
# Default input & output files
defaultLibraryFile = os.path.expanduser('iTunesLibrary.xml')
defaultOutputFile = os.path.dirname(os.path.realpath(__file__)) + '/es-music-data.json'
# Get options
parser = OptionParser(version="%prog 1.0")
parser.add_option('-f', '--file', dest='file', type='string',
help='iTunes Library XML file path',
default=defaultLibraryFile)
parser.add_option('-o', '--output', dest='output', type='string',
help='Output to file (default=./js/music-data.json)',
default=defaultOutputFile)
parser.add_option('-c', '--console', dest='console', action='store_true',
help='Output to console instead of file')
parser.add_option('-p', '--jsonp', dest='jsonp', action='store_true',
help='Output in JSON-P format')
parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
help='Verbose output')
if __name__ == '__main__':
(options, args) = parser.parse_args()
analyzer = ITunesAnalyzer(options.file)
analyzer.analyzeGenres()