using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; using System.Linq; namespace MediaBrowser.Dlna.PlayTo { internal class DidlBuilder { const string CRLF = "\r\n"; const string UNKNOWN = "Unknown"; const string DIDL_START = @"" + CRLF; const string DIDL_TITLE = @" {0}" + CRLF; const string DIDL_ARTIST = @"{0}" + CRLF; const string DIDL_ALBUM = @"{0}" + CRLF; const string DIDL_TRACKNUM = @"{0}" + CRLF; const string DIDL_VIDEOCLASS = @" object.item.videoItem" + CRLF; const string DIDL_AUDIOCLASS = @" object.item.audioItem.musicTrack" + CRLF; const string DIDL_IMAGE = @" {0}" + CRLF + @" {0}" + CRLF; const string DIDL_RELEASEDATE = @" {0}" + CRLF; const string DIDL_GENRE = @" {0}" + CRLF; const string DESCRIPTION = @" {0}" + CRLF; const string DIDL_VIDEO_RES = @" {4}" + CRLF; const string DIDL_AUDIO_RES = @" {3}" + CRLF; const string DIDL_IMAGE_RES = @" {0}" + CRLF; const string DIDL_ALBUMIMAGE_RES = @" {0}" + CRLF; const string DIDL_RATING = @" {0}" + CRLF; const string DIDL_END = ""; /// /// Builds a Didl MetaData object for the specified dto. /// /// The dto. /// The user identifier. /// The server address. /// The stream URL. /// The streams. /// if set to true [include image resource]. /// System.String. public static string Build(BaseItem dto, string userId, string serverAddress, string streamUrl, IEnumerable streams, bool includeImageRes) { string response = string.Format(DIDL_START, dto.Id, userId); response += string.Format(DIDL_TITLE, dto.Name.Replace("&", "and")); if (IsVideo(dto)) response += DIDL_VIDEOCLASS; else response += DIDL_AUDIOCLASS; var imageUrl = GetImageUrl(dto, serverAddress); if (!string.IsNullOrWhiteSpace(imageUrl)) { response += string.Format(DIDL_IMAGE, imageUrl); } response += string.Format(DIDL_RELEASEDATE, GetDateString(dto.PremiereDate)); //TODO Add genres to didl; response += string.Format(DIDL_GENRE, UNKNOWN); if (IsVideo(dto)) { response += string.Format(DESCRIPTION, UNKNOWN); response += GetVideoDIDL(dto, streamUrl, streams); if (includeImageRes && !string.IsNullOrWhiteSpace(imageUrl)) { response += string.Format(DIDL_IMAGE_RES, imageUrl); } } else { var audio = dto as Audio; if (audio != null) { response += string.Format(DIDL_ARTIST, audio.Artists.FirstOrDefault() ?? UNKNOWN); response += string.Format(DIDL_ALBUM, audio.Album); response += string.Format(DIDL_TRACKNUM, audio.IndexNumber ?? 0); } response += GetAudioDIDL(dto, streamUrl, streams); if (includeImageRes && !string.IsNullOrWhiteSpace(imageUrl)) { response += string.Format(DIDL_ALBUMIMAGE_RES, imageUrl); } } response += DIDL_END; return response; } private static string GetVideoDIDL(BaseItem dto, string streamUrl, IEnumerable streams) { var videostream = streams.Where(stream => stream.Type == MediaStreamType.Video).OrderBy(s => s.IsDefault ? 0 : 1).FirstOrDefault(); if (videostream == null) { // TOOD: ??? return string.Empty; } return string.Format(DIDL_VIDEO_RES, videostream.BitRate.HasValue ? videostream.BitRate.Value / 10 : 0, GetDurationString(dto), videostream.Width ?? 0, videostream.Height ?? 0, streamUrl); } private static string GetAudioDIDL(BaseItem dto, string streamUrl, IEnumerable streams) { var audiostream = streams.Where(stream => stream.Type == MediaStreamType.Audio).OrderBy(s => s.IsDefault ? 0 : 1).FirstOrDefault(); if (audiostream == null) { // TOOD: ??? return string.Empty; } return string.Format(DIDL_AUDIO_RES, audiostream.BitRate.HasValue ? audiostream.BitRate.Value / 10 : 16000, GetDurationString(dto), audiostream.SampleRate ?? 0, streamUrl); } private static string GetImageUrl(BaseItem dto, string serverAddress) { const ImageType imageType = ImageType.Primary; if (!dto.HasImage(imageType)) { dto = dto.Parents.FirstOrDefault(i => i.HasImage(imageType)); } return dto == null ? null : string.Format("{0}/Items/{1}/Images/{2}", serverAddress, dto.Id, imageType); } private static string GetDurationString(BaseItem dto) { var duration = TimeSpan.FromTicks(dto.RunTimeTicks.HasValue ? dto.RunTimeTicks.Value : 0); // TODO: Bad format string? return string.Format("{0}:{1:00}:2{00}.000", duration.Hours, duration.Minutes, duration.Seconds); } private static string GetDateString(DateTime? date) { if (!date.HasValue) return UNKNOWN; return string.Format("{0}-{1:00}-{2:00}", date.Value.Year, date.Value.Month, date.Value.Day); } private static bool IsVideo(BaseItem item) { return string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); } } }