Init repo with original Script
iTunesGraphParser.py is the original script source: https://github.com/cgmartin/iTunes-Library-Network-Graph.git
This commit is contained in:
177
iTunesAnalyzer.py
Normal file
177
iTunesAnalyzer.py
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
#!/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()
|
||||||
|
|
||||||
228
iTunesGraphParser.py
Normal file
228
iTunesGraphParser.py
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
from operator import itemgetter
|
||||||
|
|
||||||
|
import os
|
||||||
|
import io
|
||||||
|
import plistlib
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class SetEncoder(json.JSONEncoder):
|
||||||
|
|
||||||
|
def default(self, obj):
|
||||||
|
if isinstance(obj, set):
|
||||||
|
return list(obj)
|
||||||
|
return json.JSONEncoder.default(self, obj)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ITunesGraphParser:
|
||||||
|
|
||||||
|
def __init__(self, libraryFile):
|
||||||
|
self.libraryFile = libraryFile
|
||||||
|
|
||||||
|
|
||||||
|
def toJson(self, rating=4, indent=None):
|
||||||
|
self._rating = rating * 20
|
||||||
|
self._maxArtistSongs = 0
|
||||||
|
self._maxArtistPlays = 0
|
||||||
|
self._maxGenreSongs = 0
|
||||||
|
self._maxGenrePlays = 0
|
||||||
|
|
||||||
|
self._processArtists()
|
||||||
|
self._processGenres()
|
||||||
|
self._processNodes()
|
||||||
|
|
||||||
|
jsonObj = {
|
||||||
|
'nodes': self._nodes,
|
||||||
|
'links': self._links,
|
||||||
|
'maxArtistSongs': self._maxArtistSongs,
|
||||||
|
'maxArtistPlays': self._maxArtistPlays,
|
||||||
|
'maxGenreSongs': self._maxGenreSongs,
|
||||||
|
'maxGenrePlays': self._maxGenrePlays
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.dumps(jsonObj, indent=indent, cls=SetEncoder)
|
||||||
|
|
||||||
|
def toJsonP(self, rating=4, indent=None):
|
||||||
|
json = self.toJson(rating, indent)
|
||||||
|
jsonp = ';itgCallback(' + json + ');'
|
||||||
|
return jsonp
|
||||||
|
|
||||||
|
|
||||||
|
def _readTracks(self):
|
||||||
|
pl = plistlib.readPlist(self.libraryFile)
|
||||||
|
return pl['Tracks']
|
||||||
|
|
||||||
|
|
||||||
|
def _processArtists(self):
|
||||||
|
tracks = self._readTracks()
|
||||||
|
self._artists = {}
|
||||||
|
|
||||||
|
for k in tracks:
|
||||||
|
track = tracks[k]
|
||||||
|
|
||||||
|
# Filter out any non-music with ratings lower than 3 stars
|
||||||
|
if (track['Track Type'] != 'File') or ('Artist' not in track) or ('Genre' not in track) or (
|
||||||
|
'Rating' not in track) or (track['Rating'] < self._rating) or (track['Artist'] == 'Various Artists'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
akey = track['Artist']
|
||||||
|
if akey not in self._artists:
|
||||||
|
self._artists[akey] = {
|
||||||
|
'id': len(self._artists),
|
||||||
|
'name': akey,
|
||||||
|
'type': 'a', 'count': 0, 'plays': 0, 'rating': 0,
|
||||||
|
'genres': set()
|
||||||
|
}
|
||||||
|
|
||||||
|
rating = (track['Rating'] // 20)
|
||||||
|
plays = track['Play Count'] if 'Play Count' in track else 0
|
||||||
|
|
||||||
|
self._artists[akey]['count'] += 1
|
||||||
|
self._artists[akey]['rating'] += rating
|
||||||
|
self._artists[akey]['plays'] += plays
|
||||||
|
|
||||||
|
self._maxArtistSongs = max(self._maxArtistSongs, self._artists[akey]['count'])
|
||||||
|
self._maxArtistPlays = max(self._maxArtistPlays, self._artists[akey]['plays'])
|
||||||
|
|
||||||
|
# Split up the Genres
|
||||||
|
genreParts = track['Genre'].split('/')
|
||||||
|
self._artists[akey]['genres'] |= set(genreParts)
|
||||||
|
|
||||||
|
|
||||||
|
def _processGenres(self):
|
||||||
|
self._genres = {}
|
||||||
|
|
||||||
|
for akey in self._artists.keys():
|
||||||
|
|
||||||
|
# Filter out any one-hit wonders
|
||||||
|
if self._artists[akey]['count'] <= 2:
|
||||||
|
del self._artists[akey]
|
||||||
|
continue
|
||||||
|
|
||||||
|
genreParts = self._artists[akey]['genres']
|
||||||
|
for gkey in list(genreParts):
|
||||||
|
if gkey == 'Mix':
|
||||||
|
genreParts.remove(gkey)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if gkey not in self._genres:
|
||||||
|
self._genres[gkey] = {
|
||||||
|
'id': len(self._genres),
|
||||||
|
'name': gkey,
|
||||||
|
'type': 'g', 'count': 0, 'plays': 0, 'rating': 0,
|
||||||
|
'adjGenres': set()
|
||||||
|
}
|
||||||
|
|
||||||
|
self._genres[gkey]['count'] += self._artists[akey]['count']
|
||||||
|
self._genres[gkey]['rating'] += self._artists[akey]['rating']
|
||||||
|
self._genres[gkey]['plays'] += self._artists[akey]['plays']
|
||||||
|
|
||||||
|
self._maxGenreSongs = max(self._maxGenreSongs, self._genres[gkey]['count'])
|
||||||
|
self._maxGenrePlays = max(self._maxGenrePlays, self._genres[gkey]['plays'])
|
||||||
|
|
||||||
|
# Add adjacencies between genre parts
|
||||||
|
for gkey in genreParts:
|
||||||
|
for gkey2 in genreParts:
|
||||||
|
if gkey != gkey2:
|
||||||
|
self._genres[gkey]['adjGenres'].add(gkey2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _processNodes(self):
|
||||||
|
self._links = []
|
||||||
|
self._nodes = sorted(self._genres.itervalues(), key=itemgetter('id'))
|
||||||
|
|
||||||
|
for idx, genre in enumerate(self._nodes):
|
||||||
|
#for gid in genre['adjGenres']:
|
||||||
|
#self._links.append({ 'source': gid, 'target': idx })
|
||||||
|
|
||||||
|
del genre['adjGenres']
|
||||||
|
|
||||||
|
idx = len(self._nodes);
|
||||||
|
for akey in self._artists.keys():
|
||||||
|
self._nodes.append(self._artists[akey])
|
||||||
|
for g in self._artists[akey]['genres']:
|
||||||
|
self._links.append({ 'source': idx, 'target': self._genres[g]['id'] })
|
||||||
|
idx += 1
|
||||||
|
|
||||||
|
|
||||||
|
#### main block ####
|
||||||
|
|
||||||
|
defaultLibraryFile = os.path.expanduser('iTunesMiniLibrary.xml')
|
||||||
|
defaultOutputFile = os.path.dirname(os.path.realpath(__file__)) + '/js/music-data.json'
|
||||||
|
|
||||||
|
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('-r', '--rating', dest='rating', type='int',
|
||||||
|
help='Minimum rating filter (default = 4)',
|
||||||
|
default=4)
|
||||||
|
parser.add_option('-p', '--jsonp', dest='jsonp', action='store_true',
|
||||||
|
help='Output in JSON-P format')
|
||||||
|
parser.add_option('-i', '--indent', dest='indent', type='int',
|
||||||
|
help='Indent level for output format (default=None)')
|
||||||
|
parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
|
||||||
|
help='Verbose output')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
|
itunesParser = ITunesGraphParser(options.file)
|
||||||
|
if options.jsonp:
|
||||||
|
output = itunesParser.toJsonP(options.rating, options.indent)
|
||||||
|
else:
|
||||||
|
output = itunesParser.toJson(options.rating, options.indent)
|
||||||
|
|
||||||
|
if options.console:
|
||||||
|
print output
|
||||||
|
else:
|
||||||
|
with io.open(options.output, 'wb') as outfile:
|
||||||
|
outfile.write(output)
|
||||||
|
print "JSON data written to: " + options.output
|
||||||
39315
iTunesMiniLibrary.xml
Normal file
39315
iTunesMiniLibrary.xml
Normal file
File diff suppressed because it is too large
Load Diff
239
iTunesParser.py
Normal file
239
iTunesParser.py
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
--> Parse library and just do JSON
|
||||||
|
|
||||||
|
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 SetEncoder(json.JSONEncoder):
|
||||||
|
def default(self, obj):
|
||||||
|
if isinstance(obj, set):
|
||||||
|
return list(obj)
|
||||||
|
if isinstance(obj, datetime.datetime):
|
||||||
|
return obj.isoformat()
|
||||||
|
# encoded_object = int(mktime(obj.timetuple()))
|
||||||
|
return json.JSONEncoder.default(self, obj)
|
||||||
|
|
||||||
|
|
||||||
|
class ITunesParser:
|
||||||
|
def __init__(self, libraryFile):
|
||||||
|
self._albums = {}
|
||||||
|
self._artists = {}
|
||||||
|
self.libraryFile = libraryFile
|
||||||
|
|
||||||
|
def toJson(self):
|
||||||
|
output = self._processSongs()
|
||||||
|
|
||||||
|
# self._writeArtists()
|
||||||
|
# self._writeAlbums()
|
||||||
|
|
||||||
|
# return json.dumps(jsonObj, indent=indent, cls=SetEncoder)
|
||||||
|
return output
|
||||||
|
|
||||||
|
def toJsonP(self, rating=4):
|
||||||
|
json = self.toJson()
|
||||||
|
jsonp = ';itgCallback(' + json + ');'
|
||||||
|
return jsonp
|
||||||
|
|
||||||
|
|
||||||
|
def _readTracks(self):
|
||||||
|
pl = plistlib.readPlist(self.libraryFile)
|
||||||
|
return pl['Tracks']
|
||||||
|
|
||||||
|
def _processSongs(self):
|
||||||
|
tracks = self._readTracks()
|
||||||
|
output = ""
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
persistentId = track['Persistent ID']
|
||||||
|
jsonTrackIndic = {
|
||||||
|
"index": {"_index": "itunessongs", "_type": "song", "_id": persistentId}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Retrieve for each track artist information
|
||||||
|
self._processArtist(track)
|
||||||
|
# Retrieve for each track album information
|
||||||
|
self._processAlbum(track)
|
||||||
|
|
||||||
|
output += json.dumps(jsonTrackIndic, indent=None, cls=SetEncoder)
|
||||||
|
output += "\n"
|
||||||
|
output += json.dumps(track, indent=None, cls=SetEncoder)
|
||||||
|
output += "\n"
|
||||||
|
return output
|
||||||
|
|
||||||
|
def _processArtist(self, track):
|
||||||
|
if 'Artist' not in track:
|
||||||
|
return
|
||||||
|
|
||||||
|
akey = track['Artist']
|
||||||
|
# Add artist
|
||||||
|
if akey not in self._artists:
|
||||||
|
self._artists[akey] = {
|
||||||
|
'id': len(self._artists),
|
||||||
|
'name': akey,
|
||||||
|
'count': 0, 'plays': 0, 'rating': 0,
|
||||||
|
'genres': 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._artists[akey]['count'] += 1
|
||||||
|
self._artists[akey]['rating'] += rating
|
||||||
|
self._artists[akey]['plays'] += plays
|
||||||
|
|
||||||
|
if 'Genre' not in track:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Split up the Genres
|
||||||
|
genreParts = track['Genre'].split('/')
|
||||||
|
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
|
||||||
|
|
||||||
|
# def _writeArtists(self):
|
||||||
|
# fileArtist = io.open('es-artist-data.json', 'wb')
|
||||||
|
# for a in self._artists:
|
||||||
|
# jsonTrackIndic = {
|
||||||
|
# "index": {"_index": "itunessongs", "_type": "artist"}
|
||||||
|
# }
|
||||||
|
# fileArtist.write(json.dumps(jsonTrackIndic, indent=None, cls=SetEncoder))
|
||||||
|
# fileArtist.write("\n")
|
||||||
|
# fileArtist.write(json.dumps(self._artists[a], indent=None, cls=SetEncoder))
|
||||||
|
# fileArtist.write("\n")
|
||||||
|
# fileArtist.close()
|
||||||
|
|
||||||
|
# def _writeAlbums(self):
|
||||||
|
# fileAlbums = io.open('es-albums-data.json', 'wb')
|
||||||
|
# for a in self._albums:
|
||||||
|
# jsonTrackIndic = {
|
||||||
|
# "index": {"_index": "itunessongs", "_type": "album"}
|
||||||
|
# }
|
||||||
|
# fileAlbums.write(json.dumps(jsonTrackIndic, indent=None, cls=SetEncoder))
|
||||||
|
# fileAlbums.write("\n")
|
||||||
|
# fileAlbums.write(json.dumps(self._albums[a], indent=None, cls=SetEncoder))
|
||||||
|
# fileAlbums.write("\n")
|
||||||
|
# fileAlbums.close()
|
||||||
|
|
||||||
|
#### main block ####
|
||||||
|
|
||||||
|
# Default input & output files
|
||||||
|
defaultLibraryFile = os.path.expanduser('iTunesMiniLibrary.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()
|
||||||
|
|
||||||
|
itunesParser = ITunesParser(options.file)
|
||||||
|
if options.jsonp:
|
||||||
|
output = itunesParser.toJsonP()
|
||||||
|
else:
|
||||||
|
output = itunesParser.toJson()
|
||||||
|
|
||||||
|
if options.console:
|
||||||
|
print(output)
|
||||||
|
else:
|
||||||
|
with io.open(options.output, 'wb') as outfile:
|
||||||
|
outfile.write(output)
|
||||||
|
print('JSON data written to: ' + options.output)
|
||||||
Reference in New Issue
Block a user