diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 7db282dc86..5d6ef1adfe 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -491,18 +491,27 @@ namespace Emby.Dlna.ContentDirectory return GetGenreItems(item, null, user, sort, startIndex, limit); } - var collectionFolder = item as ICollectionFolder; - if (collectionFolder != null && string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase)) + if (!stubType.HasValue || stubType.Value != StubType.Folder) { - return GetMusicFolders(item, user, stubType, sort, startIndex, limit); - } - if (collectionFolder != null && string.Equals(CollectionType.Movies, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase)) - { - return GetMovieFolders(item, user, stubType, sort, startIndex, limit); - } - if (collectionFolder != null && string.Equals(CollectionType.TvShows, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase)) - { - return GetTvFolders(item, user, stubType, sort, startIndex, limit); + var collectionFolder = item as ICollectionFolder; + if (collectionFolder != null && string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase)) + { + return GetMusicFolders(item, user, stubType, sort, startIndex, limit); + } + if (collectionFolder != null && string.Equals(CollectionType.Movies, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase)) + { + return GetMovieFolders(item, user, stubType, sort, startIndex, limit); + } + if (collectionFolder != null && string.Equals(CollectionType.TvShows, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase)) + { + return GetTvFolders(item, user, stubType, sort, startIndex, limit); + } + + var userView = item as UserView; + if (userView != null && string.Equals(CollectionType.Folders, userView.ViewType, StringComparison.OrdinalIgnoreCase)) + { + return GetFolders(item, user, stubType, sort, startIndex, limit); + } } if (stubType.HasValue) @@ -513,7 +522,10 @@ namespace Emby.Dlna.ContentDirectory return GetItemsFromPerson(person, user, startIndex, limit); } - return ApplyPaging(new QueryResult(), startIndex, limit); + if (stubType.Value != StubType.Folder) + { + return ApplyPaging(new QueryResult(), startIndex, limit); + } } var folder = (Folder)item; @@ -733,6 +745,23 @@ namespace Emby.Dlna.ContentDirectory }; } + private QueryResult GetFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit) + { + var folders = user.RootFolder.GetChildren(user, true) + .OrderBy(i => i.SortName) + .Select(i => new ServerItem(i) + { + StubType = StubType.Folder + }) + .ToArray(); + + return new QueryResult + { + Items = folders, + TotalRecordCount = folders.Length + }; + } + private QueryResult GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit) { var query = new InternalItemsQuery(user) diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index bdc523c8bc..8caa086ee2 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -554,7 +554,6 @@ namespace Emby.Dlna var list = new List { new SamsungSmartTvProfile(), - new Xbox360Profile(), new XboxOneProfile(), new SonyPs3Profile(), new SonyPs4Profile(), diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 365774b0d9..eb8b178a85 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -105,7 +105,6 @@ - @@ -175,7 +174,6 @@ - diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index ba1d3a6de2..bd0a9e1f42 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -328,7 +328,7 @@ namespace Emby.Dlna.PlayTo { if (isFirst && command.StartPositionTicks.HasValue) { - playlist.Add(CreatePlaylistItem(item, user, command.StartPositionTicks.Value, null, null, null)); + playlist.Add(CreatePlaylistItem(item, user, command.StartPositionTicks.Value, command.MediaSourceId, command.AudioStreamIndex, command.SubtitleStreamIndex)); isFirst = false; } else diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs index 46639ee424..75204b234d 100644 --- a/Emby.Dlna/Profiles/DefaultProfile.cs +++ b/Emby.Dlna/Profiles/DefaultProfile.cs @@ -65,13 +65,15 @@ namespace Emby.Dlna.Profiles { new DirectPlayProfile { - Container = "m4v,mpegts,ts,3gp,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,m2v,avi,mpg,mpeg,mp4,webm,wtv,m2ts,dvr-ms", + // play all + Container = "", Type = DlnaProfileType.Video }, new DirectPlayProfile { - Container = "aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac,m4a", + // play all + Container = "", Type = DlnaProfileType.Audio } }; diff --git a/Emby.Dlna/Profiles/WdtvLiveProfile.cs b/Emby.Dlna/Profiles/WdtvLiveProfile.cs index 01e77baa25..61819c57d2 100644 --- a/Emby.Dlna/Profiles/WdtvLiveProfile.cs +++ b/Emby.Dlna/Profiles/WdtvLiveProfile.cs @@ -58,7 +58,7 @@ namespace Emby.Dlna.Profiles Container = "avi", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1", - AudioCodec = "ac3,dca,mp2,mp3,pcm,dts" + AudioCodec = "ac3,eac3,dca,mp2,mp3,pcm,dts" }, new DirectPlayProfile @@ -66,7 +66,7 @@ namespace Emby.Dlna.Profiles Container = "mpeg", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video", - AudioCodec = "ac3,dca,mp2,mp3,pcm,dts" + AudioCodec = "ac3,eac3,dca,mp2,mp3,pcm,dts" }, new DirectPlayProfile @@ -74,7 +74,7 @@ namespace Emby.Dlna.Profiles Container = "mkv", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1", - AudioCodec = "ac3,dca,aac,mp2,mp3,pcm,dts" + AudioCodec = "ac3,eac3,dca,aac,mp2,mp3,pcm,dts" }, new DirectPlayProfile @@ -82,7 +82,7 @@ namespace Emby.Dlna.Profiles Container = "ts,m2ts", Type = DlnaProfileType.Video, VideoCodec = "mpeg1video,mpeg2video,h264,vc1", - AudioCodec = "ac3,dca,mp2,mp3,aac,dts" + AudioCodec = "ac3,eac3,dca,mp2,mp3,aac,dts" }, new DirectPlayProfile @@ -90,7 +90,7 @@ namespace Emby.Dlna.Profiles Container = "mp4,mov,m4v", Type = DlnaProfileType.Video, VideoCodec = "h264,mpeg4", - AudioCodec = "ac3,aac,mp2,mp3,dca,dts" + AudioCodec = "ac3,eac3,aac,mp2,mp3,dca,dts" }, new DirectPlayProfile diff --git a/Emby.Dlna/Profiles/Xbox360Profile.cs b/Emby.Dlna/Profiles/Xbox360Profile.cs deleted file mode 100644 index 7bdcd2a6f8..0000000000 --- a/Emby.Dlna/Profiles/Xbox360Profile.cs +++ /dev/null @@ -1,326 +0,0 @@ -using MediaBrowser.Model.Dlna; -using System.Xml.Serialization; - -namespace Emby.Dlna.Profiles -{ - /// - /// Good info on xbox 360 requirements: https://code.google.com/p/jems/wiki/XBox360Notes - /// - [XmlRoot("Profile")] - public class Xbox360Profile : DefaultProfile - { - public Xbox360Profile() - { - Name = "Xbox 360"; - - // Required according to above - ModelName = "Windows Media Player Sharing"; - - ModelNumber = "12.0"; - - FriendlyName = "${HostName}: 1"; - - ModelUrl = "http://go.microsoft.com/fwlink/?LinkId=105926"; - Manufacturer = "Microsoft Corporation"; - ManufacturerUrl = "http://www.microsoft.com"; - XDlnaDoc = "DMS-1.50"; - ModelDescription = "Emby : UPnP Media Server"; - - TimelineOffsetSeconds = 40; - RequiresPlainFolders = true; - RequiresPlainVideoItems = true; - EnableMSMediaReceiverRegistrar = true; - - Identification = new DeviceIdentification - { - ModelName = "Xbox 360", - - Headers = new[] - { - new HttpHeaderInfo {Name = "User-Agent", Value = "Xbox", Match = HeaderMatchType.Substring}, - new HttpHeaderInfo {Name = "User-Agent", Value = "Xenon", Match = HeaderMatchType.Substring} - } - }; - - TranscodingProfiles = new[] - { - new TranscodingProfile - { - Container = "mp3", - AudioCodec = "mp3", - Type = DlnaProfileType.Audio - }, - new TranscodingProfile - { - Container = "asf", - VideoCodec = "wmv2", - AudioCodec = "wmav2", - Type = DlnaProfileType.Video, - TranscodeSeekInfo = TranscodeSeekInfo.Bytes, - EstimateContentLength = true - }, - new TranscodingProfile - { - Container = "jpeg", - Type = DlnaProfileType.Photo - } - }; - - DirectPlayProfiles = new[] - { - new DirectPlayProfile - { - Container = "avi", - VideoCodec = "mpeg4", - AudioCodec = "ac3,mp3", - Type = DlnaProfileType.Video - }, - new DirectPlayProfile - { - Container = "avi", - VideoCodec = "h264", - AudioCodec = "aac", - Type = DlnaProfileType.Video - }, - new DirectPlayProfile - { - Container = "mp4,mov", - VideoCodec = "h264,mpeg4", - AudioCodec = "aac,ac3", - Type = DlnaProfileType.Video - }, - new DirectPlayProfile - { - Container = "asf", - VideoCodec = "wmv2,wmv3,vc1", - AudioCodec = "wmav2,wmapro", - Type = DlnaProfileType.Video - }, - new DirectPlayProfile - { - Container = "asf", - AudioCodec = "wmav2,wmapro,wmavoice", - Type = DlnaProfileType.Audio - }, - new DirectPlayProfile - { - Container = "mp3", - AudioCodec = "mp3", - Type = DlnaProfileType.Audio - }, - new DirectPlayProfile - { - Container = "jpeg", - Type = DlnaProfileType.Photo - } - }; - - ResponseProfiles = new[] - { - new ResponseProfile - { - Container = "avi", - MimeType = "video/avi", - Type = DlnaProfileType.Video - } - }; - - ContainerProfiles = new[] - { - new ContainerProfile - { - Type = DlnaProfileType.Video, - Container = "mp4,mov", - - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.Equals, - Property = ProfileConditionValue.Has64BitOffsets, - Value = "false", - IsRequired = false - } - } - }, - - new ContainerProfile - { - Type = DlnaProfileType.Photo, - - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Width, - Value = "1920" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Height, - Value = "1080" - } - } - } - }; - - CodecProfiles = new[] - { - new CodecProfile - { - Type = CodecType.Video, - Codec = "mpeg4", - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Width, - Value = "1280" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Height, - Value = "720" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoFramerate, - Value = "30", - IsRequired = false - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoBitrate, - Value = "5120000", - IsRequired = false - } - } - }, - - new CodecProfile - { - Type = CodecType.Video, - Codec = "h264", - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Width, - Value = "1920" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Height, - Value = "1080" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoLevel, - Value = "41", - IsRequired = false - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoBitrate, - Value = "10240000", - IsRequired = false - } - } - }, - - new CodecProfile - { - Type = CodecType.Video, - Codec = "wmv2,wmv3,vc1", - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Width, - Value = "1920" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.Height, - Value = "1080" - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoFramerate, - Value = "30", - IsRequired = false - }, - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.VideoBitrate, - Value = "15360000", - IsRequired = false - } - } - }, - - new CodecProfile - { - Type = CodecType.VideoAudio, - Codec = "ac3,wmav2,wmapro", - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.AudioChannels, - Value = "6", - IsRequired = false - } - } - }, - - new CodecProfile - { - Type = CodecType.VideoAudio, - Codec = "aac", - Conditions = new [] - { - new ProfileCondition - { - Condition = ProfileConditionType.LessThanEqual, - Property = ProfileConditionValue.AudioChannels, - Value = "2", - IsRequired = false - }, - new ProfileCondition - { - Condition = ProfileConditionType.Equals, - Property = ProfileConditionValue.AudioProfile, - Value = "lc", - IsRequired = false - } - } - } - }; - - SubtitleProfiles = new[] - { - new SubtitleProfile - { - Format = "srt", - Method = SubtitleDeliveryMethod.Embed - } - }; - } - } -} diff --git a/Emby.Dlna/Profiles/XboxOneProfile.cs b/Emby.Dlna/Profiles/XboxOneProfile.cs index e17640f2fa..99e52510f7 100644 --- a/Emby.Dlna/Profiles/XboxOneProfile.cs +++ b/Emby.Dlna/Profiles/XboxOneProfile.cs @@ -60,7 +60,7 @@ namespace Emby.Dlna.Profiles new DirectPlayProfile { Container = "ts", - VideoCodec = "h264,mpeg2video", + VideoCodec = "h264,mpeg2video,hevc", AudioCodec = "ac3,aac,mp3", Type = DlnaProfileType.Video }, @@ -81,7 +81,7 @@ namespace Emby.Dlna.Profiles new DirectPlayProfile { Container = "mp4,mov,mkv,m4v", - VideoCodec = "h264,mpeg4,mpeg2video", + VideoCodec = "h264,mpeg4,mpeg2video,hevc", AudioCodec = "aac,ac3", Type = DlnaProfileType.Video }, diff --git a/Emby.Dlna/Profiles/Xml/Default.xml b/Emby.Dlna/Profiles/Xml/Default.xml index b07a2f7c26..133f4abf1a 100644 --- a/Emby.Dlna/Profiles/Xml/Default.xml +++ b/Emby.Dlna/Profiles/Xml/Default.xml @@ -29,8 +29,8 @@ false - - + + diff --git a/Emby.Dlna/Profiles/Xml/WDTV Live.xml b/Emby.Dlna/Profiles/Xml/WDTV Live.xml index 71b3463187..44d130e929 100644 --- a/Emby.Dlna/Profiles/Xml/WDTV Live.xml +++ b/Emby.Dlna/Profiles/Xml/WDTV Live.xml @@ -36,11 +36,11 @@ true - - - - - + + + + + diff --git a/Emby.Dlna/Profiles/Xml/Xbox 360.xml b/Emby.Dlna/Profiles/Xml/Xbox 360.xml deleted file mode 100644 index 3b7d2963f2..0000000000 --- a/Emby.Dlna/Profiles/Xml/Xbox 360.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - Xbox 360 - - Xbox 360 - - - - - - ${HostName}: 1 - Microsoft Corporation - http://www.microsoft.com - Windows Media Player Sharing - Emby : UPnP Media Server - 12.0 - http://go.microsoft.com/fwlink/?LinkId=105926 - false - false - false - Audio,Photo,Video - JPEG_SM - 480 - 480 - 48 - 48 - 40000000 - 40000000 - 192000 - - DMS-1.50 - http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_AC3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HD_50_AC3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_BASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMA_FULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-matroska:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_720p_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_HD_1080i_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_HP_HD_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_LPCM;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_ASP_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_SP_L6_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_MP4_NDSD;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_SD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_MPEG1_L3_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_HD_50_LPCM_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=01500000000000000000000000000000 - 40 - true - true - true - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Emby.Dlna/Profiles/Xml/Xbox One.xml b/Emby.Dlna/Profiles/Xml/Xbox One.xml index 423327a389..6354232342 100644 --- a/Emby.Dlna/Profiles/Xml/Xbox One.xml +++ b/Emby.Dlna/Profiles/Xml/Xbox One.xml @@ -36,10 +36,10 @@ false - + - + diff --git a/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj b/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj index 6024c3d2c0..f2b32d52cb 100644 --- a/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj +++ b/Emby.Drawing.Skia/Emby.Drawing.Skia.csproj @@ -63,8 +63,8 @@ - - ..\packages\SkiaSharp.1.59.2\lib\portable-net45+win8+wpa81+wp8\SkiaSharp.dll + + ..\packages\SkiaSharp.1.58.1\lib\portable-net45+win8+wpa81+wp8\SkiaSharp.dll diff --git a/Emby.Drawing.Skia/packages.config b/Emby.Drawing.Skia/packages.config index 1aa3653cd7..2b9b0aee43 100644 --- a/Emby.Drawing.Skia/packages.config +++ b/Emby.Drawing.Skia/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c1ab9ec222..745d83eb5f 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -799,7 +799,7 @@ namespace Emby.Server.Implementations protected abstract IConnectManager CreateConnectManager(); protected abstract ISyncManager CreateSyncManager(); - + protected virtual IHttpClient CreateHttpClient() { return new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamFactory, GetDefaultUserAgent); @@ -953,6 +953,7 @@ namespace Emby.Server.Implementations var deviceRepo = new SqliteDeviceRepository(LogManager.GetLogger("DeviceManager"), ServerConfigurationManager, FileSystemManager, JsonSerializer); deviceRepo.Initialize(); DeviceManager = new DeviceManager(deviceRepo, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager); + RegisterSingleInstance(deviceRepo); RegisterSingleInstance(DeviceManager); var newsService = new Emby.Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer); @@ -996,7 +997,7 @@ namespace Emby.Server.Implementations NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager); RegisterSingleInstance(NotificationManager); - SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager); + SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager, ServerConfigurationManager); RegisterSingleInstance(SubtitleManager); RegisterSingleInstance(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager, SocketFactory, TimerFactory)); diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index c42f22804b..c566ca25bd 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -296,7 +296,7 @@ namespace Emby.Server.Implementations.Channels if (requiresCallback != null) { - results = await GetChannelItemMediaSourcesInternal(requiresCallback, GetItemExternalId(item), cancellationToken) + results = await GetChannelItemMediaSourcesInternal(requiresCallback, item.ExternalId, cancellationToken) .ConfigureAwait(false); } else @@ -990,18 +990,6 @@ namespace Emby.Server.Implementations.Channels return result; } - private string GetItemExternalId(BaseItem item) - { - var externalId = item.ExternalId; - - if (string.IsNullOrWhiteSpace(externalId)) - { - externalId = item.GetProviderId("ProviderExternalId"); - } - - return externalId; - } - private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1); private async Task GetChannelItems(IChannel channel, User user, @@ -1080,7 +1068,7 @@ namespace Emby.Server.Implementations.Channels { var categoryItem = _libraryManager.GetItemById(new Guid(folderId)); - query.FolderId = GetItemExternalId(categoryItem); + query.FolderId = categoryItem.ExternalId; } var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 8ff1b63c03..7ccf54d20f 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -117,7 +117,6 @@ - @@ -405,11 +404,11 @@ - + diff --git a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs index cd7c98dc85..4a9e417f2e 100644 --- a/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs +++ b/Emby.Server.Implementations/HttpClientManager/HttpClientManager.cs @@ -16,6 +16,7 @@ using MediaBrowser.Common.Net; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; +using MediaBrowser.Controller.IO; namespace Emby.Server.Implementations.HttpClientManager { @@ -633,12 +634,9 @@ namespace Emby.Server.Implementations.HttpClientManager } else { - using (var stream = ProgressStream.CreateReadProgressStream(httpResponse.GetResponseStream(), options.Progress.Report, contentLength.Value)) + using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { - using (var fs = _fileSystem.GetFileStream(tempFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) - { - await stream.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, options.CancellationToken).ConfigureAwait(false); - } + await StreamHelper.CopyToAsync(httpResponse.GetResponseStream(), fs, StreamDefaults.DefaultCopyToBufferSize, options.Progress, contentLength.Value, options.CancellationToken).ConfigureAwait(false); } } diff --git a/Emby.Server.Implementations/IO/ProgressStream.cs b/Emby.Server.Implementations/IO/ProgressStream.cs deleted file mode 100644 index be1ff72f8e..0000000000 --- a/Emby.Server.Implementations/IO/ProgressStream.cs +++ /dev/null @@ -1,240 +0,0 @@ -using System; -using System.IO; - -namespace Emby.Server.Implementations.IO -{ - /// - /// Measures progress when reading from a stream or writing to one - /// - public class ProgressStream : Stream - { - /// - /// Gets the base stream. - /// - /// The base stream. - public Stream BaseStream { get; private set; } - - /// - /// Gets or sets the bytes processed. - /// - /// The bytes processed. - private long BytesProcessed { get; set; } - /// - /// Gets or sets the length of the write. - /// - /// The length of the write. - private long WriteLength { get; set; } - - /// - /// Gets or sets the length of the read. - /// - /// The length of the read. - private long? ReadLength { get; set; } - - /// - /// Gets or sets the progress action. - /// - /// The progress action. - private Action ProgressAction { get; set; } - - /// - /// Creates the read progress stream. - /// - /// The base stream. - /// The progress action. - /// Length of the read. - /// ProgressStream. - public static ProgressStream CreateReadProgressStream(Stream baseStream, Action progressAction, long? readLength = null) - { - return new ProgressStream - { - BaseStream = baseStream, - ProgressAction = progressAction, - ReadLength = readLength - }; - } - - /// - /// Creates the write progress stream. - /// - /// The base stream. - /// The progress action. - /// Length of the write. - /// ProgressStream. - public static ProgressStream CreateWriteProgressStream(Stream baseStream, Action progressAction, long writeLength) - { - return new ProgressStream - { - BaseStream = baseStream, - ProgressAction = progressAction, - WriteLength = writeLength - }; - } - - /// - /// When overridden in a derived class, gets a value indicating whether the current stream supports reading. - /// - /// true if this instance can read; otherwise, false. - /// true if the stream supports reading; otherwise, false. - public override bool CanRead - { - get { return BaseStream.CanRead; } - } - - /// - /// When overridden in a derived class, gets a value indicating whether the current stream supports seeking. - /// - /// true if this instance can seek; otherwise, false. - /// true if the stream supports seeking; otherwise, false. - public override bool CanSeek - { - get { return BaseStream.CanSeek; } - } - - /// - /// When overridden in a derived class, gets a value indicating whether the current stream supports writing. - /// - /// true if this instance can write; otherwise, false. - /// true if the stream supports writing; otherwise, false. - public override bool CanWrite - { - get { return BaseStream.CanWrite; } - } - - /// - /// When overridden in a derived class, clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - public override void Flush() - { - BaseStream.Flush(); - } - - /// - /// When overridden in a derived class, gets the length in bytes of the stream. - /// - /// The length. - /// A long value representing the length of the stream in bytes. - public override long Length - { - get { return BaseStream.Length; } - } - - /// - /// When overridden in a derived class, gets or sets the position within the current stream. - /// - /// The position. - /// The current position within the stream. - public override long Position - { - get { return BaseStream.Position; } - set - { - BaseStream.Position = value; - } - } - - /// - /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - public override int Read(byte[] buffer, int offset, int count) - { - var read = BaseStream.Read(buffer, offset, count); - - BytesProcessed += read; - - double percent = BytesProcessed; - percent /= ReadLength ?? BaseStream.Length; - percent *= 100; - - ProgressAction(percent); - - return read; - } - - public override int EndRead(IAsyncResult asyncResult) - { - var read = base.EndRead(asyncResult); - - BytesProcessed += read; - - double percent = BytesProcessed; - percent /= ReadLength ?? BaseStream.Length; - percent *= 100; - - ProgressAction(percent); - - return read; - } - - /// - /// When overridden in a derived class, sets the position within the current stream. - /// - /// A byte offset relative to the parameter. - /// A value of type indicating the reference point used to obtain the new position. - /// The new position within the current stream. - public override long Seek(long offset, SeekOrigin origin) - { - return BaseStream.Seek(offset, origin); - } - - /// - /// When overridden in a derived class, sets the length of the current stream. - /// - /// The desired length of the current stream in bytes. - public override void SetLength(long value) - { - BaseStream.SetLength(value); - } - - /// - /// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies bytes from to the current stream. - /// The zero-based byte offset in at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - public override void Write(byte[] buffer, int offset, int count) - { - BaseStream.Write(buffer, offset, count); - - BytesProcessed += count; - - double percent = BytesProcessed; - percent /= WriteLength; - percent *= 100; - - ProgressAction(percent); - } - - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - var result = base.BeginWrite(buffer, offset, count, callback, state); - - BytesProcessed += count; - - double percent = BytesProcessed; - percent /= WriteLength; - percent *= 100; - - ProgressAction(percent); - - return result; - } - - /// - /// Releases the unmanaged resources used by the and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool disposing) - { - if (disposing) - { - BaseStream.Dispose(); - } - base.Dispose(disposing); - } - } -} diff --git a/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs b/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs index 1d21796b1a..0e1f6bb008 100644 --- a/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs +++ b/Emby.Server.Implementations/IO/SharpCifsFileSystem.cs @@ -487,14 +487,17 @@ namespace Emby.Server.Implementations.IO AssertDirectoryExists(dir, path); var list = ListFiles(dir, recursive); + var result = new List(); foreach (var file in list) { if (file.IsDirectory()) { - yield return ToMetadata(file); + result.Add(ToMetadata(file)); } } + + return result; } public IEnumerable GetFiles(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false) @@ -503,6 +506,7 @@ namespace Emby.Server.Implementations.IO AssertDirectoryExists(dir, path); var list = ListFiles(dir, recursive); + var result = new List(); foreach (var file in list) { @@ -513,10 +517,12 @@ namespace Emby.Server.Implementations.IO if (extensions == null || extensions.Length == 0 || extensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { - yield return ToMetadata(file); + result.Add(ToMetadata(file)); } } } + + return result; } public IEnumerable GetFileSystemEntries(string path, bool recursive = false) @@ -525,15 +531,19 @@ namespace Emby.Server.Implementations.IO AssertDirectoryExists(dir, path); var list = ListFiles(dir, recursive); + var result = new List(); foreach (var file in list) { - yield return ToMetadata(file); + result.Add(ToMetadata(file)); } + + return result; } - public IEnumerable GetFileSystemEntryPaths(string path, bool recursive = false) + public List GetFileSystemEntryPaths(string path, bool recursive = false) { + var result = new List(); var dir = CreateSmbDirectoryForListFiles(path); AssertDirectoryExists(dir, path); @@ -541,16 +551,18 @@ namespace Emby.Server.Implementations.IO foreach (var file in list) { - yield return GetReturnPath(file); + result.Add(GetReturnPath(file)); } + return result; } - public IEnumerable GetFilePaths(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false) + public List GetFilePaths(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false) { var dir = CreateSmbDirectoryForListFiles(path); AssertDirectoryExists(dir, path); var list = ListFiles(dir, recursive); + var result = new List(); foreach (var file in list) { @@ -561,44 +573,52 @@ namespace Emby.Server.Implementations.IO if (extensions == null || extensions.Length == 0 || extensions.Contains(extension ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { - yield return filePath; + result.Add(filePath); } } } + + return result; } - public IEnumerable GetDirectoryPaths(string path, bool recursive = false) + public List GetDirectoryPaths(string path, bool recursive = false) { var dir = CreateSmbDirectoryForListFiles(path); AssertDirectoryExists(dir, path); var list = ListFiles(dir, recursive); + var result = new List(); foreach (var file in list) { if (file.IsDirectory()) { - yield return GetReturnPath(file); + result.Add(GetReturnPath(file)); } } + + return result; } private IEnumerable ListFiles(SmbFile dir, bool recursive) { var list = dir.ListFiles(); + var result = new List(); foreach (var file in list) { - yield return file; + result.Add(file); if (recursive && file.IsDirectory()) { foreach (var subFile in ListFiles(file, recursive)) { - yield return subFile; + result.Add(subFile); } } } + + return result; } } } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index bd8a095503..f71e2714ac 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -709,6 +709,13 @@ namespace Emby.Server.Implementations.Library var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)); + // In case program data folder was moved + if (!string.Equals(rootFolder.Path, rootFolderPath, StringComparison.Ordinal)) + { + _logger.Info("Resetting root folder path to {0}", rootFolderPath); + rootFolder.Path = rootFolderPath; + } + // Add in the plug-in folders foreach (var child in PluginFolderCreators) { @@ -771,6 +778,13 @@ namespace Emby.Server.Implementations.Library tmpItem = (UserRootFolder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath)); } + // In case program data folder was moved + if (!string.Equals(tmpItem.Path, userRootPath, StringComparison.Ordinal)) + { + _logger.Info("Resetting user root folder path to {0}", userRootPath); + tmpItem.Path = userRootPath; + } + _userRootFolder = tmpItem; } } diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index e2f2946db1..d30aaa133b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -4,19 +4,26 @@ using MediaBrowser.Model.Entities; using System; using MediaBrowser.Controller.Entities; using System.IO; +using System.Linq; +using MediaBrowser.Controller.Providers; +using System.Collections.Generic; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Extensions; +using Emby.Naming.Video; +using Emby.Naming.AudioBook; namespace Emby.Server.Implementations.Library.Resolvers.Audio { /// /// Class AudioResolver /// - public class AudioResolver : ItemResolver + public class AudioResolver : ItemResolver, IMultiItemResolver { - private readonly ILibraryManager _libraryManager; + private readonly ILibraryManager LibraryManager; public AudioResolver(ILibraryManager libraryManager) { - _libraryManager = libraryManager; + LibraryManager = libraryManager; } /// @@ -25,7 +32,38 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// The priority. public override ResolverPriority Priority { - get { return ResolverPriority.Last; } + get { return ResolverPriority.Fourth; } + } + + public MultiItemResolverResult ResolveMultiple(Folder parent, + List files, + string collectionType, + IDirectoryService directoryService) + { + var result = ResolveMultipleInternal(parent, files, collectionType, directoryService); + + if (result != null) + { + foreach (var item in result.Items) + { + SetInitialItemValues((MediaBrowser.Controller.Entities.Audio.Audio)item, null); + } + } + + return result; + } + + private MultiItemResolverResult ResolveMultipleInternal(Folder parent, + List files, + string collectionType, + IDirectoryService directoryService) + { + if (string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase)) + { + return ResolveMultipleAudio(parent, files, directoryService, false, collectionType, true); + } + + return null; } /// @@ -37,46 +75,199 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio { // Return audio if the path is a file and has a matching extension - if (!args.IsDirectory) + var libraryOptions = args.GetLibraryOptions(); + var collectionType = args.GetCollectionType(); + + var isBooksCollectionType = string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase); + + if (args.IsDirectory) { - var libraryOptions = args.GetLibraryOptions(); - - if (_libraryManager.IsAudioFile(args.Path, libraryOptions)) + if (!isBooksCollectionType) { - if (string.Equals(Path.GetExtension(args.Path), ".cue", StringComparison.OrdinalIgnoreCase)) - { - // if audio file exists of same name, return null - - return null; - } - - var collectionType = args.GetCollectionType(); - - var isMixed = string.IsNullOrWhiteSpace(collectionType); - - // For conflicting extensions, give priority to videos - if (isMixed && _libraryManager.IsVideoFile(args.Path, libraryOptions)) - { - return null; - } - - var isStandalone = args.Parent == null; - - if (isStandalone || - string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) || - isMixed) - { - return new MediaBrowser.Controller.Entities.Audio.Audio(); - } - - if (string.Equals(collectionType, CollectionType.Books, StringComparison.OrdinalIgnoreCase)) - { - return new AudioBook(); - } + return null; } + + var files = args.FileSystemChildren + .Where(i => !LibraryManager.IgnoreFile(i, args.Parent)) + .ToList(); + + if (isBooksCollectionType) + { + return FindAudio(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false); + } + + return null; + } + + if (LibraryManager.IsAudioFile(args.Path, libraryOptions)) + { + if (string.Equals(Path.GetExtension(args.Path), ".cue", StringComparison.OrdinalIgnoreCase)) + { + // if audio file exists of same name, return null + return null; + } + + var isMixedCollectionType = string.IsNullOrWhiteSpace(collectionType); + + // For conflicting extensions, give priority to videos + if (isMixedCollectionType && LibraryManager.IsVideoFile(args.Path, libraryOptions)) + { + return null; + } + + MediaBrowser.Controller.Entities.Audio.Audio item = null; + + var isMusicCollectionType = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase); + + // Use regular audio type for mixed libraries, owned items and music + if (isMixedCollectionType || + args.Parent == null || + isMusicCollectionType) + { + item = new MediaBrowser.Controller.Entities.Audio.Audio(); + } + + else if (isBooksCollectionType) + { + item = new AudioBook(); + } + + if (item != null) + { + item.IsInMixedFolder = true; + } + + return item; } return null; } + + private T FindAudio(ItemResolveArgs args, string path, Folder parent, List fileSystemEntries, IDirectoryService directoryService, string collectionType, bool parseName) + where T : MediaBrowser.Controller.Entities.Audio.Audio, new() + { + var multiDiscFolders = new List(); + + var libraryOptions = args.GetLibraryOptions(); + var filesFromOtherItems = new List(); + + // TODO: Allow GetMultiDiscMovie in here + var supportsMultiVersion = false; + + var result = ResolveMultipleAudio(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ?? + new MultiItemResolverResult(); + + if (result.Items.Count == 1) + { + var videoPath = result.Items[0].Path; + + // If we were supporting this we'd be checking filesFromOtherItems + var hasOtherItems = false; + + if (!hasOtherItems) + { + var item = (T)result.Items[0]; + item.IsInMixedFolder = false; + item.Name = Path.GetFileName(item.ContainingFolderPath); + return item; + } + } + + if (result.Items.Count == 0 && multiDiscFolders.Count > 0) + { + //return GetMultiDiscAudio(multiDiscFolders, directoryService); + } + + return null; + } + + private MultiItemResolverResult ResolveMultipleAudio(Folder parent, IEnumerable fileSystemEntries, IDirectoryService directoryService, bool suppportMultiEditions, string collectionType, bool parseName) + where T : MediaBrowser.Controller.Entities.Audio.Audio, new() + { + var files = new List(); + var items = new List(); + var leftOver = new List(); + + // Loop through each child file/folder and see if we find a video + foreach (var child in fileSystemEntries) + { + if (child.IsDirectory) + { + leftOver.Add(child); + } + else if (IsIgnored(child.Name)) + { + + } + else + { + files.Add(child); + } + } + + var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions(); + + var resolver = new AudioBookListResolver(namingOptions); + var resolverResult = resolver.Resolve(files).ToList(); + + var result = new MultiItemResolverResult + { + ExtraFiles = leftOver, + Items = items + }; + + var isInMixedFolder = resolverResult.Count > 1 || (parent != null && parent.IsTopParent); + + foreach (var resolvedItem in resolverResult) + { + if (resolvedItem.Files.Count > 1) + { + // For now, until we sort out naming for multi-part books + continue; + } + + var firstMedia = resolvedItem.Files.First(); + + var libraryItem = new T + { + Path = firstMedia.Path, + IsInMixedFolder = isInMixedFolder, + ProductionYear = resolvedItem.Year, + Name = parseName ? + resolvedItem.Name : + Path.GetFileNameWithoutExtension(firstMedia.Path), + //AdditionalParts = resolvedItem.Files.Skip(1).Select(i => i.Path).ToArray(), + //LocalAlternateVersions = resolvedItem.AlternateVersions.Select(i => i.Path).ToArray() + }; + + result.Items.Add(libraryItem); + } + + result.ExtraFiles.AddRange(files.Where(i => !ContainsFile(resolverResult, i))); + + return result; + } + + private bool ContainsFile(List result, FileSystemMetadata file) + { + return result.Any(i => ContainsFile(i, file)); + } + + private bool ContainsFile(AudioBookInfo result, FileSystemMetadata file) + { + return result.Files.Any(i => ContainsFile(i, file)) || + result.AlternateVersions.Any(i => ContainsFile(i, file)) || + result.Extras.Any(i => ContainsFile(i, file)); + } + + private bool ContainsFile(AudioBookFileInfo result, FileSystemMetadata file) + { + return string.Equals(result.Path, file.FullName, StringComparison.OrdinalIgnoreCase); + } + + private bool IsIgnored(string filename) + { + return false; + } } } diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index e3200a0995..7e960f85e6 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -274,7 +274,7 @@ namespace Emby.Server.Implementations.Library.Resolvers return false; } - return FileSystem.GetFilePaths(fullPath).Any(i => string.Equals(Path.GetExtension(i), ".vob", StringComparison.OrdinalIgnoreCase)); + return directoryService.GetFilePaths(fullPath).Any(i => string.Equals(Path.GetExtension(i), ".vob", StringComparison.OrdinalIgnoreCase)); } /// diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index d69a2b2404..6676164142 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -202,14 +202,14 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies { var collectionType = args.GetCollectionType(); - if (IsInvalid(args.Parent, collectionType)) - { - return null; - } - // Find movies with their own folders if (args.IsDirectory) { + if (IsInvalid(args.Parent, collectionType)) + { + return null; + } + var files = args.FileSystemChildren .Where(i => !LibraryManager.IgnoreFile(i, args.Parent)) .ToList(); @@ -251,8 +251,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return null; } - // Owned items will be caught by the plain video resolver + // Handle owned items if (args.Parent == null) + { + return base.Resolve(args); + } + + if (IsInvalid(args.Parent, collectionType)) { return null; } @@ -528,6 +533,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return returnVideo; } + private string[] ValidCollectionTypes = new[] + { + CollectionType.Movies, + CollectionType.HomeVideos, + CollectionType.MusicVideos, + CollectionType.Movies, + CollectionType.Photos + }; + private bool IsInvalid(Folder parent, string collectionType) { if (parent != null) @@ -538,21 +552,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies } } - var validCollectionTypes = new[] - { - CollectionType.Movies, - CollectionType.HomeVideos, - CollectionType.MusicVideos, - CollectionType.Movies, - CollectionType.Photos - }; - if (string.IsNullOrWhiteSpace(collectionType)) { return false; } - return !validCollectionTypes.Contains(collectionType, StringComparer.OrdinalIgnoreCase); + return !ValidCollectionTypes.Contains(collectionType, StringComparer.OrdinalIgnoreCase); } private IImageProcessor _imageProcessor; diff --git a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs index 5c7a528f54..030ff88f7d 100644 --- a/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/VideoResolver.cs @@ -5,36 +5,6 @@ using MediaBrowser.Model.IO; namespace Emby.Server.Implementations.Library.Resolvers { - /// - /// Resolves a Path into a Video - /// - public class VideoResolver : BaseVideoResolver