public static bool UseLegacyApi(PlayableItem item) { // Extenders don't support MediaCollections if (Application.RunningOnExtender) { return true; } int numFiles = item.FilesFormattedForPlayer.Count(); // Use the old api when there is just one file in order to avoid the annoying ding sound after playback. if (numFiles == 1) { return true; } // MediaCollections have performance issues with a large number of items if (numFiles > 200) { return true; } if (item.HasVideo) { return false; } // No videos found, use the legacy api return true; }
public static bool UseLegacyApi(PlayableItem item) { // Extenders don't support MediaCollections if (Application.RunningOnExtender) { return(true); } int numFiles = item.FilesFormattedForPlayer.Count(); // Use the old api when there is just one file in order to avoid the annoying ding sound after playback. if (numFiles == 1) { return(true); } // MediaCollections have performance issues with a large number of items if (numFiles > 200) { return(true); } if (item.HasVideo) { return(false); } // No videos found, use the legacy api return(true); }
public static Microsoft.MediaCenter.MediaType GetMediaType(PlayableItem playable) { if (playable.HasVideo) { return(Microsoft.MediaCenter.MediaType.Video); } return(Microsoft.MediaCenter.MediaType.Audio); }
/// <summary> /// Then playback is based on Media items, this will populate the MediaCollection using the items /// </summary> public static void PopulateMediaCollectionUsingMediaItems(PlaybackController controllerInstance, MediaCollection coll, PlayableItem playable) { int currentFileIndex = 0; int collectionIndex = coll.Count; int numItems = playable.MediaItems.Count(); for (int mediaIndex = 0; mediaIndex < numItems; mediaIndex++) { Media media = playable.MediaItems.ElementAt(mediaIndex); IEnumerable<string> files = controllerInstance.GetPlayableFiles(media); int numFiles = files.Count(); // Create a MediaCollectionItem for each file to play for (int i = 0; i < numFiles; i++) { string path = files.ElementAt(i); Dictionary<string, object> friendlyData = new Dictionary<string, object>(); // Embed the playlist index, since we could have multiple playlists queued up // which prevents us from being able to use MediaCollection.CurrentIndex friendlyData["FilePlaylistPosition"] = currentFileIndex.ToString(); // Embed the PlayableItemId so we can identify which one to track progress for friendlyData["PlayableItemId"] = playable.Id.ToString(); // Embed the Media index so we can identify which one to track progress for friendlyData["MediaIndex"] = mediaIndex.ToString(); // Set a friendly title friendlyData["Title"] = media.Name; coll.AddItem(path, collectionIndex, -1, string.Empty, friendlyData); currentFileIndex++; collectionIndex++; } } }
/// <summary> /// When playback is based purely on file paths, this will populate the MediaCollection using the paths /// </summary> public static void PopulateMediaCollectionUsingFiles(MediaCollection coll, PlayableItem playable, int startIndex, int count) { int numFiles = playable.Files.Count(); string idString = playable.Id.ToString(); // Create a MediaCollectionItem for each file to play for (int i = startIndex; i < count; i++) { string path = playable.Files.ElementAt(i); Dictionary <string, object> friendlyData = new Dictionary <string, object>(); // Embed the playlist index, since we could have multiple playlists queued up // which prevents us from being able to use MediaCollection.CurrentIndex friendlyData["FilePlaylistPosition"] = i.ToString(); // Embed the PlayableItemId so we can identify which one to track progress for friendlyData["PlayableItemId"] = idString; coll.AddItem(path, i, -1, string.Empty, friendlyData); } }
public static bool RequiresWPL(PlayableItem playable) { return playable.FilesFormattedForPlayer.Count() > 1 && playable.HasVideo; }
/// <summary> /// When playback is based purely on file paths, this will populate the MediaCollection using the paths /// </summary> public static void PopulateMediaCollectionUsingFiles(MediaCollection coll, PlayableItem playable, int startIndex, int count) { int numFiles = playable.Files.Count(); string idString = playable.Id.ToString(); // Create a MediaCollectionItem for each file to play for (int i = startIndex; i < count; i++) { string path = playable.Files.ElementAt(i); Dictionary<string, object> friendlyData = new Dictionary<string, object>(); // Embed the playlist index, since we could have multiple playlists queued up // which prevents us from being able to use MediaCollection.CurrentIndex friendlyData["FilePlaylistPosition"] = i.ToString(); // Embed the PlayableItemId so we can identify which one to track progress for friendlyData["PlayableItemId"] = idString; coll.AddItem(path, i, -1, string.Empty, friendlyData); } }
/// <summary> /// When playback is based purely on file paths, this will populate the MediaCollection using the paths /// </summary> public static void PopulateMediaCollectionUsingFiles(MediaCollection coll, PlayableItem playable) { PopulateMediaCollectionUsingFiles(coll, playable, 0, playable.Files.Count()); }
public static Microsoft.MediaCenter.MediaType GetMediaType(PlayableItem playable) { if (playable.HasVideo) { return Microsoft.MediaCenter.MediaType.Video; } return Microsoft.MediaCenter.MediaType.Audio; }
private void QueuePlayableItemLegacy(PlayableItem playable) { Microsoft.MediaCenter.MediaType type = MediaType.Audio; bool success = true; foreach (string file in playable.FilesFormattedForPlayer) { if (!PlaybackControllerHelper.CallPlayMedia(AddInHost.Current.MediaCenterEnvironment, type, file, true)) { success = false; break; } } if (!success) { OnErrorPlayingItem(playable, "PlayMedia returned false"); } }
/// <summary> /// Then playback is based on Media items, this will populate the MediaCollection using the items /// </summary> public static void PopulateMediaCollectionUsingMediaItems(PlaybackController controllerInstance, MediaCollection coll, PlayableItem playable) { int currentFileIndex = 0; int collectionIndex = coll.Count; int numItems = playable.MediaItems.Count(); for (int mediaIndex = 0; mediaIndex < numItems; mediaIndex++) { Media media = playable.MediaItems.ElementAt(mediaIndex); IEnumerable <string> files = controllerInstance.GetPlayableFiles(media); int numFiles = files.Count(); // Create a MediaCollectionItem for each file to play for (int i = 0; i < numFiles; i++) { string path = files.ElementAt(i); Dictionary <string, object> friendlyData = new Dictionary <string, object>(); // Embed the playlist index, since we could have multiple playlists queued up // which prevents us from being able to use MediaCollection.CurrentIndex friendlyData["FilePlaylistPosition"] = currentFileIndex.ToString(); // Embed the PlayableItemId so we can identify which one to track progress for friendlyData["PlayableItemId"] = playable.Id.ToString(); // Embed the Media index so we can identify which one to track progress for friendlyData["MediaIndex"] = mediaIndex.ToString(); // Set a friendly title friendlyData["Title"] = media.Name; var song = media as Song; if (song != null) { var itemImage = song.PrimaryImagePath; // The following "friendly" data fields are undocumented but working for song items... if (playable.Folder != null) { // This is either a music album or a playlist friendlyData["AlbumTitle"] = playable.Folder.Name; if (string.IsNullOrEmpty(itemImage)) { itemImage = playable.Folder.PrimaryImagePath; } } else { friendlyData["AlbumTitle"] = song.Album; } friendlyData["AlbumArtist"] = song.AlbumArtist; friendlyData["Artist"] = song.Artist; friendlyData["TrackNumber"] = currentFileIndex; if (song.PremierDate != DateTime.MinValue) { friendlyData["YearReleased"] = song.PremierDate.Year; } if (!string.IsNullOrEmpty(itemImage)) { friendlyData["AlbumCoverUrl"] = itemImage; } } coll.AddItem(path, collectionIndex, -1, string.Empty, friendlyData); currentFileIndex++; collectionIndex++; } } }
/// <summary> /// Calls PlayMedia /// </summary> private bool CallPlayMediaLegacy(MediaCenterEnvironment mediaCenterEnvironment, PlayableItem playable) { Microsoft.MediaCenter.MediaType type = PlaybackControllerHelper.GetMediaType(playable); bool playedWithPlaylist = false; // Need to create a playlist if (PlaybackControllerHelper.RequiresWPL(playable)) { IEnumerable<string> files = playable.FilesFormattedForPlayer; string playlistFile = PlaybackControllerHelper.CreateWPLPlaylist(playable.Id.ToString(), files, playable.StartPlaylistPosition); if (!PlaybackControllerHelper.CallPlayMedia(mediaCenterEnvironment, type, playlistFile, false)) { return false; } playedWithPlaylist = true; } // If we're playing a dvd and the last item played was a MediaCollection, we need to make sure the MediaCollection has // fully cleared out of the player or there will be quirks such as ff/rew remote buttons not working if (playable.HasMediaItems) { Video video = playable.MediaItems.First() as Video; Microsoft.MediaCenter.Extensibility.MediaType lastMediaType = PlaybackControllerHelper.GetCurrentMediaType(); if (video != null && video.MediaType == Library.MediaType.DVD && (lastMediaType == Microsoft.MediaCenter.Extensibility.MediaType.MediaCollection || lastMediaType == Microsoft.MediaCenter.Extensibility.MediaType.Unknown)) { System.Threading.Thread.Sleep(500); } } if (!playedWithPlaylist) { bool queue = false; foreach (string fileToPlay in playable.FilesFormattedForPlayer) { if (!PlaybackControllerHelper.CallPlayMedia(mediaCenterEnvironment, type, fileToPlay, queue)) { return false; } queue = true; } } return true; }
/// <summary> /// Calls PlayMedia using either a MediaCollection or a single file /// </summary> private bool CallPlayMediaForPlayableItem(MediaCenterEnvironment mediaCenterEnvironment, PlayableItem playable) { if (PlaybackControllerHelper.UseLegacyApi(playable)) { bool success = CallPlayMediaLegacy(mediaCenterEnvironment, playable); _CurrentMediaCollection = null; return success; } else { return CallPlayMediaUsingMediaCollection(mediaCenterEnvironment, playable); } }
protected virtual void QueuePlayableItem(PlayableItem playable) { if (_CurrentMediaCollection == null) { QueuePlayableItemLegacy(playable); } else { QueuePlayableItemIntoMediaCollection(playable); } }
/// <summary> /// Plays or queues Media /// </summary> protected virtual void PlayPlayableItem(PlayableItem playable) { _HasStartedPlaying = false; // Get this now since we'll be using it frequently MediaCenterEnvironment mediaCenterEnvironment = AddInHost.Current.MediaCenterEnvironment; try { // Attach event handler to MediaCenterEnvironment // We need this because if you press stop on a dvd menu without ever playing, Transport.PropertyChanged will never fire mediaCenterEnvironment.PropertyChanged -= MediaCenterEnvironment_PropertyChanged; mediaCenterEnvironment.PropertyChanged += MediaCenterEnvironment_PropertyChanged; if (!CallPlayMediaForPlayableItem(mediaCenterEnvironment, playable)) { mediaCenterEnvironment.PropertyChanged -= MediaCenterEnvironment_PropertyChanged; OnErrorPlayingItem(playable, "PlayMedia returned false"); return; } MediaExperience exp = mediaCenterEnvironment.MediaExperience ?? PlaybackControllerHelper.GetMediaExperienceUsingReflection(); if (exp != null) { MediaTransport transport = exp.Transport; if (transport != null) { transport.PropertyChanged -= MediaTransport_PropertyChanged; transport.PropertyChanged += MediaTransport_PropertyChanged; // If using the legacy api we have to resume manually if (_CurrentMediaCollection == null) { long startPosition = playable.StartPositionTicks; if (startPosition > 0) { TimeSpan startPos = TimeSpan.FromTicks(startPosition); Logger.ReportVerbose("Seeking to " + startPos.ToString()); transport.Position = startPos; } } } else { Logger.ReportWarning("PlayPlayableItem: MediaTransport is null"); } if (playable.GoFullScreen) { Logger.ReportVerbose("Going fullscreen"); exp.GoToFullScreen(); } } else { Logger.ReportWarning("PlayPlayableItem: MediaExperience is null"); } } catch (Exception ex) { OnErrorPlayingItem(playable, ex); } }
/// <summary> /// Plays Media /// </summary> protected override void PlayMediaInternal(PlayableItem playable) { if (playable.QueueItem) { Microsoft.MediaCenter.UI.Application.DeferredInvoke(_ => QueuePlayableItem(playable)); } else { Microsoft.MediaCenter.UI.Application.DeferredInvoke(_ => PlayPlayableItem(playable)); } }
/// <summary> /// Then playback is based on Media items, this will populate the MediaCollection using the items /// </summary> public static void PopulateMediaCollectionUsingMediaItems(PlaybackController controllerInstance, MediaCollection coll, PlayableItem playable) { int currentFileIndex = 0; int collectionIndex = coll.Count; int numItems = playable.MediaItems.Count(); for (int mediaIndex = 0; mediaIndex < numItems; mediaIndex++) { Media media = playable.MediaItems.ElementAt(mediaIndex); IEnumerable <string> files = controllerInstance.GetPlayableFiles(media); int numFiles = files.Count(); // Create a MediaCollectionItem for each file to play for (int i = 0; i < numFiles; i++) { string path = files.ElementAt(i); Dictionary <string, object> friendlyData = new Dictionary <string, object>(); // Embed the playlist index, since we could have multiple playlists queued up // which prevents us from being able to use MediaCollection.CurrentIndex friendlyData["FilePlaylistPosition"] = currentFileIndex.ToString(); // Embed the PlayableItemId so we can identify which one to track progress for friendlyData["PlayableItemId"] = playable.Id.ToString(); // Embed the Media index so we can identify which one to track progress for friendlyData["MediaIndex"] = mediaIndex.ToString(); // Set a friendly title friendlyData["Title"] = media.Name; coll.AddItem(path, collectionIndex, -1, string.Empty, friendlyData); currentFileIndex++; collectionIndex++; } } }
public static bool RequiresWPL(PlayableItem playable) { return(playable.FilesFormattedForPlayer.Count() > 1 && playable.HasVideo); }
public static string CreateASXPlaylist(PlayableItem playable) { // we need to filter out all invalid chars var name = new string(playable.Id.ToString() .ToCharArray() .Where(e => !Path.GetInvalidFileNameChars().Contains(e)) .ToArray()); var playListFile = Path.Combine(ApplicationPaths.AutoPlaylistPath, name + ".asx"); BaseItem primaryItem = playable.Folder; if (primaryItem == null) { primaryItem = playable.MediaItems.First(); } StringWriter writer = new StringWriter(); XmlTextWriter xml = new XmlTextWriter(writer); xml.Indentation = 2; xml.IndentChar = ' '; xml.WriteStartElement("ASX"); xml.WriteAttributeString("version", "3.0"); xml.WriteStartElement("TITLE"); xml.WriteString(primaryItem.Name); xml.WriteEndElement(); foreach (var mediaItem in playable.MediaItems) { foreach (string file in mediaItem.Files) { xml.WriteStartElement("ENTRY"); xml.WriteStartElement("TITLE"); xml.WriteString(mediaItem.Name); xml.WriteEndElement(); if (!string.IsNullOrEmpty(mediaItem.PrimaryImagePath)) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "WM/AlbumCoverURL"); xml.WriteAttributeString("value", mediaItem.PrimaryImagePath); xml.WriteEndElement(); } if (!string.IsNullOrEmpty(mediaItem.Overview)) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "Description"); xml.WriteAttributeString("value", mediaItem.Overview); xml.WriteEndElement(); } var song = mediaItem as Song; if (song != null) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "MediaType"); xml.WriteAttributeString("value", "audio"); xml.WriteEndElement(); if (song.CriticRating.HasValue) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "UserRating"); xml.WriteAttributeString("value", Math.Ceiling(song.CriticRating.Value).ToString()); xml.WriteEndElement(); } if (song.PremierDate != DateTime.MinValue) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "ReleaseDateDay"); xml.WriteAttributeString("value", song.PremierDate.Day.ToString()); xml.WriteEndElement(); xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "ReleaseDateMonth"); xml.WriteAttributeString("value", song.PremierDate.Month.ToString()); xml.WriteEndElement(); xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "ReleaseDateYear"); xml.WriteAttributeString("value", song.PremierDate.Year.ToString()); xml.WriteEndElement(); } if (song.RuntimeTicks > 0) { var ts = TimeSpan.FromTicks(song.RuntimeTicks); xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "Duration"); xml.WriteAttributeString("value", ts.TotalSeconds.ToString("F0")); xml.WriteEndElement(); } if (!string.IsNullOrEmpty(song.Artist)) { xml.WriteStartElement("AUTHOR"); xml.WriteString(song.Artist); xml.WriteEndElement(); } if (!string.IsNullOrEmpty(song.AlbumArtist)) { xml.WriteStartElement("WM/AlbumArtist"); xml.WriteString(song.AlbumArtist); xml.WriteEndElement(); } var albumName = song.Album; if (string.IsNullOrEmpty(albumName) && primaryItem is Folder) { albumName = primaryItem.Name; } if (!string.IsNullOrEmpty(albumName)) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "WM/AlbumTitle"); xml.WriteAttributeString("value", albumName); xml.WriteEndElement(); } if (song.Genres != null) { foreach (var genre in song.Genres) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "WM/Genre"); xml.WriteAttributeString("value", genre); xml.WriteEndElement(); } } } xml.WriteStartElement("REF"); xml.WriteAttributeString("href", file); xml.WriteEndElement(); xml.WriteEndElement(); } } xml.WriteEndElement(); File.WriteAllText(playListFile, writer.ToString()); return playListFile; }
private bool CallPlayMediaUsingMediaCollection(MediaCenterEnvironment mediaCenterEnvironment, PlayableItem playable) { MediaCollection coll = new MediaCollection(); // Create a MediaCollectionItem for each file to play if (playable.HasMediaItems) { PlaybackControllerHelper.PopulateMediaCollectionUsingMediaItems(this, coll, playable); } else { PlaybackControllerHelper.PopulateMediaCollectionUsingFiles(coll, playable); } // Set starting position if we're resuming if (playable.Resume) { var playstate = playable.MediaItems.First().PlaybackStatus; coll.CurrentIndex = playstate.PlaylistPosition; coll[playstate.PlaylistPosition].Start = new TimeSpan(playstate.PositionTicks); } _CurrentMediaCollection = coll; bool success = PlaybackControllerHelper.CallPlayMedia(mediaCenterEnvironment, MediaType.MediaCollection, _CurrentMediaCollection, false); if (!success) { _CurrentMediaCollection = null; } return success; }
public static string CreateASXPlaylist(PlayableItem playable) { // we need to filter out all invalid chars var name = new string(playable.Id.ToString() .ToCharArray() .Where(e => !Path.GetInvalidFileNameChars().Contains(e)) .ToArray()); var playListFile = Path.Combine(ApplicationPaths.AutoPlaylistPath, name + ".asx"); BaseItem primaryItem = playable.Folder; if (primaryItem == null) { primaryItem = playable.MediaItems.First(); } StringWriter writer = new StringWriter(); XmlTextWriter xml = new XmlTextWriter(writer); xml.Indentation = 2; xml.IndentChar = ' '; xml.WriteStartElement("ASX"); xml.WriteAttributeString("version", "3.0"); xml.WriteStartElement("TITLE"); xml.WriteString(primaryItem.Name); xml.WriteEndElement(); foreach (var mediaItem in playable.MediaItems) { foreach (string file in mediaItem.Files) { xml.WriteStartElement("ENTRY"); xml.WriteStartElement("TITLE"); xml.WriteString(mediaItem.Name); xml.WriteEndElement(); if (!string.IsNullOrEmpty(mediaItem.PrimaryImagePath)) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "WM/AlbumCoverURL"); xml.WriteAttributeString("value", mediaItem.PrimaryImagePath); xml.WriteEndElement(); } if (!string.IsNullOrEmpty(mediaItem.Overview)) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "Description"); xml.WriteAttributeString("value", mediaItem.Overview); xml.WriteEndElement(); } var song = mediaItem as Song; if (song != null) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "MediaType"); xml.WriteAttributeString("value", "audio"); xml.WriteEndElement(); if (song.CriticRating.HasValue) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "UserRating"); xml.WriteAttributeString("value", Math.Ceiling(song.CriticRating.Value).ToString()); xml.WriteEndElement(); } if (song.PremierDate != DateTime.MinValue) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "ReleaseDateDay"); xml.WriteAttributeString("value", song.PremierDate.Day.ToString()); xml.WriteEndElement(); xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "ReleaseDateMonth"); xml.WriteAttributeString("value", song.PremierDate.Month.ToString()); xml.WriteEndElement(); xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "ReleaseDateYear"); xml.WriteAttributeString("value", song.PremierDate.Year.ToString()); xml.WriteEndElement(); } if (song.RuntimeTicks > 0) { var ts = TimeSpan.FromTicks(song.RuntimeTicks); xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "Duration"); xml.WriteAttributeString("value", ts.TotalSeconds.ToString("F0")); xml.WriteEndElement(); } if (!string.IsNullOrEmpty(song.Artist)) { xml.WriteStartElement("AUTHOR"); xml.WriteString(song.Artist); xml.WriteEndElement(); } if (!string.IsNullOrEmpty(song.AlbumArtist)) { xml.WriteStartElement("WM/AlbumArtist"); xml.WriteString(song.AlbumArtist); xml.WriteEndElement(); } var albumName = song.Album; if (string.IsNullOrEmpty(albumName) && primaryItem is Folder) { albumName = primaryItem.Name; } if (!string.IsNullOrEmpty(albumName)) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "WM/AlbumTitle"); xml.WriteAttributeString("value", albumName); xml.WriteEndElement(); } if (song.Genres != null) { foreach (var genre in song.Genres) { xml.WriteStartElement("PARAM"); xml.WriteAttributeString("name", "WM/Genre"); xml.WriteAttributeString("value", genre); xml.WriteEndElement(); } } } xml.WriteStartElement("REF"); xml.WriteAttributeString("href", file); xml.WriteEndElement(); xml.WriteEndElement(); } } xml.WriteEndElement(); File.WriteAllText(playListFile, writer.ToString()); return(playListFile); }
private void QueuePlayableItemIntoMediaCollection(PlayableItem playable) { try { // Create a MediaCollectionItem for each file to play if (playable.HasMediaItems) { PlaybackControllerHelper.PopulateMediaCollectionUsingMediaItems(this, _CurrentMediaCollection, playable); } else { PlaybackControllerHelper.PopulateMediaCollectionUsingFiles(_CurrentMediaCollection, playable); } } catch (Exception ex) { OnErrorPlayingItem(playable, ex); } }
/// <summary> /// Then playback is based on Media items, this will populate the MediaCollection using the items /// </summary> public static void PopulateMediaCollectionUsingMediaItems(PlaybackController controllerInstance, MediaCollection coll, PlayableItem playable) { int currentFileIndex = 0; int collectionIndex = coll.Count; int numItems = playable.MediaItems.Count(); for (int mediaIndex = 0; mediaIndex < numItems; mediaIndex++) { Media media = playable.MediaItems.ElementAt(mediaIndex); IEnumerable<string> files = controllerInstance.GetPlayableFiles(media); int numFiles = files.Count(); // Create a MediaCollectionItem for each file to play for (int i = 0; i < numFiles; i++) { string path = files.ElementAt(i); Dictionary<string, object> friendlyData = new Dictionary<string, object>(); // Embed the playlist index, since we could have multiple playlists queued up // which prevents us from being able to use MediaCollection.CurrentIndex friendlyData["FilePlaylistPosition"] = currentFileIndex.ToString(); // Embed the PlayableItemId so we can identify which one to track progress for friendlyData["PlayableItemId"] = playable.Id.ToString(); // Embed the Media index so we can identify which one to track progress for friendlyData["MediaIndex"] = mediaIndex.ToString(); // Set a friendly title friendlyData["Title"] = media.Name; var song = media as Song; if (song != null) { var itemImage = song.PrimaryImagePath; // The following "friendly" data fields are undocumented but working for song items... if (playable.Folder != null) { // This is either a music album or a playlist friendlyData["AlbumTitle"] = playable.Folder.Name; if (string.IsNullOrEmpty(itemImage)) { itemImage = playable.Folder.PrimaryImagePath; } } else { friendlyData["AlbumTitle"] = song.Album; } friendlyData["AlbumArtist"] = song.AlbumArtist; friendlyData["Artist"] = song.Artist; friendlyData["TrackNumber"] = currentFileIndex; if (song.PremierDate != DateTime.MinValue) { friendlyData["YearReleased"] = song.PremierDate.Year; } if (!string.IsNullOrEmpty(itemImage)) { friendlyData["AlbumCoverUrl"] = itemImage; } } coll.AddItem(path, collectionIndex, -1, string.Empty, friendlyData); currentFileIndex++; collectionIndex++; } } }