import xmltodict import json import xmltodict import json import re from src.Library import * class XmlToJson: def flatten_dict(self, d): out = [] for song in d: newSong = {} strCount = 0 intCount = 0 dateCount = 0 def getStr(id): nonlocal strCount if id not in song["key"]: return "undef" if len(song["string"]) <= strCount: return "error" strCount += 1 return song["string"][strCount - 1] def getInt(id): nonlocal intCount if id not in song["key"]: return -1 if len(song["integer"]) <= intCount: return -1 intCount += 1 return song["integer"][intCount - 1] def getDate(id): nonlocal dateCount if id not in song["key"]: return "undef" if len(song["date"]) <= dateCount: return "error" dateCount += 1 return song["date"][dateCount - 1] newSong["Track ID"] = getInt("Track ID") newSong["Name"] = getStr("Name") newSong["Artist"] = getStr("Artist") newSong["Album Artist"] = getStr("Album Artist") newSong["Composer"] = getStr("Composer") newSong["Album"] = getStr("Album") newSong["Genre"] = getStr("Genre") newSong["Kind"] = getStr("Kind") newSong["Size"] = getInt("Size") newSong["Total Time"] = getInt("Total Time") newSong["Disc Number"] = getInt("Disc Number") newSong["Disc Count"] = getInt("Disc Count") newSong["Track Number"] = getInt("Track Number") newSong["Track Count"] = getInt("Track Count") newSong["Year"] = getInt("Year") newSong["Date Modified"] = getDate("Date Modified") newSong["Date Added"] = getDate("Date Added") newSong["Bit Rate"] = getInt("Bit Rate") newSong["Sample Rate"] = getInt("Sample Rate") newSong["Play Count"] = getInt("Play Count") newSong["Play Date"] = getInt("Play Date") newSong["Play Date UTC"] = getDate("Play Date UTC") newSong["Skip Count"] = getInt("Skip Count") newSong["Skip Date"] = getDate("Skip Date") newSong["Release Date"] = getDate("Release Date") newSong["Album Rating"] = getInt("Album Rating") newSong["Album Rating Computed"] = "Album Rating Computed" in song["key"] newSong["Loved"] = "Loved" in song["key"] newSong["Album Loved"] = "Album Loved" in song["key"] newSong["Explicit"] = "Explicit" in song["key"] newSong["Compilation"] = "Compilation" in song["key"] newSong["Artwork Count"] = getInt("Artwork Count") newSong["Sort Album"] = getStr("Sort Album") newSong["Sort Artist"] = getStr("Sort Artist") newSong["Sort Name"] = getStr("Sort Name") newSong["Persistent ID"] = getStr("Persistent ID") newSong["Track Type"] = getStr("Track Type") out.append(newSong) return out def convert(self, filename, out_path): with open(filename, 'r', encoding='utf-8') as xml_file: data_dict = xmltodict.parse(xml_file.read()) # Extract the "Tracks" dictionary to be flattened tracks_dict = data_dict['plist']['dict']['dict'] flat_tracks_dict = self.flatten_dict(tracks_dict["dict"]) json_data = json.dumps(flat_tracks_dict, indent=2) with open(out_path, 'w', encoding='utf-8') as json_file: json_file.write(json_data) class ParseItunesXml: def __init__(self): self.track_map: dict[str, Track] = {} self.artist_map: dict[str, Artist] = {} self.album_map: dict[str, Album] = {} self.library = Library() def parse_artists(self, data: list) -> List[Artist]: if not data: raise "No artist found" artists = [] for artist_data in data: name = artist_data artist_id = name if artist_id in self.artist_map: artists.append(self.artist_map[artist_id]) continue artist = Artist() artist.id = artist_id artist.title = name self.artist_map[artist_id] = artist self.library.artists.append(artist) artists.append(artist) return artists def parse_album(self, data: str) -> Album: if not data: raise "album is none" name = data album_id = name if album_id in self.album_map: return self.album_map[album_id] album = Album() album.id = album_id album.title = name self.album_map[album_id] = album self.library.albums.append(album) return album def parse(self, tracks_file_xml: Path, tracks_file_json: Path) -> Library: XmlToJson().convert(tracks_file_xml, tracks_file_json) tracks_content = json.loads(tracks_file_json.read_text()) for data in tracks_content: track = Track() track.id = data.get("Track ID") track.title = data.get("Name") track.artists = self.parse_artists([data.get("Artist")]) track.album = self.parse_album(data.get("Album")) track.play_count = int(data.get("Play Count")) if not track.album or not len(track.artists) or not track.title: raise "error parsing" self.track_map[track.id] = track self.library.tracks.append(track) for track_id, track in self.track_map.items(): for artist in track.artists: if artist not in track.album.artists: track.album.artists.append(artist) self.library.update_cache() self.library.name = "Itunes" for track in self.library.tracks: track.auto_score = track.play_count return self.library