diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index ec6ca7737b..7289ac0862 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -714,8 +714,10 @@ namespace MediaBrowser.Api.Playback /// true if the specified stream is H264; otherwise, false. protected bool IsH264(MediaStream stream) { - return stream.Codec.IndexOf("264", StringComparison.OrdinalIgnoreCase) != -1 || - stream.Codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1; + var codec = stream.Codec ?? string.Empty; + + return codec.IndexOf("264", StringComparison.OrdinalIgnoreCase) != -1 || + codec.IndexOf("avc", StringComparison.OrdinalIgnoreCase) != -1; } /// diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index bea4ba37ed..30ba25ae47 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -367,16 +367,6 @@ namespace MediaBrowser.Api.Playback.Hls { var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); - if (string.Equals(request.AudioCodec, "copy", StringComparison.OrdinalIgnoreCase)) - { - throw new ArgumentException("Audio codec copy is not allowed here."); - } - - if (string.Equals(request.VideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) - { - throw new ArgumentException("Video codec copy is not allowed here."); - } - if (string.IsNullOrEmpty(request.MediaSourceId)) { throw new ArgumentException("MediaSourceId is required"); @@ -638,7 +628,7 @@ namespace MediaBrowser.Api.Playback.Hls // See if we can save come cpu cycles by avoiding encoding if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase)) { - return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy"; + return state.VideoStream != null && IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy"; } var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 02acf7e718..06fa4065c2 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -139,7 +139,7 @@ namespace MediaBrowser.Api.Playback.Hls // See if we can save come cpu cycles by avoiding encoding if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) { - return IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy"; + return state.VideoStream != null && IsH264(state.VideoStream) ? "-codec:v:0 copy -bsf:v h264_mp4toannexb" : "-codec:v:0 copy"; } var keyFrameArg = string.Format(" -force_key_frames expr:gte(t,n_forced*{0})", diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index 09bf6e2328..81f1d70d39 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -94,6 +94,8 @@ namespace MediaBrowser.Common.Net public CacheMode CacheMode { get; set; } public TimeSpan CacheLength { get; set; } + public int TimeoutMs { get; set; } + private string GetHeaderValue(string name) { string value; @@ -115,6 +117,8 @@ namespace MediaBrowser.Common.Net LogRequest = true; CacheMode = CacheMode.None; + + TimeoutMs = 20000; } public void SetPostData(IDictionary values) diff --git a/MediaBrowser.Controller/Library/IMetadataSaver.cs b/MediaBrowser.Controller/Library/IMetadataSaver.cs index ce8feb4c6c..f13cf45879 100644 --- a/MediaBrowser.Controller/Library/IMetadataSaver.cs +++ b/MediaBrowser.Controller/Library/IMetadataSaver.cs @@ -1,5 +1,4 @@ using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; using System.Threading; namespace MediaBrowser.Controller.Library diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index 3f9ce66c5c..b92a4174ef 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs @@ -1,7 +1,9 @@ -using MediaBrowser.Common.Extensions; +using System.Linq; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Dlna.Didl; @@ -85,7 +87,9 @@ namespace MediaBrowser.Dlna.ContentDirectory { var id = sparams["ObjectID"]; - var item = GetItemFromObjectId(id, user); + var serverItem = GetItemFromObjectId(id, user); + + var item = serverItem.Item; var newbookmark = int.Parse(sparams["PosSecond"], _usCulture); @@ -173,49 +177,48 @@ namespace MediaBrowser.Dlna.ContentDirectory //didl.SetAttribute("xmlns:sec", NS_SEC); result.AppendChild(didl); - var item = GetItemFromObjectId(id, user); + var serverItem = GetItemFromObjectId(id, user); + var item = serverItem.Item; var totalCount = 0; if (string.Equals(flag, "BrowseMetadata")) { - var folder = item as Folder; - - if (folder == null) + if (item.IsFolder || serverItem.StubType.HasValue) { - result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, item, null, deviceId, filter)); + var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requested).ConfigureAwait(false)); + totalCount = childrenResult.TotalRecordCount; + + result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, item, serverItem.StubType, null, totalCount, filter, id)); } else { - var childrenResult = (await GetUserItems(folder, user, sortCriteria, start, requested).ConfigureAwait(false)); - totalCount = childrenResult.TotalRecordCount; - - result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, folder, totalCount, filter, id)); + result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, item, null, null, deviceId, filter)); } + provided++; } else { - var folder = (Folder)item; - - var childrenResult = (await GetUserItems(folder, user, sortCriteria, start, requested).ConfigureAwait(false)); + var childrenResult = (await GetUserItems(item, serverItem.StubType, user, sortCriteria, start, requested).ConfigureAwait(false)); totalCount = childrenResult.TotalRecordCount; provided = childrenResult.Items.Length; foreach (var i in childrenResult.Items) { - if (i.IsFolder) + var displayStubType = GetDisplayStubType(i, serverItem.Item); + + if (i.IsFolder || displayStubType.HasValue) { - var f = (Folder)i; - var childCount = (await GetUserItems(f, user, sortCriteria, null, 0).ConfigureAwait(false)) + var childCount = (await GetUserItems(i, displayStubType, user, sortCriteria, null, 0).ConfigureAwait(false)) .TotalRecordCount; - result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter)); + result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, i, displayStubType, item, childCount, filter)); } else { - result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, folder, deviceId, filter)); + result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, item, serverItem.StubType, deviceId, filter)); } } } @@ -231,6 +234,24 @@ namespace MediaBrowser.Dlna.ContentDirectory }; } + private StubType? GetDisplayStubType(BaseItem item, BaseItem context) + { + if (context == null || context.IsFolder) + { + var movie = item as Movie; + if (movie != null) + { + if (movie.LocalTrailerIds.Count > 0 || + movie.SpecialFeatureIds.Count > 0) + { + return StubType.Folder; + } + } + } + + return null; + } + private async Task>> HandleSearch(Headers sparams, User user, string deviceId) { var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", "")); @@ -269,9 +290,11 @@ namespace MediaBrowser.Dlna.ContentDirectory result.AppendChild(didl); - var folder = (Folder)GetItemFromObjectId(sparams["ContainerID"], user); + var serverItem = GetItemFromObjectId(sparams["ContainerID"], user); - var childrenResult = (await GetChildrenSorted(folder, user, searchCriteria, sortCriteria, start, requested).ConfigureAwait(false)); + var item = serverItem.Item; + + var childrenResult = (await GetChildrenSorted(item, user, searchCriteria, sortCriteria, start, requested).ConfigureAwait(false)); var totalCount = childrenResult.TotalRecordCount; @@ -281,15 +304,14 @@ namespace MediaBrowser.Dlna.ContentDirectory { if (i.IsFolder) { - var f = (Folder)i; - var childCount = (await GetChildrenSorted(f, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false)) + var childCount = (await GetChildrenSorted(i, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false)) .TotalRecordCount; - result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter)); + result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, i, null, item, childCount, filter)); } else { - result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, folder, deviceId, filter)); + result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, i, item, serverItem.StubType, deviceId, filter)); } } @@ -304,8 +326,10 @@ namespace MediaBrowser.Dlna.ContentDirectory }; } - private async Task> GetChildrenSorted(Folder folder, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit) + private async Task> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit) { + var folder = (Folder)item; + var sortOrders = new List(); if (!folder.IsPreSorted) { @@ -340,7 +364,7 @@ namespace MediaBrowser.Dlna.ContentDirectory //items = items.OfType(); isFolder = true; } - + return await folder.GetItems(new InternalItemsQuery { Limit = limit, @@ -356,8 +380,20 @@ namespace MediaBrowser.Dlna.ContentDirectory }).ConfigureAwait(false); } - private async Task> GetUserItems(Folder folder, User user, SortCriteria sort, int? startIndex, int? limit) + private async Task> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit) { + if (stubType.HasValue) + { + var movie = item as Movie; + + if (movie != null) + { + return await GetMovieItems(movie).ConfigureAwait(false); + } + } + + var folder = (Folder)item; + var sortOrders = new List(); if (!folder.IsPreSorted) { @@ -376,6 +412,23 @@ namespace MediaBrowser.Dlna.ContentDirectory }).ConfigureAwait(false); } + private Task> GetMovieItems(Movie item) + { + var list = new List(); + + list.Add(item); + + list.AddRange(item.LocalTrailerIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null)); + list.AddRange(item.SpecialFeatureIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null)); + list.AddRange(item.ThemeVideoIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null)); + + return Task.FromResult(new QueryResult + { + Items = list.ToArray(), + TotalRecordCount = list.Count + }); + } + private bool FilterUnsupportedContent(BaseItem i, User user) { // Unplayable @@ -399,26 +452,50 @@ namespace MediaBrowser.Dlna.ContentDirectory return true; } - private BaseItem GetItemFromObjectId(string id, User user) + private ServerItem GetItemFromObjectId(string id, User user) { return DidlBuilder.IsIdRoot(id) - ? user.RootFolder + ? new ServerItem { Item = user.RootFolder } : ParseItemId(id, user); } - private BaseItem ParseItemId(string id, User user) + private ServerItem ParseItemId(string id, User user) { Guid itemId; + StubType? stubType = null; + + if (id.StartsWith("folder_", StringComparison.OrdinalIgnoreCase)) + { + stubType = StubType.Folder; + id = id.Split(new[] { '_' }, 2)[1]; + } if (Guid.TryParse(id, out itemId)) { - return _libraryManager.GetItemById(itemId); + var item = _libraryManager.GetItemById(itemId); + + return new ServerItem + { + Item = item, + StubType = stubType + }; } Logger.Error("Error parsing item Id: {0}. Returning user root folder.", id); - return user.RootFolder; + return new ServerItem { Item = user.RootFolder }; } } + + internal class ServerItem + { + public BaseItem Item { get; set; } + public StubType? StubType { get; set; } + } + + public enum StubType + { + Folder = 0 + } } diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 6bc50b1ea9..912612d299 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Playlists; +using MediaBrowser.Dlna.ContentDirectory; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; @@ -63,25 +64,31 @@ namespace MediaBrowser.Dlna.Didl result.AppendChild(didl); - result.DocumentElement.AppendChild(GetItemElement(result, item, context, deviceId, filter, streamInfo)); + result.DocumentElement.AppendChild(GetItemElement(result, item, context, null, deviceId, filter, streamInfo)); return result.DocumentElement.OuterXml; } - public XmlElement GetItemElement(XmlDocument doc, BaseItem item, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo = null) + public XmlElement GetItemElement(XmlDocument doc, BaseItem item, BaseItem context, StubType? contextStubType, string deviceId, Filter filter, StreamInfo streamInfo = null) { + var clientId = GetClientId(item, null); + var element = doc.CreateElement(string.Empty, "item", NS_DIDL); element.SetAttribute("restricted", "1"); - element.SetAttribute("id", item.Id.ToString("N")); + element.SetAttribute("id", clientId); - if (item.Parent != null) + if (context != null) { - element.SetAttribute("parentID", item.Parent.Id.ToString("N")); + element.SetAttribute("parentID", GetClientId(context, contextStubType)); + } + else if (item.Parent != null) + { + element.SetAttribute("parentID", GetClientId(item.Parent, null)); } //AddBookmarkInfo(item, user, element); - AddGeneralProperties(item, context, element, filter); + AddGeneralProperties(item, null, context, element, filter); // refID? // storeAttribute(itemNode, object, ClassProperties.REF_ID, false); @@ -111,14 +118,14 @@ namespace MediaBrowser.Dlna.Didl { var sources = _user == null ? video.GetMediaSources(true).ToList() : video.GetMediaSources(true, _user).ToList(); - streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions - { - ItemId = video.Id.ToString("N"), - MediaSources = sources, - Profile = _profile, - DeviceId = deviceId, - MaxBitrate = _profile.MaxStreamingBitrate - }); + streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions + { + ItemId = GetClientId(video), + MediaSources = sources, + Profile = _profile, + DeviceId = deviceId, + MaxBitrate = _profile.MaxStreamingBitrate + }); } var targetWidth = streamInfo.TargetWidth; @@ -311,7 +318,7 @@ namespace MediaBrowser.Dlna.Didl return item.Name; } - + private void AddAudioResource(XmlElement container, IHasMediaSources audio, string deviceId, Filter filter, StreamInfo streamInfo = null) { var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL); @@ -322,7 +329,7 @@ namespace MediaBrowser.Dlna.Didl streamInfo = new StreamBuilder().BuildAudioItem(new AudioOptions { - ItemId = audio.Id.ToString("N"), + ItemId = GetClientId(audio), MediaSources = sources, Profile = _profile, DeviceId = deviceId @@ -403,8 +410,8 @@ namespace MediaBrowser.Dlna.Didl public static bool IsIdRoot(string id) { - if (string.IsNullOrWhiteSpace(id) || - + if (string.IsNullOrWhiteSpace(id) || + string.Equals(id, "0", StringComparison.OrdinalIgnoreCase) // Samsung sometimes uses 1 as root @@ -416,13 +423,15 @@ namespace MediaBrowser.Dlna.Didl return false; } - public XmlElement GetFolderElement(XmlDocument doc, BaseItem folder, int childCount, Filter filter, string requestedId = null) + public XmlElement GetFolderElement(XmlDocument doc, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null) { var container = doc.CreateElement(string.Empty, "container", NS_DIDL); container.SetAttribute("restricted", "0"); container.SetAttribute("searchable", "1"); container.SetAttribute("childCount", childCount.ToString(_usCulture)); + var clientId = GetClientId(folder, stubType); + if (string.Equals(requestedId, "0")) { container.SetAttribute("id", "0"); @@ -430,20 +439,20 @@ namespace MediaBrowser.Dlna.Didl } else { - container.SetAttribute("id", folder.Id.ToString("N")); + container.SetAttribute("id", clientId); - var parent = folder.Parent; + var parent = context ?? folder.Parent; if (parent == null) { container.SetAttribute("parentID", "0"); } else { - container.SetAttribute("parentID", parent.Id.ToString("N")); + container.SetAttribute("parentID", GetClientId(parent, null)); } } - AddCommonFields(folder, null, container, filter); + AddCommonFields(folder, stubType, null, container, filter); AddCover(folder, container); @@ -466,10 +475,11 @@ namespace MediaBrowser.Dlna.Didl /// Adds fields used by both items and folders /// /// The item. + /// Type of the item stub. /// The context. /// The element. /// The filter. - private void AddCommonFields(BaseItem item, BaseItem context, XmlElement element, Filter filter) + private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlElement element, Filter filter) { // Don't filter on dc:title because not all devices will include it in the filter // MediaMonkey for example won't display content without a title @@ -478,7 +488,7 @@ namespace MediaBrowser.Dlna.Didl AddValue(element, "dc", "title", GetDisplayName(item, context), NS_DC); } - element.AppendChild(CreateObjectClass(element.OwnerDocument, item)); + element.AppendChild(CreateObjectClass(element.OwnerDocument, item, itemStubType)); if (filter.Contains("dc:date")) { @@ -539,14 +549,14 @@ namespace MediaBrowser.Dlna.Didl AddPeople(item, element); } - private XmlElement CreateObjectClass(XmlDocument result, BaseItem item) + private XmlElement CreateObjectClass(XmlDocument result, BaseItem item, StubType? stubType) { // More types here // http://oss.linn.co.uk/repos/Public/LibUpnpCil/DidlLite/UpnpAv/Test/TestDidlLite.cs var objectClass = result.CreateElement("upnp", "class", NS_UPNP); - if (item.IsFolder) + if (item.IsFolder || stubType.HasValue) { string classType = null; @@ -560,7 +570,7 @@ namespace MediaBrowser.Dlna.Didl { classType = "object.container.person.musicArtist"; } - else if (item is Series || item is Season || item is BoxSet) + else if (item is Series || item is Season || item is BoxSet || item is Video) { classType = "object.container.album.videoAlbum"; } @@ -628,9 +638,9 @@ namespace MediaBrowser.Dlna.Didl } } - private void AddGeneralProperties(BaseItem item, BaseItem context, XmlElement element, Filter filter) + private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlElement element, Filter filter) { - AddCommonFields(item, context, element, filter); + AddCommonFields(item, itemStubType, context, element, filter); var audio = item as Audio; @@ -773,20 +783,26 @@ namespace MediaBrowser.Dlna.Didl } } - AddImageResElement(item, element, 4096, 4096, playbackPercentage, "jpg", "JPEG_LRG"); - AddImageResElement(item, element, 4096, 4096, playbackPercentage, "png", "PNG_LRG"); - AddImageResElement(item, element, 1024, 768, playbackPercentage, "jpg", "JPEG_MED"); - AddImageResElement(item, element, 640, 480, playbackPercentage, "jpg", "JPEG_SM"); + var imageLimit = _profile.DidlAlbumArtLimit ?? 100; + AddImageResElement(item, element, 160, 160, playbackPercentage, "jpg", "JPEG_TN"); - AddImageResElement(item, element, 160, 160, playbackPercentage, "png", "PNG_TN"); + + if (imageLimit > 1) + { + AddImageResElement(item, element, 4096, 4096, playbackPercentage, "jpg", "JPEG_LRG"); + AddImageResElement(item, element, 1024, 768, playbackPercentage, "jpg", "JPEG_MED"); + AddImageResElement(item, element, 640, 480, playbackPercentage, "jpg", "JPEG_SM"); + AddImageResElement(item, element, 4096, 4096, playbackPercentage, "png", "PNG_LRG"); + AddImageResElement(item, element, 160, 160, playbackPercentage, "png", "PNG_TN"); + } } - private void AddImageResElement(BaseItem item, - XmlElement element, - int maxWidth, - int maxHeight, + private void AddImageResElement(BaseItem item, + XmlElement element, + int maxWidth, + int maxHeight, int playbackPercentage, - string format, + string format, string org_Pn) { var imageInfo = GetImageInfo(item); @@ -920,6 +936,25 @@ namespace MediaBrowser.Dlna.Didl internal int? Height; } + public static string GetClientId(BaseItem item, StubType? stubType) + { + var id = item.Id.ToString("N"); + + if (stubType.HasValue) + { + id = stubType.Value.ToString().ToLower() + "_" + id; + } + + return id; + } + + public static string GetClientId(IHasMediaSources item) + { + var id = item.Id.ToString("N"); + + return id; + } + private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, int playbackPercentage, string format) { var url = string.Format("{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/{7}", diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index c4a79c22d0..f4578eca7c 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -528,7 +528,8 @@ namespace MediaBrowser.Dlna new AndroidProfile(), new DirectTvProfile(), new DishHopperJoeyProfile(), - new DefaultProfile() + new DefaultProfile(), + new PopcornHourProfile() }; foreach (var item in list) diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj index 761ce52c88..bc6e4f32a6 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -75,6 +75,7 @@ + @@ -194,6 +195,9 @@ + + +