/// <summary> /// Parses the XML property list. /// </summary> /// <param name="dict">A PList in which the parsed elements will be output.</param> /// <param name="elements">A collection of the elements in the property list.</param> internal void Parse(PList dict, IEnumerable <XElement> elements) { Contract.Requires(dict != null); Contract.Requires(elements != null); for (int i = 0; i < elements.Count(); i += 2) { XElement key = elements.ElementAt(i); XElement val = elements.ElementAt(i + 1); dict[key.Value] = ParseValue(val); } }
/// <summary> /// Parses an element in the XML property list. /// </summary> /// <param name="val">The XML element to parse.</param> /// <returns>The converted object representation of the element.</returns> private object ParseValue(XElement val) { Contract.Requires(val != null); switch (val.Name.ToString()) { case "string": return(val.Value); case "integer": return(long.Parse(val.Value)); case "real": return(float.Parse(val.Value)); case "true": return(true); case "false": return(false); case "dict": PList plist = new PList(); Parse(plist, val.Elements()); return(plist); case "array": List <object> list = ParseArray(val.Elements()); return(list); case "date": return(DateTime.Parse(val.Value)); case "data": return(Convert.FromBase64String(val.Value)); default: throw new ArgumentException("Unsupported"); } }
/// <summary> /// Loads the iTunes Library from an XML file and reads the PLIST-formatted contents. /// </summary> /// <param name="library">The folder containing the iTunes Library.</param> /// <returns>Whether the XML file exists or not.</returns> /// <returns>Whether the process was successful or not.</returns> private static async Task <bool> LoadLibraryFromXml(StorageFolder library) { try { StorageFile xmlFile = await library.GetFileAsync(ITUNES_XML); XDocument xmlDocument = null; // Attempt to read the XML file format var xmlStreamInfo = await xmlFile.TryReadAsync(); OperatingSystem libraryType = xmlStreamInfo.Item1.Value; using (var xmlStream = xmlStreamInfo.Item2) { xmlDocument = XDocument.Load(xmlStream.AsStreamForRead()); } // Get the PLIST from the XML and parse var plist = new PList(xmlDocument); IEnumerable <Album> libraryAlbums; IEnumerable <Playlist> libraryPlaylists; ParseXml(library.Path, plist, out libraryAlbums, out libraryPlaylists); // Save the parsed objects if possible TunesDataSource.Default = new TunesDataSource(libraryAlbums, libraryPlaylists, libraryType); await SerializeAsync(); return(true); } catch (Exception) { return(false); } }
/// <summary> /// Helper method to process an iTunes PLIST into a List of Album objects. /// </summary> /// <returns>Whether the process was successful or not.</returns> /// <exception cref="System.Exception">if the PLIST parsing failed.</exception> private static void ParseXml(string actualLibaryPath, PList libraryIndex, out IEnumerable <Album> outAlbums, out IEnumerable <Playlist> outPlaylists) { Contract.Requires(!string.IsNullOrEmpty(actualLibaryPath)); Contract.Requires(libraryIndex != null); // file://localhost/C:/Users/Steve/Music/iTunes/iTunes%20Media/ // file://localhost/Users/Steve/Music/iTunes/iTunes%20Media/ string musicFolderString = libraryIndex.GetStringOrDefault("Music Folder"); string declaredMediaPath = new Uri(musicFolderString).LocalPath; var absoluteMediaPath = GetAbsoluteMediaPathUri(actualLibaryPath, declaredMediaPath); IEnumerable <PList> trackListRaw = libraryIndex.GetOrDefault <PList>("Tracks").Values.Cast <PList>(); IEnumerable <PList> playlistListRaw = libraryIndex.GetOrDefault <List <object> >("Playlists").Cast <PList>(); if (string.IsNullOrEmpty(declaredMediaPath) || trackListRaw == null || playlistListRaw == null) { throw new InvalidOperationException(); } Dictionary <AlbumKey, AlbumBuilder> albumBuilders = new Dictionary <AlbumKey, AlbumBuilder>(); foreach (var trackInfoRaw in trackListRaw) { string trackLocationString = trackInfoRaw.GetStringOrDefault("Location"); long trackBitRateLong = trackInfoRaw.GetLongOrDefault("Bit Rate"); if (IsValidMediaKind(trackLocationString, trackBitRateLong)) { string titleString = trackInfoRaw.GetStringOrDefault("Name"); string albumString = trackInfoRaw.GetStringOrDefault("Album"); string artistString = trackInfoRaw.GetStringOrDefault("Artist"); string albumArtistString = trackInfoRaw.GetStringOrDefault("Album Artist") ?? artistString; int trackIDInt = trackInfoRaw.GetIntOrDefault("Track ID"); int discNumberInt = trackInfoRaw.GetIntOrDefault("Disc Number"); int trackNumberInt = trackInfoRaw.GetIntOrDefault("Track Number"); int yearInt = trackInfoRaw.GetIntOrDefault("Year"); int totalTimeInt = trackInfoRaw.GetIntOrDefault("Total Time"); var relativeTrackLocation = new Uri(trackLocationString).LocalPath.Replace(declaredMediaPath, string.Empty); string formattedLocationString = Path.Combine(absoluteMediaPath, relativeTrackLocation); AlbumBuilder albumBuilder; var albumBuilderID = new AlbumKey(albumString, albumArtistString); if (!albumBuilders.TryGetValue(albumBuilderID, out albumBuilder)) { albumBuilder = new AlbumBuilder(albumString, albumArtistString, yearInt); albumBuilders.Add(albumBuilderID, albumBuilder); } Contract.Assume(albumBuilder != null); // New AlbumBuilder should have been created if it doesn't yet exist. albumBuilder.AddTrack( trackID: trackIDInt, title: titleString, artist: artistString, discNumber: discNumberInt, trackNumber: trackNumberInt, location: formattedLocationString, totalTime: new TimeSpan(0, 0, totalTimeInt / 1000)); } } IEnumerable <Album> libraryAlbumsUnsorted = albumBuilders.Values.AsParallel().Select(thatBuilder => thatBuilder.GetAlbum()).ToList(); IEnumerable <Track> libraryTracksUnsorted = libraryAlbumsUnsorted.AsParallel().SelectMany(thatAlbum => thatAlbum.TrackList); List <Playlist> libraryPlaylists = new List <Playlist>(); foreach (var playlistInfoRaw in playlistListRaw) { List <object> playlistItemsRaw = playlistInfoRaw.GetOrDefault <List <object> >("Playlist Items"); if (playlistItemsRaw != null) { List <Track> playlistItems = playlistItemsRaw .Cast <PList>() // This is the only thing I can think of that would cause an exception in this method. .Select(thatPlist => thatPlist.GetIntOrDefault("Track ID")) .Join(libraryTracksUnsorted, itemIndex => itemIndex, track => track.TrackID, (index, track) => track).ToList(); if (playlistItems.Count > 0) { libraryPlaylists.Add(new Playlist(playlistInfoRaw.GetStringOrDefault("Name"), playlistItems)); } } } outAlbums = libraryAlbumsUnsorted.OrderBy(thatAlbum => thatAlbum.AlbumArtist).ThenBy(thatAlbum => thatAlbum.Title); outPlaylists = libraryPlaylists.OrderBy(playlist => playlist.Title); }