浏览代码

Merge pull request #2847 from MediaBrowser/beta

Beta
Luke 8 年之前
父节点
当前提交
f3ee129bd9
共有 100 个文件被更改,包括 1340 次插入1594 次删除
  1. 14 4
      Emby.Dlna/ContentDirectory/ContentDirectory.cs
  2. 25 18
      Emby.Dlna/ContentDirectory/ControlHandler.cs
  3. 3 17
      Emby.Dlna/Didl/DidlBuilder.cs
  4. 2 5
      Emby.Dlna/Didl/Filter.cs
  5. 0 1
      Emby.Dlna/Didl/StringWriterWithEncoding.cs
  6. 5 5
      Emby.Dlna/PlayTo/PlayToController.cs
  7. 1 1
      Emby.Dlna/PlayTo/PlayToManager.cs
  8. 135 135
      Emby.Dlna/Profiles/PanasonicVieraProfile.cs
  9. 5 4
      Emby.Dlna/Ssdp/Extensions.cs
  10. 3 3
      Emby.Drawing.ImageMagick/ImageHelpers.cs
  11. 3 4
      Emby.Drawing.ImageMagick/ImageMagickEncoder.cs
  12. 6 6
      Emby.Drawing.ImageMagick/StripCollageBuilder.cs
  13. 88 12
      Emby.Drawing/ImageProcessor.cs
  14. 6 6
      Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs
  15. 3 3
      Emby.Server.Implementations/Activity/ActivityManager.cs
  16. 3 4
      Emby.Server.Implementations/Activity/ActivityRepository.cs
  17. 20 13
      Emby.Server.Implementations/ApplicationHost.cs
  18. 16 24
      Emby.Server.Implementations/Channels/ChannelManager.cs
  19. 23 12
      Emby.Server.Implementations/Collections/CollectionManager.cs
  20. 4 5
      Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
  21. 17 23
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  22. 7 8
      Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
  23. 2 3
      Emby.Server.Implementations/Data/SqliteUserRepository.cs
  24. 5 1
      Emby.Server.Implementations/Devices/DeviceRepository.cs
  25. 84 31
      Emby.Server.Implementations/Dto/DtoService.cs
  26. 3 4
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  27. 5 5
      Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
  28. 1 1
      Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
  29. 1 1
      Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
  30. 5 3
      Emby.Server.Implementations/IO/LibraryMonitor.cs
  31. 27 30
      Emby.Server.Implementations/Library/LibraryManager.cs
  32. 1 1
      Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
  33. 13 4
      Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
  34. 1 1
      Emby.Server.Implementations/Library/SearchEngine.cs
  35. 7 7
      Emby.Server.Implementations/Library/UserDataManager.cs
  36. 34 34
      Emby.Server.Implementations/Library/UserManager.cs
  37. 11 10
      Emby.Server.Implementations/Library/UserViewManager.cs
  38. 105 60
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  39. 4 0
      Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
  40. 21 10
      Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
  41. 19 32
      Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs
  42. 135 93
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  43. 16 6
      Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs
  44. 1 1
      Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
  45. 71 57
      Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
  46. 6 12
      Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
  47. 16 34
      Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
  48. 5 77
      Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs
  49. 0 179
      Emby.Server.Implementations/Localization/Core/core.json
  50. 0 14
      Emby.Server.Implementations/Localization/Core/en-US.json
  51. 10 10
      Emby.Server.Implementations/Localization/LocalizationManager.cs
  52. 3 10
      Emby.Server.Implementations/Logging/SimpleLogManager.cs
  53. 7 2
      Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
  54. 16 16
      Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs
  55. 1 1
      Emby.Server.Implementations/Notifications/NotificationManager.cs
  56. 2 2
      Emby.Server.Implementations/Notifications/Notifications.cs
  57. 2 2
      Emby.Server.Implementations/Playlists/PlaylistManager.cs
  58. 3 4
      Emby.Server.Implementations/Security/AuthenticationRepository.cs
  59. 7 33
      Emby.Server.Implementations/Services/ServiceController.cs
  60. 4 4
      Emby.Server.Implementations/Services/ServiceHandler.cs
  61. 65 58
      Emby.Server.Implementations/Session/SessionManager.cs
  62. 4 4
      Emby.Server.Implementations/Social/SharingManager.cs
  63. 2 4
      Emby.Server.Implementations/Social/SharingRepository.cs
  64. 1 1
      Emby.Server.Implementations/Sorting/ArtistComparer.cs
  65. 3 3
      Emby.Server.Implementations/TV/TVSeriesManager.cs
  66. 74 56
      Emby.Server.Implementations/Updates/InstallationManager.cs
  67. 1 1
      Emby.Server.Implementations/packages.config
  68. 20 4
      MediaBrowser.Api/BaseApiService.cs
  69. 4 4
      MediaBrowser.Api/ChannelService.cs
  70. 2 3
      MediaBrowser.Api/ConfigurationService.cs
  71. 1 2
      MediaBrowser.Api/Devices/DeviceService.cs
  72. 1 3
      MediaBrowser.Api/DisplayPreferencesService.cs
  73. 0 1
      MediaBrowser.Api/Dlna/DlnaServerService.cs
  74. 4 5
      MediaBrowser.Api/Dlna/DlnaService.cs
  75. 3 6
      MediaBrowser.Api/EnvironmentService.cs
  76. 1 1
      MediaBrowser.Api/FilterService.cs
  77. 8 52
      MediaBrowser.Api/GamesService.cs
  78. 2 2
      MediaBrowser.Api/IHasItemFields.cs
  79. 1 14
      MediaBrowser.Api/Images/ImageService.cs
  80. 5 5
      MediaBrowser.Api/Images/RemoteImageService.cs
  81. 6 6
      MediaBrowser.Api/ItemUpdateService.cs
  82. 8 8
      MediaBrowser.Api/Library/LibraryService.cs
  83. 11 9
      MediaBrowser.Api/LiveTv/LiveTvService.cs
  84. 6 7
      MediaBrowser.Api/LocalizationService.cs
  85. 0 1
      MediaBrowser.Api/MediaBrowser.Api.csproj
  86. 4 5
      MediaBrowser.Api/Movies/CollectionService.cs
  87. 5 5
      MediaBrowser.Api/Movies/MoviesService.cs
  88. 2 1
      MediaBrowser.Api/Movies/TrailersService.cs
  89. 9 3
      MediaBrowser.Api/Music/InstantMixService.cs
  90. 1 1
      MediaBrowser.Api/NotificationsService.cs
  91. 0 162
      MediaBrowser.Api/PackageReviewService.cs
  92. 14 13
      MediaBrowser.Api/PackageService.cs
  93. 5 7
      MediaBrowser.Api/PlaylistService.cs
  94. 5 6
      MediaBrowser.Api/PluginService.cs
  95. 1 1
      MediaBrowser.Api/Reports/Data/ReportBuilder.cs
  96. 2 2
      MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs
  97. 4 4
      MediaBrowser.Api/SearchService.cs
  98. 6 9
      MediaBrowser.Api/Session/SessionsService.cs
  99. 4 4
      MediaBrowser.Api/SimilarItemsHelper.cs
  100. 2 3
      MediaBrowser.Api/Social/SharingService.cs

+ 14 - 4
Emby.Dlna/ContentDirectory/ContentDirectory.cs

@@ -10,7 +10,6 @@ using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Linq;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.TV;
 using MediaBrowser.Controller.TV;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
@@ -129,9 +128,20 @@ namespace Emby.Dlna.ContentDirectory
                 }
                 }
             }
             }
 
 
-            // No configuration so it's going to be pretty arbitrary
-            return _userManager.Users.FirstOrDefault(i => i.Policy.IsAdministrator) ??
-                _userManager.Users.First();
+            foreach (var user in _userManager.Users)
+            {
+                if (user.Policy.IsAdministrator)
+                {
+                    return user;
+                }
+            }
+
+            foreach (var user in _userManager.Users)
+            {
+                return user;
+            }
+
+            return null;
         }
         }
 
 
         public void Dispose()
         public void Dispose()

+ 25 - 18
Emby.Dlna/ContentDirectory/ControlHandler.cs

@@ -487,6 +487,11 @@ namespace Emby.Dlna.ContentDirectory
                 return GetMusicArtistItems(item, null, user, sort, startIndex, limit);
                 return GetMusicArtistItems(item, null, user, sort, startIndex, limit);
             }
             }
 
 
+            if (item is Genre)
+            {
+                return GetGenreItems(item, null, user, sort, startIndex, limit);
+            }
+
             var collectionFolder = item as ICollectionFolder;
             var collectionFolder = item as ICollectionFolder;
             if (collectionFolder != null && string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
             if (collectionFolder != null && string.Equals(CollectionType.Music, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase))
             {
             {
@@ -503,23 +508,6 @@ namespace Emby.Dlna.ContentDirectory
 
 
             if (stubType.HasValue)
             if (stubType.HasValue)
             {
             {
-                if (stubType.Value == StubType.People)
-                {
-                    var items = _libraryManager.GetPeopleItems(new InternalPeopleQuery
-                    {
-                        ItemId = item.Id
-
-                    });
-
-                    var result = new QueryResult<ServerItem>
-                    {
-                        Items = items.Select(i => new ServerItem(i)).ToArray(items.Count),
-                        TotalRecordCount = items.Count
-                    };
-
-                    return ApplyPaging(result, startIndex, limit);
-                }
-
                 var person = item as Person;
                 var person = item as Person;
                 if (person != null)
                 if (person != null)
                 {
                 {
@@ -1173,6 +1161,26 @@ namespace Emby.Dlna.ContentDirectory
             return ToResult(result);
             return ToResult(result);
         }
         }
 
 
+        private QueryResult<ServerItem> GetGenreItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit)
+        {
+            var query = new InternalItemsQuery(user)
+            {
+                Recursive = true,
+                ParentId = parentId,
+                GenreIds = new[] { item.Id.ToString("N") },
+                IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name },
+                Limit = limit,
+                StartIndex = startIndex,
+                DtoOptions = GetDtoOptions()
+            };
+
+            SetSorting(query, sort, false);
+
+            var result = _libraryManager.GetItemsResult(query);
+
+            return ToResult(result);
+        }
+
         private QueryResult<ServerItem> GetMusicGenreItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit)
         private QueryResult<ServerItem> GetMusicGenreItems(BaseItem item, Guid? parentId, User user, SortCriteria sort, int? startIndex, int? limit)
         {
         {
             var query = new InternalItemsQuery(user)
             var query = new InternalItemsQuery(user)
@@ -1331,7 +1339,6 @@ namespace Emby.Dlna.ContentDirectory
     public enum StubType
     public enum StubType
     {
     {
         Folder = 0,
         Folder = 0,
-        People = 1,
         Latest = 2,
         Latest = 2,
         Playlists = 3,
         Playlists = 3,
         Albums = 4,
         Albums = 4,

+ 3 - 17
Emby.Dlna/Didl/DidlBuilder.cs

@@ -198,7 +198,7 @@ namespace Emby.Dlna.Didl
                 streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildVideoItem(new VideoOptions
                 streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildVideoItem(new VideoOptions
                 {
                 {
                     ItemId = GetClientId(video),
                     ItemId = GetClientId(video),
-                    MediaSources = sources,
+                    MediaSources = sources.ToArray(sources.Count),
                     Profile = _profile,
                     Profile = _profile,
                     DeviceId = deviceId,
                     DeviceId = deviceId,
                     MaxBitrate = _profile.MaxStreamingBitrate
                     MaxBitrate = _profile.MaxStreamingBitrate
@@ -236,7 +236,7 @@ namespace Emby.Dlna.Didl
                 AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo);
                 AddVideoResource(writer, video, deviceId, filter, contentFeature, streamInfo);
             }
             }
 
 
-            var subtitleProfiles = streamInfo.GetSubtitleProfiles(false, _serverAddress, _accessToken)
+            var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken)
                 .Where(subtitle => subtitle.DeliveryMethod == SubtitleDeliveryMethod.External)
                 .Where(subtitle => subtitle.DeliveryMethod == SubtitleDeliveryMethod.External)
                 .ToList();
                 .ToList();
 
 
@@ -391,14 +391,6 @@ namespace Emby.Dlna.Didl
 
 
         private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
         private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
         {
         {
-            if (itemStubType.HasValue && itemStubType.Value == StubType.People)
-            {
-                if (item is Video)
-                {
-                    return _localization.GetLocalizedString("HeaderCastCrew");
-                }
-                return _localization.GetLocalizedString("HeaderPeople");
-            }
             if (itemStubType.HasValue && itemStubType.Value == StubType.Latest)
             if (itemStubType.HasValue && itemStubType.Value == StubType.Latest)
             {
             {
                 return _localization.GetLocalizedString("ViewTypeMusicLatest");
                 return _localization.GetLocalizedString("ViewTypeMusicLatest");
@@ -513,7 +505,7 @@ namespace Emby.Dlna.Didl
                 streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions
                 streamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger(options)).BuildAudioItem(new AudioOptions
                 {
                 {
                     ItemId = GetClientId(audio),
                     ItemId = GetClientId(audio),
-                    MediaSources = sources,
+                    MediaSources = sources.ToArray(sources.Count),
                     Profile = _profile,
                     Profile = _profile,
                     DeviceId = deviceId
                     DeviceId = deviceId
                 });
                 });
@@ -961,12 +953,6 @@ namespace Emby.Dlna.Didl
 
 
         private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer)
         private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer)
         {
         {
-            if (stubType.HasValue && stubType.Value == StubType.People)
-            {
-                AddEmbeddedImageAsCover("people", writer);
-                return;
-            }
-
             ImageDownloadInfo imageInfo = null;
             ImageDownloadInfo imageInfo = null;
 
 
             if (context is UserView)
             if (context is UserView)

+ 2 - 5
Emby.Dlna/Didl/Filter.cs

@@ -1,13 +1,12 @@
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Extensions;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Linq;
 
 
 namespace Emby.Dlna.Didl
 namespace Emby.Dlna.Didl
 {
 {
     public class Filter
     public class Filter
     {
     {
-        private readonly List<string> _fields;
+        private readonly string[] _fields;
         private readonly bool _all;
         private readonly bool _all;
 
 
         public Filter()
         public Filter()
@@ -20,9 +19,7 @@ namespace Emby.Dlna.Didl
         {
         {
             _all = StringHelper.EqualsIgnoreCase(filter, "*");
             _all = StringHelper.EqualsIgnoreCase(filter, "*");
 
 
-            var list = (filter ?? string.Empty).Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).ToList();
-
-            _fields = list;
+            _fields = (filter ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
         }
         }
 
 
         public bool Contains(string field)
         public bool Contains(string field)

+ 0 - 1
Emby.Dlna/Didl/StringWriterWithEncoding.cs

@@ -1,7 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
-using System.Linq;
 using System.Text;
 using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 

+ 5 - 5
Emby.Dlna/PlayTo/PlayToController.cs

@@ -13,13 +13,13 @@ using MediaBrowser.Model.System;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
-using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.Extensions;
 
 
 namespace Emby.Dlna.PlayTo
 namespace Emby.Dlna.PlayTo
 {
 {
@@ -48,7 +48,7 @@ namespace Emby.Dlna.PlayTo
         {
         {
             get
             get
             {
             {
-                var lastDateKnownActivity = new[] { _creationTime, _device.DateLastActivity }.Max();
+                var lastDateKnownActivity = _creationTime > _device.DateLastActivity ? _creationTime : _device.DateLastActivity;
 
 
                 if (DateTime.UtcNow >= lastDateKnownActivity.AddSeconds(120))
                 if (DateTime.UtcNow >= lastDateKnownActivity.AddSeconds(120))
                 {
                 {
@@ -564,7 +564,7 @@ namespace Emby.Dlna.PlayTo
                     streamInfo.TargetVideoCodecTag,
                     streamInfo.TargetVideoCodecTag,
                     streamInfo.IsTargetAVC);
                     streamInfo.IsTargetAVC);
 
 
-                return list.FirstOrDefault();
+                return list.Count == 0 ? null : list[0];
             }
             }
 
 
             return null;
             return null;
@@ -589,7 +589,7 @@ namespace Emby.Dlna.PlayTo
                     StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildVideoItem(new VideoOptions
                     StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildVideoItem(new VideoOptions
                     {
                     {
                         ItemId = item.Id.ToString("N"),
                         ItemId = item.Id.ToString("N"),
-                        MediaSources = mediaSources,
+                        MediaSources = mediaSources.ToArray(mediaSources.Count),
                         Profile = profile,
                         Profile = profile,
                         DeviceId = deviceId,
                         DeviceId = deviceId,
                         MaxBitrate = profile.MaxStreamingBitrate,
                         MaxBitrate = profile.MaxStreamingBitrate,
@@ -609,7 +609,7 @@ namespace Emby.Dlna.PlayTo
                     StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildAudioItem(new AudioOptions
                     StreamInfo = new StreamBuilder(_mediaEncoder, GetStreamBuilderLogger()).BuildAudioItem(new AudioOptions
                     {
                     {
                         ItemId = item.Id.ToString("N"),
                         ItemId = item.Id.ToString("N"),
-                        MediaSources = mediaSources,
+                        MediaSources = mediaSources.ToArray(mediaSources.Count),
                         Profile = profile,
                         Profile = profile,
                         DeviceId = deviceId,
                         DeviceId = deviceId,
                         MaxBitrate = profile.MaxStreamingBitrate,
                         MaxBitrate = profile.MaxStreamingBitrate,

+ 1 - 1
Emby.Dlna/PlayTo/PlayToManager.cs

@@ -182,7 +182,7 @@ namespace Emby.Dlna.PlayTo
                     {
                     {
                         PlayableMediaTypes = profile.GetSupportedMediaTypes(),
                         PlayableMediaTypes = profile.GetSupportedMediaTypes(),
 
 
-                        SupportedCommands = new List<string>
+                        SupportedCommands = new string[]
                         {
                         {
                             GeneralCommandType.VolumeDown.ToString(),
                             GeneralCommandType.VolumeDown.ToString(),
                             GeneralCommandType.VolumeUp.ToString(),
                             GeneralCommandType.VolumeUp.ToString(),

+ 135 - 135
Emby.Dlna/Profiles/PanasonicVieraProfile.cs

@@ -16,14 +16,14 @@ namespace Emby.Dlna.Profiles
                 Manufacturer = "Panasonic",
                 Manufacturer = "Panasonic",
 
 
                 Headers = new[]
                 Headers = new[]
-               {
-                   new HttpHeaderInfo
-                   {
-                       Name = "User-Agent",
-                       Value = "Panasonic MIL DLNA",
-                       Match = HeaderMatchType.Substring
-                   }
-               }
+                {
+                    new HttpHeaderInfo
+                    {
+                        Name = "User-Agent",
+                        Value = "Panasonic MIL DLNA",
+                        Match = HeaderMatchType.Substring
+                    }
+                }
             };
             };
 
 
             AddXmlRootAttribute("xmlns:pv", "http://www.pv.com/pvns/");
             AddXmlRootAttribute("xmlns:pv", "http://www.pv.com/pvns/");
@@ -31,105 +31,105 @@ namespace Emby.Dlna.Profiles
             TimelineOffsetSeconds = 10;
             TimelineOffsetSeconds = 10;
 
 
             TranscodingProfiles = new[]
             TranscodingProfiles = new[]
-           {
-               new TranscodingProfile
-               {
-                   Container = "mp3",
-                   AudioCodec = "mp3",
-                   Type = DlnaProfileType.Audio
-               },
-               new TranscodingProfile
-               {
-                   Container = "ts",
-                   AudioCodec = "ac3",
-                   VideoCodec = "h264",
-                   Type = DlnaProfileType.Video
-               },
-               new TranscodingProfile
-               {
-                   Container = "jpeg",
-                   Type = DlnaProfileType.Photo
-               }
-           };
+            {
+                new TranscodingProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+                new TranscodingProfile
+                {
+                    Container = "ts",
+                    AudioCodec = "ac3",
+                    VideoCodec = "h264",
+                    Type = DlnaProfileType.Video
+                },
+                new TranscodingProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
 
 
             DirectPlayProfiles = new[]
             DirectPlayProfiles = new[]
-           {
-               new DirectPlayProfile
-               {
-                   Container = "mpeg,mpg",
-                   VideoCodec = "mpeg2video,mpeg4",
-                   AudioCodec = "ac3,mp3,pcm_dvd",
-                   Type = DlnaProfileType.Video
-               },
-
-               new DirectPlayProfile
-               {
-                   Container = "mkv",
-                   VideoCodec = "h264,mpeg2video",
-                   AudioCodec = "aac,ac3,dca,mp3,mp2,pcm,dts",
-                   Type = DlnaProfileType.Video
-               },
-
-               new DirectPlayProfile
-               {
-                   Container = "ts",
-                   VideoCodec = "h264,mpeg2video",
-                   AudioCodec = "aac,mp3,mp2",
-                   Type = DlnaProfileType.Video
-               },
-
-               new DirectPlayProfile
-               {
-                   Container = "mp4,m4v",
-                   VideoCodec = "h264",
-                   AudioCodec = "aac,ac3,mp3,pcm",
-                   Type = DlnaProfileType.Video
-               },
-
-               new DirectPlayProfile
-               {
-                   Container = "mov",
-                   VideoCodec = "h264",
-                   AudioCodec = "aac,pcm",
-                   Type = DlnaProfileType.Video
-               },
-
-               new DirectPlayProfile
-               {
-                   Container = "avi",
-                   VideoCodec = "mpeg4",
-                   AudioCodec = "pcm",
-                   Type = DlnaProfileType.Video
-               },
-
-               new DirectPlayProfile
-               {
-                   Container = "flv",
-                   VideoCodec = "h264",
-                   AudioCodec = "aac",
-                   Type = DlnaProfileType.Video
-               },
-
-               new DirectPlayProfile
-               {
-                   Container = "mp3",
-                   AudioCodec = "mp3",
-                   Type = DlnaProfileType.Audio
-               },
-
-               new DirectPlayProfile
-               {
-                   Container = "mp4",
-                   AudioCodec = "aac",
-                   Type = DlnaProfileType.Audio
-               },
-
-               new DirectPlayProfile
-               {
-                   Container = "jpeg",
-                   Type = DlnaProfileType.Photo
-               }
-           };
+            {
+                new DirectPlayProfile
+                {
+                    Container = "mpeg,mpg",
+                    VideoCodec = "mpeg2video,mpeg4",
+                    AudioCodec = "ac3,mp3,pcm_dvd",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mkv",
+                    VideoCodec = "h264,mpeg2video",
+                    AudioCodec = "aac,ac3,dca,mp3,mp2,pcm,dts",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "ts",
+                    VideoCodec = "h264,mpeg2video",
+                    AudioCodec = "aac,mp3,mp2",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mp4,m4v",
+                    VideoCodec = "h264",
+                    AudioCodec = "aac,ac3,mp3,pcm",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mov",
+                    VideoCodec = "h264",
+                    AudioCodec = "aac,pcm",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "avi",
+                    VideoCodec = "mpeg4",
+                    AudioCodec = "pcm",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "flv",
+                    VideoCodec = "h264",
+                    AudioCodec = "aac",
+                    Type = DlnaProfileType.Video
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mp3",
+                    AudioCodec = "mp3",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "mp4",
+                    AudioCodec = "aac",
+                    Type = DlnaProfileType.Audio
+                },
+
+                new DirectPlayProfile
+                {
+                    Container = "jpeg",
+                    Type = DlnaProfileType.Photo
+                }
+            };
 
 
             ContainerProfiles = new[]
             ContainerProfiles = new[]
             {
             {
@@ -156,35 +156,35 @@ namespace Emby.Dlna.Profiles
             };
             };
 
 
             CodecProfiles = new[]
             CodecProfiles = new[]
-           {
-               new CodecProfile
-               {
-                   Type = CodecType.Video,
-
-                   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.VideoBitDepth,
-                           Value = "8",
-                           IsRequired = false
-                       }
-                   }
-               }
-           };
+            {
+                new CodecProfile
+                {
+                    Type = CodecType.Video,
+
+                    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.VideoBitDepth,
+                            Value = "8",
+                            IsRequired = false
+                        }
+                    }
+                }
+            };
 
 
             SubtitleProfiles = new[]
             SubtitleProfiles = new[]
             {
             {
@@ -218,4 +218,4 @@ namespace Emby.Dlna.Profiles
             };
             };
         }
         }
     }
     }
-}
+}

+ 5 - 4
Emby.Dlna/Ssdp/Extensions.cs

@@ -1,5 +1,4 @@
 using System;
 using System;
-using System.Linq;
 using System.Net;
 using System.Net;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using System.Xml.Linq;
 using System.Xml.Linq;
@@ -24,10 +23,12 @@ namespace Emby.Dlna.Ssdp
 
 
         public static string GetDescendantValue(this XElement container, XName name)
         public static string GetDescendantValue(this XElement container, XName name)
         {
         {
-            var node = container.Descendants(name)
-                .FirstOrDefault();
+            foreach (var node in container.Descendants(name))
+            {
+                return node.Value;
+            }
 
 
-            return node == null ? null : node.Value;
+            return null;
         }
         }
     }
     }
 }
 }

+ 3 - 3
Emby.Drawing.ImageMagick/ImageHelpers.cs

@@ -6,13 +6,13 @@ namespace Emby.Drawing.ImageMagick
 {
 {
     internal static class ImageHelpers
     internal static class ImageHelpers
     {
     {
-        internal static List<string> ProjectPaths(List<string> paths, int count)
+        internal static List<string> ProjectPaths(string[] paths, int count)
         {
         {
             if (count <= 0)
             if (count <= 0)
             {
             {
                 throw new ArgumentOutOfRangeException("count");
                 throw new ArgumentOutOfRangeException("count");
             }
             }
-            if (paths.Count == 0)
+            if (paths.Length == 0)
             {
             {
                 throw new ArgumentOutOfRangeException("paths");
                 throw new ArgumentOutOfRangeException("paths");
             }
             }
@@ -24,7 +24,7 @@ namespace Emby.Drawing.ImageMagick
             return list.Take(count).ToList();
             return list.Take(count).ToList();
         }
         }
 
 
-        private static void AddToList(List<string> list, List<string> paths, int count)
+        private static void AddToList(List<string> list, string[] paths, int count)
         {
         {
             while (list.Count < count)
             while (list.Count < count)
             {
             {

+ 3 - 4
Emby.Drawing.ImageMagick/ImageMagickEncoder.cs

@@ -7,7 +7,6 @@ using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using System;
 using System;
 using System.IO;
 using System.IO;
-using System.Linq;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.System;
 using MediaBrowser.Model.System;
 
 
@@ -306,15 +305,15 @@ namespace Emby.Drawing.ImageMagick
 
 
             if (ratio >= 1.4)
             if (ratio >= 1.4)
             {
             {
-                new StripCollageBuilder(_appPaths, _fileSystem).BuildThumbCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height);
+                new StripCollageBuilder(_appPaths, _fileSystem).BuildThumbCollage(options.InputPaths, options.OutputPath, options.Width, options.Height);
             }
             }
             else if (ratio >= .9)
             else if (ratio >= .9)
             {
             {
-                new StripCollageBuilder(_appPaths, _fileSystem).BuildSquareCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height);
+                new StripCollageBuilder(_appPaths, _fileSystem).BuildSquareCollage(options.InputPaths, options.OutputPath, options.Width, options.Height);
             }
             }
             else
             else
             {
             {
-                new StripCollageBuilder(_appPaths, _fileSystem).BuildPosterCollage(options.InputPaths.ToList(), options.OutputPath, options.Width, options.Height);
+                new StripCollageBuilder(_appPaths, _fileSystem).BuildPosterCollage(options.InputPaths, options.OutputPath, options.Width, options.Height);
             }
             }
         }
         }
 
 

+ 6 - 6
Emby.Drawing.ImageMagick/StripCollageBuilder.cs

@@ -19,7 +19,7 @@ namespace Emby.Drawing.ImageMagick
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
         }
         }
 
 
-        public void BuildPosterCollage(List<string> paths, string outputPath, int width, int height)
+        public void BuildPosterCollage(string[] paths, string outputPath, int width, int height)
         {
         {
             using (var wand = BuildPosterCollageWand(paths, width, height))
             using (var wand = BuildPosterCollageWand(paths, width, height))
             {
             {
@@ -27,7 +27,7 @@ namespace Emby.Drawing.ImageMagick
             }
             }
         }
         }
 
 
-        public void BuildSquareCollage(List<string> paths, string outputPath, int width, int height)
+        public void BuildSquareCollage(string[] paths, string outputPath, int width, int height)
         {
         {
             using (var wand = BuildSquareCollageWand(paths, width, height))
             using (var wand = BuildSquareCollageWand(paths, width, height))
             {
             {
@@ -35,7 +35,7 @@ namespace Emby.Drawing.ImageMagick
             }
             }
         }
         }
 
 
-        public void BuildThumbCollage(List<string> paths, string outputPath, int width, int height)
+        public void BuildThumbCollage(string[] paths, string outputPath, int width, int height)
         {
         {
             using (var wand = BuildThumbCollageWand(paths, width, height))
             using (var wand = BuildThumbCollageWand(paths, width, height))
             {
             {
@@ -43,7 +43,7 @@ namespace Emby.Drawing.ImageMagick
             }
             }
         }
         }
 
 
-        private MagickWand BuildPosterCollageWand(List<string> paths, int width, int height)
+        private MagickWand BuildPosterCollageWand(string[] paths, int width, int height)
         {
         {
             var inputPaths = ImageHelpers.ProjectPaths(paths, 3);
             var inputPaths = ImageHelpers.ProjectPaths(paths, 3);
             using (var wandImages = new MagickWand(inputPaths.ToArray()))
             using (var wandImages = new MagickWand(inputPaths.ToArray()))
@@ -108,7 +108,7 @@ namespace Emby.Drawing.ImageMagick
             }
             }
         }
         }
 
 
-        private MagickWand BuildThumbCollageWand(List<string> paths, int width, int height)
+        private MagickWand BuildThumbCollageWand(string[] paths, int width, int height)
         {
         {
             var inputPaths = ImageHelpers.ProjectPaths(paths, 4);
             var inputPaths = ImageHelpers.ProjectPaths(paths, 4);
             using (var wandImages = new MagickWand(inputPaths.ToArray()))
             using (var wandImages = new MagickWand(inputPaths.ToArray()))
@@ -173,7 +173,7 @@ namespace Emby.Drawing.ImageMagick
             }
             }
         }
         }
 
 
-        private MagickWand BuildSquareCollageWand(List<string> paths, int width, int height)
+        private MagickWand BuildSquareCollageWand(string[] paths, int width, int height)
         {
         {
             var inputPaths = ImageHelpers.ProjectPaths(paths, 4);
             var inputPaths = ImageHelpers.ProjectPaths(paths, 4);
             var outputWand = new MagickWand(width, height, new PixelWand("none", 1));
             var outputWand = new MagickWand(width, height, new PixelWand("none", 1));

+ 88 - 12
Emby.Drawing/ImageProcessor.cs

@@ -18,6 +18,7 @@ using System.Threading.Tasks;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using Emby.Drawing.Common;
 using Emby.Drawing.Common;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaEncoding;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Threading;
 using MediaBrowser.Model.Threading;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Extensions;
@@ -44,7 +45,7 @@ namespace Emby.Drawing
         /// Image processors are specialized metadata providers that run after the normal ones
         /// Image processors are specialized metadata providers that run after the normal ones
         /// </summary>
         /// </summary>
         /// <value>The image enhancers.</value>
         /// <value>The image enhancers.</value>
-        public IEnumerable<IImageEnhancer> ImageEnhancers { get; private set; }
+        public IImageEnhancer[] ImageEnhancers { get; private set; }
 
 
         /// <summary>
         /// <summary>
         /// The _logger
         /// The _logger
@@ -56,22 +57,24 @@ namespace Emby.Drawing
         private readonly IServerApplicationPaths _appPaths;
         private readonly IServerApplicationPaths _appPaths;
         private IImageEncoder _imageEncoder;
         private IImageEncoder _imageEncoder;
         private readonly Func<ILibraryManager> _libraryManager;
         private readonly Func<ILibraryManager> _libraryManager;
+        private readonly Func<IMediaEncoder> _mediaEncoder;
 
 
         public ImageProcessor(ILogger logger,
         public ImageProcessor(ILogger logger,
             IServerApplicationPaths appPaths,
             IServerApplicationPaths appPaths,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
             IJsonSerializer jsonSerializer,
             IJsonSerializer jsonSerializer,
             IImageEncoder imageEncoder,
             IImageEncoder imageEncoder,
-            Func<ILibraryManager> libraryManager, ITimerFactory timerFactory)
+            Func<ILibraryManager> libraryManager, ITimerFactory timerFactory, Func<IMediaEncoder> mediaEncoder)
         {
         {
             _logger = logger;
             _logger = logger;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _jsonSerializer = jsonSerializer;
             _jsonSerializer = jsonSerializer;
             _imageEncoder = imageEncoder;
             _imageEncoder = imageEncoder;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
+            _mediaEncoder = mediaEncoder;
             _appPaths = appPaths;
             _appPaths = appPaths;
 
 
-            ImageEnhancers = new List<IImageEnhancer>();
+            ImageEnhancers = new IImageEnhancer[] { };
             _saveImageSizeTimer = timerFactory.Create(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite);
             _saveImageSizeTimer = timerFactory.Create(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite);
             ImageHelper.ImageProcessor = this;
             ImageHelper.ImageProcessor = this;
 
 
@@ -120,7 +123,36 @@ namespace Emby.Drawing
         {
         {
             get
             get
             {
             {
-                return _imageEncoder.SupportedInputFormats;
+                return new string[]
+                {
+                    "tiff",
+                    "jpeg",
+                    "jpg",
+                    "png",
+                    "aiff",
+                    "cr2",
+                    "crw",
+                    "dng", 
+
+                    // Remove until supported
+                    //"nef", 
+                    "orf",
+                    "pef",
+                    "arw",
+                    "webp",
+                    "gif",
+                    "bmp",
+                    "erf",
+                    "raf",
+                    "rw2",
+                    "nrw",
+                    "dng",
+                    "ico",
+                    "astc",
+                    "ktx",
+                    "pkm",
+                    "wbmp"
+                };
             }
             }
         }
         }
 
 
@@ -203,6 +235,10 @@ namespace Emby.Drawing
                 return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
                 return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
             }
             }
 
 
+            var supportedImageInfo = await GetSupportedImage(originalImagePath, dateModified).ConfigureAwait(false);
+            originalImagePath = supportedImageInfo.Item1;
+            dateModified = supportedImageInfo.Item2;
+
             if (options.Enhancers.Count > 0)
             if (options.Enhancers.Count > 0)
             {
             {
                 if (item == null)
                 if (item == null)
@@ -618,7 +654,7 @@ namespace Emby.Drawing
 
 
             var supportedEnhancers = GetSupportedEnhancers(item, image.Type);
             var supportedEnhancers = GetSupportedEnhancers(item, image.Type);
 
 
-            return GetImageCacheTag(item, image, supportedEnhancers.ToList());
+            return GetImageCacheTag(item, image, supportedEnhancers);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -663,6 +699,42 @@ namespace Emby.Drawing
             return string.Join("|", cacheKeys.ToArray(cacheKeys.Count)).GetMD5().ToString("N");
             return string.Join("|", cacheKeys.ToArray(cacheKeys.Count)).GetMD5().ToString("N");
         }
         }
 
 
+        private async Task<Tuple<string, DateTime>> GetSupportedImage(string originalImagePath, DateTime dateModified)
+        {
+            var inputFormat = (Path.GetExtension(originalImagePath) ?? string.Empty)
+                .TrimStart('.')
+                .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
+
+            if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat, StringComparer.OrdinalIgnoreCase))
+            {
+                try
+                {
+                    var filename = (originalImagePath + dateModified.Ticks.ToString(UsCulture)).GetMD5().ToString("N");
+
+                    var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + ".webp");
+
+                    var file = _fileSystem.GetFileInfo(outputPath);
+                    if (!file.Exists)
+                    {
+                        await _mediaEncoder().ConvertImage(originalImagePath, outputPath).ConfigureAwait(false);
+                        dateModified = _fileSystem.GetLastWriteTimeUtc(outputPath);
+                    }
+                    else
+                    {
+                        dateModified = file.LastWriteTimeUtc;
+                    }
+
+                    originalImagePath = outputPath;
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Image conversion failed for {0}", ex, originalImagePath);
+                }
+            }
+
+            return new Tuple<string, DateTime>(originalImagePath, dateModified);
+        }
+
         /// <summary>
         /// <summary>
         /// Gets the enhanced image.
         /// Gets the enhanced image.
         /// </summary>
         /// </summary>
@@ -672,7 +744,7 @@ namespace Emby.Drawing
         /// <returns>Task{System.String}.</returns>
         /// <returns>Task{System.String}.</returns>
         public async Task<string> GetEnhancedImage(IHasMetadata item, ImageType imageType, int imageIndex)
         public async Task<string> GetEnhancedImage(IHasMetadata item, ImageType imageType, int imageIndex)
         {
         {
-            var enhancers = GetSupportedEnhancers(item, imageType).ToList();
+            var enhancers = GetSupportedEnhancers(item, imageType);
 
 
             var imageInfo = item.GetImageInfo(imageType, imageIndex);
             var imageInfo = item.GetImageInfo(imageType, imageIndex);
 
 
@@ -866,21 +938,25 @@ namespace Emby.Drawing
             _logger.Info("Completed creation of image collage and saved to {0}", options.OutputPath);
             _logger.Info("Completed creation of image collage and saved to {0}", options.OutputPath);
         }
         }
 
 
-        public IEnumerable<IImageEnhancer> GetSupportedEnhancers(IHasMetadata item, ImageType imageType)
+        public List<IImageEnhancer> GetSupportedEnhancers(IHasMetadata item, ImageType imageType)
         {
         {
-            return ImageEnhancers.Where(i =>
+            var list = new List<IImageEnhancer>();
+
+            foreach (var i in ImageEnhancers)
             {
             {
                 try
                 try
                 {
                 {
-                    return i.Supports(item, imageType);
+                    if (i.Supports(item, imageType))
+                    {
+                        list.Add(i);
+                    }
                 }
                 }
                 catch (Exception ex)
                 catch (Exception ex)
                 {
                 {
                     _logger.ErrorException("Error in image enhancer: {0}", ex, i.GetType().Name);
                     _logger.ErrorException("Error in image enhancer: {0}", ex, i.GetType().Name);
-
-                    return false;
                 }
                 }
-            });
+            }
+            return list;
         }
         }
 
 
         private bool _disposed;
         private bool _disposed;

+ 6 - 6
Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs

@@ -128,7 +128,7 @@ namespace Emby.Server.Implementations.Activity
             {
             {
                 // Don't report theme song or local trailer playback
                 // Don't report theme song or local trailer playback
                 return;
                 return;
-            } 
+            }
 
 
             if (e.Users.Count == 0)
             if (e.Users.Count == 0)
             {
             {
@@ -160,8 +160,8 @@ namespace Emby.Server.Implementations.Activity
             {
             {
                 // Don't report theme song or local trailer playback
                 // Don't report theme song or local trailer playback
                 return;
                 return;
-            } 
-            
+            }
+
             if (e.Users.Count == 0)
             if (e.Users.Count == 0)
             {
             {
                 return;
                 return;
@@ -416,7 +416,7 @@ namespace Emby.Server.Implementations.Activity
             {
             {
                 return;
                 return;
             }
             }
-            
+
             var time = result.EndTimeUtc - result.StartTimeUtc;
             var time = result.EndTimeUtc - result.StartTimeUtc;
             var runningTime = string.Format(_localization.GetLocalizedString("LabelRunningTimeValue"), ToUserFriendlyString(time));
             var runningTime = string.Format(_localization.GetLocalizedString("LabelRunningTimeValue"), ToUserFriendlyString(time));
 
 
@@ -444,11 +444,11 @@ namespace Emby.Server.Implementations.Activity
             }
             }
         }
         }
 
 
-        private async void CreateLogEntry(ActivityLogEntry entry)
+        private void CreateLogEntry(ActivityLogEntry entry)
         {
         {
             try
             try
             {
             {
-                await _activityManager.Create(entry).ConfigureAwait(false);
+                _activityManager.Create(entry);
             }
             }
             catch
             catch
             {
             {

+ 3 - 3
Emby.Server.Implementations/Activity/ActivityManager.cs

@@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.Activity
     public class ActivityManager : IActivityManager
     public class ActivityManager : IActivityManager
     {
     {
         public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
         public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
-        
+
         private readonly IActivityRepository _repo;
         private readonly IActivityRepository _repo;
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IUserManager _userManager;
         private readonly IUserManager _userManager;
@@ -25,12 +25,12 @@ namespace Emby.Server.Implementations.Activity
             _userManager = userManager;
             _userManager = userManager;
         }
         }
 
 
-        public async Task Create(ActivityLogEntry entry)
+        public void Create(ActivityLogEntry entry)
         {
         {
             entry.Id = Guid.NewGuid().ToString("N");
             entry.Id = Guid.NewGuid().ToString("N");
             entry.Date = DateTime.UtcNow;
             entry.Date = DateTime.UtcNow;
 
 
-            await _repo.Create(entry).ConfigureAwait(false);
+            _repo.Create(entry);
 
 
             EventHelper.FireEventIfNotNull(EntryCreated, this, new GenericEventArgs<ActivityLogEntry>(entry), _logger);
             EventHelper.FireEventIfNotNull(EntryCreated, this, new GenericEventArgs<ActivityLogEntry>(entry), _logger);
         }
         }

+ 3 - 4
Emby.Server.Implementations/Activity/ActivityRepository.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
-using System.Threading.Tasks;
 using Emby.Server.Implementations.Data;
 using Emby.Server.Implementations.Data;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Model.Activity;
 using MediaBrowser.Model.Activity;
@@ -41,12 +40,12 @@ namespace Emby.Server.Implementations.Activity
 
 
         private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLogEntries";
         private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLogEntries";
 
 
-        public Task Create(ActivityLogEntry entry)
+        public void Create(ActivityLogEntry entry)
         {
         {
-            return Update(entry);
+            Update(entry);
         }
         }
 
 
-        public async Task Update(ActivityLogEntry entry)
+        public void Update(ActivityLogEntry entry)
         {
         {
             if (entry == null)
             if (entry == null)
             {
             {

+ 20 - 13
Emby.Server.Implementations/ApplicationHost.cs

@@ -227,6 +227,8 @@ namespace Emby.Server.Implementations
 
 
         protected IEnvironmentInfo EnvironmentInfo { get; set; }
         protected IEnvironmentInfo EnvironmentInfo { get; set; }
 
 
+        private IBlurayExaminer BlurayExaminer { get; set; }
+
         public PackageVersionClass SystemUpdateLevel
         public PackageVersionClass SystemUpdateLevel
         {
         {
             get
             get
@@ -424,11 +426,6 @@ namespace Emby.Server.Implementations
 
 
             SetBaseExceptionMessage();
             SetBaseExceptionMessage();
 
 
-            if (environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
-            {
-                fileSystem.AddShortcutHandler(new LnkShortcutHandler());
-            }
-
             fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
             fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
         }
         }
 
 
@@ -866,7 +863,7 @@ namespace Emby.Server.Implementations
             SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager, FileSystemManager, CryptographyProvider);
             SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager, FileSystemManager, CryptographyProvider);
             RegisterSingleInstance(SecurityManager);
             RegisterSingleInstance(SecurityManager);
 
 
-            InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager, CryptographyProvider);
+            InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager, CryptographyProvider, PackageRuntime);
             RegisterSingleInstance(InstallationManager);
             RegisterSingleInstance(InstallationManager);
 
 
             ZipClient = new ZipClient(FileSystemManager);
             ZipClient = new ZipClient(FileSystemManager);
@@ -889,7 +886,8 @@ namespace Emby.Server.Implementations
             ITextEncoding textEncoding = new TextEncoding.TextEncoding(FileSystemManager, LogManager.GetLogger("TextEncoding"), JsonSerializer);
             ITextEncoding textEncoding = new TextEncoding.TextEncoding(FileSystemManager, LogManager.GetLogger("TextEncoding"), JsonSerializer);
             RegisterSingleInstance(textEncoding);
             RegisterSingleInstance(textEncoding);
             Utilities.EncodingHelper = textEncoding;
             Utilities.EncodingHelper = textEncoding;
-            RegisterSingleInstance<IBlurayExaminer>(() => new BdInfoExaminer(FileSystemManager, textEncoding));
+            BlurayExaminer = new BdInfoExaminer(FileSystemManager, textEncoding);
+            RegisterSingleInstance(BlurayExaminer);
 
 
             RegisterSingleInstance<IXmlReaderSettingsFactory>(new XmlReaderSettingsFactory());
             RegisterSingleInstance<IXmlReaderSettingsFactory>(new XmlReaderSettingsFactory());
 
 
@@ -1050,7 +1048,15 @@ namespace Emby.Server.Implementations
 
 
             SetStaticProperties();
             SetStaticProperties();
 
 
-            await ((UserManager)UserManager).Initialize().ConfigureAwait(false);
+            ((UserManager)UserManager).Initialize();
+        }
+
+        protected virtual string PackageRuntime
+        {
+            get
+            {
+                return "netframework";
+            }
         }
         }
 
 
         public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths, bool isStartup)
         public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths, bool isStartup)
@@ -1199,7 +1205,7 @@ namespace Emby.Server.Implementations
 
 
         private IImageProcessor GetImageProcessor()
         private IImageProcessor GetImageProcessor()
         {
         {
-            return new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, ImageEncoder, () => LibraryManager, TimerFactory);
+            return new ImageProcessor(LogManager.GetLogger("ImageProcessor"), ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, ImageEncoder, () => LibraryManager, TimerFactory, () => MediaEncoder);
         }
         }
 
 
         protected virtual FFMpegInstallInfo GetFfmpegInstallInfo()
         protected virtual FFMpegInstallInfo GetFfmpegInstallInfo()
@@ -1332,7 +1338,8 @@ namespace Emby.Server.Implementations
                 ProcessFactory,
                 ProcessFactory,
                 (Environment.ProcessorCount > 2 ? 14000 : 40000),
                 (Environment.ProcessorCount > 2 ? 14000 : 40000),
                 EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows,
                 EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows,
-                EnvironmentInfo);
+                EnvironmentInfo,
+                BlurayExaminer);
 
 
             MediaEncoder = mediaEncoder;
             MediaEncoder = mediaEncoder;
             RegisterSingleInstance(MediaEncoder);
             RegisterSingleInstance(MediaEncoder);
@@ -1858,9 +1865,9 @@ namespace Emby.Server.Implementations
                 HasPendingRestart = HasPendingRestart,
                 HasPendingRestart = HasPendingRestart,
                 Version = ApplicationVersion.ToString(),
                 Version = ApplicationVersion.ToString(),
                 WebSocketPortNumber = HttpPort,
                 WebSocketPortNumber = HttpPort,
-                FailedPluginAssemblies = FailedAssemblies.ToList(),
-                InProgressInstallations = InstallationManager.CurrentInstallations.Select(i => i.Item1).ToList(),
-                CompletedInstallations = InstallationManager.CompletedInstallations.ToList(),
+                FailedPluginAssemblies = FailedAssemblies.ToArray(),
+                InProgressInstallations = InstallationManager.CurrentInstallations.Select(i => i.Item1).ToArray(),
+                CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(),
                 Id = SystemId,
                 Id = SystemId,
                 ProgramDataPath = ApplicationPaths.ProgramDataPath,
                 ProgramDataPath = ApplicationPaths.ProgramDataPath,
                 LogPath = ApplicationPaths.LogDirectoryPath,
                 LogPath = ApplicationPaths.LogDirectoryPath,

+ 16 - 24
Emby.Server.Implementations/Channels/ChannelManager.cs

@@ -182,10 +182,8 @@ namespace Emby.Server.Implementations.Channels
             {
             {
             };
             };
 
 
-            var returnList = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user)
+            var returnItems = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user)
                 .ConfigureAwait(false));
                 .ConfigureAwait(false));
-            var returnItems = returnList
-                .ToArray(returnList.Count);
 
 
             var result = new QueryResult<BaseItemDto>
             var result = new QueryResult<BaseItemDto>
             {
             {
@@ -431,7 +429,7 @@ namespace Emby.Server.Implementations.Channels
 
 
             if (isNew)
             if (isNew)
             {
             {
-                await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
+                _libraryManager.CreateItem(item, cancellationToken);
             }
             }
             else if (forceUpdate)
             else if (forceUpdate)
             {
             {
@@ -464,14 +462,14 @@ namespace Emby.Server.Implementations.Channels
             return _libraryManager.GetItemById(id) as Channel;
             return _libraryManager.GetItemById(id) as Channel;
         }
         }
 
 
-        public IEnumerable<ChannelFeatures> GetAllChannelFeatures()
+        public ChannelFeatures[] GetAllChannelFeatures()
         {
         {
             return _libraryManager.GetItemIds(new InternalItemsQuery
             return _libraryManager.GetItemIds(new InternalItemsQuery
             {
             {
                 IncludeItemTypes = new[] { typeof(Channel).Name },
                 IncludeItemTypes = new[] { typeof(Channel).Name },
                 SortBy = new[] { ItemSortBy.SortName }
                 SortBy = new[] { ItemSortBy.SortName }
 
 
-            }).Select(i => GetChannelFeatures(i.ToString("N")));
+            }).Select(i => GetChannelFeatures(i.ToString("N"))).ToArray();
         }
         }
 
 
         public ChannelFeatures GetChannelFeatures(string id)
         public ChannelFeatures GetChannelFeatures(string id)
@@ -511,10 +509,10 @@ namespace Emby.Server.Implementations.Channels
             {
             {
                 CanFilter = !features.MaxPageSize.HasValue,
                 CanFilter = !features.MaxPageSize.HasValue,
                 CanSearch = provider is ISearchableChannel,
                 CanSearch = provider is ISearchableChannel,
-                ContentTypes = features.ContentTypes,
-                DefaultSortFields = features.DefaultSortFields,
+                ContentTypes = features.ContentTypes.ToArray(),
+                DefaultSortFields = features.DefaultSortFields.ToArray(),
                 MaxPageSize = features.MaxPageSize,
                 MaxPageSize = features.MaxPageSize,
-                MediaTypes = features.MediaTypes,
+                MediaTypes = features.MediaTypes.ToArray(),
                 SupportsSortOrderToggle = features.SupportsSortOrderToggle,
                 SupportsSortOrderToggle = features.SupportsSortOrderToggle,
                 SupportsLatestMedia = supportsLatest,
                 SupportsLatestMedia = supportsLatest,
                 Name = channel.Name,
                 Name = channel.Name,
@@ -566,12 +564,10 @@ namespace Emby.Server.Implementations.Channels
 
 
             var dtoOptions = new DtoOptions()
             var dtoOptions = new DtoOptions()
             {
             {
-                Fields = query.Fields.ToList()
+                Fields = query.Fields
             };
             };
 
 
-            var returnList = (await _dtoService.GetBaseItemDtos(items, dtoOptions, user).ConfigureAwait(false));
-            var returnItems = returnList
-                .ToArray(returnList.Count);
+            var returnItems = (await _dtoService.GetBaseItemDtos(items, dtoOptions, user).ConfigureAwait(false));
 
 
             var result = new QueryResult<BaseItemDto>
             var result = new QueryResult<BaseItemDto>
             {
             {
@@ -833,13 +829,11 @@ namespace Emby.Server.Implementations.Channels
 
 
             var dtoOptions = new DtoOptions()
             var dtoOptions = new DtoOptions()
             {
             {
-                Fields = query.Fields.ToList()
+                Fields = query.Fields
             };
             };
 
 
-            var returnList = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user)
+            var returnItems = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user)
                 .ConfigureAwait(false));
                 .ConfigureAwait(false));
-            var returnItems = returnList
-                .ToArray(returnList.Count);
 
 
             var result = new QueryResult<BaseItemDto>
             var result = new QueryResult<BaseItemDto>
             {
             {
@@ -987,13 +981,11 @@ namespace Emby.Server.Implementations.Channels
 
 
             var dtoOptions = new DtoOptions()
             var dtoOptions = new DtoOptions()
             {
             {
-                Fields = query.Fields.ToList()
+                Fields = query.Fields
             };
             };
 
 
-            var returnList = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user)
+            var returnItems = (await _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user)
                 .ConfigureAwait(false));
                 .ConfigureAwait(false));
-            var returnItems = returnList
-                .ToArray(returnList.Count);
 
 
             var result = new QueryResult<BaseItemDto>
             var result = new QueryResult<BaseItemDto>
             {
             {
@@ -1338,7 +1330,7 @@ namespace Emby.Server.Implementations.Channels
             var hasArtists = item as IHasArtist;
             var hasArtists = item as IHasArtist;
             if (hasArtists != null)
             if (hasArtists != null)
             {
             {
-                hasArtists.Artists = info.Artists;
+                hasArtists.Artists = info.Artists.ToArray();
             }
             }
 
 
             var hasAlbumArtists = item as IHasAlbumArtist;
             var hasAlbumArtists = item as IHasAlbumArtist;
@@ -1396,11 +1388,11 @@ namespace Emby.Server.Implementations.Channels
 
 
             if (isNew)
             if (isNew)
             {
             {
-                await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
+                _libraryManager.CreateItem(item, cancellationToken);
 
 
                 if (info.People != null && info.People.Count > 0)
                 if (info.People != null && info.People.Count > 0)
                 {
                 {
-                    await _libraryManager.UpdatePeople(item, info.People ?? new List<PersonInfo>()).ConfigureAwait(false);
+                    _libraryManager.UpdatePeople(item, info.People ?? new List<PersonInfo>());
                 }
                 }
             }
             }
             else if (forceUpdate)
             else if (forceUpdate)

+ 23 - 12
Emby.Server.Implementations/Collections/CollectionManager.cs

@@ -84,15 +84,15 @@ namespace Emby.Server.Implementations.Collections
                     ProviderIds = options.ProviderIds,
                     ProviderIds = options.ProviderIds,
                     Shares = options.UserIds.Select(i => new Share
                     Shares = options.UserIds.Select(i => new Share
                     {
                     {
-                        UserId = i.ToString("N"),
+                        UserId = i,
                         CanEdit = true
                         CanEdit = true
 
 
                     }).ToList()
                     }).ToList()
                 };
                 };
 
 
-                await parentFolder.AddChild(collection, CancellationToken.None).ConfigureAwait(false);
+                parentFolder.AddChild(collection, CancellationToken.None);
 
 
-                if (options.ItemIdList.Count > 0)
+                if (options.ItemIdList.Length > 0)
                 {
                 {
                     await AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(_fileSystem)
                     await AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(_fileSystem)
                     {
                     {
@@ -149,12 +149,17 @@ namespace Emby.Server.Implementations.Collections
             return GetCollectionsFolder(string.Empty);
             return GetCollectionsFolder(string.Empty);
         }
         }
 
 
-        public Task AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
+        public Task AddToCollection(Guid collectionId, IEnumerable<string> ids)
         {
         {
             return AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(_fileSystem));
             return AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(_fileSystem));
         }
         }
 
 
-        private async Task AddToCollection(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
+        public Task AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
+        {
+            return AddToCollection(collectionId, ids.Select(i => i.ToString("N")), true, new MetadataRefreshOptions(_fileSystem));
+        }
+
+        private async Task AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
         {
         {
             var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
             var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
 
 
@@ -165,11 +170,12 @@ namespace Emby.Server.Implementations.Collections
 
 
             var list = new List<LinkedChild>();
             var list = new List<LinkedChild>();
             var itemList = new List<BaseItem>();
             var itemList = new List<BaseItem>();
-            var currentLinkedChildren = collection.GetLinkedChildren().ToList();
+            var currentLinkedChildrenIds = collection.GetLinkedChildren().Select(i => i.Id).ToList();
 
 
-            foreach (var itemId in ids)
+            foreach (var id in ids)
             {
             {
-                var item = _libraryManager.GetItemById(itemId);
+                var guidId = new Guid(id);
+                var item = _libraryManager.GetItemById(guidId);
 
 
                 if (string.IsNullOrWhiteSpace(item.Path))
                 if (string.IsNullOrWhiteSpace(item.Path))
                 {
                 {
@@ -183,7 +189,7 @@ namespace Emby.Server.Implementations.Collections
 
 
                 itemList.Add(item);
                 itemList.Add(item);
 
 
-                if (currentLinkedChildren.All(i => i.Id != itemId))
+                if (!currentLinkedChildrenIds.Contains(guidId))
                 {
                 {
                     list.Add(LinkedChild.Create(item));
                     list.Add(LinkedChild.Create(item));
                 }
                 }
@@ -213,6 +219,11 @@ namespace Emby.Server.Implementations.Collections
             }
             }
         }
         }
 
 
+        public Task RemoveFromCollection(Guid collectionId, IEnumerable<string> itemIds)
+        {
+            return RemoveFromCollection(collectionId, itemIds.Select(i => new Guid(i)));
+        }
+
         public async Task RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds)
         public async Task RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds)
         {
         {
             var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
             var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
@@ -225,11 +236,11 @@ namespace Emby.Server.Implementations.Collections
             var list = new List<LinkedChild>();
             var list = new List<LinkedChild>();
             var itemList = new List<BaseItem>();
             var itemList = new List<BaseItem>();
 
 
-            foreach (var itemId in itemIds)
+            foreach (var guidId in itemIds)
             {
             {
-                var childItem = _libraryManager.GetItemById(itemId);
+                var childItem = _libraryManager.GetItemById(guidId);
 
 
-                var child = collection.LinkedChildren.FirstOrDefault(i => (i.ItemId.HasValue && i.ItemId.Value == itemId) || (childItem != null && string.Equals(childItem.Path, i.Path, StringComparison.OrdinalIgnoreCase)));
+                var child = collection.LinkedChildren.FirstOrDefault(i => (i.ItemId.HasValue && i.ItemId.Value == guidId) || (childItem != null && string.Equals(childItem.Path, i.Path, StringComparison.OrdinalIgnoreCase)));
 
 
                 if (child == null)
                 if (child == null)
                 {
                 {

+ 4 - 5
Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs

@@ -2,7 +2,6 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Threading;
 using System.Threading;
-using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Persistence;
@@ -75,7 +74,7 @@ namespace Emby.Server.Implementations.Data
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">item</exception>
         /// <exception cref="System.ArgumentNullException">item</exception>
-        public async Task SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, CancellationToken cancellationToken)
+        public void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, CancellationToken cancellationToken)
         {
         {
             if (displayPreferences == null)
             if (displayPreferences == null)
             {
             {
@@ -123,7 +122,7 @@ namespace Emby.Server.Implementations.Data
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">item</exception>
         /// <exception cref="System.ArgumentNullException">item</exception>
-        public async Task SaveAllDisplayPreferences(IEnumerable<DisplayPreferences> displayPreferences, Guid userId, CancellationToken cancellationToken)
+        public void SaveAllDisplayPreferences(IEnumerable<DisplayPreferences> displayPreferences, Guid userId, CancellationToken cancellationToken)
         {
         {
             if (displayPreferences == null)
             if (displayPreferences == null)
             {
             {
@@ -226,9 +225,9 @@ namespace Emby.Server.Implementations.Data
             }
             }
         }
         }
 
 
-        public Task SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken)
+        public void SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken)
         {
         {
-            return SaveDisplayPreferences(displayPreferences, new Guid(userId), client, cancellationToken);
+            SaveDisplayPreferences(displayPreferences, new Guid(userId), client, cancellationToken);
         }
         }
 
 
         public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client)
         public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client)

+ 17 - 23
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -132,8 +132,7 @@ namespace Emby.Server.Implementations.Data
         /// <summary>
         /// <summary>
         /// Opens the connection to the database
         /// Opens the connection to the database
         /// </summary>
         /// </summary>
-        /// <returns>Task.</returns>
-        public async Task Initialize(SqliteUserDataRepository userDataRepo)
+        public void Initialize(SqliteUserDataRepository userDataRepo)
         {
         {
             using (var connection = CreateConnection())
             using (var connection = CreateConnection())
             {
             {
@@ -149,7 +148,7 @@ namespace Emby.Server.Implementations.Data
 
 
                                 "create table if not exists AncestorIds (ItemId GUID, AncestorId GUID, AncestorIdText TEXT, PRIMARY KEY (ItemId, AncestorId))",
                                 "create table if not exists AncestorIds (ItemId GUID, AncestorId GUID, AncestorIdText TEXT, PRIMARY KEY (ItemId, AncestorId))",
                                 "create index if not exists idx_AncestorIds1 on AncestorIds(AncestorId)",
                                 "create index if not exists idx_AncestorIds1 on AncestorIds(AncestorId)",
-                                "create index if not exists idx_AncestorIds2 on AncestorIds(AncestorIdText)",
+                                "create index if not exists idx_AncestorIds5 on AncestorIds(AncestorIdText,ItemId)",
 
 
                                 "create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT, CleanValue TEXT)",
                                 "create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT, CleanValue TEXT)",
 
 
@@ -308,6 +307,7 @@ namespace Emby.Server.Implementations.Data
                 "drop index if exists idx_TypeSeriesPresentationUniqueKey2",
                 "drop index if exists idx_TypeSeriesPresentationUniqueKey2",
                 "drop index if exists idx_AncestorIds3",
                 "drop index if exists idx_AncestorIds3",
                 "drop index if exists idx_AncestorIds4",
                 "drop index if exists idx_AncestorIds4",
+                "drop index if exists idx_AncestorIds2",
 
 
                 "create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
                 "create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
                 "create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
                 "create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
@@ -599,16 +599,15 @@ namespace Emby.Server.Implementations.Data
         /// </summary>
         /// </summary>
         /// <param name="item">The item.</param>
         /// <param name="item">The item.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">item</exception>
         /// <exception cref="System.ArgumentNullException">item</exception>
-        public Task SaveItem(BaseItem item, CancellationToken cancellationToken)
+        public void SaveItem(BaseItem item, CancellationToken cancellationToken)
         {
         {
             if (item == null)
             if (item == null)
             {
             {
                 throw new ArgumentNullException("item");
                 throw new ArgumentNullException("item");
             }
             }
 
 
-            return SaveItems(new List<BaseItem> { item }, cancellationToken);
+            SaveItems(new List<BaseItem> { item }, cancellationToken);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -616,13 +615,12 @@ namespace Emby.Server.Implementations.Data
         /// </summary>
         /// </summary>
         /// <param name="items">The items.</param>
         /// <param name="items">The items.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">
         /// <exception cref="System.ArgumentNullException">
         /// items
         /// items
         /// or
         /// or
         /// cancellationToken
         /// cancellationToken
         /// </exception>
         /// </exception>
-        public async Task SaveItems(List<BaseItem> items, CancellationToken cancellationToken)
+        public void SaveItems(List<BaseItem> items, CancellationToken cancellationToken)
         {
         {
             if (items == null)
             if (items == null)
             {
             {
@@ -1027,9 +1025,9 @@ namespace Emby.Server.Implementations.Data
             var hasArtists = item as IHasArtist;
             var hasArtists = item as IHasArtist;
             if (hasArtists != null)
             if (hasArtists != null)
             {
             {
-                if (hasArtists.Artists.Count > 0)
+                if (hasArtists.Artists.Length > 0)
                 {
                 {
-                    artists = string.Join("|", hasArtists.Artists.ToArray());
+                    artists = string.Join("|", hasArtists.Artists);
                 }
                 }
             }
             }
             saveItemStatement.TryBind("@Artists", artists);
             saveItemStatement.TryBind("@Artists", artists);
@@ -1907,7 +1905,7 @@ namespace Emby.Server.Implementations.Data
                 var hasArtists = item as IHasArtist;
                 var hasArtists = item as IHasArtist;
                 if (hasArtists != null && !reader.IsDBNull(index))
                 if (hasArtists != null && !reader.IsDBNull(index))
                 {
                 {
-                    hasArtists.Artists = reader.GetString(index).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList();
+                    hasArtists.Artists = reader.GetString(index).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                 }
                 }
                 index++;
                 index++;
 
 
@@ -1958,22 +1956,18 @@ namespace Emby.Server.Implementations.Data
         /// Gets the critic reviews.
         /// Gets the critic reviews.
         /// </summary>
         /// </summary>
         /// <param name="itemId">The item id.</param>
         /// <param name="itemId">The item id.</param>
-        /// <returns>Task{IEnumerable{ItemReview}}.</returns>
         public List<ItemReview> GetCriticReviews(Guid itemId)
         public List<ItemReview> GetCriticReviews(Guid itemId)
         {
         {
             return new List<ItemReview>();
             return new List<ItemReview>();
         }
         }
 
 
-        private readonly Task _cachedTask = Task.FromResult(true);
         /// <summary>
         /// <summary>
         /// Saves the critic reviews.
         /// Saves the critic reviews.
         /// </summary>
         /// </summary>
         /// <param name="itemId">The item id.</param>
         /// <param name="itemId">The item id.</param>
         /// <param name="criticReviews">The critic reviews.</param>
         /// <param name="criticReviews">The critic reviews.</param>
-        /// <returns>Task.</returns>
-        public Task SaveCriticReviews(Guid itemId, IEnumerable<ItemReview> criticReviews)
+        public void SaveCriticReviews(Guid itemId, IEnumerable<ItemReview> criticReviews)
         {
         {
-            return _cachedTask;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -2078,7 +2072,7 @@ namespace Emby.Server.Implementations.Data
         /// <summary>
         /// <summary>
         /// Saves the chapters.
         /// Saves the chapters.
         /// </summary>
         /// </summary>
-        public async Task SaveChapters(Guid id, List<ChapterInfo> chapters)
+        public void SaveChapters(Guid id, List<ChapterInfo> chapters)
         {
         {
             CheckDisposed();
             CheckDisposed();
 
 
@@ -4653,12 +4647,12 @@ namespace Emby.Server.Implementations.Data
             typeof(AggregateFolder)
             typeof(AggregateFolder)
         };
         };
 
 
-        public async Task UpdateInheritedValues(CancellationToken cancellationToken)
+        public void UpdateInheritedValues(CancellationToken cancellationToken)
         {
         {
-            await UpdateInheritedTags(cancellationToken).ConfigureAwait(false);
+            UpdateInheritedTags(cancellationToken);
         }
         }
 
 
-        private async Task UpdateInheritedTags(CancellationToken cancellationToken)
+        private void UpdateInheritedTags(CancellationToken cancellationToken)
         {
         {
             var newValues = new List<Tuple<Guid, string[]>>();
             var newValues = new List<Tuple<Guid, string[]>>();
 
 
@@ -4753,7 +4747,7 @@ limit 100";
             return new[] { value }.Where(IsValidType);
             return new[] { value }.Where(IsValidType);
         }
         }
 
 
-        public async Task DeleteItem(Guid id, CancellationToken cancellationToken)
+        public void DeleteItem(Guid id, CancellationToken cancellationToken)
         {
         {
             if (id == Guid.Empty)
             if (id == Guid.Empty)
             {
             {
@@ -5484,7 +5478,7 @@ limit 100";
             }
             }
         }
         }
 
 
-        public async Task UpdatePeople(Guid itemId, List<PersonInfo> people)
+        public void UpdatePeople(Guid itemId, List<PersonInfo> people)
         {
         {
             if (itemId == Guid.Empty)
             if (itemId == Guid.Empty)
             {
             {
@@ -5614,7 +5608,7 @@ limit 100";
             }
             }
         }
         }
 
 
-        public async Task SaveMediaStreams(Guid id, List<MediaStream> streams, CancellationToken cancellationToken)
+        public void SaveMediaStreams(Guid id, List<MediaStream> streams, CancellationToken cancellationToken)
         {
         {
             CheckDisposed();
             CheckDisposed();
 
 

+ 7 - 8
Emby.Server.Implementations/Data/SqliteUserDataRepository.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
-using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Persistence;
@@ -153,7 +152,7 @@ namespace Emby.Server.Implementations.Data
         /// userId
         /// userId
         /// or
         /// or
         /// userDataId</exception>
         /// userDataId</exception>
-        public Task SaveUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken)
+        public void SaveUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken)
         {
         {
             if (userData == null)
             if (userData == null)
             {
             {
@@ -168,10 +167,10 @@ namespace Emby.Server.Implementations.Data
                 throw new ArgumentNullException("key");
                 throw new ArgumentNullException("key");
             }
             }
 
 
-            return PersistUserData(userId, key, userData, cancellationToken);
+            PersistUserData(userId, key, userData, cancellationToken);
         }
         }
 
 
-        public Task SaveAllUserData(Guid userId, IEnumerable<UserItemData> userData, CancellationToken cancellationToken)
+        public void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken)
         {
         {
             if (userData == null)
             if (userData == null)
             {
             {
@@ -182,7 +181,7 @@ namespace Emby.Server.Implementations.Data
                 throw new ArgumentNullException("userId");
                 throw new ArgumentNullException("userId");
             }
             }
 
 
-            return PersistAllUserData(userId, userData.ToList(), cancellationToken);
+            PersistAllUserData(userId, userData, cancellationToken);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -193,7 +192,7 @@ namespace Emby.Server.Implementations.Data
         /// <param name="userData">The user data.</param>
         /// <param name="userData">The user data.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        public async Task PersistUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken)
+        public void PersistUserData(Guid userId, string key, UserItemData userData, CancellationToken cancellationToken)
         {
         {
             cancellationToken.ThrowIfCancellationRequested();
             cancellationToken.ThrowIfCancellationRequested();
 
 
@@ -264,7 +263,7 @@ namespace Emby.Server.Implementations.Data
         /// <summary>
         /// <summary>
         /// Persist all user data for the specified user
         /// Persist all user data for the specified user
         /// </summary>
         /// </summary>
-        private async Task PersistAllUserData(Guid userId, List<UserItemData> userDataList, CancellationToken cancellationToken)
+        private void PersistAllUserData(Guid userId, UserItemData[] userDataList, CancellationToken cancellationToken)
         {
         {
             cancellationToken.ThrowIfCancellationRequested();
             cancellationToken.ThrowIfCancellationRequested();
 
 
@@ -349,7 +348,7 @@ namespace Emby.Server.Implementations.Data
         /// </summary>
         /// </summary>
         /// <param name="userId"></param>
         /// <param name="userId"></param>
         /// <returns></returns>
         /// <returns></returns>
-        public IEnumerable<UserItemData> GetAllUserData(Guid userId)
+        public List<UserItemData> GetAllUserData(Guid userId)
         {
         {
             if (userId == Guid.Empty)
             if (userId == Guid.Empty)
             {
             {

+ 2 - 3
Emby.Server.Implementations/Data/SqliteUserRepository.cs

@@ -2,7 +2,6 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using System.Threading;
 using System.Threading;
-using System.Threading.Tasks;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Persistence;
@@ -72,7 +71,7 @@ namespace Emby.Server.Implementations.Data
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">user</exception>
         /// <exception cref="System.ArgumentNullException">user</exception>
-        public async Task SaveUser(User user, CancellationToken cancellationToken)
+        public void SaveUser(User user, CancellationToken cancellationToken)
         {
         {
             if (user == null)
             if (user == null)
             {
             {
@@ -139,7 +138,7 @@ namespace Emby.Server.Implementations.Data
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">user</exception>
         /// <exception cref="System.ArgumentNullException">user</exception>
-        public async Task DeleteUser(User user, CancellationToken cancellationToken)
+        public void DeleteUser(User user, CancellationToken cancellationToken)
         {
         {
             if (user == null)
             if (user == null)
             {
             {

+ 5 - 1
Emby.Server.Implementations/Devices/DeviceRepository.cs

@@ -11,6 +11,7 @@ using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Session;
 using MediaBrowser.Model.Session;
+using MediaBrowser.Model.Extensions;
 
 
 namespace Emby.Server.Implementations.Devices
 namespace Emby.Server.Implementations.Devices
 {
 {
@@ -199,7 +200,10 @@ namespace Emby.Server.Implementations.Devices
                 }
                 }
 
 
                 history.DeviceId = deviceId;
                 history.DeviceId = deviceId;
-                history.FilesUploaded.Add(file);
+
+                var list = history.FilesUploaded.ToList();
+                list.Add(file);
+                history.FilesUploaded = list.ToArray(list.Count);
 
 
                 _json.SerializeToFile(history, path);
                 _json.SerializeToFile(history, path);
             }
             }

+ 84 - 31
Emby.Server.Implementations/Dto/DtoService.cs

@@ -77,7 +77,7 @@ namespace Emby.Server.Implementations.Dto
         /// <param name="owner">The owner.</param>
         /// <param name="owner">The owner.</param>
         /// <returns>Task{DtoBaseItem}.</returns>
         /// <returns>Task{DtoBaseItem}.</returns>
         /// <exception cref="System.ArgumentNullException">item</exception>
         /// <exception cref="System.ArgumentNullException">item</exception>
-        public BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null)
+        public BaseItemDto GetBaseItemDto(BaseItem item, ItemFields[] fields, User user = null, BaseItem owner = null)
         {
         {
             var options = new DtoOptions
             var options = new DtoOptions
             {
             {
@@ -87,7 +87,17 @@ namespace Emby.Server.Implementations.Dto
             return GetBaseItemDto(item, options, user, owner);
             return GetBaseItemDto(item, options, user, owner);
         }
         }
 
 
-        public async Task<List<BaseItemDto>> GetBaseItemDtos(IEnumerable<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
+        public Task<BaseItemDto[]> GetBaseItemDtos(List<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null)
+        {
+            return GetBaseItemDtos(items, items.Count, options, user, owner);
+        }
+
+        public Task<BaseItemDto[]> GetBaseItemDtos(BaseItem[] items, DtoOptions options, User user = null, BaseItem owner = null)
+        {
+            return GetBaseItemDtos(items, items.Length, options, user, owner);
+        }
+
+        public async Task<BaseItemDto[]> GetBaseItemDtos(IEnumerable<BaseItem> items, int itemCount, DtoOptions options, User user = null, BaseItem owner = null)
         {
         {
             if (items == null)
             if (items == null)
             {
             {
@@ -101,17 +111,14 @@ namespace Emby.Server.Implementations.Dto
 
 
             var syncDictionary = GetSyncedItemProgress(options);
             var syncDictionary = GetSyncedItemProgress(options);
 
 
-            var list = new List<BaseItemDto>();
+            var returnItems = new BaseItemDto[itemCount];
             var programTuples = new List<Tuple<BaseItem, BaseItemDto>>();
             var programTuples = new List<Tuple<BaseItem, BaseItemDto>>();
             var channelTuples = new List<Tuple<BaseItemDto, LiveTvChannel>>();
             var channelTuples = new List<Tuple<BaseItemDto, LiveTvChannel>>();
 
 
-            var refreshQueue = options.Fields.Contains(ItemFields.RefreshState)
-                ? _providerManager.GetRefreshQueue()
-                : null;
-
+            var index = 0;
             foreach (var item in items)
             foreach (var item in items)
             {
             {
-                var dto = GetBaseItemDtoInternal(item, options, refreshQueue, user, owner);
+                var dto = GetBaseItemDtoInternal(item, options, user, owner);
 
 
                 var tvChannel = item as LiveTvChannel;
                 var tvChannel = item as LiveTvChannel;
                 if (tvChannel != null)
                 if (tvChannel != null)
@@ -144,7 +151,8 @@ namespace Emby.Server.Implementations.Dto
 
 
                 FillSyncInfo(dto, item, options, user, syncDictionary);
                 FillSyncInfo(dto, item, options, user, syncDictionary);
 
 
-                list.Add(dto);
+                returnItems[index] = dto;
+                index++;
             }
             }
 
 
             if (programTuples.Count > 0)
             if (programTuples.Count > 0)
@@ -157,18 +165,14 @@ namespace Emby.Server.Implementations.Dto
                 await _livetvManager().AddChannelInfo(channelTuples, options, user).ConfigureAwait(false);
                 await _livetvManager().AddChannelInfo(channelTuples, options, user).ConfigureAwait(false);
             }
             }
 
 
-            return list;
+            return returnItems;
         }
         }
 
 
         public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
         public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
         {
         {
             var syncDictionary = GetSyncedItemProgress(options);
             var syncDictionary = GetSyncedItemProgress(options);
 
 
-            var refreshQueue = options.Fields.Contains(ItemFields.RefreshState)
-                ? _providerManager.GetRefreshQueue()
-                : null;
-
-            var dto = GetBaseItemDtoInternal(item, options, refreshQueue, user, owner);
+            var dto = GetBaseItemDtoInternal(item, options, user, owner);
             var tvChannel = item as LiveTvChannel;
             var tvChannel = item as LiveTvChannel;
             if (tvChannel != null)
             if (tvChannel != null)
             {
             {
@@ -300,7 +304,7 @@ namespace Emby.Server.Implementations.Dto
             }
             }
         }
         }
 
 
-        private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, Dictionary<Guid, Guid> currentRefreshQueue, User user = null, BaseItem owner = null)
+        private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
         {
         {
             var fields = options.Fields;
             var fields = options.Fields;
 
 
@@ -365,6 +369,8 @@ namespace Emby.Server.Implementations.Dto
                     {
                     {
                         dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true, user);
                         dto.MediaSources = _mediaSourceManager().GetStaticMediaSources(hasMediaSources, true, user);
                     }
                     }
+
+                    NormalizeMediaSourceContainers(dto);
                 }
                 }
             }
             }
 
 
@@ -400,25 +406,72 @@ namespace Emby.Server.Implementations.Dto
                 dto.Etag = item.GetEtag(user);
                 dto.Etag = item.GetEtag(user);
             }
             }
 
 
-            if (currentRefreshQueue != null)
+            var liveTvManager = _livetvManager();
+            if (item is ILiveTvRecording)
             {
             {
-                //dto.RefreshState = item.GetRefreshState(currentRefreshQueue);
+                liveTvManager.AddInfoToRecordingDto(item, dto, user);
             }
             }
-
-            if (item is ILiveTvRecording)
+            else
             {
             {
-                _livetvManager().AddInfoToRecordingDto(item, dto, user);
+                var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path);
+                if (activeRecording != null)
+                {
+                    dto.Type = "Recording";
+                    dto.CanDownload = false;
+                    if (!string.IsNullOrWhiteSpace(dto.SeriesName))
+                    {
+                        dto.EpisodeTitle = dto.Name;
+                        dto.Name = dto.SeriesName;
+                    }
+                    liveTvManager.AddInfoToRecordingDto(item, dto, activeRecording, user);
+                }
             }
             }
 
 
             return dto;
             return dto;
         }
         }
 
 
+        private void NormalizeMediaSourceContainers(BaseItemDto dto)
+        {
+            foreach (var mediaSource in dto.MediaSources)
+            {
+                var container = mediaSource.Container;
+                if (string.IsNullOrWhiteSpace(container))
+                {
+                    continue;
+                }
+                var containers = container.Split(new[] { ',' });
+                if (containers.Length < 2)
+                {
+                    continue;
+                }
+
+                var path = mediaSource.Path;
+                string fileExtensionContainer = null;
+
+                if (!string.IsNullOrWhiteSpace(path))
+                {
+                    path = Path.GetExtension(path);
+                    if (!string.IsNullOrWhiteSpace(path))
+                    {
+                        path = Path.GetExtension(path);
+                        if (!string.IsNullOrWhiteSpace(path))
+                        {
+                            path = path.TrimStart('.');
+                        }
+                        if (!string.IsNullOrWhiteSpace(path) && containers.Contains(path, StringComparer.OrdinalIgnoreCase))
+                        {
+                            fileExtensionContainer = path;
+                        }
+                    }
+                }
+
+                mediaSource.Container = fileExtensionContainer ?? containers[0];
+            }
+        }
+
         public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, Dictionary<string, SyncedItemProgress> syncProgress, User user = null)
         public BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, Dictionary<string, SyncedItemProgress> syncProgress, User user = null)
         {
         {
-            var refreshQueue = options.Fields.Contains(ItemFields.RefreshState)
-                ? _providerManager.GetRefreshQueue()
-                : null;
-            var dto = GetBaseItemDtoInternal(item, options, refreshQueue, user);
+            var dto = GetBaseItemDtoInternal(item, options, user);
 
 
             if (taggedItems != null && options.Fields.Contains(ItemFields.ItemCounts))
             if (taggedItems != null && options.Fields.Contains(ItemFields.ItemCounts))
             {
             {
@@ -992,7 +1045,7 @@ namespace Emby.Server.Implementations.Dto
             {
             {
                 dto.RemoteTrailers = hasTrailers != null ?
                 dto.RemoteTrailers = hasTrailers != null ?
                     hasTrailers.RemoteTrailers :
                     hasTrailers.RemoteTrailers :
-                    new MediaUrl[] {};
+                    new MediaUrl[] { };
             }
             }
 
 
             dto.Name = item.Name;
             dto.Name = item.Name;
@@ -1053,7 +1106,7 @@ namespace Emby.Server.Implementations.Dto
 
 
                 if (dto.Taglines == null)
                 if (dto.Taglines == null)
                 {
                 {
-                    dto.Taglines = new string[]{};
+                    dto.Taglines = new string[] { };
                 }
                 }
             }
             }
 
 
@@ -1243,17 +1296,17 @@ namespace Emby.Server.Implementations.Dto
 
 
                 if (iHasMediaSources != null)
                 if (iHasMediaSources != null)
                 {
                 {
-                    List<MediaStream> mediaStreams;
+                    MediaStream[] mediaStreams;
 
 
                     if (dto.MediaSources != null && dto.MediaSources.Count > 0)
                     if (dto.MediaSources != null && dto.MediaSources.Count > 0)
                     {
                     {
                         mediaStreams = dto.MediaSources.Where(i => new Guid(i.Id) == item.Id)
                         mediaStreams = dto.MediaSources.Where(i => new Guid(i.Id) == item.Id)
                             .SelectMany(i => i.MediaStreams)
                             .SelectMany(i => i.MediaStreams)
-                            .ToList();
+                            .ToArray();
                     }
                     }
                     else
                     else
                     {
                     {
-                        mediaStreams = _mediaSourceManager().GetStaticMediaSources(iHasMediaSources, true).First().MediaStreams;
+                        mediaStreams = _mediaSourceManager().GetStaticMediaSources(iHasMediaSources, true).First().MediaStreams.ToArray();
                     }
                     }
 
 
                     dto.MediaStreams = mediaStreams;
                     dto.MediaStreams = mediaStreams;
@@ -1564,7 +1617,7 @@ namespace Emby.Server.Implementations.Dto
                 return null;
                 return null;
             }
             }
 
 
-            var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToList();
+            var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary);
 
 
             ImageSize size;
             ImageSize size;
 
 

+ 3 - 4
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -133,7 +133,6 @@
     <Compile Include="IO\FileRefresher.cs" />
     <Compile Include="IO\FileRefresher.cs" />
     <Compile Include="IO\IsoManager.cs" />
     <Compile Include="IO\IsoManager.cs" />
     <Compile Include="IO\LibraryMonitor.cs" />
     <Compile Include="IO\LibraryMonitor.cs" />
-    <Compile Include="IO\LnkShortcutHandler.cs" />
     <Compile Include="IO\ManagedFileSystem.cs" />
     <Compile Include="IO\ManagedFileSystem.cs" />
     <Compile Include="IO\MbLinkShortcutHandler.cs" />
     <Compile Include="IO\MbLinkShortcutHandler.cs" />
     <Compile Include="IO\MemoryStreamProvider.cs" />
     <Compile Include="IO\MemoryStreamProvider.cs" />
@@ -662,8 +661,9 @@
     <Reference Include="Emby.XmlTv, Version=1.0.6387.29335, Culture=neutral, processorArchitecture=MSIL">
     <Reference Include="Emby.XmlTv, Version=1.0.6387.29335, Culture=neutral, processorArchitecture=MSIL">
       <HintPath>..\packages\Emby.XmlTv.1.0.10\lib\portable-net45+netstandard2.0+win8\Emby.XmlTv.dll</HintPath>
       <HintPath>..\packages\Emby.XmlTv.1.0.10\lib\portable-net45+netstandard2.0+win8\Emby.XmlTv.dll</HintPath>
     </Reference>
     </Reference>
-    <Reference Include="MediaBrowser.Naming, Version=1.0.6437.24226, Culture=neutral, processorArchitecture=MSIL">
-      <HintPath>..\packages\MediaBrowser.Naming.1.0.6\lib\portable-net45+netstandard2.0+win8\MediaBrowser.Naming.dll</HintPath>
+    <Reference Include="MediaBrowser.Naming, Version=1.0.6447.2217, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\MediaBrowser.Naming.1.0.7\lib\portable-net45+netstandard2.0+win8\MediaBrowser.Naming.dll</HintPath>
+      <Private>True</Private>
     </Reference>
     </Reference>
     <Reference Include="ServiceStack.Text, Version=4.5.8.0, Culture=neutral, processorArchitecture=MSIL">
     <Reference Include="ServiceStack.Text, Version=4.5.8.0, Culture=neutral, processorArchitecture=MSIL">
       <HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
       <HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
@@ -699,7 +699,6 @@
     <EmbeddedResource Include="Localization\Core\ar.json" />
     <EmbeddedResource Include="Localization\Core\ar.json" />
     <EmbeddedResource Include="Localization\Core\bg-BG.json" />
     <EmbeddedResource Include="Localization\Core\bg-BG.json" />
     <EmbeddedResource Include="Localization\Core\ca.json" />
     <EmbeddedResource Include="Localization\Core\ca.json" />
-    <EmbeddedResource Include="Localization\Core\core.json" />
     <EmbeddedResource Include="Localization\Core\cs.json" />
     <EmbeddedResource Include="Localization\Core\cs.json" />
     <EmbeddedResource Include="Localization\Core\da.json" />
     <EmbeddedResource Include="Localization\Core\da.json" />
     <EmbeddedResource Include="Localization\Core\de.json" />
     <EmbeddedResource Include="Localization\Core\de.json" />

+ 5 - 5
Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs

@@ -367,15 +367,15 @@ namespace Emby.Server.Implementations.EntryPoints
 
 
             return new LibraryUpdateInfo
             return new LibraryUpdateInfo
             {
             {
-                ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
+                ItemsAdded = itemsAdded.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
 
 
-                ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
+                ItemsUpdated = itemsUpdated.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
 
 
-                ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
+                ItemsRemoved = itemsRemoved.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user, true)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
 
 
-                FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList(),
+                FoldersAddedTo = foldersAddedTo.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray(),
 
 
-                FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToList()
+                FoldersRemovedFrom = foldersRemovedFrom.SelectMany(i => TranslatePhysicalItemToUserLibrary(i, user)).Select(i => i.Id.ToString("N")).Distinct().ToArray()
             };
             };
         }
         }
 
 

+ 1 - 1
Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs

@@ -126,7 +126,7 @@ namespace Emby.Server.Implementations.EntryPoints
                             dto.ItemId = i.Id.ToString("N");
                             dto.ItemId = i.Id.ToString("N");
                             return dto;
                             return dto;
                         })
                         })
-                        .ToList();
+                        .ToArray();
 
 
                     var info = new UserDataChangeInfo
                     var info = new UserDataChangeInfo
                     {
                     {

+ 1 - 1
Emby.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -162,7 +162,7 @@ namespace Emby.Server.Implementations.HttpServer
             return serviceType;
             return serviceType;
         }
         }
 
 
-        public void AddServiceInfo(Type serviceType, Type requestType, Type responseType)
+        public void AddServiceInfo(Type serviceType, Type requestType)
         {
         {
             ServiceOperationsMap[requestType] = serviceType;
             ServiceOperationsMap[requestType] = serviceType;
         }
         }

+ 5 - 3
Emby.Server.Implementations/IO/LibraryMonitor.cs

@@ -35,7 +35,7 @@ namespace Emby.Server.Implementations.IO
         /// <summary>
         /// <summary>
         /// Any file name ending in any of these will be ignored by the watchers
         /// Any file name ending in any of these will be ignored by the watchers
         /// </summary>
         /// </summary>
-        private readonly IReadOnlyList<string> _alwaysIgnoreFiles = new List<string>
+        private readonly string[] _alwaysIgnoreFiles = new string[]
         {
         {
             "small.jpg",
             "small.jpg",
             "albumart.jpg",
             "albumart.jpg",
@@ -45,7 +45,7 @@ namespace Emby.Server.Implementations.IO
             "TempSBE"
             "TempSBE"
         };
         };
 
 
-        private readonly IReadOnlyList<string> _alwaysIgnoreSubstrings = new List<string>
+        private readonly string[] _alwaysIgnoreSubstrings = new string[]
         {
         {
             // Synology
             // Synology
             "eaDir",
             "eaDir",
@@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.IO
             ".actors"
             ".actors"
         };
         };
 
 
-        private readonly IReadOnlyList<string> _alwaysIgnoreExtensions = new List<string>
+        private readonly string[] _alwaysIgnoreExtensions = new string[]
         {
         {
             // thumbs.db
             // thumbs.db
             ".db",
             ".db",
@@ -85,6 +85,8 @@ namespace Emby.Server.Implementations.IO
 
 
         public bool IsPathLocked(string path)
         public bool IsPathLocked(string path)
         {
         {
+            // This method is not used by the core but it used by auto-organize
+
             var lockedPaths = _tempIgnoredPaths.Keys.ToList();
             var lockedPaths = _tempIgnoredPaths.Keys.ToList();
             return lockedPaths.Any(i => _fileSystem.AreEqual(i, path) || _fileSystem.ContainsSubPath(i, path));
             return lockedPaths.Any(i => _fileSystem.AreEqual(i, path) || _fileSystem.ContainsSubPath(i, path));
         }
         }

+ 27 - 30
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -353,7 +353,7 @@ namespace Emby.Server.Implementations.Library
             }
             }
             else
             else
             {
             {
-                if (item is Photo)
+                if (!(item is Video))
                 {
                 {
                     return;
                     return;
                 }
                 }
@@ -461,10 +461,10 @@ namespace Emby.Server.Implementations.Library
                 parent.RemoveChild(item);
                 parent.RemoveChild(item);
             }
             }
 
 
-            await ItemRepository.DeleteItem(item.Id, CancellationToken.None).ConfigureAwait(false);
+            ItemRepository.DeleteItem(item.Id, CancellationToken.None);
             foreach (var child in children)
             foreach (var child in children)
             {
             {
-                await ItemRepository.DeleteItem(child.Id, CancellationToken.None).ConfigureAwait(false);
+                ItemRepository.DeleteItem(child.Id, CancellationToken.None);
             }
             }
 
 
             BaseItem removed;
             BaseItem removed;
@@ -599,18 +599,16 @@ namespace Emby.Server.Implementations.Library
                 // When resolving the root, we need it's grandchildren (children of user views)
                 // When resolving the root, we need it's grandchildren (children of user views)
                 var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
                 var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
 
 
-                var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, _fileSystem, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
+                var files = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, _fileSystem, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
 
 
                 // Need to remove subpaths that may have been resolved from shortcuts
                 // Need to remove subpaths that may have been resolved from shortcuts
                 // Example: if \\server\movies exists, then strip out \\server\movies\action
                 // Example: if \\server\movies exists, then strip out \\server\movies\action
                 if (isPhysicalRoot)
                 if (isPhysicalRoot)
                 {
                 {
-                    var paths = NormalizeRootPathList(fileSystemDictionary.Values);
-
-                    fileSystemDictionary = paths.ToDictionary(i => i.FullName);
+                    files = NormalizeRootPathList(files).ToArray();
                 }
                 }
 
 
-                args.FileSystemDictionary = fileSystemDictionary;
+                args.FileSystemChildren = files;
             }
             }
 
 
             // Check to see if we should resolve based on our contents
             // Check to see if we should resolve based on our contents
@@ -656,7 +654,7 @@ namespace Emby.Server.Implementations.Library
             return false;
             return false;
         }
         }
 
 
-        public IEnumerable<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths)
+        public List<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths)
         {
         {
             var originalList = paths.ToList();
             var originalList = paths.ToList();
 
 
@@ -999,8 +997,7 @@ namespace Emby.Server.Implementations.Library
                     Path = path
                     Path = path
                 };
                 };
 
 
-                var task = CreateItem(item, CancellationToken.None);
-                Task.WaitAll(task);
+                CreateItem(item, CancellationToken.None);
             }
             }
 
 
             return item;
             return item;
@@ -1172,7 +1169,7 @@ namespace Emby.Server.Implementations.Library
                 progress.Report(percent * 100);
                 progress.Report(percent * 100);
             }
             }
 
 
-            await ItemRepository.UpdateInheritedValues(cancellationToken).ConfigureAwait(false);
+            ItemRepository.UpdateInheritedValues(cancellationToken);
 
 
             progress.Report(100);
             progress.Report(100);
         }
         }
@@ -1208,7 +1205,7 @@ namespace Emby.Server.Implementations.Library
                 .Where(i => string.Equals(ShortcutFileExtension, Path.GetExtension(i), StringComparison.OrdinalIgnoreCase))
                 .Where(i => string.Equals(ShortcutFileExtension, Path.GetExtension(i), StringComparison.OrdinalIgnoreCase))
                     .Select(_fileSystem.ResolveShortcut)
                     .Select(_fileSystem.ResolveShortcut)
                     .OrderBy(i => i)
                     .OrderBy(i => i)
-                    .ToList(),
+                    .ToArray(),
 
 
                 CollectionType = GetCollectionType(dir)
                 CollectionType = GetCollectionType(dir)
             };
             };
@@ -1554,7 +1551,7 @@ namespace Emby.Server.Implementations.Library
                     IncludeHidden = true,
                     IncludeHidden = true,
                     IncludeExternalContent = allowExternalContent
                     IncludeExternalContent = allowExternalContent
 
 
-                }, CancellationToken.None).Result.ToList();
+                }, CancellationToken.None).Result;
 
 
                 query.TopParentIds = userViews.SelectMany(i => GetTopParentIdsForQuery(i, user)).Select(i => i.ToString("N")).ToArray();
                 query.TopParentIds = userViews.SelectMany(i => GetTopParentIdsForQuery(i, user)).Select(i => i.ToString("N")).ToArray();
             }
             }
@@ -1814,9 +1811,9 @@ namespace Emby.Server.Implementations.Library
         /// <param name="item">The item.</param>
         /// <param name="item">The item.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        public Task CreateItem(BaseItem item, CancellationToken cancellationToken)
+        public void CreateItem(BaseItem item, CancellationToken cancellationToken)
         {
         {
-            return CreateItems(new[] { item }, cancellationToken);
+            CreateItems(new[] { item }, cancellationToken);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -1825,11 +1822,11 @@ namespace Emby.Server.Implementations.Library
         /// <param name="items">The items.</param>
         /// <param name="items">The items.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        public async Task CreateItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
+        public void CreateItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
         {
         {
             var list = items.ToList();
             var list = items.ToList();
 
 
-            await ItemRepository.SaveItems(list, cancellationToken).ConfigureAwait(false);
+            ItemRepository.SaveItems(list, cancellationToken);
 
 
             foreach (var item in list)
             foreach (var item in list)
             {
             {
@@ -1872,7 +1869,7 @@ namespace Emby.Server.Implementations.Library
             var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name;
             var logName = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name;
             _logger.Debug("Saving {0} to database.", logName);
             _logger.Debug("Saving {0} to database.", logName);
 
 
-            await ItemRepository.SaveItem(item, cancellationToken).ConfigureAwait(false);
+            ItemRepository.SaveItem(item, cancellationToken);
 
 
             RegisterItem(item);
             RegisterItem(item);
 
 
@@ -2069,7 +2066,7 @@ namespace Emby.Server.Implementations.Library
         private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
         private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromHours(24);
         //private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromMinutes(1);
         //private readonly TimeSpan _viewRefreshInterval = TimeSpan.FromMinutes(1);
 
 
-        public Task<UserView> GetNamedView(User user,
+        public UserView GetNamedView(User user,
             string name,
             string name,
             string viewType,
             string viewType,
             string sortName,
             string sortName,
@@ -2107,7 +2104,7 @@ namespace Emby.Server.Implementations.Library
                     ForcedSortName = sortName
                     ForcedSortName = sortName
                 };
                 };
 
 
-                await CreateItem(item, cancellationToken).ConfigureAwait(false);
+                CreateItem(item, cancellationToken);
 
 
                 refresh = true;
                 refresh = true;
             }
             }
@@ -2138,7 +2135,7 @@ namespace Emby.Server.Implementations.Library
             return item;
             return item;
         }
         }
 
 
-        public async Task<UserView> GetNamedView(User user,
+        public UserView GetNamedView(User user,
             string name,
             string name,
             string parentId,
             string parentId,
             string viewType,
             string viewType,
@@ -2175,7 +2172,7 @@ namespace Emby.Server.Implementations.Library
                     item.DisplayParentId = new Guid(parentId);
                     item.DisplayParentId = new Guid(parentId);
                 }
                 }
 
 
-                await CreateItem(item, cancellationToken).ConfigureAwait(false);
+                CreateItem(item, cancellationToken);
 
 
                 isNew = true;
                 isNew = true;
             }
             }
@@ -2201,7 +2198,7 @@ namespace Emby.Server.Implementations.Library
             return item;
             return item;
         }
         }
 
 
-        public async Task<UserView> GetShadowView(BaseItem parent,
+        public UserView GetShadowView(BaseItem parent,
         string viewType,
         string viewType,
         string sortName,
         string sortName,
         CancellationToken cancellationToken)
         CancellationToken cancellationToken)
@@ -2240,7 +2237,7 @@ namespace Emby.Server.Implementations.Library
 
 
                 item.DisplayParentId = parentId;
                 item.DisplayParentId = parentId;
 
 
-                await CreateItem(item, cancellationToken).ConfigureAwait(false);
+                CreateItem(item, cancellationToken);
 
 
                 isNew = true;
                 isNew = true;
             }
             }
@@ -2311,7 +2308,7 @@ namespace Emby.Server.Implementations.Library
                     item.DisplayParentId = new Guid(parentId);
                     item.DisplayParentId = new Guid(parentId);
                 }
                 }
 
 
-                await CreateItem(item, cancellationToken).ConfigureAwait(false);
+                CreateItem(item, cancellationToken);
 
 
                 isNew = true;
                 isNew = true;
             }
             }
@@ -2825,14 +2822,14 @@ namespace Emby.Server.Implementations.Library
             return ItemRepository.GetPeopleNames(query);
             return ItemRepository.GetPeopleNames(query);
         }
         }
 
 
-        public Task UpdatePeople(BaseItem item, List<PersonInfo> people)
+        public void UpdatePeople(BaseItem item, List<PersonInfo> people)
         {
         {
             if (!item.SupportsPeople)
             if (!item.SupportsPeople)
             {
             {
-                return Task.FromResult(true);
+                return;
             }
             }
 
 
-            return ItemRepository.UpdatePeople(item.Id, people);
+            ItemRepository.UpdatePeople(item.Id, people);
         }
         }
 
 
         public async Task<ItemImageInfo> ConvertImageToLocal(IHasMetadata item, ItemImageInfo image, int imageIndex)
         public async Task<ItemImageInfo> ConvertImageToLocal(IHasMetadata item, ItemImageInfo image, int imageIndex)
@@ -3061,7 +3058,7 @@ namespace Emby.Server.Implementations.Library
             var topLibraryFolders = GetUserRootFolder().Children.ToList();
             var topLibraryFolders = GetUserRootFolder().Children.ToList();
             var info = GetVirtualFolderInfo(virtualFolderPath, topLibraryFolders, null);
             var info = GetVirtualFolderInfo(virtualFolderPath, topLibraryFolders, null);
 
 
-            if (info.Locations.Count > 0 && info.Locations.Count != options.PathInfos.Length)
+            if (info.Locations.Length > 0 && info.Locations.Length != options.PathInfos.Length)
             {
             {
                 var list = options.PathInfos.ToList();
                 var list = options.PathInfos.ToList();
 
 

+ 1 - 1
Emby.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs

@@ -53,7 +53,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
                     return new CollectionFolder
                     return new CollectionFolder
                     {
                     {
                         CollectionType = GetCollectionType(args),
                         CollectionType = GetCollectionType(args),
-                        PhysicalLocationsList = args.PhysicalLocations.ToList()
+                        PhysicalLocationsList = args.PhysicalLocations
                     };
                     };
                 }
                 }
             }
             }

+ 13 - 4
Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs

@@ -1,6 +1,8 @@
-using MediaBrowser.Controller.Configuration;
+using System.Globalization;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Globalization;
 using MediaBrowser.Naming.Common;
 using MediaBrowser.Naming.Common;
 using MediaBrowser.Naming.TV;
 using MediaBrowser.Naming.TV;
 
 
@@ -17,15 +19,18 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
 
 
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
+        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+        private readonly ILocalizationManager _localization;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="SeasonResolver"/> class.
         /// Initializes a new instance of the <see cref="SeasonResolver"/> class.
         /// </summary>
         /// </summary>
         /// <param name="config">The config.</param>
         /// <param name="config">The config.</param>
-        public SeasonResolver(IServerConfigurationManager config, ILibraryManager libraryManager)
+        public SeasonResolver(IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization)
         {
         {
             _config = config;
             _config = config;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
+            _localization = localization;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -47,9 +52,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                     SeriesName = series.Name
                     SeriesName = series.Name
                 };
                 };
 
 
-                if (season.IndexNumber.HasValue && season.IndexNumber.Value == 0)
+                if (season.IndexNumber.HasValue)
                 {
                 {
-                    season.Name = _config.Configuration.SeasonZeroDisplayName;
+                    var seasonNumber = season.IndexNumber.Value;
+
+                    season.Name = seasonNumber == 0 ?
+                        _config.Configuration.SeasonZeroDisplayName :
+                        string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.ToString(UsCulture));
                 }
                 }
 
 
                 return season;
                 return season;

+ 1 - 1
Emby.Server.Implementations/Library/SearchEngine.cs

@@ -181,7 +181,7 @@ namespace Emby.Server.Implementations.Library
 
 
                 DtoOptions = new DtoOptions
                 DtoOptions = new DtoOptions
                 {
                 {
-                    Fields = new List<ItemFields>
+                    Fields = new ItemFields[]
                     {
                     {
                          ItemFields.AirTime,
                          ItemFields.AirTime,
                          ItemFields.DateCreated,
                          ItemFields.DateCreated,

+ 7 - 7
Emby.Server.Implementations/Library/UserDataManager.cs

@@ -41,7 +41,7 @@ namespace Emby.Server.Implementations.Library
         /// <value>The repository.</value>
         /// <value>The repository.</value>
         public IUserDataRepository Repository { get; set; }
         public IUserDataRepository Repository { get; set; }
 
 
-        public async Task SaveUserData(Guid userId, IHasUserData item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
+        public void SaveUserData(Guid userId, IHasUserData item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
         {
         {
             if (userData == null)
             if (userData == null)
             {
             {
@@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.Library
 
 
             foreach (var key in keys)
             foreach (var key in keys)
             {
             {
-                await Repository.SaveUserData(userId, key, userData, cancellationToken).ConfigureAwait(false);
+                Repository.SaveUserData(userId, key, userData, cancellationToken);
             }
             }
 
 
             var cacheKey = GetCacheKey(userId, item.Id);
             var cacheKey = GetCacheKey(userId, item.Id);
@@ -86,7 +86,7 @@ namespace Emby.Server.Implementations.Library
         /// <param name="userData"></param>
         /// <param name="userData"></param>
         /// <param name="cancellationToken"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
         /// <returns></returns>
-        public async Task SaveAllUserData(Guid userId, IEnumerable<UserItemData> userData, CancellationToken cancellationToken)
+        public void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken)
         {
         {
             if (userData == null)
             if (userData == null)
             {
             {
@@ -99,7 +99,7 @@ namespace Emby.Server.Implementations.Library
 
 
             cancellationToken.ThrowIfCancellationRequested();
             cancellationToken.ThrowIfCancellationRequested();
 
 
-            await Repository.SaveAllUserData(userId, userData, cancellationToken).ConfigureAwait(false);
+            Repository.SaveAllUserData(userId, userData, cancellationToken);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -107,7 +107,7 @@ namespace Emby.Server.Implementations.Library
         /// </summary>
         /// </summary>
         /// <param name="userId"></param>
         /// <param name="userId"></param>
         /// <returns></returns>
         /// <returns></returns>
-        public IEnumerable<UserItemData> GetAllUserData(Guid userId)
+        public List<UserItemData> GetAllUserData(Guid userId)
         {
         {
             if (userId == Guid.Empty)
             if (userId == Guid.Empty)
             {
             {
@@ -187,11 +187,11 @@ namespace Emby.Server.Implementations.Library
             var userData = GetUserData(user.Id, item);
             var userData = GetUserData(user.Id, item);
             var dto = GetUserItemDataDto(userData);
             var dto = GetUserItemDataDto(userData);
 
 
-            item.FillUserDataDtoValues(dto, userData, null, user, new List<ItemFields>());
+            item.FillUserDataDtoValues(dto, userData, null, user, new ItemFields[] { });
             return dto;
             return dto;
         }
         }
 
 
-        public UserItemDataDto GetUserDataDto(IHasUserData item, BaseItemDto itemDto, User user, List<ItemFields> fields)
+        public UserItemDataDto GetUserDataDto(IHasUserData item, BaseItemDto itemDto, User user, ItemFields[] fields)
         {
         {
             var userData = GetUserData(user.Id, item);
             var userData = GetUserData(user.Id, item);
             var dto = GetUserItemDataDto(userData);
             var dto = GetUserItemDataDto(userData);

+ 34 - 34
Emby.Server.Implementations/Library/UserManager.cs

@@ -160,9 +160,9 @@ namespace Emby.Server.Implementations.Library
             return Users.FirstOrDefault(u => string.Equals(u.Name, name, StringComparison.OrdinalIgnoreCase));
             return Users.FirstOrDefault(u => string.Equals(u.Name, name, StringComparison.OrdinalIgnoreCase));
         }
         }
 
 
-        public async Task Initialize()
+        public void Initialize()
         {
         {
-            Users = await LoadUsers().ConfigureAwait(false);
+            Users = LoadUsers();
 
 
             var users = Users.ToList();
             var users = Users.ToList();
 
 
@@ -174,7 +174,7 @@ namespace Emby.Server.Implementations.Library
                     if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value == UserLinkType.LinkedUser)
                     if (!user.ConnectLinkType.HasValue || user.ConnectLinkType.Value == UserLinkType.LinkedUser)
                     {
                     {
                         user.Policy.IsAdministrator = true;
                         user.Policy.IsAdministrator = true;
-                        await UpdateUserPolicy(user, user.Policy, false).ConfigureAwait(false);
+                        UpdateUserPolicy(user, user.Policy, false);
                     }
                     }
                 }
                 }
             }
             }
@@ -294,12 +294,12 @@ namespace Emby.Server.Implementations.Library
             if (success)
             if (success)
             {
             {
                 user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
                 user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
-                await UpdateUser(user).ConfigureAwait(false);
-                await UpdateInvalidLoginAttemptCount(user, 0).ConfigureAwait(false);
+                UpdateUser(user);
+                UpdateInvalidLoginAttemptCount(user, 0);
             }
             }
             else
             else
             {
             {
-                await UpdateInvalidLoginAttemptCount(user, user.Policy.InvalidLoginAttemptCount + 1).ConfigureAwait(false);
+                UpdateInvalidLoginAttemptCount(user, user.Policy.InvalidLoginAttemptCount + 1);
             }
             }
 
 
             _logger.Info("Authentication request for {0} {1}.", user.Name, success ? "has succeeded" : "has been denied");
             _logger.Info("Authentication request for {0} {1}.", user.Name, success ? "has succeeded" : "has been denied");
@@ -307,7 +307,7 @@ namespace Emby.Server.Implementations.Library
             return success ? user : null;
             return success ? user : null;
         }
         }
 
 
-        private async Task UpdateInvalidLoginAttemptCount(User user, int newValue)
+        private void UpdateInvalidLoginAttemptCount(User user, int newValue)
         {
         {
             if (user.Policy.InvalidLoginAttemptCount != newValue || newValue > 0)
             if (user.Policy.InvalidLoginAttemptCount != newValue || newValue > 0)
             {
             {
@@ -327,7 +327,7 @@ namespace Emby.Server.Implementations.Library
                     //fireLockout = true;
                     //fireLockout = true;
                 }
                 }
 
 
-                await UpdateUserPolicy(user, user.Policy, false).ConfigureAwait(false);
+                UpdateUserPolicy(user, user.Policy, false);
 
 
                 if (fireLockout)
                 if (fireLockout)
                 {
                 {
@@ -372,7 +372,7 @@ namespace Emby.Server.Implementations.Library
         /// Loads the users from the repository
         /// Loads the users from the repository
         /// </summary>
         /// </summary>
         /// <returns>IEnumerable{User}.</returns>
         /// <returns>IEnumerable{User}.</returns>
-        private async Task<IEnumerable<User>> LoadUsers()
+        private List<User> LoadUsers()
         {
         {
             var users = UserRepository.RetrieveAllUsers().ToList();
             var users = UserRepository.RetrieveAllUsers().ToList();
 
 
@@ -385,14 +385,14 @@ namespace Emby.Server.Implementations.Library
 
 
                 user.DateLastSaved = DateTime.UtcNow;
                 user.DateLastSaved = DateTime.UtcNow;
 
 
-                await UserRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false);
+                UserRepository.SaveUser(user, CancellationToken.None);
 
 
                 users.Add(user);
                 users.Add(user);
 
 
                 user.Policy.IsAdministrator = true;
                 user.Policy.IsAdministrator = true;
                 user.Policy.EnableContentDeletion = true;
                 user.Policy.EnableContentDeletion = true;
                 user.Policy.EnableRemoteControlOfOtherUsers = true;
                 user.Policy.EnableRemoteControlOfOtherUsers = true;
-                await UpdateUserPolicy(user, user.Policy, false).ConfigureAwait(false);
+                UpdateUserPolicy(user, user.Policy, false);
             }
             }
 
 
             return users;
             return users;
@@ -539,7 +539,7 @@ namespace Emby.Server.Implementations.Library
         /// <param name="user">The user.</param>
         /// <param name="user">The user.</param>
         /// <exception cref="System.ArgumentNullException">user</exception>
         /// <exception cref="System.ArgumentNullException">user</exception>
         /// <exception cref="System.ArgumentException"></exception>
         /// <exception cref="System.ArgumentException"></exception>
-        public async Task UpdateUser(User user)
+        public void UpdateUser(User user)
         {
         {
             if (user == null)
             if (user == null)
             {
             {
@@ -554,7 +554,7 @@ namespace Emby.Server.Implementations.Library
             user.DateModified = DateTime.UtcNow;
             user.DateModified = DateTime.UtcNow;
             user.DateLastSaved = DateTime.UtcNow;
             user.DateLastSaved = DateTime.UtcNow;
 
 
-            await UserRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false);
+            UserRepository.SaveUser(user, CancellationToken.None);
 
 
             OnUserUpdated(user);
             OnUserUpdated(user);
         }
         }
@@ -599,7 +599,7 @@ namespace Emby.Server.Implementations.Library
 
 
                 user.DateLastSaved = DateTime.UtcNow;
                 user.DateLastSaved = DateTime.UtcNow;
 
 
-                await UserRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false);
+                UserRepository.SaveUser(user, CancellationToken.None);
 
 
                 EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs<User> { Argument = user }, _logger);
                 EventHelper.QueueEventIfNotNull(UserCreated, this, new GenericEventArgs<User> { Argument = user }, _logger);
 
 
@@ -653,7 +653,7 @@ namespace Emby.Server.Implementations.Library
             {
             {
                 var configPath = GetConfigurationFilePath(user);
                 var configPath = GetConfigurationFilePath(user);
 
 
-                await UserRepository.DeleteUser(user, CancellationToken.None).ConfigureAwait(false);
+                UserRepository.DeleteUser(user, CancellationToken.None);
 
 
                 try
                 try
                 {
                 {
@@ -667,7 +667,7 @@ namespace Emby.Server.Implementations.Library
                 DeleteUserPolicy(user);
                 DeleteUserPolicy(user);
 
 
                 // Force this to be lazy loaded again
                 // Force this to be lazy loaded again
-                Users = await LoadUsers().ConfigureAwait(false);
+                Users = LoadUsers();
 
 
                 OnUserDeleted(user);
                 OnUserDeleted(user);
             }
             }
@@ -681,17 +681,17 @@ namespace Emby.Server.Implementations.Library
         /// Resets the password by clearing it.
         /// Resets the password by clearing it.
         /// </summary>
         /// </summary>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        public Task ResetPassword(User user)
+        public void ResetPassword(User user)
         {
         {
-            return ChangePassword(user, GetSha1String(string.Empty));
+            ChangePassword(user, GetSha1String(string.Empty));
         }
         }
 
 
-        public Task ResetEasyPassword(User user)
+        public void ResetEasyPassword(User user)
         {
         {
-            return ChangeEasyPassword(user, GetSha1String(string.Empty));
+            ChangeEasyPassword(user, GetSha1String(string.Empty));
         }
         }
 
 
-        public async Task ChangePassword(User user, string newPasswordSha1)
+        public void ChangePassword(User user, string newPasswordSha1)
         {
         {
             if (user == null)
             if (user == null)
             {
             {
@@ -709,12 +709,12 @@ namespace Emby.Server.Implementations.Library
 
 
             user.Password = newPasswordSha1;
             user.Password = newPasswordSha1;
 
 
-            await UpdateUser(user).ConfigureAwait(false);
+            UpdateUser(user);
 
 
             EventHelper.FireEventIfNotNull(UserPasswordChanged, this, new GenericEventArgs<User>(user), _logger);
             EventHelper.FireEventIfNotNull(UserPasswordChanged, this, new GenericEventArgs<User>(user), _logger);
         }
         }
 
 
-        public async Task ChangeEasyPassword(User user, string newPasswordSha1)
+        public void ChangeEasyPassword(User user, string newPasswordSha1)
         {
         {
             if (user == null)
             if (user == null)
             {
             {
@@ -727,7 +727,7 @@ namespace Emby.Server.Implementations.Library
 
 
             user.EasyPassword = newPasswordSha1;
             user.EasyPassword = newPasswordSha1;
 
 
-            await UpdateUser(user).ConfigureAwait(false);
+            UpdateUser(user);
 
 
             EventHelper.FireEventIfNotNull(UserPasswordChanged, this, new GenericEventArgs<User>(user), _logger);
             EventHelper.FireEventIfNotNull(UserPasswordChanged, this, new GenericEventArgs<User>(user), _logger);
         }
         }
@@ -842,7 +842,7 @@ namespace Emby.Server.Implementations.Library
             };
             };
         }
         }
 
 
-        public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
+        public PinRedeemResult RedeemPasswordResetPin(string pin)
         {
         {
             DeletePinFile();
             DeletePinFile();
 
 
@@ -863,12 +863,12 @@ namespace Emby.Server.Implementations.Library
 
 
                 foreach (var user in users)
                 foreach (var user in users)
                 {
                 {
-                    await ResetPassword(user).ConfigureAwait(false);
+                    ResetPassword(user);
 
 
                     if (user.Policy.IsDisabled)
                     if (user.Policy.IsDisabled)
                     {
                     {
                         user.Policy.IsDisabled = false;
                         user.Policy.IsDisabled = false;
-                        await UpdateUserPolicy(user, user.Policy, true).ConfigureAwait(false);
+                        UpdateUserPolicy(user, user.Policy, true);
                     }
                     }
                     usersReset.Add(user.Name);
                     usersReset.Add(user.Name);
                 }
                 }
@@ -945,13 +945,13 @@ namespace Emby.Server.Implementations.Library
         }
         }
 
 
         private readonly object _policySyncLock = new object();
         private readonly object _policySyncLock = new object();
-        public Task UpdateUserPolicy(string userId, UserPolicy userPolicy)
+        public void UpdateUserPolicy(string userId, UserPolicy userPolicy)
         {
         {
             var user = GetUserById(userId);
             var user = GetUserById(userId);
-            return UpdateUserPolicy(user, userPolicy, true);
+            UpdateUserPolicy(user, userPolicy, true);
         }
         }
 
 
-        private async Task UpdateUserPolicy(User user, UserPolicy userPolicy, bool fireEvent)
+        private void UpdateUserPolicy(User user, UserPolicy userPolicy, bool fireEvent)
         {
         {
             // The xml serializer will output differently if the type is not exact
             // The xml serializer will output differently if the type is not exact
             if (userPolicy.GetType() != typeof(UserPolicy))
             if (userPolicy.GetType() != typeof(UserPolicy))
@@ -970,7 +970,7 @@ namespace Emby.Server.Implementations.Library
                 user.Policy = userPolicy;
                 user.Policy = userPolicy;
             }
             }
 
 
-            await UpdateConfiguration(user, user.Configuration, true).ConfigureAwait(false);
+            UpdateConfiguration(user, user.Configuration, true);
         }
         }
 
 
         private void DeleteUserPolicy(User user)
         private void DeleteUserPolicy(User user)
@@ -1032,13 +1032,13 @@ namespace Emby.Server.Implementations.Library
         }
         }
 
 
         private readonly object _configSyncLock = new object();
         private readonly object _configSyncLock = new object();
-        public Task UpdateConfiguration(string userId, UserConfiguration config)
+        public void UpdateConfiguration(string userId, UserConfiguration config)
         {
         {
             var user = GetUserById(userId);
             var user = GetUserById(userId);
-            return UpdateConfiguration(user, config, true);
+            UpdateConfiguration(user, config, true);
         }
         }
 
 
-        private async Task UpdateConfiguration(User user, UserConfiguration config, bool fireEvent)
+        private void UpdateConfiguration(User user, UserConfiguration config, bool fireEvent)
         {
         {
             var path = GetConfigurationFilePath(user);
             var path = GetConfigurationFilePath(user);
 
 

+ 11 - 10
Emby.Server.Implementations/Library/UserViewManager.cs

@@ -39,7 +39,7 @@ namespace Emby.Server.Implementations.Library
             _config = config;
             _config = config;
         }
         }
 
 
-        public async Task<IEnumerable<Folder>> GetUserViews(UserViewQuery query, CancellationToken cancellationToken)
+        public async Task<Folder[]> GetUserViews(UserViewQuery query, CancellationToken cancellationToken)
         {
         {
             var user = _userManager.GetUserById(query.UserId);
             var user = _userManager.GetUserById(query.UserId);
 
 
@@ -68,7 +68,7 @@ namespace Emby.Server.Implementations.Library
 
 
                 if (UserView.IsUserSpecific(folder))
                 if (UserView.IsUserSpecific(folder))
                 {
                 {
-                    list.Add(await _libraryManager.GetNamedView(user, folder.Name, folder.Id.ToString("N"), folderViewType, null, cancellationToken).ConfigureAwait(false));
+                    list.Add(_libraryManager.GetNamedView(user, folder.Name, folder.Id.ToString("N"), folderViewType, null, cancellationToken));
                     continue;
                     continue;
                 }
                 }
 
 
@@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.Library
 
 
                 if (query.PresetViews.Contains(folderViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
                 if (query.PresetViews.Contains(folderViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
                 {
                 {
-                    list.Add(await GetUserView(folder, folderViewType, string.Empty, cancellationToken).ConfigureAwait(false));
+                    list.Add(GetUserView(folder, folderViewType, string.Empty, cancellationToken));
                 }
                 }
                 else
                 else
                 {
                 {
@@ -95,7 +95,7 @@ namespace Emby.Server.Implementations.Library
 
 
                 if (parents.Count > 0)
                 if (parents.Count > 0)
                 {
                 {
-                    list.Add(await GetUserView(parents, viewType, string.Empty, user, query.PresetViews, cancellationToken).ConfigureAwait(false));
+                    list.Add(GetUserView(parents, viewType, string.Empty, user, query.PresetViews, cancellationToken));
                 }
                 }
             }
             }
 
 
@@ -114,7 +114,7 @@ namespace Emby.Server.Implementations.Library
                 }, cancellationToken).ConfigureAwait(false);
                 }, cancellationToken).ConfigureAwait(false);
 
 
                 var channels = channelResult.Items;
                 var channels = channelResult.Items;
-                
+
                 if (_config.Configuration.EnableChannelView && channels.Length > 0)
                 if (_config.Configuration.EnableChannelView && channels.Length > 0)
                 {
                 {
                     list.Add(await _channelManager.GetInternalChannelFolder(cancellationToken).ConfigureAwait(false));
                     list.Add(await _channelManager.GetInternalChannelFolder(cancellationToken).ConfigureAwait(false));
@@ -154,7 +154,8 @@ namespace Emby.Server.Implementations.Library
                     return index == -1 ? int.MaxValue : index;
                     return index == -1 ? int.MaxValue : index;
                 })
                 })
                 .ThenBy(sorted.IndexOf)
                 .ThenBy(sorted.IndexOf)
-                .ThenBy(i => i.SortName);
+                .ThenBy(i => i.SortName)
+                .ToArray();
         }
         }
 
 
         public Task<UserView> GetUserSubView(string name, string parentId, string type, string sortName, CancellationToken cancellationToken)
         public Task<UserView> GetUserSubView(string name, string parentId, string type, string sortName, CancellationToken cancellationToken)
@@ -171,7 +172,7 @@ namespace Emby.Server.Implementations.Library
             return GetUserSubView(name, parentId, type, sortName, cancellationToken);
             return GetUserSubView(name, parentId, type, sortName, cancellationToken);
         }
         }
 
 
-        private async Task<Folder> GetUserView(List<ICollectionFolder> parents, string viewType, string sortName, User user, string[] presetViews, CancellationToken cancellationToken)
+        private Folder GetUserView(List<ICollectionFolder> parents, string viewType, string sortName, User user, string[] presetViews, CancellationToken cancellationToken)
         {
         {
             if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
             if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
             {
             {
@@ -180,14 +181,14 @@ namespace Emby.Server.Implementations.Library
                     return (Folder)parents[0];
                     return (Folder)parents[0];
                 }
                 }
 
 
-                return await GetUserView((Folder)parents[0], viewType, string.Empty, cancellationToken).ConfigureAwait(false);
+                return GetUserView((Folder)parents[0], viewType, string.Empty, cancellationToken);
             }
             }
 
 
             var name = _localizationManager.GetLocalizedString("ViewType" + viewType);
             var name = _localizationManager.GetLocalizedString("ViewType" + viewType);
-            return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
+            return _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken);
         }
         }
 
 
-        public Task<UserView> GetUserView(Folder parent, string viewType, string sortName, CancellationToken cancellationToken)
+        public UserView GetUserView(Folder parent, string viewType, string sortName, CancellationToken cancellationToken)
         {
         {
             return _libraryManager.GetShadowView(parent, viewType, sortName, cancellationToken);
             return _libraryManager.GetShadowView(parent, viewType, sortName, cancellationToken);
         }
         }

+ 105 - 60
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -208,7 +208,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                     continue;
                     continue;
                 }
                 }
 
 
-                if (virtualFolder.Locations.Count == 1)
+                if (virtualFolder.Locations.Length == 1)
                 {
                 {
                     // remove entire virtual folder
                     // remove entire virtual folder
                     try
                     try
@@ -458,7 +458,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             return GetEpgChannelFromTunerChannel(info, tunerChannel, epgChannels);
             return GetEpgChannelFromTunerChannel(info, tunerChannel, epgChannels);
         }
         }
 
 
-        private string GetMappedChannel(string channelId, List<NameValuePair> mappings)
+        private string GetMappedChannel(string channelId, NameValuePair[] mappings)
         {
         {
             foreach (NameValuePair mapping in mappings)
             foreach (NameValuePair mapping in mappings)
             {
             {
@@ -472,10 +472,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
         private ChannelInfo GetEpgChannelFromTunerChannel(ListingsProviderInfo info, ChannelInfo tunerChannel, List<ChannelInfo> epgChannels)
         private ChannelInfo GetEpgChannelFromTunerChannel(ListingsProviderInfo info, ChannelInfo tunerChannel, List<ChannelInfo> epgChannels)
         {
         {
-            return GetEpgChannelFromTunerChannel(info.ChannelMappings.ToList(), tunerChannel, epgChannels);
+            return GetEpgChannelFromTunerChannel(info.ChannelMappings, tunerChannel, epgChannels);
         }
         }
 
 
-        public ChannelInfo GetEpgChannelFromTunerChannel(List<NameValuePair> mappings, ChannelInfo tunerChannel, List<ChannelInfo> epgChannels)
+        public ChannelInfo GetEpgChannelFromTunerChannel(NameValuePair[] mappings, ChannelInfo tunerChannel, List<ChannelInfo> epgChannels)
         {
         {
             if (!string.IsNullOrWhiteSpace(tunerChannel.Id))
             if (!string.IsNullOrWhiteSpace(tunerChannel.Id))
             {
             {
@@ -607,20 +607,22 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             var timer = _timerProvider.GetTimer(timerId);
             var timer = _timerProvider.GetTimer(timerId);
             if (timer != null)
             if (timer != null)
             {
             {
+                timer.Status = RecordingStatus.Cancelled;
+
                 if (string.IsNullOrWhiteSpace(timer.SeriesTimerId) || isSeriesCancelled)
                 if (string.IsNullOrWhiteSpace(timer.SeriesTimerId) || isSeriesCancelled)
                 {
                 {
                     _timerProvider.Delete(timer);
                     _timerProvider.Delete(timer);
                 }
                 }
                 else
                 else
                 {
                 {
-                    timer.Status = RecordingStatus.Cancelled;
                     _timerProvider.AddOrUpdate(timer, false);
                     _timerProvider.AddOrUpdate(timer, false);
                 }
                 }
             }
             }
             ActiveRecordingInfo activeRecordingInfo;
             ActiveRecordingInfo activeRecordingInfo;
 
 
             if (_activeRecordings.TryGetValue(timerId, out activeRecordingInfo))
             if (_activeRecordings.TryGetValue(timerId, out activeRecordingInfo))
-            {
+            { 
+                activeRecordingInfo.Timer = timer;
                 activeRecordingInfo.CancellationTokenSource.Cancel();
                 activeRecordingInfo.CancellationTokenSource.Cancel();
             }
             }
         }
         }
@@ -830,6 +832,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             existingTimer.IsKids = updatedTimer.IsKids;
             existingTimer.IsKids = updatedTimer.IsKids;
             existingTimer.IsNews = updatedTimer.IsNews;
             existingTimer.IsNews = updatedTimer.IsNews;
             existingTimer.IsMovie = updatedTimer.IsMovie;
             existingTimer.IsMovie = updatedTimer.IsMovie;
+            existingTimer.IsSeries = updatedTimer.IsSeries;
+            existingTimer.IsLive = updatedTimer.IsLive;
+            existingTimer.IsPremiere = updatedTimer.IsPremiere;
             existingTimer.IsProgramSeries = updatedTimer.IsProgramSeries;
             existingTimer.IsProgramSeries = updatedTimer.IsProgramSeries;
             existingTimer.IsRepeat = updatedTimer.IsRepeat;
             existingTimer.IsRepeat = updatedTimer.IsRepeat;
             existingTimer.IsSports = updatedTimer.IsSports;
             existingTimer.IsSports = updatedTimer.IsSports;
@@ -861,7 +866,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
         public async Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(CancellationToken cancellationToken)
         public async Task<IEnumerable<RecordingInfo>> GetRecordingsAsync(CancellationToken cancellationToken)
         {
         {
-            return _activeRecordings.Values.ToList().Select(GetRecordingInfo).ToList();
+            return new List<RecordingInfo>();
         }
         }
 
 
         public string GetActiveRecordingPath(string id)
         public string GetActiveRecordingPath(string id)
@@ -875,49 +880,31 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             return null;
             return null;
         }
         }
 
 
-        private RecordingInfo GetRecordingInfo(ActiveRecordingInfo info)
-        {
-            var timer = info.Timer;
-            var program = info.Program;
-
-            var result = new RecordingInfo
-            {
-                ChannelId = timer.ChannelId,
-                CommunityRating = timer.CommunityRating,
-                DateLastUpdated = DateTime.UtcNow,
-                EndDate = timer.EndDate,
-                EpisodeTitle = timer.EpisodeTitle,
-                Genres = timer.Genres,
-                Id = "recording" + timer.Id,
-                IsKids = timer.IsKids,
-                IsMovie = timer.IsMovie,
-                IsNews = timer.IsNews,
-                IsRepeat = timer.IsRepeat,
-                IsSeries = timer.IsProgramSeries,
-                IsSports = timer.IsSports,
-                Name = timer.Name,
-                OfficialRating = timer.OfficialRating,
-                OriginalAirDate = timer.OriginalAirDate,
-                Overview = timer.Overview,
-                ProgramId = timer.ProgramId,
-                SeriesTimerId = timer.SeriesTimerId,
-                StartDate = timer.StartDate,
-                Status = RecordingStatus.InProgress,
-                TimerId = timer.Id
-            };
+        public IEnumerable<ActiveRecordingInfo> GetAllActiveRecordings()
+        {
+            return _activeRecordings.Values.Where(i => i.Timer.Status == RecordingStatus.InProgress && !i.CancellationTokenSource.IsCancellationRequested);
+        }
 
 
-            if (program != null)
+        public ActiveRecordingInfo GetActiveRecordingInfo(string path)
+        {
+            if (string.IsNullOrWhiteSpace(path))
             {
             {
-                result.Audio = program.Audio;
-                result.ImagePath = program.ImagePath;
-                result.ImageUrl = program.ImageUrl;
-                result.IsHD = program.IsHD;
-                result.IsLive = program.IsLive;
-                result.IsPremiere = program.IsPremiere;
-                result.ShowId = program.ShowId;
+                return null;
             }
             }
 
 
-            return result;
+            foreach (var recording in _activeRecordings.Values)
+            {
+                if (string.Equals(recording.Path, path, StringComparison.Ordinal) && !recording.CancellationTokenSource.IsCancellationRequested)
+                {
+                    var timer = recording.Timer;
+                    if (timer.Status != RecordingStatus.InProgress)
+                    {
+                        return null;
+                    }
+                    return recording;
+                }
+            }
+            return null;
         }
         }
 
 
         public Task<IEnumerable<TimerInfo>> GetTimersAsync(CancellationToken cancellationToken)
         public Task<IEnumerable<TimerInfo>> GetTimersAsync(CancellationToken cancellationToken)
@@ -1245,6 +1232,33 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             throw new FileNotFoundException();
             throw new FileNotFoundException();
         }
         }
 
 
+        public async Task<List<MediaSourceInfo>> GetRecordingStreamMediaSources(ActiveRecordingInfo info, CancellationToken cancellationToken)
+        {
+            var stream = new MediaSourceInfo
+            {
+                Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
+                Id = info.Id,
+                SupportsDirectPlay = false,
+                SupportsDirectStream = true,
+                SupportsTranscoding = true,
+                IsInfiniteStream = true,
+                RequiresOpening = false,
+                RequiresClosing = false,
+                Protocol = MediaBrowser.Model.MediaInfo.MediaProtocol.Http,
+                BufferMs = 0,
+                IgnoreDts = true,
+                IgnoreIndex = true
+            };
+
+            var isAudio = false;
+            await new LiveStreamHelper(_mediaEncoder, _logger).AddMediaInfoWithProbe(stream, isAudio, cancellationToken).ConfigureAwait(false);
+
+            return new List<MediaSourceInfo>
+            {
+                stream
+            };
+        }
+
         public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
         public async Task CloseLiveStream(string id, CancellationToken cancellationToken)
         {
         {
             // Ignore the consumer id
             // Ignore the consumer id
@@ -1327,7 +1341,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 var activeRecordingInfo = new ActiveRecordingInfo
                 var activeRecordingInfo = new ActiveRecordingInfo
                 {
                 {
                     CancellationTokenSource = new CancellationTokenSource(),
                     CancellationTokenSource = new CancellationTokenSource(),
-                    Timer = timer
+                    Timer = timer,
+                    Id = timer.Id
                 };
                 };
 
 
                 if (_activeRecordings.TryAdd(timer.Id, activeRecordingInfo))
                 if (_activeRecordings.TryAdd(timer.Id, activeRecordingInfo))
@@ -1493,7 +1508,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
                 recordPath = recorder.GetOutputPath(mediaStreamInfo, recordPath);
                 recordPath = EnsureFileUnique(recordPath, timer.Id);
                 recordPath = EnsureFileUnique(recordPath, timer.Id);
 
 
-                _libraryManager.RegisterIgnoredPath(recordPath);
                 _libraryMonitor.ReportFileSystemChangeBeginning(recordPath);
                 _libraryMonitor.ReportFileSystemChangeBeginning(recordPath);
                 _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(recordPath));
                 _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(recordPath));
                 activeRecordingInfo.Path = recordPath;
                 activeRecordingInfo.Path = recordPath;
@@ -1512,6 +1526,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                     _timerProvider.AddOrUpdate(timer, false);
                     _timerProvider.AddOrUpdate(timer, false);
 
 
                     SaveRecordingMetadata(timer, recordPath, seriesPath);
                     SaveRecordingMetadata(timer, recordPath, seriesPath);
+                    TriggerRefresh(recordPath);
                     EnforceKeepUpTo(timer, seriesPath);
                     EnforceKeepUpTo(timer, seriesPath);
                 };
                 };
 
 
@@ -1543,7 +1558,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 }
                 }
             }
             }
 
 
-            _libraryManager.UnRegisterIgnoredPath(recordPath);
+            TriggerRefresh(recordPath);
             _libraryMonitor.ReportFileSystemChangeComplete(recordPath, true);
             _libraryMonitor.ReportFileSystemChangeComplete(recordPath, true);
 
 
             ActiveRecordingInfo removed;
             ActiveRecordingInfo removed;
@@ -1574,6 +1589,44 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             OnRecordingStatusChanged();
             OnRecordingStatusChanged();
         }
         }
 
 
+        private void TriggerRefresh(string path)
+        {
+            var item = GetAffectedBaseItem(_fileSystem.GetDirectoryName(path));
+
+            if (item != null)
+            {
+                item.ChangedExternally();
+            }
+        }
+
+        private BaseItem GetAffectedBaseItem(string path)
+        {
+            BaseItem item = null;
+
+            while (item == null && !string.IsNullOrEmpty(path))
+            {
+                item = _libraryManager.FindByPath(path, null);
+
+                path = _fileSystem.GetDirectoryName(path);
+            }
+
+            if (item != null)
+            {
+                // If the item has been deleted find the first valid parent that still exists
+                while (!_fileSystem.DirectoryExists(item.Path) && !_fileSystem.FileExists(item.Path))
+                {
+                    item = item.GetParent();
+
+                    if (item == null)
+                    {
+                        break;
+                    }
+                }
+            }
+
+            return item;
+        }
+
         private void OnRecordingStatusChanged()
         private void OnRecordingStatusChanged()
         {
         {
             EventHelper.FireEventIfNotNull(RecordingStatusChanged, this, new RecordingStatusChangedEventArgs
             EventHelper.FireEventIfNotNull(RecordingStatusChanged, this, new RecordingStatusChangedEventArgs
@@ -2591,7 +2644,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             {
             {
                 list.Add(new VirtualFolderInfo
                 list.Add(new VirtualFolderInfo
                 {
                 {
-                    Locations = new List<string> { defaultFolder },
+                    Locations = new string[] { defaultFolder },
                     Name = defaultName
                     Name = defaultName
                 });
                 });
             }
             }
@@ -2601,7 +2654,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             {
             {
                 list.Add(new VirtualFolderInfo
                 list.Add(new VirtualFolderInfo
                 {
                 {
-                    Locations = new List<string> { customPath },
+                    Locations = new string[] { customPath },
                     Name = "Recorded Movies",
                     Name = "Recorded Movies",
                     CollectionType = CollectionType.Movies
                     CollectionType = CollectionType.Movies
                 });
                 });
@@ -2612,7 +2665,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             {
             {
                 list.Add(new VirtualFolderInfo
                 list.Add(new VirtualFolderInfo
                 {
                 {
-                    Locations = new List<string> { customPath },
+                    Locations = new string[] { customPath },
                     Name = "Recorded Shows",
                     Name = "Recorded Shows",
                     CollectionType = CollectionType.TvShows
                     CollectionType = CollectionType.TvShows
                 });
                 });
@@ -2621,14 +2674,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             return list;
             return list;
         }
         }
 
 
-        class ActiveRecordingInfo
-        {
-            public string Path { get; set; }
-            public TimerInfo Timer { get; set; }
-            public ProgramInfo Program { get; set; }
-            public CancellationTokenSource CancellationTokenSource { get; set; }
-        }
-
         private const int TunerDiscoveryDurationMs = 3000;
         private const int TunerDiscoveryDurationMs = 3000;
 
 
         public async Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)
         public async Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)

+ 4 - 0
Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs

@@ -58,6 +58,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             timerInfo.OriginalAirDate = programInfo.OriginalAirDate;
             timerInfo.OriginalAirDate = programInfo.OriginalAirDate;
             timerInfo.IsProgramSeries = programInfo.IsSeries;
             timerInfo.IsProgramSeries = programInfo.IsSeries;
 
 
+            timerInfo.IsSeries = programInfo.IsSeries;
+            timerInfo.IsLive = programInfo.IsLive;
+            timerInfo.IsPremiere = programInfo.IsPremiere;
+
             timerInfo.HomePageUrl = programInfo.HomePageUrl;
             timerInfo.HomePageUrl = programInfo.HomePageUrl;
             timerInfo.CommunityRating = programInfo.CommunityRating;
             timerInfo.CommunityRating = programInfo.CommunityRating;
             timerInfo.Overview = programInfo.Overview;
             timerInfo.Overview = programInfo.Overview;

+ 21 - 10
Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs

@@ -247,7 +247,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings
             ProgramAudio audioType = ProgramAudio.Stereo;
             ProgramAudio audioType = ProgramAudio.Stereo;
 
 
             bool repeat = programInfo.@new == null;
             bool repeat = programInfo.@new == null;
-            string newID = programInfo.programID + "T" + startAt.Ticks + "C" + channelId;
+
+            var programId = programInfo.programID ?? string.Empty;
+
+            string newID = programId + "T" + startAt.Ticks + "C" + channelId;
 
 
             if (programInfo.audioProperties != null)
             if (programInfo.audioProperties != null)
             {
             {
@@ -300,7 +303,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
                 Etag = programInfo.md5
                 Etag = programInfo.md5
             };
             };
 
 
-            var showId = programInfo.programID ?? string.Empty;
+            var showId = programId;
 
 
             if (!info.IsSeries)
             if (!info.IsSeries)
             {
             {
@@ -339,11 +342,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
 
             if (details.descriptions != null)
             if (details.descriptions != null)
             {
             {
-                if (details.descriptions.description1000 != null)
+                if (details.descriptions.description1000 != null && details.descriptions.description1000.Count > 0)
                 {
                 {
                     info.Overview = details.descriptions.description1000[0].description;
                     info.Overview = details.descriptions.description1000[0].description;
                 }
                 }
-                else if (details.descriptions.description100 != null)
+                else if (details.descriptions.description100 != null && details.descriptions.description100.Count > 0)
                 {
                 {
                     info.Overview = details.descriptions.description100[0].description;
                     info.Overview = details.descriptions.description100[0].description;
                 }
                 }
@@ -351,16 +354,24 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
 
             if (info.IsSeries)
             if (info.IsSeries)
             {
             {
-                info.SeriesId = programInfo.programID.Substring(0, 10);
+                info.SeriesId = programId.Substring(0, 10);
 
 
                 if (details.metadata != null)
                 if (details.metadata != null)
                 {
                 {
-                    var gracenote = details.metadata.Find(x => x.Gracenote != null).Gracenote;
-                    info.SeasonNumber = gracenote.season;
-
-                    if (gracenote.episode > 0)
+                    foreach (var metadataProgram in details.metadata)
                     {
                     {
-                        info.EpisodeNumber = gracenote.episode;
+                        var gracenote = metadataProgram.Gracenote;
+                        if (gracenote != null)
+                        {
+                            info.SeasonNumber = gracenote.season;
+
+                            if (gracenote.episode > 0)
+                            {
+                                info.EpisodeNumber = gracenote.episode;
+                            }
+
+                            break;
+                        }
                     }
                     }
                 }
                 }
             }
             }

+ 19 - 32
Emby.Server.Implementations/LiveTv/LiveTvDtoService.cs

@@ -15,6 +15,8 @@ using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Querying;
 
 
 namespace Emby.Server.Implementations.LiveTv
 namespace Emby.Server.Implementations.LiveTv
 {
 {
@@ -110,7 +112,7 @@ namespace Emby.Server.Implementations.LiveTv
                 PostPaddingSeconds = info.PostPaddingSeconds,
                 PostPaddingSeconds = info.PostPaddingSeconds,
                 IsPostPaddingRequired = info.IsPostPaddingRequired,
                 IsPostPaddingRequired = info.IsPostPaddingRequired,
                 IsPrePaddingRequired = info.IsPrePaddingRequired,
                 IsPrePaddingRequired = info.IsPrePaddingRequired,
-                Days = info.Days,
+                Days = info.Days.ToArray(),
                 Priority = info.Priority,
                 Priority = info.Priority,
                 RecordAnyChannel = info.RecordAnyChannel,
                 RecordAnyChannel = info.RecordAnyChannel,
                 RecordAnyTime = info.RecordAnyTime,
                 RecordAnyTime = info.RecordAnyTime,
@@ -135,7 +137,7 @@ namespace Emby.Server.Implementations.LiveTv
                 dto.ProgramId = GetInternalProgramId(service.Name, info.ProgramId).ToString("N");
                 dto.ProgramId = GetInternalProgramId(service.Name, info.ProgramId).ToString("N");
             }
             }
 
 
-            dto.DayPattern = info.Days == null ? null : GetDayPattern(info.Days);
+            dto.DayPattern = info.Days == null ? null : GetDayPattern(info.Days.ToArray(info.Days.Count));
 
 
             FillImages(dto, info.Name, info.SeriesId);
             FillImages(dto, info.Name, info.SeriesId);
 
 
@@ -150,10 +152,7 @@ namespace Emby.Server.Implementations.LiveTv
                 Name = seriesName,
                 Name = seriesName,
                 Limit = 1,
                 Limit = 1,
                 ImageTypes = new ImageType[] { ImageType.Thumb },
                 ImageTypes = new ImageType[] { ImageType.Thumb },
-                DtoOptions = new DtoOptions
-                {
-                    Fields = new List<MediaBrowser.Model.Querying.ItemFields>()
-                }
+                DtoOptions = new DtoOptions(false)
 
 
             }).FirstOrDefault();
             }).FirstOrDefault();
 
 
@@ -196,10 +195,7 @@ namespace Emby.Server.Implementations.LiveTv
                     ExternalSeriesId = programSeriesId,
                     ExternalSeriesId = programSeriesId,
                     Limit = 1,
                     Limit = 1,
                     ImageTypes = new ImageType[] { ImageType.Primary },
                     ImageTypes = new ImageType[] { ImageType.Primary },
-                    DtoOptions = new DtoOptions
-                    {
-                        Fields = new List<MediaBrowser.Model.Querying.ItemFields>()
-                    }
+                    DtoOptions = new DtoOptions(false)
 
 
                 }).FirstOrDefault();
                 }).FirstOrDefault();
 
 
@@ -248,10 +244,7 @@ namespace Emby.Server.Implementations.LiveTv
                 Name = seriesName,
                 Name = seriesName,
                 Limit = 1,
                 Limit = 1,
                 ImageTypes = new ImageType[] { ImageType.Thumb },
                 ImageTypes = new ImageType[] { ImageType.Thumb },
-                DtoOptions = new DtoOptions
-                {
-                    Fields = new List<MediaBrowser.Model.Querying.ItemFields>()
-                }
+                DtoOptions = new DtoOptions(false)
 
 
             }).FirstOrDefault();
             }).FirstOrDefault();
 
 
@@ -274,7 +267,7 @@ namespace Emby.Server.Implementations.LiveTv
                 {
                 {
                     try
                     try
                     {
                     {
-                        dto.ParentBackdropImageTags = new List<string>
+                        dto.ParentBackdropImageTags = new string[]
                             {
                             {
                                 _imageProcessor.GetImageCacheTag(librarySeries, image)
                                 _imageProcessor.GetImageCacheTag(librarySeries, image)
                             };
                             };
@@ -294,10 +287,7 @@ namespace Emby.Server.Implementations.LiveTv
                     Name = seriesName,
                     Name = seriesName,
                     Limit = 1,
                     Limit = 1,
                     ImageTypes = new ImageType[] { ImageType.Primary },
                     ImageTypes = new ImageType[] { ImageType.Primary },
-                    DtoOptions = new DtoOptions
-                    {
-                        Fields = new List<MediaBrowser.Model.Querying.ItemFields>()
-                    }
+                    DtoOptions = new DtoOptions(false)
 
 
                 }).FirstOrDefault() ?? _libraryManager.GetItemList(new InternalItemsQuery
                 }).FirstOrDefault() ?? _libraryManager.GetItemList(new InternalItemsQuery
                 {
                 {
@@ -305,10 +295,7 @@ namespace Emby.Server.Implementations.LiveTv
                     ExternalSeriesId = programSeriesId,
                     ExternalSeriesId = programSeriesId,
                     Limit = 1,
                     Limit = 1,
                     ImageTypes = new ImageType[] { ImageType.Primary },
                     ImageTypes = new ImageType[] { ImageType.Primary },
-                    DtoOptions = new DtoOptions
-                    {
-                        Fields = new List<MediaBrowser.Model.Querying.ItemFields>()
-                    }
+                    DtoOptions = new DtoOptions(false)
 
 
                 }).FirstOrDefault();
                 }).FirstOrDefault();
 
 
@@ -327,14 +314,14 @@ namespace Emby.Server.Implementations.LiveTv
                         }
                         }
                     }
                     }
 
 
-                    if (dto.ParentBackdropImageTags == null || dto.ParentBackdropImageTags.Count == 0)
+                    if (dto.ParentBackdropImageTags == null || dto.ParentBackdropImageTags.Length == 0)
                     {
                     {
                         image = program.GetImageInfo(ImageType.Backdrop, 0);
                         image = program.GetImageInfo(ImageType.Backdrop, 0);
                         if (image != null)
                         if (image != null)
                         {
                         {
                             try
                             try
                             {
                             {
-                                dto.ParentBackdropImageTags = new List<string>
+                                dto.ParentBackdropImageTags = new string[]
                             {
                             {
                                 _imageProcessor.GetImageCacheTag(program, image)
                                 _imageProcessor.GetImageCacheTag(program, image)
                             };
                             };
@@ -349,24 +336,24 @@ namespace Emby.Server.Implementations.LiveTv
             }
             }
         }
         }
 
 
-        public DayPattern? GetDayPattern(List<DayOfWeek> days)
+        public DayPattern? GetDayPattern(DayOfWeek[] days)
         {
         {
             DayPattern? pattern = null;
             DayPattern? pattern = null;
 
 
-            if (days.Count > 0)
+            if (days.Length > 0)
             {
             {
-                if (days.Count == 7)
+                if (days.Length == 7)
                 {
                 {
                     pattern = DayPattern.Daily;
                     pattern = DayPattern.Daily;
                 }
                 }
-                else if (days.Count == 2)
+                else if (days.Length == 2)
                 {
                 {
                     if (days.Contains(DayOfWeek.Saturday) && days.Contains(DayOfWeek.Sunday))
                     if (days.Contains(DayOfWeek.Saturday) && days.Contains(DayOfWeek.Sunday))
                     {
                     {
                         pattern = DayPattern.Weekends;
                         pattern = DayPattern.Weekends;
                     }
                     }
                 }
                 }
-                else if (days.Count == 5)
+                else if (days.Length == 5)
                 {
                 {
                     if (days.Contains(DayOfWeek.Monday) && days.Contains(DayOfWeek.Tuesday) && days.Contains(DayOfWeek.Wednesday) && days.Contains(DayOfWeek.Thursday) && days.Contains(DayOfWeek.Friday))
                     if (days.Contains(DayOfWeek.Monday) && days.Contains(DayOfWeek.Tuesday) && days.Contains(DayOfWeek.Wednesday) && days.Contains(DayOfWeek.Thursday) && days.Contains(DayOfWeek.Friday))
                     {
                     {
@@ -384,7 +371,7 @@ namespace Emby.Server.Implementations.LiveTv
             {
             {
                 Name = info.Name,
                 Name = info.Name,
                 Id = info.Id,
                 Id = info.Id,
-                Clients = info.Clients,
+                Clients = info.Clients.ToArray(),
                 ProgramName = info.ProgramName,
                 ProgramName = info.ProgramName,
                 SourceType = info.SourceType,
                 SourceType = info.SourceType,
                 Status = info.Status,
                 Status = info.Status,
@@ -543,7 +530,7 @@ namespace Emby.Server.Implementations.LiveTv
                 PostPaddingSeconds = dto.PostPaddingSeconds,
                 PostPaddingSeconds = dto.PostPaddingSeconds,
                 IsPostPaddingRequired = dto.IsPostPaddingRequired,
                 IsPostPaddingRequired = dto.IsPostPaddingRequired,
                 IsPrePaddingRequired = dto.IsPrePaddingRequired,
                 IsPrePaddingRequired = dto.IsPrePaddingRequired,
-                Days = dto.Days,
+                Days = dto.Days.ToList(),
                 Priority = dto.Priority,
                 Priority = dto.Priority,
                 RecordAnyChannel = dto.RecordAnyChannel,
                 RecordAnyChannel = dto.RecordAnyChannel,
                 RecordAnyTime = dto.RecordAnyTime,
                 RecordAnyTime = dto.RecordAnyTime,

+ 135 - 93
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -60,7 +60,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
         private readonly LiveTvDtoService _tvDtoService;
         private readonly LiveTvDtoService _tvDtoService;
 
 
-        private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
+        private ILiveTvService[] _services = new ILiveTvService[] { };
 
 
         private readonly SemaphoreSlim _refreshRecordingsLock = new SemaphoreSlim(1, 1);
         private readonly SemaphoreSlim _refreshRecordingsLock = new SemaphoreSlim(1, 1);
 
 
@@ -124,7 +124,7 @@ namespace Emby.Server.Implementations.LiveTv
         /// <param name="listingProviders">The listing providers.</param>
         /// <param name="listingProviders">The listing providers.</param>
         public void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders)
         public void AddParts(IEnumerable<ILiveTvService> services, IEnumerable<ITunerHost> tunerHosts, IEnumerable<IListingsProvider> listingProviders)
         {
         {
-            _services.AddRange(services);
+            _services = services.ToArray();
             _tunerHosts.AddRange(tunerHosts);
             _tunerHosts.AddRange(tunerHosts);
             _listingProviders.AddRange(listingProviders);
             _listingProviders.AddRange(listingProviders);
 
 
@@ -558,7 +558,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             if (isNew)
             if (isNew)
             {
             {
-                await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
+                _libraryManager.CreateItem(item, cancellationToken);
             }
             }
             else if (forceUpdate)
             else if (forceUpdate)
             {
             {
@@ -875,7 +875,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             if (isNew)
             if (isNew)
             {
             {
-                await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
+                _libraryManager.CreateItem(item, cancellationToken);
             }
             }
             else if (dataChanged || info.DateLastUpdated > recording.DateLastSaved || statusChanged)
             else if (dataChanged || info.DateLastUpdated > recording.DateLastSaved || statusChanged)
             {
             {
@@ -985,9 +985,8 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             var queryResult = _libraryManager.QueryItems(internalQuery);
             var queryResult = _libraryManager.QueryItems(internalQuery);
 
 
-            var returnList = (await _dtoService.GetBaseItemDtos(queryResult.Items, options, user)
+            var returnArray = (await _dtoService.GetBaseItemDtos(queryResult.Items, options, user)
                 .ConfigureAwait(false));
                 .ConfigureAwait(false));
-            var returnArray = returnList.ToArray(returnList.Count);
 
 
             var result = new QueryResult<BaseItemDto>
             var result = new QueryResult<BaseItemDto>
             {
             {
@@ -998,7 +997,7 @@ namespace Emby.Server.Implementations.LiveTv
             return result;
             return result;
         }
         }
 
 
-        public async Task<QueryResult<LiveTvProgram>> GetRecommendedProgramsInternal(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
+        public async Task<QueryResult<BaseItem>> GetRecommendedProgramsInternal(RecommendedProgramQuery query, DtoOptions options, CancellationToken cancellationToken)
         {
         {
             var user = _userManager.GetUserById(query.UserId);
             var user = _userManager.GetUserById(query.UserId);
 
 
@@ -1036,10 +1035,10 @@ namespace Emby.Server.Implementations.LiveTv
                 }
                 }
             }
             }
 
 
-            var programList = _libraryManager.QueryItems(internalQuery).Items.Cast<LiveTvProgram>().ToList();
-            var totalCount = programList.Count;
+            var programList = _libraryManager.QueryItems(internalQuery).Items;
+            var totalCount = programList.Length;
 
 
-            IOrderedEnumerable<LiveTvProgram> orderedPrograms = programList.OrderBy(i => i.StartDate.Date);
+            IOrderedEnumerable<LiveTvProgram> orderedPrograms = programList.Cast<LiveTvProgram>().OrderBy(i => i.StartDate.Date);
 
 
             if (query.IsAiring ?? false)
             if (query.IsAiring ?? false)
             {
             {
@@ -1047,14 +1046,14 @@ namespace Emby.Server.Implementations.LiveTv
                     .ThenByDescending(i => GetRecommendationScore(i, user.Id, true));
                     .ThenByDescending(i => GetRecommendationScore(i, user.Id, true));
             }
             }
 
 
-            IEnumerable<LiveTvProgram> programs = orderedPrograms;
+            IEnumerable<BaseItem> programs = orderedPrograms;
 
 
             if (query.Limit.HasValue)
             if (query.Limit.HasValue)
             {
             {
                 programs = programs.Take(query.Limit.Value);
                 programs = programs.Take(query.Limit.Value);
             }
             }
 
 
-            var result = new QueryResult<LiveTvProgram>
+            var result = new QueryResult<BaseItem>
             {
             {
                 Items = programs.ToArray(),
                 Items = programs.ToArray(),
                 TotalRecordCount = totalCount
                 TotalRecordCount = totalCount
@@ -1071,9 +1070,8 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             var user = _userManager.GetUserById(query.UserId);
             var user = _userManager.GetUserById(query.UserId);
 
 
-            var returnList = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user)
+            var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user)
                 .ConfigureAwait(false));
                 .ConfigureAwait(false));
-            var returnArray = returnList.ToArray(returnList.Count);
 
 
             var result = new QueryResult<BaseItemDto>
             var result = new QueryResult<BaseItemDto>
             {
             {
@@ -1223,9 +1221,9 @@ namespace Emby.Server.Implementations.LiveTv
             await EmbyTV.EmbyTV.Current.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false);
             await EmbyTV.EmbyTV.Current.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false);
 
 
             var numComplete = 0;
             var numComplete = 0;
-            double progressPerService = _services.Count == 0
+            double progressPerService = _services.Length == 0
                 ? 0
                 ? 0
-                : 1 / _services.Count;
+                : 1 / _services.Length;
 
 
             var newChannelIdList = new List<Guid>();
             var newChannelIdList = new List<Guid>();
             var newProgramIdList = new List<Guid>();
             var newProgramIdList = new List<Guid>();
@@ -1257,13 +1255,13 @@ namespace Emby.Server.Implementations.LiveTv
 
 
                 numComplete++;
                 numComplete++;
                 double percent = numComplete;
                 double percent = numComplete;
-                percent /= _services.Count;
+                percent /= _services.Length;
 
 
                 progress.Report(100 * percent);
                 progress.Report(100 * percent);
             }
             }
 
 
-            await CleanDatabaseInternal(newChannelIdList, new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken).ConfigureAwait(false);
-            await CleanDatabaseInternal(newProgramIdList, new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken).ConfigureAwait(false);
+            await CleanDatabaseInternal(newChannelIdList.ToArray(), new[] { typeof(LiveTvChannel).Name }, progress, cancellationToken).ConfigureAwait(false);
+            await CleanDatabaseInternal(newProgramIdList.ToArray(), new[] { typeof(LiveTvProgram).Name }, progress, cancellationToken).ConfigureAwait(false);
 
 
             var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault();
             var coreService = _services.OfType<EmbyTV.EmbyTV>().FirstOrDefault();
 
 
@@ -1275,8 +1273,11 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             // Load these now which will prefetch metadata
             // Load these now which will prefetch metadata
             var dtoOptions = new DtoOptions();
             var dtoOptions = new DtoOptions();
-            dtoOptions.Fields.Remove(ItemFields.SyncInfo);
-            dtoOptions.Fields.Remove(ItemFields.BasicSyncInfo);
+            var fields = dtoOptions.Fields.ToList();
+            fields.Remove(ItemFields.SyncInfo);
+            fields.Remove(ItemFields.BasicSyncInfo);
+            dtoOptions.Fields = fields.ToArray(fields.Count);
+
             await GetRecordings(new RecordingQuery(), dtoOptions, cancellationToken).ConfigureAwait(false);
             await GetRecordings(new RecordingQuery(), dtoOptions, cancellationToken).ConfigureAwait(false);
 
 
             progress.Report(100);
             progress.Report(100);
@@ -1409,7 +1410,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
                     if (newPrograms.Count > 0)
                     if (newPrograms.Count > 0)
                     {
                     {
-                        await _libraryManager.CreateItems(newPrograms, cancellationToken).ConfigureAwait(false);
+                        _libraryManager.CreateItems(newPrograms, cancellationToken);
                     }
                     }
 
 
                     // TODO: Do this in bulk
                     // TODO: Do this in bulk
@@ -1446,14 +1447,14 @@ namespace Emby.Server.Implementations.LiveTv
             return new Tuple<List<Guid>, List<Guid>>(channels, programs);
             return new Tuple<List<Guid>, List<Guid>>(channels, programs);
         }
         }
 
 
-        private async Task CleanDatabaseInternal(List<Guid> currentIdList, string[] validTypes, IProgress<double> progress, CancellationToken cancellationToken)
+        private async Task CleanDatabaseInternal(Guid[] currentIdList, string[] validTypes, IProgress<double> progress, CancellationToken cancellationToken)
         {
         {
             var list = _itemRepo.GetItemIdsList(new InternalItemsQuery
             var list = _itemRepo.GetItemIdsList(new InternalItemsQuery
             {
             {
                 IncludeItemTypes = validTypes,
                 IncludeItemTypes = validTypes,
                 DtoOptions = new DtoOptions(false)
                 DtoOptions = new DtoOptions(false)
 
 
-            }).ToList();
+            });
 
 
             var numComplete = 0;
             var numComplete = 0;
 
 
@@ -1543,7 +1544,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
                 var idList = await Task.WhenAll(recordingTasks).ConfigureAwait(false);
                 var idList = await Task.WhenAll(recordingTasks).ConfigureAwait(false);
 
 
-                await CleanDatabaseInternal(idList.ToList(), new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
+                await CleanDatabaseInternal(idList, new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }, new SimpleProgress<double>(), cancellationToken).ConfigureAwait(false);
 
 
                 _lastRecordingRefreshTime = DateTime.UtcNow;
                 _lastRecordingRefreshTime = DateTime.UtcNow;
             }
             }
@@ -1560,11 +1561,6 @@ namespace Emby.Server.Implementations.LiveTv
                 return new QueryResult<BaseItem>();
                 return new QueryResult<BaseItem>();
             }
             }
 
 
-            if ((query.IsInProgress ?? false))
-            {
-                return new QueryResult<BaseItem>();
-            }
-
             var folderIds = EmbyTV.EmbyTV.Current.GetRecordingFolders()
             var folderIds = EmbyTV.EmbyTV.Current.GetRecordingFolders()
                 .SelectMany(i => i.Locations)
                 .SelectMany(i => i.Locations)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
@@ -1576,13 +1572,10 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             var excludeItemTypes = new List<string>();
             var excludeItemTypes = new List<string>();
 
 
-            if (!query.IsInProgress.HasValue)
-            {
-                folderIds.Add(internalLiveTvFolderId);
+            folderIds.Add(internalLiveTvFolderId);
 
 
-                excludeItemTypes.Add(typeof(LiveTvChannel).Name);
-                excludeItemTypes.Add(typeof(LiveTvProgram).Name);
-            }
+            excludeItemTypes.Add(typeof(LiveTvChannel).Name);
+            excludeItemTypes.Add(typeof(LiveTvProgram).Name);
 
 
             if (folderIds.Count == 0)
             if (folderIds.Count == 0)
             {
             {
@@ -1631,6 +1624,19 @@ namespace Emby.Server.Implementations.LiveTv
                 }
                 }
             }
             }
 
 
+            if ((query.IsInProgress ?? false))
+            {
+                // TODO: filter
+                var allActivePaths = EmbyTV.EmbyTV.Current.GetAllActiveRecordings().Select(i => i.Path).ToArray();
+                var items = allActivePaths.Select(i => _libraryManager.FindByPath(i, false)).Where(i => i != null).ToArray();
+
+                return new QueryResult<BaseItem>
+                {
+                    Items = items,
+                    TotalRecordCount = items.Length
+                };
+            }
+
             return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
             return _libraryManager.GetItemsResult(new InternalItemsQuery(user)
             {
             {
                 MediaTypes = new[] { MediaType.Video },
                 MediaTypes = new[] { MediaType.Video },
@@ -1658,11 +1664,6 @@ namespace Emby.Server.Implementations.LiveTv
                 return new QueryResult<BaseItemDto>();
                 return new QueryResult<BaseItemDto>();
             }
             }
 
 
-            if (_services.Count > 1)
-            {
-                return new QueryResult<BaseItemDto>();
-            }
-
             if (user == null || (query.IsInProgress ?? false))
             if (user == null || (query.IsInProgress ?? false))
             {
             {
                 return new QueryResult<BaseItemDto>();
                 return new QueryResult<BaseItemDto>();
@@ -1701,11 +1702,9 @@ namespace Emby.Server.Implementations.LiveTv
                 DtoOptions = options
                 DtoOptions = options
             });
             });
 
 
-            var returnList = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user)
+            var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user)
                 .ConfigureAwait(false));
                 .ConfigureAwait(false));
 
 
-            var returnArray = returnList.ToArray(returnList.Count);
-
             return new QueryResult<BaseItemDto>
             return new QueryResult<BaseItemDto>
             {
             {
                 Items = returnArray,
                 Items = returnArray,
@@ -1723,13 +1722,9 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             var folder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
             var folder = await GetInternalLiveTvFolder(cancellationToken).ConfigureAwait(false);
 
 
-            if (_services.Count == 1 && (!query.IsInProgress.HasValue || !query.IsInProgress.Value) && (!query.IsLibraryItem.HasValue || query.IsLibraryItem.Value))
+            // TODO: Figure out how to merge emby recordings + service recordings
+            if (_services.Length == 1)
             {
             {
-                if (!query.IsInProgress.HasValue)
-                {
-                    await RefreshRecordings(folder.Id, cancellationToken).ConfigureAwait(false);
-                }
-
                 return GetEmbyRecordings(query, options, folder.Id, user);
                 return GetEmbyRecordings(query, options, folder.Id, user);
             }
             }
 
 
@@ -1841,7 +1836,7 @@ namespace Emby.Server.Implementations.LiveTv
             };
             };
         }
         }
 
 
-        public async Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> tuples, List<ItemFields> fields, User user = null)
+        public async Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> tuples, ItemFields[] fields, User user = null)
         {
         {
             var programTuples = new List<Tuple<BaseItemDto, string, string, string>>();
             var programTuples = new List<Tuple<BaseItemDto, string, string, string>>();
             var hasChannelImage = fields.Contains(ItemFields.ChannelImage);
             var hasChannelImage = fields.Contains(ItemFields.ChannelImage);
@@ -1921,6 +1916,11 @@ namespace Emby.Server.Implementations.LiveTv
             await AddRecordingInfo(programTuples, CancellationToken.None).ConfigureAwait(false);
             await AddRecordingInfo(programTuples, CancellationToken.None).ConfigureAwait(false);
         }
         }
 
 
+        public ActiveRecordingInfo GetActiveRecordingInfo(string path)
+        {
+            return EmbyTV.EmbyTV.Current.GetActiveRecordingInfo(path);
+        }
+
         public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, User user = null)
         public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, User user = null)
         {
         {
             var recording = (ILiveTvRecording)item;
             var recording = (ILiveTvRecording)item;
@@ -1950,27 +1950,72 @@ namespace Emby.Server.Implementations.LiveTv
             dto.IsKids = info.IsKids;
             dto.IsKids = info.IsKids;
             dto.IsPremiere = info.IsPremiere;
             dto.IsPremiere = info.IsPremiere;
 
 
-            dto.CanDelete = user == null
-                ? recording.CanDelete()
-                : recording.CanDelete(user);
-
-            if (dto.MediaSources == null)
+            if (info.Status == RecordingStatus.InProgress && info.EndDate.HasValue)
             {
             {
-                dto.MediaSources = recording.GetMediaSources(true);
+                var now = DateTime.UtcNow.Ticks;
+                var start = info.StartDate.Ticks;
+                var end = info.EndDate.Value.Ticks;
+
+                var pct = now - start;
+                pct /= end;
+                pct *= 100;
+                dto.CompletionPercentage = pct;
             }
             }
 
 
-            if (dto.MediaStreams == null)
+            if (channel != null)
             {
             {
-                dto.MediaStreams = dto.MediaSources.SelectMany(i => i.MediaStreams).ToList();
+                dto.ChannelName = channel.Name;
+
+                if (channel.HasImage(ImageType.Primary))
+                {
+                    dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel);
+                }
             }
             }
+        }
 
 
-            if (info.Status == RecordingStatus.InProgress && info.EndDate.HasValue)
+        public void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, User user = null)
+        {
+            var service = EmbyTV.EmbyTV.Current;
+
+            var info = activeRecordingInfo.Timer;
+
+            var channel = string.IsNullOrWhiteSpace(info.ChannelId) ? null : GetInternalChannel(_tvDtoService.GetInternalChannelId(service.Name, info.ChannelId));
+
+            dto.SeriesTimerId = string.IsNullOrEmpty(info.SeriesTimerId)
+                ? null
+                : _tvDtoService.GetInternalSeriesTimerId(service.Name, info.SeriesTimerId).ToString("N");
+
+            dto.TimerId = string.IsNullOrEmpty(info.Id)
+                ? null
+                : _tvDtoService.GetInternalTimerId(service.Name, info.Id).ToString("N");
+
+            var startDate = info.StartDate;
+            var endDate = info.EndDate;
+
+            dto.StartDate = startDate;
+            dto.EndDate = endDate;
+            dto.Status = info.Status.ToString();
+            dto.IsRepeat = info.IsRepeat;
+            dto.EpisodeTitle = info.EpisodeTitle;
+            dto.IsMovie = info.IsMovie;
+            dto.IsSeries = info.IsSeries;
+            dto.IsSports = info.IsSports;
+            dto.IsLive = info.IsLive;
+            dto.IsNews = info.IsNews;
+            dto.IsKids = info.IsKids;
+            dto.IsPremiere = info.IsPremiere;
+
+            if (info.Status == RecordingStatus.InProgress)
             {
             {
+                startDate = info.StartDate.AddSeconds(0 - info.PrePaddingSeconds);
+                endDate = info.EndDate.AddSeconds(info.PostPaddingSeconds);
+
                 var now = DateTime.UtcNow.Ticks;
                 var now = DateTime.UtcNow.Ticks;
-                var start = info.StartDate.Ticks;
-                var end = info.EndDate.Value.Ticks;
+                var start = startDate.Ticks;
+                var end = endDate.Ticks;
 
 
                 var pct = now - start;
                 var pct = now - start;
+
                 pct /= end;
                 pct /= end;
                 pct *= 100;
                 pct *= 100;
                 dto.CompletionPercentage = pct;
                 dto.CompletionPercentage = pct;
@@ -1995,9 +2040,8 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             var internalResult = await GetInternalRecordings(query, options, cancellationToken).ConfigureAwait(false);
             var internalResult = await GetInternalRecordings(query, options, cancellationToken).ConfigureAwait(false);
 
 
-            var returnList = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user)
+            var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user)
                 .ConfigureAwait(false));
                 .ConfigureAwait(false));
-            var returnArray = returnList.ToArray(returnList.Count);
 
 
             return new QueryResult<BaseItemDto>
             return new QueryResult<BaseItemDto>
             {
             {
@@ -2100,7 +2144,6 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             if (service is EmbyTV.EmbyTV)
             if (service is EmbyTV.EmbyTV)
             {
             {
-                // We can't trust that we'll be able to direct stream it through emby server,  no matter what the provider says
                 return service.DeleteRecordingAsync(GetItemExternalId(recording), CancellationToken.None);
                 return service.DeleteRecordingAsync(GetItemExternalId(recording), CancellationToken.None);
             }
             }
 
 
@@ -2350,7 +2393,6 @@ namespace Emby.Server.Implementations.LiveTv
             var currentChannelsDict = new Dictionary<string, BaseItemDto>();
             var currentChannelsDict = new Dictionary<string, BaseItemDto>();
 
 
             var addCurrentProgram = options.AddCurrentProgram;
             var addCurrentProgram = options.AddCurrentProgram;
-            var addMediaSources = options.Fields.Contains(ItemFields.MediaSources);
             var addServiceName = options.Fields.Contains(ItemFields.ServiceName);
             var addServiceName = options.Fields.Contains(ItemFields.ServiceName);
 
 
             foreach (var tuple in tuples)
             foreach (var tuple in tuples)
@@ -2369,11 +2411,6 @@ namespace Emby.Server.Implementations.LiveTv
 
 
                 currentChannelsDict[dto.Id] = dto;
                 currentChannelsDict[dto.Id] = dto;
 
 
-                if (addMediaSources)
-                {
-                    dto.MediaSources = channel.GetMediaSources(true);
-                }
-
                 if (addCurrentProgram)
                 if (addCurrentProgram)
                 {
                 {
                     var channelIdString = channel.Id.ToString("N");
                     var channelIdString = channel.Id.ToString("N");
@@ -2479,7 +2516,7 @@ namespace Emby.Server.Implementations.LiveTv
             var defaults = await GetNewTimerDefaultsInternal(cancellationToken, program).ConfigureAwait(false);
             var defaults = await GetNewTimerDefaultsInternal(cancellationToken, program).ConfigureAwait(false);
             var info = _tvDtoService.GetSeriesTimerInfoDto(defaults.Item1, defaults.Item2, null);
             var info = _tvDtoService.GetSeriesTimerInfoDto(defaults.Item1, defaults.Item2, null);
 
 
-            info.Days = defaults.Item1.Days;
+            info.Days = defaults.Item1.Days.ToArray();
 
 
             info.DayPattern = _tvDtoService.GetDayPattern(info.Days);
             info.DayPattern = _tvDtoService.GetDayPattern(info.Days);
 
 
@@ -2656,8 +2693,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             var series = recordings
             var series = recordings
                 .Where(i => i.IsSeries)
                 .Where(i => i.IsSeries)
-                .ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase)
-                .ToList();
+                .ToLookup(i => i.Name, StringComparer.OrdinalIgnoreCase);
 
 
             groups.AddRange(series.OrderByString(i => i.Key).Select(i => new BaseItemDto
             groups.AddRange(series.OrderByString(i => i.Key).Select(i => new BaseItemDto
             {
             {
@@ -2762,7 +2798,7 @@ namespace Emby.Server.Implementations.LiveTv
             }
             }
         }
         }
 
 
-        private async Task<IEnumerable<LiveTvServiceInfo>> GetServiceInfos(CancellationToken cancellationToken)
+        private async Task<LiveTvServiceInfo[]> GetServiceInfos(CancellationToken cancellationToken)
         {
         {
             var tasks = Services.Select(i => GetServiceInfo(i, cancellationToken));
             var tasks = Services.Select(i => GetServiceInfo(i, cancellationToken));
 
 
@@ -2806,7 +2842,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
                     return dto;
                     return dto;
 
 
-                }).ToList();
+                }).ToArray();
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
@@ -2822,25 +2858,24 @@ namespace Emby.Server.Implementations.LiveTv
         public async Task<LiveTvInfo> GetLiveTvInfo(CancellationToken cancellationToken)
         public async Task<LiveTvInfo> GetLiveTvInfo(CancellationToken cancellationToken)
         {
         {
             var services = await GetServiceInfos(CancellationToken.None).ConfigureAwait(false);
             var services = await GetServiceInfos(CancellationToken.None).ConfigureAwait(false);
-            var servicesList = services.ToList();
 
 
             var info = new LiveTvInfo
             var info = new LiveTvInfo
             {
             {
-                Services = servicesList.ToList(),
-                IsEnabled = servicesList.Count > 0
+                Services = services,
+                IsEnabled = services.Length > 0
             };
             };
 
 
             info.EnabledUsers = _userManager.Users
             info.EnabledUsers = _userManager.Users
                 .Where(IsLiveTvEnabled)
                 .Where(IsLiveTvEnabled)
                 .Select(i => i.Id.ToString("N"))
                 .Select(i => i.Id.ToString("N"))
-                .ToList();
+                .ToArray();
 
 
             return info;
             return info;
         }
         }
 
 
         private bool IsLiveTvEnabled(User user)
         private bool IsLiveTvEnabled(User user)
         {
         {
-            return user.Policy.EnableLiveTvAccess && (Services.Count > 1 || GetConfiguration().TunerHosts.Count > 0);
+            return user.Policy.EnableLiveTvAccess && (Services.Count > 1 || GetConfiguration().TunerHosts.Length > 0);
         }
         }
 
 
         public IEnumerable<User> GetEnabledUsers()
         public IEnumerable<User> GetEnabledUsers()
@@ -2880,10 +2915,13 @@ namespace Emby.Server.Implementations.LiveTv
 
 
         private void RemoveFields(DtoOptions options)
         private void RemoveFields(DtoOptions options)
         {
         {
-            options.Fields.Remove(ItemFields.CanDelete);
-            options.Fields.Remove(ItemFields.CanDownload);
-            options.Fields.Remove(ItemFields.DisplayPreferencesId);
-            options.Fields.Remove(ItemFields.Etag);
+            var fields = options.Fields.ToList();
+
+            fields.Remove(ItemFields.CanDelete);
+            fields.Remove(ItemFields.CanDownload);
+            fields.Remove(ItemFields.DisplayPreferencesId);
+            fields.Remove(ItemFields.Etag);
+            options.Fields = fields.ToArray(fields.Count);
         }
         }
 
 
         public async Task<Folder> GetInternalLiveTvFolder(CancellationToken cancellationToken)
         public async Task<Folder> GetInternalLiveTvFolder(CancellationToken cancellationToken)
@@ -2911,12 +2949,14 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             var config = GetConfiguration();
             var config = GetConfiguration();
 
 
-            var index = config.TunerHosts.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
+            var list = config.TunerHosts.ToList();
+            var index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
 
 
             if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
             if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
             {
             {
                 info.Id = Guid.NewGuid().ToString("N");
                 info.Id = Guid.NewGuid().ToString("N");
-                config.TunerHosts.Add(info);
+                list.Add(info);
+                config.TunerHosts = list.ToArray(list.Count);
             }
             }
             else
             else
             {
             {
@@ -2948,12 +2988,14 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             var config = GetConfiguration();
             var config = GetConfiguration();
 
 
-            var index = config.ListingProviders.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
+            var list = config.ListingProviders.ToList();
+            var index = list.FindIndex(i => string.Equals(i.Id, info.Id, StringComparison.OrdinalIgnoreCase));
 
 
             if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
             if (index == -1 || string.IsNullOrWhiteSpace(info.Id))
             {
             {
                 info.Id = Guid.NewGuid().ToString("N");
                 info.Id = Guid.NewGuid().ToString("N");
-                config.ListingProviders.Add(info);
+                list.Add(info);
+                config.ListingProviders = list.ToArray(list.Count);
                 info.EnableNewProgramIds = true;
                 info.EnableNewProgramIds = true;
             }
             }
             else
             else
@@ -2972,7 +3014,7 @@ namespace Emby.Server.Implementations.LiveTv
         {
         {
             var config = GetConfiguration();
             var config = GetConfiguration();
 
 
-            config.ListingProviders = config.ListingProviders.Where(i => !string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase)).ToList();
+            config.ListingProviders = config.ListingProviders.Where(i => !string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray();
 
 
             _config.SaveConfiguration("livetv", config);
             _config.SaveConfiguration("livetv", config);
             _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
             _taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
@@ -3004,7 +3046,7 @@ namespace Emby.Server.Implementations.LiveTv
             var providerChannels = await GetChannelsFromListingsProviderData(providerId, CancellationToken.None)
             var providerChannels = await GetChannelsFromListingsProviderData(providerId, CancellationToken.None)
                      .ConfigureAwait(false);
                      .ConfigureAwait(false);
 
 
-            var mappings = listingsProviderInfo.ChannelMappings.ToList();
+            var mappings = listingsProviderInfo.ChannelMappings;
 
 
             var tunerChannelMappings =
             var tunerChannelMappings =
                 tunerChannels.Select(i => GetTunerChannelMapping(i, mappings, providerChannels)).ToList();
                 tunerChannels.Select(i => GetTunerChannelMapping(i, mappings, providerChannels)).ToList();
@@ -3014,7 +3056,7 @@ namespace Emby.Server.Implementations.LiveTv
             return tunerChannelMappings.First(i => string.Equals(i.Id, tunerChannelId, StringComparison.OrdinalIgnoreCase));
             return tunerChannelMappings.First(i => string.Equals(i.Id, tunerChannelId, StringComparison.OrdinalIgnoreCase));
         }
         }
 
 
-        public TunerChannelMapping GetTunerChannelMapping(ChannelInfo tunerChannel, List<NameValuePair> mappings, List<ChannelInfo> epgChannels)
+        public TunerChannelMapping GetTunerChannelMapping(ChannelInfo tunerChannel, NameValuePair[] mappings, List<ChannelInfo> epgChannels)
         {
         {
             var result = new TunerChannelMapping
             var result = new TunerChannelMapping
             {
             {
@@ -3078,7 +3120,7 @@ namespace Emby.Server.Implementations.LiveTv
             if (string.Equals(feature, "dvr-l", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(feature, "dvr-l", StringComparison.OrdinalIgnoreCase))
             {
             {
                 var config = GetConfiguration();
                 var config = GetConfiguration();
-                if (config.TunerHosts.Count > 0 &&
+                if (config.TunerHosts.Length > 0 &&
                     config.ListingProviders.Count(i => (i.EnableAllTuners || i.EnabledTuners.Length > 0) && string.Equals(i.Type, SchedulesDirect.TypeName, StringComparison.OrdinalIgnoreCase)) > 0)
                     config.ListingProviders.Count(i => (i.EnableAllTuners || i.EnabledTuners.Length > 0) && string.Equals(i.Type, SchedulesDirect.TypeName, StringComparison.OrdinalIgnoreCase)) > 0)
                 {
                 {
                     return Task.FromResult(new MBRegistrationRecord
                     return Task.FromResult(new MBRegistrationRecord

+ 16 - 6
Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs

@@ -43,9 +43,11 @@ namespace Emby.Server.Implementations.LiveTv
 
 
             if (baseItem.SourceType == SourceType.LiveTV)
             if (baseItem.SourceType == SourceType.LiveTV)
             {
             {
-                if (string.IsNullOrWhiteSpace(baseItem.Path))
+                var activeRecordingInfo = _liveTvManager.GetActiveRecordingInfo(item.Path);
+
+                if (string.IsNullOrWhiteSpace(baseItem.Path) || activeRecordingInfo != null)
                 {
                 {
-                    return GetMediaSourcesInternal(item, cancellationToken);
+                    return GetMediaSourcesInternal(item, activeRecordingInfo, cancellationToken);
                 }
                 }
             }
             }
 
 
@@ -56,7 +58,7 @@ namespace Emby.Server.Implementations.LiveTv
         private const char StreamIdDelimeter = '_';
         private const char StreamIdDelimeter = '_';
         private const string StreamIdDelimeterString = "_";
         private const string StreamIdDelimeterString = "_";
 
 
-        private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(IHasMediaSources item, CancellationToken cancellationToken)
+        private async Task<IEnumerable<MediaSourceInfo>> GetMediaSourcesInternal(IHasMediaSources item, ActiveRecordingInfo activeRecordingInfo, CancellationToken cancellationToken)
         {
         {
             IEnumerable<MediaSourceInfo> sources;
             IEnumerable<MediaSourceInfo> sources;
 
 
@@ -67,12 +69,20 @@ namespace Emby.Server.Implementations.LiveTv
                 if (item is ILiveTvRecording)
                 if (item is ILiveTvRecording)
                 {
                 {
                     sources = await _liveTvManager.GetRecordingMediaSources(item, cancellationToken)
                     sources = await _liveTvManager.GetRecordingMediaSources(item, cancellationToken)
-                                .ConfigureAwait(false);
+                        .ConfigureAwait(false);
                 }
                 }
                 else
                 else
                 {
                 {
-                    sources = await _liveTvManager.GetChannelMediaSources(item, cancellationToken)
-                                .ConfigureAwait(false);
+                    if (activeRecordingInfo != null)
+                    {
+                        sources = await EmbyTV.EmbyTV.Current.GetRecordingStreamMediaSources(activeRecordingInfo, cancellationToken)
+                            .ConfigureAwait(false);
+                    }
+                    else
+                    {
+                        sources = await _liveTvManager.GetChannelMediaSources(item, cancellationToken)
+                            .ConfigureAwait(false);
+                    }
                 }
                 }
             }
             }
             catch (NotImplementedException)
             catch (NotImplementedException)

+ 1 - 1
Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs

@@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.LiveTv
 
 
         public bool IsHidden
         public bool IsHidden
         {
         {
-            get { return _liveTvManager.Services.Count == 1 && GetConfiguration().TunerHosts.Count == 0; }
+            get { return _liveTvManager.Services.Count == 1 && GetConfiguration().TunerHosts.Length == 0; }
         }
         }
 
 
         public bool IsEnabled
         public bool IsEnabled

+ 71 - 57
Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -25,12 +25,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
         private readonly IServerApplicationHost _appHost;
         private readonly IServerApplicationHost _appHost;
         private readonly IEnvironmentInfo _environment;
         private readonly IEnvironmentInfo _environment;
+        private readonly INetworkManager _networkManager;
 
 
-        public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, IEnvironmentInfo environment) : base(config, logger, jsonSerializer, mediaEncoder, fileSystem)
+        public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost, IEnvironmentInfo environment, INetworkManager networkManager) : base(config, logger, jsonSerializer, mediaEncoder, fileSystem)
         {
         {
             _httpClient = httpClient;
             _httpClient = httpClient;
             _appHost = appHost;
             _appHost = appHost;
             _environment = environment;
             _environment = environment;
+            _networkManager = networkManager;
         }
         }
 
 
         public override string Type
         public override string Type
@@ -38,7 +40,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             get { return "m3u"; }
             get { return "m3u"; }
         }
         }
 
 
-        public string Name
+        public virtual string Name
         {
         {
             get { return "M3U Tuner"; }
             get { return "M3U Tuner"; }
         }
         }
@@ -99,72 +101,84 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             }
             }
 
 
             var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false);
             var channels = await GetChannels(info, true, cancellationToken).ConfigureAwait(false);
-            var m3uchannels = channels.Cast<M3UChannel>();
-            var channel = m3uchannels.FirstOrDefault(c => string.Equals(c.Id, channelId, StringComparison.OrdinalIgnoreCase));
+            var channel = channels.FirstOrDefault(c => string.Equals(c.Id, channelId, StringComparison.OrdinalIgnoreCase));
             if (channel != null)
             if (channel != null)
             {
             {
-                var path = channel.Path;
-                MediaProtocol protocol = MediaProtocol.File;
-                if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
-                {
-                    protocol = MediaProtocol.Http;
-                }
-                else if (path.StartsWith("rtmp", StringComparison.OrdinalIgnoreCase))
-                {
-                    protocol = MediaProtocol.Rtmp;
-                }
-                else if (path.StartsWith("rtsp", StringComparison.OrdinalIgnoreCase))
-                {
-                    protocol = MediaProtocol.Rtsp;
-                }
-                else if (path.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
-                {
-                    protocol = MediaProtocol.Udp;
-                }
-                else if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase))
-                {
-                    protocol = MediaProtocol.Rtmp;
-                }
+                return new List<MediaSourceInfo> { CreateMediaSourceInfo(info, channel) };
+            }
+            return new List<MediaSourceInfo>();
+        }
+
+        protected virtual MediaSourceInfo CreateMediaSourceInfo(TunerHostInfo info, ChannelInfo channel)
+        {
+            var path = channel.Path;
+            MediaProtocol protocol = MediaProtocol.File;
+            if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+            {
+                protocol = MediaProtocol.Http;
+            }
+            else if (path.StartsWith("rtmp", StringComparison.OrdinalIgnoreCase))
+            {
+                protocol = MediaProtocol.Rtmp;
+            }
+            else if (path.StartsWith("rtsp", StringComparison.OrdinalIgnoreCase))
+            {
+                protocol = MediaProtocol.Rtsp;
+            }
+            else if (path.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
+            {
+                protocol = MediaProtocol.Udp;
+            }
+            else if (path.StartsWith("rtp", StringComparison.OrdinalIgnoreCase))
+            {
+                protocol = MediaProtocol.Rtmp;
+            }
+
+            Uri uri;
+            var isRemote = true;
+            if (Uri.TryCreate(path, UriKind.Absolute, out uri))
+            {
+                isRemote = !_networkManager.IsInLocalNetwork(uri.Host);
+            }
 
 
-                var mediaSource = new MediaSourceInfo
+            var mediaSource = new MediaSourceInfo
+            {
+                Path = path,
+                Protocol = protocol,
+                MediaStreams = new List<MediaStream>
                 {
                 {
-                    Path = channel.Path,
-                    Protocol = protocol,
-                    MediaStreams = new List<MediaStream>
+                    new MediaStream
                     {
                     {
-                        new MediaStream
-                        {
-                            Type = MediaStreamType.Video,
-                            // Set the index to -1 because we don't know the exact index of the video stream within the container
-                            Index = -1,
-                            IsInterlaced = true
-                        },
-                        new MediaStream
-                        {
-                            Type = MediaStreamType.Audio,
-                            // Set the index to -1 because we don't know the exact index of the audio stream within the container
-                            Index = -1
-
-                        }
+                        Type = MediaStreamType.Video,
+                        // Set the index to -1 because we don't know the exact index of the video stream within the container
+                        Index = -1,
+                        IsInterlaced = true
                     },
                     },
-                    RequiresOpening = true,
-                    RequiresClosing = true,
-                    RequiresLooping = info.EnableStreamLooping,
+                    new MediaStream
+                    {
+                        Type = MediaStreamType.Audio,
+                        // Set the index to -1 because we don't know the exact index of the audio stream within the container
+                        Index = -1
 
 
-                    ReadAtNativeFramerate = false,
+                    }
+                },
+                RequiresOpening = true,
+                RequiresClosing = true,
+                RequiresLooping = info.EnableStreamLooping,
+                EnableMpDecimate = info.EnableMpDecimate,
 
 
-                    Id = channel.Path.GetMD5().ToString("N"),
-                    IsInfiniteStream = true,
-                    IsRemote = true,
+                ReadAtNativeFramerate = false,
 
 
-                    IgnoreDts = true
-                };
+                Id = channel.Path.GetMD5().ToString("N"),
+                IsInfiniteStream = true,
+                IsRemote = isRemote,
 
 
-                mediaSource.InferTotalBitrate();
+                IgnoreDts = true
+            };
 
 
-                return new List<MediaSourceInfo> { mediaSource };
-            }
-            return new List<MediaSourceInfo>();
+            mediaSource.InferTotalBitrate();
+
+            return mediaSource;
         }
         }
 
 
         protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)
         protected override Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken)

+ 6 - 12
Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs

@@ -32,7 +32,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             _appHost = appHost;
             _appHost = appHost;
         }
         }
 
 
-        public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken)
+        public async Task<List<ChannelInfo>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken)
         {
         {
             // Read the file and display it line by line.
             // Read the file and display it line by line.
             using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false)))
             using (var reader = new StreamReader(await GetListingsStream(url, cancellationToken).ConfigureAwait(false)))
@@ -41,7 +41,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             }
             }
         }
         }
 
 
-        public List<M3UChannel> ParseString(string text, string channelIdPrefix, string tunerHostId)
+        public List<ChannelInfo> ParseString(string text, string channelIdPrefix, string tunerHostId)
         {
         {
             // Read the file and display it line by line.
             // Read the file and display it line by line.
             using (var reader = new StringReader(text))
             using (var reader = new StringReader(text))
@@ -66,9 +66,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
         }
         }
 
 
         const string ExtInfPrefix = "#EXTINF:";
         const string ExtInfPrefix = "#EXTINF:";
-        private List<M3UChannel> GetChannels(TextReader reader, string channelIdPrefix, string tunerHostId)
+        private List<ChannelInfo> GetChannels(TextReader reader, string channelIdPrefix, string tunerHostId)
         {
         {
-            var channels = new List<M3UChannel>();
+            var channels = new List<ChannelInfo>();
             string line;
             string line;
             string extInf = "";
             string extInf = "";
 
 
@@ -111,9 +111,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             return channels;
             return channels;
         }
         }
 
 
-        private M3UChannel GetChannelnfo(string extInf, string tunerHostId, string mediaUrl)
+        private ChannelInfo GetChannelnfo(string extInf, string tunerHostId, string mediaUrl)
         {
         {
-            var channel = new M3UChannel();
+            var channel = new ChannelInfo();
             channel.TunerHostId = tunerHostId;
             channel.TunerHostId = tunerHostId;
 
 
             extInf = extInf.Trim();
             extInf = extInf.Trim();
@@ -335,10 +335,4 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             return dict;
             return dict;
         }
         }
     }
     }
-
-
-    public class M3UChannel : ChannelInfo
-    {
-        public string Path { get; set; }
-    }
 }
 }

+ 16 - 34
Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs

@@ -24,8 +24,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
 
         public async Task CopyUntilCancelled(Stream source, Action onStarted, CancellationToken cancellationToken)
         public async Task CopyUntilCancelled(Stream source, Action onStarted, CancellationToken cancellationToken)
         {
         {
-            byte[] buffer = new byte[BufferSize];
-
             if (source == null)
             if (source == null)
             {
             {
                 throw new ArgumentNullException("source");
                 throw new ArgumentNullException("source");
@@ -35,25 +33,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             {
             {
                 cancellationToken.ThrowIfCancellationRequested();
                 cancellationToken.ThrowIfCancellationRequested();
 
 
+                byte[] buffer = new byte[BufferSize];
+
                 var bytesRead = source.Read(buffer, 0, buffer.Length);
                 var bytesRead = source.Read(buffer, 0, buffer.Length);
 
 
                 if (bytesRead > 0)
                 if (bytesRead > 0)
                 {
                 {
-                    var allStreams = _outputStreams.ToList();
-
-                    //if (allStreams.Count == 1)
-                    //{
-                    //    allStreams[0].Value.Write(buffer, 0, bytesRead);
-                    //}
-                    //else
+                    foreach (var stream in _outputStreams)
                     {
                     {
-                        byte[] copy = new byte[bytesRead];
-                        Buffer.BlockCopy(buffer, 0, copy, 0, bytesRead);
-
-                        foreach (var stream in allStreams)
-                        {
-                            stream.Value.Queue(copy, 0, copy.Length);
-                        }
+                        stream.Value.Queue(buffer, 0, bytesRead);
                     }
                     }
 
 
                     if (onStarted != null)
                     if (onStarted != null)
@@ -73,27 +61,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
 
 
         public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
         public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
         {
         {
-            var result = new QueueStream(stream, _logger)
-            {
-                OnFinished = OnFinished
-            };
-
-            _outputStreams.TryAdd(result.Id, result);
+            var queueStream = new QueueStream(stream, _logger);
 
 
-            result.Start(cancellationToken);
-
-            return result.TaskCompletion.Task;
-        }
+            _outputStreams.TryAdd(queueStream.Id, queueStream);
 
 
-        public void RemoveOutputStream(QueueStream stream)
-        {
-            QueueStream removed;
-            _outputStreams.TryRemove(stream.Id, out removed);
-        }
+            try
+            {
+                queueStream.Start(cancellationToken);
+            }
+            finally
+            {
+                _outputStreams.TryRemove(queueStream.Id, out queueStream);
+                GC.Collect();
+            }
 
 
-        private void OnFinished(QueueStream queueStream)
-        {
-            RemoveOutputStream(queueStream);
+            return Task.FromResult(true);
         }
         }
     }
     }
 }
 }

+ 5 - 77
Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs

@@ -13,10 +13,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
     public class QueueStream
     public class QueueStream
     {
     {
         private readonly Stream _outputStream;
         private readonly Stream _outputStream;
-        private readonly ConcurrentQueue<Tuple<byte[], int, int>> _queue = new ConcurrentQueue<Tuple<byte[], int, int>>();
-        public TaskCompletionSource<bool> TaskCompletion { get; private set; }
+        private readonly BlockingCollection<Tuple<byte[], int, int>> _queue = new BlockingCollection<Tuple<byte[], int, int>>();
 
 
-        public Action<QueueStream> OnFinished { get; set; }
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         public Guid Id = Guid.NewGuid();
         public Guid Id = Guid.NewGuid();
 
 
@@ -24,94 +22,24 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
         {
         {
             _outputStream = outputStream;
             _outputStream = outputStream;
             _logger = logger;
             _logger = logger;
-            TaskCompletion = new TaskCompletionSource<bool>();
         }
         }
 
 
         public void Queue(byte[] bytes, int offset, int count)
         public void Queue(byte[] bytes, int offset, int count)
         {
         {
-            _queue.Enqueue(new Tuple<byte[], int, int>(bytes, offset, count));
+            _queue.Add(new Tuple<byte[], int, int>(bytes, offset, count));
         }
         }
 
 
         public void Start(CancellationToken cancellationToken)
         public void Start(CancellationToken cancellationToken)
         {
         {
-            Task.Run(() => StartInternal(cancellationToken));
-        }
-
-        private Tuple<byte[], int, int> Dequeue()
-        {
-            Tuple<byte[], int, int> result;
-            if (_queue.TryDequeue(out result))
-            {
-                return result;
-            }
-
-            return null;
-        }
-
-        private void OnClosed()
-        {
-            GC.Collect();
-            if (OnFinished != null)
-            {
-                OnFinished(this);
-            }
-        }
-
-        public void Write(byte[] bytes, int offset, int count)
-        {
-            //return _outputStream.WriteAsync(bytes, offset, count, cancellationToken);
-
-            try
+            while (true)
             {
             {
-                _outputStream.Write(bytes, offset, count);
-            }
-            catch (OperationCanceledException)
-            {
-                _logger.Debug("QueueStream cancelled");
-                TaskCompletion.TrySetCanceled();
-                OnClosed();
-            }
-            catch (Exception ex)
-            {
-                _logger.ErrorException("Error in QueueStream", ex);
-                TaskCompletion.TrySetException(ex);
-                OnClosed();
-            }
-        }
-
-        private async Task StartInternal(CancellationToken cancellationToken)
-        {
-            try
-            {
-                while (true)
+                foreach (var result in _queue.GetConsumingEnumerable())
                 {
                 {
                     cancellationToken.ThrowIfCancellationRequested();
                     cancellationToken.ThrowIfCancellationRequested();
 
 
-                    var result = Dequeue();
-                    if (result != null)
-                    {
-                        _outputStream.Write(result.Item1, result.Item2, result.Item3);
-                    }
-                    else
-                    {
-                        await Task.Delay(50, cancellationToken).ConfigureAwait(false);
-                    }
+                    _outputStream.Write(result.Item1, result.Item2, result.Item3);
                 }
                 }
             }
             }
-            catch (OperationCanceledException)
-            {
-                _logger.Debug("QueueStream cancelled");
-                TaskCompletion.TrySetCanceled();
-            }
-            catch (Exception ex)
-            {
-                _logger.ErrorException("Error in QueueStream", ex);
-                TaskCompletion.TrySetException(ex);
-            }
-            finally
-            {
-                OnClosed();
-            }
         }
         }
     }
     }
 }
 }

+ 0 - 179
Emby.Server.Implementations/Localization/Core/core.json

@@ -1,179 +0,0 @@
-{
-    "AppDeviceValues": "App: {0}, Device: {1}",
-    "UserDownloadingItemWithValues": "{0} is downloading {1}",
-    "FolderTypeMixed": "Mixed content",
-    "FolderTypeMovies": "Movies",
-    "FolderTypeMusic": "Music",
-    "FolderTypeAdultVideos": "Adult videos",
-    "FolderTypePhotos": "Photos",
-    "FolderTypeMusicVideos": "Music videos",
-    "FolderTypeHomeVideos": "Home videos",
-    "FolderTypeGames": "Games",
-    "FolderTypeBooks": "Books",
-    "FolderTypeTvShows": "TV",
-    "FolderTypeInherit": "Inherit",
-    "HeaderCastCrew": "Cast & Crew",
-    "HeaderPeople": "People",
-    "ValueSpecialEpisodeName": "Special - {0}",
-    "LabelChapterName": "Chapter {0}",
-    "NameSeasonNumber": "Season {0}",
-    "LabelExit": "Exit",
-    "LabelVisitCommunity": "Visit Community",
-    "LabelGithub": "Github",
-    "LabelApiDocumentation": "Api Documentation",
-    "LabelDeveloperResources": "Developer Resources",
-    "LabelBrowseLibrary": "Browse Library",
-    "LabelConfigureServer": "Configure Emby",
-    "LabelRestartServer": "Restart Server",
-    "CategorySync": "Sync",
-    "CategoryUser": "User",
-    "CategorySystem": "System",
-    "CategoryApplication": "Application",
-    "CategoryPlugin": "Plugin",
-    "NotificationOptionPluginError": "Plugin failure",
-    "NotificationOptionApplicationUpdateAvailable": "Application update available",
-    "NotificationOptionApplicationUpdateInstalled": "Application update installed",
-    "NotificationOptionPluginUpdateInstalled": "Plugin update installed",
-    "NotificationOptionPluginInstalled": "Plugin installed",
-    "NotificationOptionPluginUninstalled": "Plugin uninstalled",
-    "NotificationOptionVideoPlayback": "Video playback started",
-    "NotificationOptionAudioPlayback": "Audio playback started",
-    "NotificationOptionGamePlayback": "Game playback started",
-    "NotificationOptionVideoPlaybackStopped": "Video playback stopped",
-    "NotificationOptionAudioPlaybackStopped": "Audio playback stopped",
-    "NotificationOptionGamePlaybackStopped": "Game playback stopped",
-    "NotificationOptionTaskFailed": "Scheduled task failure",
-    "NotificationOptionInstallationFailed": "Installation failure",
-    "NotificationOptionNewLibraryContent": "New content added",
-    "NotificationOptionNewLibraryContentMultiple": "New content added (multiple)",
-    "NotificationOptionCameraImageUploaded": "Camera image uploaded",
-    "NotificationOptionUserLockedOut": "User locked out",
-    "NotificationOptionServerRestartRequired": "Server restart required",
-    "ViewTypePlaylists": "Playlists",
-    "ViewTypeMovies": "Movies",
-    "ViewTypeTvShows": "TV",
-    "ViewTypeGames": "Games",
-    "ViewTypeMusic": "Music",
-    "ViewTypeMusicGenres": "Genres",
-    "ViewTypeMusicArtists": "Artists",
-    "ViewTypeBoxSets": "Collections",
-    "ViewTypeChannels": "Channels",
-    "ViewTypeLiveTV": "Live TV",
-    "ViewTypeLiveTvNowPlaying": "Now Airing",
-    "ViewTypeLatestGames": "Latest Games",
-    "ViewTypeRecentlyPlayedGames": "Recently Played",
-    "ViewTypeGameFavorites": "Favorites",
-    "ViewTypeGameSystems": "Game Systems",
-    "ViewTypeGameGenres": "Genres",
-    "ViewTypeTvResume": "Resume",
-    "ViewTypeTvNextUp": "Next Up",
-    "ViewTypeTvLatest": "Latest",
-    "ViewTypeTvShowSeries": "Series",
-    "ViewTypeTvGenres": "Genres",
-    "ViewTypeTvFavoriteSeries": "Favorite Series",
-    "ViewTypeTvFavoriteEpisodes": "Favorite Episodes",
-    "ViewTypeMovieResume": "Resume",
-    "ViewTypeMovieLatest": "Latest",
-    "ViewTypeMovieMovies": "Movies",
-    "ViewTypeMovieCollections": "Collections",
-    "ViewTypeMovieFavorites": "Favorites",
-    "ViewTypeMovieGenres": "Genres",
-    "ViewTypeMusicLatest": "Latest",
-    "ViewTypeMusicPlaylists": "Playlists",
-    "ViewTypeMusicAlbums": "Albums",
-    "ViewTypeMusicAlbumArtists": "Album Artists",
-    "HeaderOtherDisplaySettings": "Display Settings",
-    "ViewTypeMusicSongs": "Songs",
-    "ViewTypeMusicFavorites": "Favorites",
-    "ViewTypeMusicFavoriteAlbums": "Favorite Albums",
-    "ViewTypeMusicFavoriteArtists": "Favorite Artists",
-    "ViewTypeMusicFavoriteSongs": "Favorite Songs",
-    "ViewTypeFolders": "Folders",
-    "ViewTypeLiveTvRecordingGroups": "Recordings",
-    "ViewTypeLiveTvChannels": "Channels",
-    "ScheduledTaskFailedWithName": "{0} failed",
-    "LabelRunningTimeValue": "Running time: {0}",
-    "ScheduledTaskStartedWithName": "{0} started",
-    "VersionNumber": "Version {0}",
-    "PluginInstalledWithName": "{0} was installed",
-    "PluginUpdatedWithName": "{0} was updated",
-    "PluginUninstalledWithName": "{0} was uninstalled",
-    "ItemAddedWithName": "{0} was added to the library",
-    "ItemRemovedWithName": "{0} was removed from the library",
-    "LabelIpAddressValue": "Ip address: {0}",
-    "DeviceOnlineWithName": "{0} is connected",
-    "UserOnlineFromDevice": "{0} is online from {1}",
-    "ProviderValue": "Provider: {0}",
-    "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
-    "UserConfigurationUpdatedWithName": "User configuration has been updated for {0}",
-    "UserCreatedWithName": "User {0} has been created",
-    "UserPasswordChangedWithName": "Password has been changed for user {0}",
-    "UserDeletedWithName": "User {0} has been deleted",
-    "MessageServerConfigurationUpdated": "Server configuration has been updated",
-    "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
-    "MessageApplicationUpdated": "Emby Server has been updated",
-    "FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
-    "AuthenticationSucceededWithUserName": "{0} successfully authenticated",
-    "DeviceOfflineWithName": "{0} has disconnected",
-    "UserLockedOutWithName": "User {0} has been locked out",
-    "UserOfflineFromDevice": "{0} has disconnected from {1}",
-    "UserStartedPlayingItemWithValues": "{0} has started playing {1}",
-    "UserStoppedPlayingItemWithValues": "{0} has stopped playing {1}",
-    "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
-    "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
-    "HeaderUnidentified": "Unidentified",
-    "HeaderImagePrimary": "Primary",
-    "HeaderImageBackdrop": "Backdrop",
-    "HeaderImageLogo": "Logo",
-    "HeaderUserPrimaryImage": "User Image",
-    "HeaderOverview": "Overview",
-    "HeaderShortOverview": "Short Overview",
-    "HeaderType": "Type",
-    "HeaderSeverity": "Severity",
-    "HeaderUser": "User",
-    "HeaderName": "Name",
-    "HeaderDate": "Date",
-    "HeaderPremiereDate": "Premiere Date",
-    "HeaderDateAdded": "Date Added",
-    "HeaderReleaseDate": "Release date",
-    "HeaderRuntime": "Runtime",
-    "HeaderPlayCount": "Play Count",
-    "HeaderSeason": "Season",
-    "HeaderSeasonNumber": "Season number",
-    "HeaderSeries": "Series:",
-    "HeaderNetwork": "Network",
-    "HeaderYear": "Year:",
-    "HeaderYears": "Years:",
-    "HeaderParentalRating": "Parental Rating",
-    "HeaderCommunityRating": "Community rating",
-    "HeaderTrailers": "Trailers",
-    "HeaderSpecials": "Specials",
-    "HeaderGameSystems": "Game Systems",
-    "HeaderPlayers": "Players:",
-    "HeaderAlbumArtists": "Album Artists",
-    "HeaderAlbums": "Albums",
-    "HeaderDisc": "Disc",
-    "HeaderTrack": "Track",
-    "HeaderAudio": "Audio",
-    "HeaderVideo": "Video",
-    "HeaderEmbeddedImage": "Embedded image",
-    "HeaderResolution": "Resolution",
-    "HeaderSubtitles": "Subtitles",
-    "HeaderGenres": "Genres",
-    "HeaderCountries": "Countries",
-    "HeaderStatus": "Status",
-    "HeaderTracks": "Tracks",
-    "HeaderMusicArtist": "Music artist",
-    "HeaderLocked": "Locked",
-    "HeaderStudios": "Studios",
-    "HeaderActor": "Actors",
-    "HeaderComposer": "Composers",
-    "HeaderDirector": "Directors",
-    "HeaderGuestStar": "Guest star",
-    "HeaderProducer": "Producers",
-    "HeaderWriter": "Writers",
-    "HeaderParentalRatings": "Parental Ratings",
-    "HeaderCommunityRatings": "Community ratings",
-    "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly.",
-    "DbUpgradeMessage":  "Please wait while your Emby Server database is upgraded. {0}% complete."
-}

+ 0 - 14
Emby.Server.Implementations/Localization/Core/en-US.json

@@ -1,5 +1,4 @@
 {
 {
-  "DbUpgradeMessage": "Please wait while your Emby Server database is upgraded. {0}% complete.",
   "AppDeviceValues": "App: {0}, Device: {1}",
   "AppDeviceValues": "App: {0}, Device: {1}",
   "UserDownloadingItemWithValues": "{0} is downloading {1}",
   "UserDownloadingItemWithValues": "{0} is downloading {1}",
   "FolderTypeMixed": "Mixed content",
   "FolderTypeMixed": "Mixed content",
@@ -13,15 +12,12 @@
   "FolderTypeBooks": "Books",
   "FolderTypeBooks": "Books",
   "FolderTypeTvShows": "TV",
   "FolderTypeTvShows": "TV",
   "FolderTypeInherit": "Inherit",
   "FolderTypeInherit": "Inherit",
-  "HeaderCastCrew": "Cast & Crew",
-  "HeaderPeople": "People",
   "ValueSpecialEpisodeName": "Special - {0}",
   "ValueSpecialEpisodeName": "Special - {0}",
   "LabelChapterName": "Chapter {0}",
   "LabelChapterName": "Chapter {0}",
   "NameSeasonUnknown": "Season Unknown",
   "NameSeasonUnknown": "Season Unknown",
   "NameSeasonNumber": "Season {0}",
   "NameSeasonNumber": "Season {0}",
   "LabelExit": "Exit",
   "LabelExit": "Exit",
   "LabelVisitCommunity": "Visit Community",
   "LabelVisitCommunity": "Visit Community",
-  "LabelGithub": "Github",
   "LabelApiDocumentation": "Api Documentation",
   "LabelApiDocumentation": "Api Documentation",
   "LabelDeveloperResources": "Developer Resources",
   "LabelDeveloperResources": "Developer Resources",
   "LabelBrowseLibrary": "Browse Library",
   "LabelBrowseLibrary": "Browse Library",
@@ -164,15 +160,5 @@
   "HeaderStatus": "Status",
   "HeaderStatus": "Status",
   "HeaderTracks": "Tracks",
   "HeaderTracks": "Tracks",
   "HeaderMusicArtist": "Music artist",
   "HeaderMusicArtist": "Music artist",
-  "HeaderLocked": "Locked",
-  "HeaderStudios": "Studios",
-  "HeaderActor": "Actors",
-  "HeaderComposer": "Composers",
-  "HeaderDirector": "Directors",
-  "HeaderGuestStar": "Guest star",
-  "HeaderProducer": "Producers",
-  "HeaderWriter": "Writers",
-  "HeaderParentalRatings": "Parental Ratings",
-  "HeaderCommunityRatings": "Community ratings",
   "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly."
   "StartupEmbyServerIsLoading": "Emby Server is loading. Please try again shortly."
 }
 }

+ 10 - 10
Emby.Server.Implementations/Localization/LocalizationManager.cs

@@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.Localization
         /// Gets the cultures.
         /// Gets the cultures.
         /// </summary>
         /// </summary>
         /// <returns>IEnumerable{CultureDto}.</returns>
         /// <returns>IEnumerable{CultureDto}.</returns>
-        public List<CultureDto> GetCultures()
+        public CultureDto[] GetCultures()
         {
         {
             var type = GetType();
             var type = GetType();
             var path = type.Namespace + ".iso6392.txt";
             var path = type.Namespace + ".iso6392.txt";
@@ -169,21 +169,21 @@ namespace Emby.Server.Implementations.Localization
             return list.Where(i => !string.IsNullOrWhiteSpace(i.Name) &&
             return list.Where(i => !string.IsNullOrWhiteSpace(i.Name) &&
                 !string.IsNullOrWhiteSpace(i.DisplayName) &&
                 !string.IsNullOrWhiteSpace(i.DisplayName) &&
                 !string.IsNullOrWhiteSpace(i.ThreeLetterISOLanguageName) &&
                 !string.IsNullOrWhiteSpace(i.ThreeLetterISOLanguageName) &&
-                !string.IsNullOrWhiteSpace(i.TwoLetterISOLanguageName)).ToList();
+                !string.IsNullOrWhiteSpace(i.TwoLetterISOLanguageName)).ToArray();
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets the countries.
         /// Gets the countries.
         /// </summary>
         /// </summary>
         /// <returns>IEnumerable{CountryInfo}.</returns>
         /// <returns>IEnumerable{CountryInfo}.</returns>
-        public List<CountryInfo> GetCountries()
+        public CountryInfo[] GetCountries()
         {
         {
             var type = GetType();
             var type = GetType();
             var path = type.Namespace + ".countries.json";
             var path = type.Namespace + ".countries.json";
 
 
             using (var stream = _assemblyInfo.GetManifestResourceStream(type, path))
             using (var stream = _assemblyInfo.GetManifestResourceStream(type, path))
             {
             {
-                return _jsonSerializer.DeserializeFromStream<List<CountryInfo>>(stream);
+                return _jsonSerializer.DeserializeFromStream<CountryInfo[]>(stream);
             }
             }
         }
         }
 
 
@@ -191,9 +191,9 @@ namespace Emby.Server.Implementations.Localization
         /// Gets the parental ratings.
         /// Gets the parental ratings.
         /// </summary>
         /// </summary>
         /// <returns>IEnumerable{ParentalRating}.</returns>
         /// <returns>IEnumerable{ParentalRating}.</returns>
-        public IEnumerable<ParentalRating> GetParentalRatings()
+        public ParentalRating[] GetParentalRatings()
         {
         {
-            return GetParentalRatingsDictionary().Values.ToList();
+            return GetParentalRatingsDictionary().Values.ToArray();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -335,7 +335,7 @@ namespace Emby.Server.Implementations.Localization
             const string prefix = "Core";
             const string prefix = "Core";
             var key = prefix + culture;
             var key = prefix + culture;
 
 
-            return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "core.json"));
+            return _dictionaries.GetOrAdd(key, k => GetDictionary(prefix, culture, "en-US.json"));
         }
         }
 
 
         private Dictionary<string, string> GetDictionary(string prefix, string culture, string baseFilename)
         private Dictionary<string, string> GetDictionary(string prefix, string culture, string baseFilename)
@@ -382,9 +382,9 @@ namespace Emby.Server.Implementations.Localization
             return culture + ".json";
             return culture + ".json";
         }
         }
 
 
-        public IEnumerable<LocalizatonOption> GetLocalizationOptions()
+        public LocalizatonOption[] GetLocalizationOptions()
         {
         {
-            return new List<LocalizatonOption>
+            return new LocalizatonOption[]
             {
             {
                 new LocalizatonOption{ Name="Arabic", Value="ar"},
                 new LocalizatonOption{ Name="Arabic", Value="ar"},
                 new LocalizatonOption{ Name="Bulgarian (Bulgaria)", Value="bg-BG"},
                 new LocalizatonOption{ Name="Bulgarian (Bulgaria)", Value="bg-BG"},
@@ -421,7 +421,7 @@ namespace Emby.Server.Implementations.Localization
                 new LocalizatonOption{ Name="Ukrainian", Value="uk"},
                 new LocalizatonOption{ Name="Ukrainian", Value="uk"},
                 new LocalizatonOption{ Name="Vietnamese", Value="vi"}
                 new LocalizatonOption{ Name="Vietnamese", Value="vi"}
 
 
-            }.OrderBy(i => i.Name);
+            };
         }
         }
     }
     }
 
 

+ 3 - 10
Emby.Server.Implementations/Logging/SimpleLogManager.cs

@@ -112,7 +112,7 @@ namespace Emby.Server.Implementations.Logging
 
 
     public class FileLogger : IDisposable
     public class FileLogger : IDisposable
     {
     {
-        private readonly Stream _fileStream;
+        private readonly FileStream _fileStream;
 
 
         private bool _disposed;
         private bool _disposed;
         private readonly CancellationTokenSource _cancellationTokenSource;
         private readonly CancellationTokenSource _cancellationTokenSource;
@@ -122,7 +122,7 @@ namespace Emby.Server.Implementations.Logging
         {
         {
             Directory.CreateDirectory(Path.GetDirectoryName(path));
             Directory.CreateDirectory(Path.GetDirectoryName(path));
 
 
-            _fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
+            _fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, 32768);
             _cancellationTokenSource = new CancellationTokenSource();
             _cancellationTokenSource = new CancellationTokenSource();
 
 
             Task.Factory.StartNew(LogInternal, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
             Task.Factory.StartNew(LogInternal, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
@@ -134,19 +134,12 @@ namespace Emby.Server.Implementations.Logging
             {
             {
                 try
                 try
                 {
                 {
-                    var any = false;
-
                     foreach (var message in _queue.GetConsumingEnumerable())
                     foreach (var message in _queue.GetConsumingEnumerable())
                     {
                     {
                         var bytes = Encoding.UTF8.GetBytes(message + Environment.NewLine);
                         var bytes = Encoding.UTF8.GetBytes(message + Environment.NewLine);
                         _fileStream.Write(bytes, 0, bytes.Length);
                         _fileStream.Write(bytes, 0, bytes.Length);
 
 
-                        any = true;
-                    }
-
-                    if (any)
-                    {
-                        _fileStream.Flush();
+                        _fileStream.Flush(true);
                     }
                     }
                 }
                 }
                 catch
                 catch

+ 7 - 2
Emby.Server.Implementations/MediaEncoder/EncodingManager.cs

@@ -75,6 +75,11 @@ namespace Emby.Server.Implementations.MediaEncoder
                 return false;
                 return false;
             }
             }
 
 
+            if (!video.IsCompleteMedia)
+            {
+                return false;
+            }
+
             // Can't extract images if there are no video streams
             // Can't extract images if there are no video streams
             return video.DefaultVideoStreamIndex.HasValue;
             return video.DefaultVideoStreamIndex.HasValue;
         }
         }
@@ -129,7 +134,7 @@ namespace Emby.Server.Implementations.MediaEncoder
 
 
                             var protocol = MediaProtocol.File;
                             var protocol = MediaProtocol.File;
 
 
-                            var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, new List<string>());
+                            var inputPath = MediaEncoderHelpers.GetInputArgument(_fileSystem, video.Path, protocol, null, new string[] { });
 
 
                             _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
                             _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
 
 
@@ -174,7 +179,7 @@ namespace Emby.Server.Implementations.MediaEncoder
 
 
             if (saveChapters && changesMade)
             if (saveChapters && changesMade)
             {
             {
-                await _chapterManager.SaveChapters(video.Id.ToString(), chapters).ConfigureAwait(false);
+                _chapterManager.SaveChapters(video.Id.ToString(), chapters);
             }
             }
 
 
             DeleteDeadImages(currentImages, chapters);
             DeleteDeadImages(currentImages, chapters);

+ 16 - 16
Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs

@@ -28,21 +28,21 @@ namespace Emby.Server.Implementations.Notifications
                      Type = NotificationType.ApplicationUpdateInstalled.ToString(),
                      Type = NotificationType.ApplicationUpdateInstalled.ToString(),
                      DefaultDescription = "{ReleaseNotes}",
                      DefaultDescription = "{ReleaseNotes}",
                      DefaultTitle = "A new version of Emby Server has been installed.",
                      DefaultTitle = "A new version of Emby Server has been installed.",
-                     Variables = new List<string>{"Version"}
+                     Variables = new string[]{"Version"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
                 {
                 {
                      Type = NotificationType.InstallationFailed.ToString(),
                      Type = NotificationType.InstallationFailed.ToString(),
                      DefaultTitle = "{Name} installation failed.",
                      DefaultTitle = "{Name} installation failed.",
-                     Variables = new List<string>{"Name", "Version"}
+                     Variables = new string[]{"Name", "Version"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
                 {
                 {
                      Type = NotificationType.PluginInstalled.ToString(),
                      Type = NotificationType.PluginInstalled.ToString(),
                      DefaultTitle = "{Name} was installed.",
                      DefaultTitle = "{Name} was installed.",
-                     Variables = new List<string>{"Name", "Version"}
+                     Variables = new string[]{"Name", "Version"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
@@ -50,14 +50,14 @@ namespace Emby.Server.Implementations.Notifications
                      Type = NotificationType.PluginError.ToString(),
                      Type = NotificationType.PluginError.ToString(),
                      DefaultTitle = "{Name} has encountered an error.",
                      DefaultTitle = "{Name} has encountered an error.",
                      DefaultDescription = "{ErrorMessage}",
                      DefaultDescription = "{ErrorMessage}",
-                     Variables = new List<string>{"Name", "ErrorMessage"}
+                     Variables = new string[]{"Name", "ErrorMessage"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
                 {
                 {
                      Type = NotificationType.PluginUninstalled.ToString(),
                      Type = NotificationType.PluginUninstalled.ToString(),
                      DefaultTitle = "{Name} was uninstalled.",
                      DefaultTitle = "{Name} was uninstalled.",
-                     Variables = new List<string>{"Name", "Version"}
+                     Variables = new string[]{"Name", "Version"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
@@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.Notifications
                      Type = NotificationType.PluginUpdateInstalled.ToString(),
                      Type = NotificationType.PluginUpdateInstalled.ToString(),
                      DefaultTitle = "{Name} was updated.",
                      DefaultTitle = "{Name} was updated.",
                      DefaultDescription = "{ReleaseNotes}",
                      DefaultDescription = "{ReleaseNotes}",
-                     Variables = new List<string>{"Name", "ReleaseNotes", "Version"}
+                     Variables = new string[]{"Name", "ReleaseNotes", "Version"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
@@ -79,70 +79,70 @@ namespace Emby.Server.Implementations.Notifications
                      Type = NotificationType.TaskFailed.ToString(),
                      Type = NotificationType.TaskFailed.ToString(),
                      DefaultTitle = "{Name} failed.",
                      DefaultTitle = "{Name} failed.",
                      DefaultDescription = "{ErrorMessage}",
                      DefaultDescription = "{ErrorMessage}",
-                     Variables = new List<string>{"Name", "ErrorMessage"}
+                     Variables = new string[]{"Name", "ErrorMessage"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
                 {
                 {
                      Type = NotificationType.NewLibraryContent.ToString(),
                      Type = NotificationType.NewLibraryContent.ToString(),
                      DefaultTitle = "{Name} has been added to your media library.",
                      DefaultTitle = "{Name} has been added to your media library.",
-                     Variables = new List<string>{"Name"}
+                     Variables = new string[]{"Name"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
                 {
                 {
                      Type = NotificationType.AudioPlayback.ToString(),
                      Type = NotificationType.AudioPlayback.ToString(),
                      DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
                      DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
-                     Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
+                     Variables = new string[]{"UserName", "ItemName", "DeviceName", "AppName"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
                 {
                 {
                      Type = NotificationType.GamePlayback.ToString(),
                      Type = NotificationType.GamePlayback.ToString(),
                      DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
                      DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
-                     Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
+                     Variables = new string[]{"UserName", "ItemName", "DeviceName", "AppName"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
                 {
                 {
                      Type = NotificationType.VideoPlayback.ToString(),
                      Type = NotificationType.VideoPlayback.ToString(),
                      DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
                      DefaultTitle = "{UserName} is playing {ItemName} on {DeviceName}.",
-                     Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
+                     Variables = new string[]{"UserName", "ItemName", "DeviceName", "AppName"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
                 {
                 {
                      Type = NotificationType.AudioPlaybackStopped.ToString(),
                      Type = NotificationType.AudioPlaybackStopped.ToString(),
                      DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
                      DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
-                     Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
+                     Variables = new string[]{"UserName", "ItemName", "DeviceName", "AppName"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
                 {
                 {
                      Type = NotificationType.GamePlaybackStopped.ToString(),
                      Type = NotificationType.GamePlaybackStopped.ToString(),
                      DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
                      DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
-                     Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
+                     Variables = new string[]{"UserName", "ItemName", "DeviceName", "AppName"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
                 {
                 {
                      Type = NotificationType.VideoPlaybackStopped.ToString(),
                      Type = NotificationType.VideoPlaybackStopped.ToString(),
                      DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
                      DefaultTitle = "{UserName} has finished playing {ItemName} on {DeviceName}.",
-                     Variables = new List<string>{"UserName", "ItemName", "DeviceName", "AppName"}
+                     Variables = new string[]{"UserName", "ItemName", "DeviceName", "AppName"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
                 {
                 {
                      Type = NotificationType.CameraImageUploaded.ToString(),
                      Type = NotificationType.CameraImageUploaded.ToString(),
                      DefaultTitle = "A new camera image has been uploaded from {DeviceName}.",
                      DefaultTitle = "A new camera image has been uploaded from {DeviceName}.",
-                     Variables = new List<string>{"DeviceName"}
+                     Variables = new string[]{"DeviceName"}
                 },
                 },
 
 
                 new NotificationTypeInfo
                 new NotificationTypeInfo
                 {
                 {
                      Type = NotificationType.UserLockedOut.ToString(),
                      Type = NotificationType.UserLockedOut.ToString(),
                      DefaultTitle = "{UserName} has been locked out.",
                      DefaultTitle = "{UserName} has been locked out.",
-                     Variables = new List<string>{"UserName"}
+                     Variables = new string[]{"UserName"}
                 }
                 }
             };
             };
 
 

+ 1 - 1
Emby.Server.Implementations/Notifications/NotificationManager.cs

@@ -251,7 +251,7 @@ namespace Emby.Server.Implementations.Notifications
             _typeFactories = notificationTypeFactories.ToArray();
             _typeFactories = notificationTypeFactories.ToArray();
         }
         }
 
 
-        public IEnumerable<NotificationTypeInfo> GetNotificationTypes()
+        public List<NotificationTypeInfo> GetNotificationTypes()
         {
         {
             var list = _typeFactories.Select(i =>
             var list = _typeFactories.Select(i =>
             {
             {

+ 2 - 2
Emby.Server.Implementations/Notifications/Notifications.cs

@@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.Notifications
             {
             {
                 var artists = hasArtist.AllArtists;
                 var artists = hasArtist.AllArtists;
 
 
-                if (artists.Count > 0)
+                if (artists.Length > 0)
                 {
                 {
                     name = hasArtist.AllArtists[0] + " - " + name;
                     name = hasArtist.AllArtists[0] + " - " + name;
                 }
                 }
@@ -440,7 +440,7 @@ namespace Emby.Server.Implementations.Notifications
                 name = item.SeriesName + " - " + name;
                 name = item.SeriesName + " - " + name;
             }
             }
 
 
-            if (item.Artists != null && item.Artists.Count > 0)
+            if (item.Artists != null && item.Artists.Length > 0)
             {
             {
                 name = item.Artists[0] + " - " + name;
                 name = item.Artists[0] + " - " + name;
             }
             }

+ 2 - 2
Emby.Server.Implementations/Playlists/PlaylistManager.cs

@@ -128,12 +128,12 @@ namespace Emby.Server.Implementations.Playlists
 
 
                 playlist.SetMediaType(options.MediaType);
                 playlist.SetMediaType(options.MediaType);
 
 
-                await parentFolder.AddChild(playlist, CancellationToken.None).ConfigureAwait(false);
+                parentFolder.AddChild(playlist, CancellationToken.None);
 
 
                 await playlist.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) { ForceSave = true }, CancellationToken.None)
                 await playlist.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) { ForceSave = true }, CancellationToken.None)
                     .ConfigureAwait(false);
                     .ConfigureAwait(false);
 
 
-                if (options.ItemIdList.Count > 0)
+                if (options.ItemIdList.Length > 0)
                 {
                 {
                     await AddToPlaylistInternal(playlist.Id.ToString("N"), options.ItemIdList, user, new DtoOptions(false)
                     await AddToPlaylistInternal(playlist.Id.ToString("N"), options.ItemIdList, user, new DtoOptions(false)
                     {
                     {

+ 3 - 4
Emby.Server.Implementations/Security/AuthenticationRepository.cs

@@ -4,7 +4,6 @@ using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
-using System.Threading.Tasks;
 using Emby.Server.Implementations.Data;
 using Emby.Server.Implementations.Data;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Security;
 using MediaBrowser.Controller.Security;
@@ -51,14 +50,14 @@ namespace Emby.Server.Implementations.Security
             }
             }
         }
         }
 
 
-        public Task Create(AuthenticationInfo info, CancellationToken cancellationToken)
+        public void Create(AuthenticationInfo info, CancellationToken cancellationToken)
         {
         {
             info.Id = Guid.NewGuid().ToString("N");
             info.Id = Guid.NewGuid().ToString("N");
 
 
-            return Update(info, cancellationToken);
+            Update(info, cancellationToken);
         }
         }
 
 
-        public async Task Update(AuthenticationInfo info, CancellationToken cancellationToken)
+        public void Update(AuthenticationInfo info, CancellationToken cancellationToken)
         {
         {
             if (info == null)
             if (info == null)
             {
             {

+ 7 - 33
Emby.Server.Implementations/Services/ServiceController.cs

@@ -29,13 +29,6 @@ namespace Emby.Server.Implementations.Services
             }
             }
         }
         }
 
 
-        private Type[] GetGenericArguments(Type type)
-        {
-            return type.GetTypeInfo().IsGenericTypeDefinition
-                ? type.GetTypeInfo().GenericTypeParameters
-                : type.GetTypeInfo().GenericTypeArguments;
-        }
-
         public void RegisterService(HttpListenerHost appHost, Type serviceType)
         public void RegisterService(HttpListenerHost appHost, Type serviceType)
         {
         {
             var processedReqs = new HashSet<Type>();
             var processedReqs = new HashSet<Type>();
@@ -50,38 +43,19 @@ namespace Emby.Server.Implementations.Services
 
 
                 ServiceExecGeneral.CreateServiceRunnersFor(requestType, actions);
                 ServiceExecGeneral.CreateServiceRunnersFor(requestType, actions);
 
 
-                var returnMarker = GetTypeWithGenericTypeDefinitionOf(requestType, typeof(IReturn<>));
-                var responseType = returnMarker != null ?
-                      GetGenericArguments(returnMarker)[0]
-                    : mi.ReturnType != typeof(object) && mi.ReturnType != typeof(void) ?
-                      mi.ReturnType
-                    : Type.GetType(requestType.FullName + "Response");
+                //var returnMarker = GetTypeWithGenericTypeDefinitionOf(requestType, typeof(IReturn<>));
+                //var responseType = returnMarker != null ?
+                //      GetGenericArguments(returnMarker)[0]
+                //    : mi.ReturnType != typeof(object) && mi.ReturnType != typeof(void) ?
+                //      mi.ReturnType
+                //    : Type.GetType(requestType.FullName + "Response");
 
 
                 RegisterRestPaths(appHost, requestType);
                 RegisterRestPaths(appHost, requestType);
 
 
-                appHost.AddServiceInfo(serviceType, requestType, responseType);
+                appHost.AddServiceInfo(serviceType, requestType);
             }
             }
         }
         }
 
 
-        private static Type GetTypeWithGenericTypeDefinitionOf(Type type, Type genericTypeDefinition)
-        {
-            foreach (var t in type.GetTypeInfo().ImplementedInterfaces)
-            {
-                if (t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == genericTypeDefinition)
-                {
-                    return t;
-                }
-            }
-
-            var genericType = FirstGenericType(type);
-            if (genericType != null && genericType.GetGenericTypeDefinition() == genericTypeDefinition)
-            {
-                return genericType;
-            }
-
-            return null;
-        }
-
         public static Type FirstGenericType(Type type)
         public static Type FirstGenericType(Type type)
         {
         {
             while (type != null)
             while (type != null)

+ 4 - 4
Emby.Server.Implementations/Services/ServiceHandler.cs

@@ -215,13 +215,13 @@ namespace Emby.Server.Implementations.Services
                 if (name == null) continue; //thank you ASP.NET
                 if (name == null) continue; //thank you ASP.NET
 
 
                 var values = request.QueryString.GetValues(name);
                 var values = request.QueryString.GetValues(name);
-                if (values.Length == 1)
+                if (values.Count == 1)
                 {
                 {
                     map[name] = values[0];
                     map[name] = values[0];
                 }
                 }
                 else
                 else
                 {
                 {
-                    for (var i = 0; i < values.Length; i++)
+                    for (var i = 0; i < values.Count; i++)
                     {
                     {
                         map[name + (i == 0 ? "" : "#" + i)] = values[i];
                         map[name + (i == 0 ? "" : "#" + i)] = values[i];
                     }
                     }
@@ -235,13 +235,13 @@ namespace Emby.Server.Implementations.Services
                     if (name == null) continue; //thank you ASP.NET
                     if (name == null) continue; //thank you ASP.NET
 
 
                     var values = request.FormData.GetValues(name);
                     var values = request.FormData.GetValues(name);
-                    if (values.Length == 1)
+                    if (values.Count == 1)
                     {
                     {
                         map[name] = values[0];
                         map[name] = values[0];
                     }
                     }
                     else
                     else
                     {
                     {
-                        for (var i = 0; i < values.Length; i++)
+                        for (var i = 0; i < values.Count; i++)
                         {
                         {
                             map[name + (i == 0 ? "" : "#" + i)] = values[i];
                             map[name + (i == 0 ? "" : "#" + i)] = values[i];
                         }
                         }

+ 65 - 58
Emby.Server.Implementations/Session/SessionManager.cs

@@ -6,11 +6,8 @@ using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Controller.Security;
 using MediaBrowser.Controller.Security;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Controller.Session;
 using MediaBrowser.Model.Devices;
 using MediaBrowser.Model.Devices;
@@ -253,7 +250,7 @@ namespace Emby.Server.Implementations.Session
                 {
                 {
                     try
                     try
                     {
                     {
-                        await _userManager.UpdateUser(user).ConfigureAwait(false);
+                        _userManager.UpdateUser(user);
                     }
                     }
                     catch (Exception ex)
                     catch (Exception ex)
                     {
                     {
@@ -468,7 +465,7 @@ namespace Emby.Server.Implementations.Session
 
 
                 if (!userId.HasValue)
                 if (!userId.HasValue)
                 {
                 {
-                    sessionInfo.AdditionalUsers.Clear();
+                    sessionInfo.AdditionalUsers = new SessionUserInfo[] { };
                 }
                 }
 
 
                 if (sessionInfo.SessionController == null)
                 if (sessionInfo.SessionController == null)
@@ -622,7 +619,7 @@ namespace Emby.Server.Implementations.Session
             {
             {
                 foreach (var user in users)
                 foreach (var user in users)
                 {
                 {
-                    await OnPlaybackStart(user.Id, libraryItem).ConfigureAwait(false);
+                    OnPlaybackStart(user.Id, libraryItem);
                 }
                 }
             }
             }
 
 
@@ -650,8 +647,7 @@ namespace Emby.Server.Implementations.Session
         /// </summary>
         /// </summary>
         /// <param name="userId">The user identifier.</param>
         /// <param name="userId">The user identifier.</param>
         /// <param name="item">The item.</param>
         /// <param name="item">The item.</param>
-        /// <returns>Task.</returns>
-        private async Task OnPlaybackStart(Guid userId, IHasUserData item)
+        private void OnPlaybackStart(Guid userId, IHasUserData item)
         {
         {
             var data = _userDataManager.GetUserData(userId, item);
             var data = _userDataManager.GetUserData(userId, item);
 
 
@@ -670,7 +666,7 @@ namespace Emby.Server.Implementations.Session
                 data.Played = false;
                 data.Played = false;
             }
             }
 
 
-            await _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None).ConfigureAwait(false);
+            _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None);
         }
         }
 
 
         public Task OnPlaybackProgress(PlaybackProgressInfo info)
         public Task OnPlaybackProgress(PlaybackProgressInfo info)
@@ -702,7 +698,7 @@ namespace Emby.Server.Implementations.Session
             {
             {
                 foreach (var user in users)
                 foreach (var user in users)
                 {
                 {
-                    await OnPlaybackProgress(user, libraryItem, info).ConfigureAwait(false);
+                    OnPlaybackProgress(user, libraryItem, info);
                 }
                 }
             }
             }
 
 
@@ -730,7 +726,7 @@ namespace Emby.Server.Implementations.Session
             StartIdleCheckTimer();
             StartIdleCheckTimer();
         }
         }
 
 
-        private async Task OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info)
+        private void OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info)
         {
         {
             var data = _userDataManager.GetUserData(user.Id, item);
             var data = _userDataManager.GetUserData(user.Id, item);
 
 
@@ -742,7 +738,7 @@ namespace Emby.Server.Implementations.Session
 
 
                 UpdatePlaybackSettings(user, info, data);
                 UpdatePlaybackSettings(user, info, data);
 
 
-                await _userDataManager.SaveUserData(user.Id, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None).ConfigureAwait(false);
+                _userDataManager.SaveUserData(user.Id, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None);
             }
             }
         }
         }
 
 
@@ -842,7 +838,7 @@ namespace Emby.Server.Implementations.Session
             {
             {
                 foreach (var user in users)
                 foreach (var user in users)
                 {
                 {
-                    playedToCompletion = await OnPlaybackStopped(user.Id, libraryItem, info.PositionTicks, info.Failed).ConfigureAwait(false);
+                    playedToCompletion = OnPlaybackStopped(user.Id, libraryItem, info.PositionTicks, info.Failed);
                 }
                 }
             }
             }
 
 
@@ -875,7 +871,7 @@ namespace Emby.Server.Implementations.Session
             await SendPlaybackStoppedNotification(session, CancellationToken.None).ConfigureAwait(false);
             await SendPlaybackStoppedNotification(session, CancellationToken.None).ConfigureAwait(false);
         }
         }
 
 
-        private async Task<bool> OnPlaybackStopped(Guid userId, BaseItem item, long? positionTicks, bool playbackFailed)
+        private bool OnPlaybackStopped(Guid userId, BaseItem item, long? positionTicks, bool playbackFailed)
         {
         {
             bool playedToCompletion = false;
             bool playedToCompletion = false;
 
 
@@ -896,7 +892,7 @@ namespace Emby.Server.Implementations.Session
                     playedToCompletion = true;
                     playedToCompletion = true;
                 }
                 }
 
 
-                await _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None).ConfigureAwait(false);
+                _userDataManager.SaveUserData(userId, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None);
             }
             }
 
 
             return playedToCompletion;
             return playedToCompletion;
@@ -1074,7 +1070,7 @@ namespace Emby.Server.Implementations.Session
                     DtoOptions = new DtoOptions(false)
                     DtoOptions = new DtoOptions(false)
                     {
                     {
                         EnableImages = false,
                         EnableImages = false,
-                        Fields = new List<ItemFields>
+                        Fields = new ItemFields[]
                         {
                         {
                             ItemFields.SortName
                             ItemFields.SortName
                         }
                         }
@@ -1097,7 +1093,7 @@ namespace Emby.Server.Implementations.Session
                     DtoOptions = new DtoOptions(false)
                     DtoOptions = new DtoOptions(false)
                     {
                     {
                         EnableImages = false,
                         EnableImages = false,
-                        Fields = new List<ItemFields>
+                        Fields = new ItemFields[]
                         {
                         {
                             ItemFields.SortName
                             ItemFields.SortName
                         }
                         }
@@ -1340,11 +1336,15 @@ namespace Emby.Server.Implementations.Session
             {
             {
                 var user = _userManager.GetUserById(userId);
                 var user = _userManager.GetUserById(userId);
 
 
-                session.AdditionalUsers.Add(new SessionUserInfo
+                var list = session.AdditionalUsers.ToList();
+
+                list.Add(new SessionUserInfo
                 {
                 {
                     UserId = userId,
                     UserId = userId,
                     UserName = user.Name
                     UserName = user.Name
                 });
                 });
+
+                session.AdditionalUsers = list.ToArray(list.Count);
             }
             }
         }
         }
 
 
@@ -1368,7 +1368,10 @@ namespace Emby.Server.Implementations.Session
 
 
             if (user != null)
             if (user != null)
             {
             {
-                session.AdditionalUsers.Remove(user);
+                var list = session.AdditionalUsers.ToList();
+                list.Remove(user);
+
+                session.AdditionalUsers = list.ToArray(list.Count);
             }
             }
         }
         }
 
 
@@ -1425,7 +1428,7 @@ namespace Emby.Server.Implementations.Session
                 user = result;
                 user = result;
             }
             }
 
 
-            var token = await GetAuthorizationToken(user.Id.ToString("N"), request.DeviceId, request.App, request.AppVersion, request.DeviceName).ConfigureAwait(false);
+            var token = GetAuthorizationToken(user.Id.ToString("N"), request.DeviceId, request.App, request.AppVersion, request.DeviceName);
 
 
             EventHelper.FireEventIfNotNull(AuthenticationSucceeded, this, new GenericEventArgs<AuthenticationRequest>(request), _logger);
             EventHelper.FireEventIfNotNull(AuthenticationSucceeded, this, new GenericEventArgs<AuthenticationRequest>(request), _logger);
 
 
@@ -1447,7 +1450,7 @@ namespace Emby.Server.Implementations.Session
         }
         }
 
 
 
 
-        private async Task<string> GetAuthorizationToken(string userId, string deviceId, string app, string appVersion, string deviceName)
+        private string GetAuthorizationToken(string userId, string deviceId, string app, string appVersion, string deviceName)
         {
         {
             var existing = _authRepo.Get(new AuthenticationInfoQuery
             var existing = _authRepo.Get(new AuthenticationInfoQuery
             {
             {
@@ -1477,12 +1480,12 @@ namespace Emby.Server.Implementations.Session
             };
             };
 
 
             _logger.Info("Creating new access token for user {0}", userId);
             _logger.Info("Creating new access token for user {0}", userId);
-            await _authRepo.Create(newToken, CancellationToken.None).ConfigureAwait(false);
+            _authRepo.Create(newToken, CancellationToken.None);
 
 
             return newToken.AccessToken;
             return newToken.AccessToken;
         }
         }
 
 
-        public async Task Logout(string accessToken)
+        public void Logout(string accessToken)
         {
         {
             if (string.IsNullOrWhiteSpace(accessToken))
             if (string.IsNullOrWhiteSpace(accessToken))
             {
             {
@@ -1502,7 +1505,7 @@ namespace Emby.Server.Implementations.Session
             {
             {
                 existing.IsActive = false;
                 existing.IsActive = false;
 
 
-                await _authRepo.Update(existing, CancellationToken.None).ConfigureAwait(false);
+                _authRepo.Update(existing, CancellationToken.None);
 
 
                 var sessions = Sessions
                 var sessions = Sessions
                     .Where(i => string.Equals(i.DeviceId, existing.DeviceId, StringComparison.OrdinalIgnoreCase))
                     .Where(i => string.Equals(i.DeviceId, existing.DeviceId, StringComparison.OrdinalIgnoreCase))
@@ -1522,7 +1525,7 @@ namespace Emby.Server.Implementations.Session
             }
             }
         }
         }
 
 
-        public async Task RevokeUserTokens(string userId, string currentAccessToken)
+        public void RevokeUserTokens(string userId, string currentAccessToken)
         {
         {
             var existing = _authRepo.Get(new AuthenticationInfoQuery
             var existing = _authRepo.Get(new AuthenticationInfoQuery
             {
             {
@@ -1534,14 +1537,14 @@ namespace Emby.Server.Implementations.Session
             {
             {
                 if (!string.Equals(currentAccessToken, info.AccessToken, StringComparison.OrdinalIgnoreCase))
                 if (!string.Equals(currentAccessToken, info.AccessToken, StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    await Logout(info.AccessToken).ConfigureAwait(false);
+                    Logout(info.AccessToken);
                 }
                 }
             }
             }
         }
         }
 
 
-        public Task RevokeToken(string token)
+        public void RevokeToken(string token)
         {
         {
-            return Logout(token);
+            Logout(token);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -1661,35 +1664,39 @@ namespace Emby.Server.Implementations.Session
                     AddProgramRecordingInfo = false
                     AddProgramRecordingInfo = false
                 };
                 };
 
 
-                dtoOptions.Fields.Remove(ItemFields.BasicSyncInfo);
-                dtoOptions.Fields.Remove(ItemFields.SyncInfo);
-                dtoOptions.Fields.Remove(ItemFields.CanDelete);
-                dtoOptions.Fields.Remove(ItemFields.CanDownload);
-                dtoOptions.Fields.Remove(ItemFields.ChildCount);
-                dtoOptions.Fields.Remove(ItemFields.CustomRating);
-                dtoOptions.Fields.Remove(ItemFields.DateLastMediaAdded);
-                dtoOptions.Fields.Remove(ItemFields.DateLastRefreshed);
-                dtoOptions.Fields.Remove(ItemFields.DateLastSaved);
-                dtoOptions.Fields.Remove(ItemFields.DisplayPreferencesId);
-                dtoOptions.Fields.Remove(ItemFields.Etag);
-                dtoOptions.Fields.Remove(ItemFields.ExternalEtag);
-                dtoOptions.Fields.Remove(ItemFields.InheritedParentalRatingValue);
-                dtoOptions.Fields.Remove(ItemFields.ItemCounts);
-                dtoOptions.Fields.Remove(ItemFields.MediaSourceCount);
-                dtoOptions.Fields.Remove(ItemFields.MediaStreams);
-                dtoOptions.Fields.Remove(ItemFields.MediaSources);
-                dtoOptions.Fields.Remove(ItemFields.People);
-                dtoOptions.Fields.Remove(ItemFields.PlayAccess);
-                dtoOptions.Fields.Remove(ItemFields.People);
-                dtoOptions.Fields.Remove(ItemFields.ProductionLocations);
-                dtoOptions.Fields.Remove(ItemFields.RecursiveItemCount);
-                dtoOptions.Fields.Remove(ItemFields.RemoteTrailers);
-                dtoOptions.Fields.Remove(ItemFields.SeasonUserData);
-                dtoOptions.Fields.Remove(ItemFields.Settings);
-                dtoOptions.Fields.Remove(ItemFields.SortName);
-                dtoOptions.Fields.Remove(ItemFields.Tags);
-                dtoOptions.Fields.Remove(ItemFields.ThemeSongIds);
-                dtoOptions.Fields.Remove(ItemFields.ThemeVideoIds);
+                var fields = dtoOptions.Fields.ToList();
+
+                fields.Remove(ItemFields.BasicSyncInfo);
+                fields.Remove(ItemFields.SyncInfo);
+                fields.Remove(ItemFields.CanDelete);
+                fields.Remove(ItemFields.CanDownload);
+                fields.Remove(ItemFields.ChildCount);
+                fields.Remove(ItemFields.CustomRating);
+                fields.Remove(ItemFields.DateLastMediaAdded);
+                fields.Remove(ItemFields.DateLastRefreshed);
+                fields.Remove(ItemFields.DateLastSaved);
+                fields.Remove(ItemFields.DisplayPreferencesId);
+                fields.Remove(ItemFields.Etag);
+                fields.Remove(ItemFields.ExternalEtag);
+                fields.Remove(ItemFields.InheritedParentalRatingValue);
+                fields.Remove(ItemFields.ItemCounts);
+                fields.Remove(ItemFields.MediaSourceCount);
+                fields.Remove(ItemFields.MediaStreams);
+                fields.Remove(ItemFields.MediaSources);
+                fields.Remove(ItemFields.People);
+                fields.Remove(ItemFields.PlayAccess);
+                fields.Remove(ItemFields.People);
+                fields.Remove(ItemFields.ProductionLocations);
+                fields.Remove(ItemFields.RecursiveItemCount);
+                fields.Remove(ItemFields.RemoteTrailers);
+                fields.Remove(ItemFields.SeasonUserData);
+                fields.Remove(ItemFields.Settings);
+                fields.Remove(ItemFields.SortName);
+                fields.Remove(ItemFields.Tags);
+                fields.Remove(ItemFields.ThemeSongIds);
+                fields.Remove(ItemFields.ThemeVideoIds);
+
+                dtoOptions.Fields = fields.ToArray(fields.Count);
 
 
                 _itemInfoDtoOptions = dtoOptions;
                 _itemInfoDtoOptions = dtoOptions;
             }
             }
@@ -1698,7 +1705,7 @@ namespace Emby.Server.Implementations.Session
 
 
             if (mediaSource != null)
             if (mediaSource != null)
             {
             {
-                info.MediaStreams = mediaSource.MediaStreams;
+                info.MediaStreams = mediaSource.MediaStreams.ToArray();
             }
             }
 
 
             return info;
             return info;

+ 4 - 4
Emby.Server.Implementations/Social/SharingManager.cs

@@ -58,8 +58,8 @@ namespace Emby.Server.Implementations.Social
             };
             };
 
 
             AddShareInfo(info, externalUrl);
             AddShareInfo(info, externalUrl);
-            
-            await _repository.CreateShare(info).ConfigureAwait(false);
+
+            _repository.CreateShare(info);
 
 
             return info;
             return info;
         }
         }
@@ -92,9 +92,9 @@ namespace Emby.Server.Implementations.Social
             }
             }
         }
         }
 
 
-        public Task DeleteShare(string id)
+        public void DeleteShare(string id)
         {
         {
-            return _repository.DeleteShare(id);
+            _repository.DeleteShare(id);
         }
         }
     }
     }
 }
 }

+ 2 - 4
Emby.Server.Implementations/Social/SharingRepository.cs

@@ -1,8 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
 using Emby.Server.Implementations.Data;
 using Emby.Server.Implementations.Data;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
@@ -42,7 +40,7 @@ namespace Emby.Server.Implementations.Social
             }
             }
         }
         }
 
 
-        public async Task CreateShare(SocialShareInfo info)
+        public void CreateShare(SocialShareInfo info)
         {
         {
             if (info == null)
             if (info == null)
             {
             {
@@ -109,7 +107,7 @@ namespace Emby.Server.Implementations.Social
             return info;
             return info;
         }
         }
 
 
-        public async Task DeleteShare(string id)
+        public void DeleteShare(string id)
         {
         {
 
 
         }
         }

+ 1 - 1
Emby.Server.Implementations/Sorting/ArtistComparer.cs

@@ -36,7 +36,7 @@ namespace Emby.Server.Implementations.Sorting
                 return string.Empty;
                 return string.Empty;
             }
             }
 
 
-            return audio.Artists.Count == 0 ? null : audio.Artists[0];
+            return audio.Artists.Length == 0 ? null : audio.Artists[0];
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 3 - 3
Emby.Server.Implementations/TV/TVSeriesManager.cs

@@ -72,7 +72,7 @@ namespace Emby.Server.Implementations.TV
                 Recursive = true,
                 Recursive = true,
                 DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
                 DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
                 {
                 {
-                    Fields = new List<ItemFields>
+                    Fields = new ItemFields[]
                     {
                     {
                         ItemFields.SeriesPresentationUniqueKey
                         ItemFields.SeriesPresentationUniqueKey
                     }
                     }
@@ -128,7 +128,7 @@ namespace Emby.Server.Implementations.TV
                 Limit = limit,
                 Limit = limit,
                 DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
                 DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
                 {
                 {
-                    Fields = new List<ItemFields>
+                    Fields = new ItemFields[]
                     {
                     {
                         ItemFields.SeriesPresentationUniqueKey
                         ItemFields.SeriesPresentationUniqueKey
                     },
                     },
@@ -207,7 +207,7 @@ namespace Emby.Server.Implementations.TV
                 ParentIndexNumberNotEquals = 0,
                 ParentIndexNumberNotEquals = 0,
                 DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
                 DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
                 {
                 {
-                    Fields = new List<ItemFields>
+                    Fields = new ItemFields[]
                     {
                     {
                         ItemFields.SortName
                         ItemFields.SortName
                     },
                     },

+ 74 - 56
Emby.Server.Implementations/Updates/InstallationManager.cs

@@ -122,7 +122,10 @@ namespace Emby.Server.Implementations.Updates
 
 
         private readonly ICryptoProvider _cryptographyProvider;
         private readonly ICryptoProvider _cryptographyProvider;
 
 
-        public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config, IFileSystem fileSystem, ICryptoProvider cryptographyProvider)
+        // netframework or netcore
+        private readonly string _packageRuntime;
+
+        public InstallationManager(ILogger logger, IApplicationHost appHost, IApplicationPaths appPaths, IHttpClient httpClient, IJsonSerializer jsonSerializer, ISecurityManager securityManager, IConfigurationManager config, IFileSystem fileSystem, ICryptoProvider cryptographyProvider, string packageRuntime)
         {
         {
             if (logger == null)
             if (logger == null)
             {
             {
@@ -140,6 +143,7 @@ namespace Emby.Server.Implementations.Updates
             _config = config;
             _config = config;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _cryptographyProvider = cryptographyProvider;
             _cryptographyProvider = cryptographyProvider;
+            _packageRuntime = packageRuntime;
             _logger = logger;
             _logger = logger;
         }
         }
 
 
@@ -157,7 +161,7 @@ namespace Emby.Server.Implementations.Updates
         /// Gets all available packages.
         /// Gets all available packages.
         /// </summary>
         /// </summary>
         /// <returns>Task{List{PackageInfo}}.</returns>
         /// <returns>Task{List{PackageInfo}}.</returns>
-        public async Task<IEnumerable<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
+        public async Task<List<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken,
             bool withRegistration = true,
             bool withRegistration = true,
             string packageType = null,
             string packageType = null,
             Version applicationVersion = null)
             Version applicationVersion = null)
@@ -171,11 +175,11 @@ namespace Emby.Server.Implementations.Updates
                     { "systemid", _applicationHost.SystemId }
                     { "systemid", _applicationHost.SystemId }
                 };
                 };
 
 
-                using (var json = await _httpClient.Post("https://www.mb3admin.com/admin/service/package/retrieveall", data, cancellationToken).ConfigureAwait(false))
+                using (var json = await _httpClient.Post("https://www.mb3admin.com/admin/service/package/retrieveall?includeAllRuntimes=true", data, cancellationToken).ConfigureAwait(false))
                 {
                 {
                     cancellationToken.ThrowIfCancellationRequested();
                     cancellationToken.ThrowIfCancellationRequested();
 
 
-                    var packages = _jsonSerializer.DeserializeFromStream<List<PackageInfo>>(json).ToList();
+                    var packages = _jsonSerializer.DeserializeFromStream<PackageInfo[]>(json);
 
 
                     return FilterPackages(packages, packageType, applicationVersion);
                     return FilterPackages(packages, packageType, applicationVersion);
                 }
                 }
@@ -184,7 +188,7 @@ namespace Emby.Server.Implementations.Updates
             {
             {
                 var packages = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
                 var packages = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
 
 
-                return FilterPackages(packages.ToList(), packageType, applicationVersion);
+                return FilterPackages(packages, packageType, applicationVersion);
             }
             }
         }
         }
 
 
@@ -195,21 +199,21 @@ namespace Emby.Server.Implementations.Updates
         /// </summary>
         /// </summary>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{List{PackageInfo}}.</returns>
         /// <returns>Task{List{PackageInfo}}.</returns>
-        public async Task<IEnumerable<PackageInfo>> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken)
+        public async Task<List<PackageInfo>> GetAvailablePackagesWithoutRegistrationInfo(CancellationToken cancellationToken)
         {
         {
             _logger.Info("Opening {0}", PackageCachePath);
             _logger.Info("Opening {0}", PackageCachePath);
             try
             try
             {
             {
                 using (var stream = _fileSystem.OpenRead(PackageCachePath))
                 using (var stream = _fileSystem.OpenRead(PackageCachePath))
                 {
                 {
-                    var packages = _jsonSerializer.DeserializeFromStream<List<PackageInfo>>(stream).ToList();
+                    var packages = _jsonSerializer.DeserializeFromStream<PackageInfo[]>(stream);
 
 
                     if (DateTime.UtcNow - _lastPackageUpdateTime > GetCacheLength())
                     if (DateTime.UtcNow - _lastPackageUpdateTime > GetCacheLength())
                     {
                     {
                         UpdateCachedPackages(CancellationToken.None, false);
                         UpdateCachedPackages(CancellationToken.None, false);
                     }
                     }
 
 
-                    return packages;
+                    return FilterPackages(packages);
                 }
                 }
             }
             }
             catch (Exception)
             catch (Exception)
@@ -221,7 +225,7 @@ namespace Emby.Server.Implementations.Updates
             await UpdateCachedPackages(cancellationToken, true).ConfigureAwait(false);
             await UpdateCachedPackages(cancellationToken, true).ConfigureAwait(false);
             using (var stream = _fileSystem.OpenRead(PackageCachePath))
             using (var stream = _fileSystem.OpenRead(PackageCachePath))
             {
             {
-                return _jsonSerializer.DeserializeFromStream<List<PackageInfo>>(stream).ToList();
+                return FilterPackages(_jsonSerializer.DeserializeFromStream<PackageInfo[]>(stream));
             }
             }
         }
         }
 
 
@@ -244,7 +248,7 @@ namespace Emby.Server.Implementations.Updates
 
 
                 var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
                 var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
                 {
                 {
-                    Url = "https://www.mb3admin.com/admin/service/MB3Packages.json",
+                    Url = "https://www.mb3admin.com/admin/service/EmbyPackages.json",
                     CancellationToken = cancellationToken,
                     CancellationToken = cancellationToken,
                     Progress = new SimpleProgress<Double>()
                     Progress = new SimpleProgress<Double>()
 
 
@@ -277,57 +281,77 @@ namespace Emby.Server.Implementations.Updates
 
 
         private TimeSpan GetCacheLength()
         private TimeSpan GetCacheLength()
         {
         {
-            switch (GetSystemUpdateLevel())
-            {
-                case PackageVersionClass.Beta:
-                    return TimeSpan.FromMinutes(30);
-                case PackageVersionClass.Dev:
-                    return TimeSpan.FromMinutes(3);
-                default:
-                    return TimeSpan.FromHours(24);
-            }
+            return TimeSpan.FromMinutes(3);
         }
         }
 
 
-        protected IEnumerable<PackageInfo> FilterPackages(List<PackageInfo> packages)
+        protected List<PackageInfo> FilterPackages(IEnumerable<PackageInfo> packages)
         {
         {
+            var list = new List<PackageInfo>();
+
             foreach (var package in packages)
             foreach (var package in packages)
             {
             {
-                package.versions = package.versions.Where(v => !string.IsNullOrWhiteSpace(v.sourceUrl))
-                    .OrderByDescending(GetPackageVersion).ToList();
+                var versions = new List<PackageVersionInfo>();
+                foreach (var version in package.versions)
+                {
+                    if (string.IsNullOrWhiteSpace(version.sourceUrl))
+                    {
+                        continue;
+                    }
+
+                    if (string.IsNullOrWhiteSpace(version.runtimes) || version.runtimes.IndexOf(_packageRuntime, StringComparison.OrdinalIgnoreCase) == -1)
+                    {
+                        continue;
+                    }
+
+                    versions.Add(version);
+                }
+
+                package.versions = versions
+                    .OrderByDescending(GetPackageVersion)
+                    .ToArray();
+
+                if (package.versions.Length == 0)
+                {
+                    continue;
+                }
+
+                list.Add(package);
             }
             }
 
 
             // Remove packages with no versions
             // Remove packages with no versions
-            packages = packages.Where(p => p.versions.Any()).ToList();
-
-            return packages;
+            return list;
         }
         }
 
 
-        protected IEnumerable<PackageInfo> FilterPackages(List<PackageInfo> packages, string packageType, Version applicationVersion)
+        protected List<PackageInfo> FilterPackages(IEnumerable<PackageInfo> packages, string packageType, Version applicationVersion)
         {
         {
-            foreach (var package in packages)
-            {
-                package.versions = package.versions.Where(v => !string.IsNullOrWhiteSpace(v.sourceUrl))
-                    .OrderByDescending(GetPackageVersion).ToList();
-            }
+            var packagesList = FilterPackages(packages);
 
 
-            if (!string.IsNullOrWhiteSpace(packageType))
-            {
-                packages = packages.Where(p => string.Equals(p.type, packageType, StringComparison.OrdinalIgnoreCase)).ToList();
-            }
+            var returnList = new List<PackageInfo>();
 
 
-            // If an app version was supplied, filter the versions for each package to only include supported versions
-            if (applicationVersion != null)
+            var filterOnPackageType = !string.IsNullOrWhiteSpace(packageType);
+
+            foreach (var p in packagesList)
             {
             {
-                foreach (var package in packages)
+                if (filterOnPackageType && !string.Equals(p.type, packageType, StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    package.versions = package.versions.Where(v => IsPackageVersionUpToDate(v, applicationVersion)).ToList();
+                    continue;
                 }
                 }
-            }
 
 
-            // Remove packages with no versions
-            packages = packages.Where(p => p.versions.Any()).ToList();
+                // If an app version was supplied, filter the versions for each package to only include supported versions
+                if (applicationVersion != null)
+                {
+                    p.versions = p.versions.Where(v => IsPackageVersionUpToDate(v, applicationVersion)).ToArray();
+                }
 
 
-            return packages;
+                if (p.versions.Length == 0)
+                {
+                    continue;
+                }
+
+                returnList.Add(p);
+            }
+
+            return returnList;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -418,30 +442,24 @@ namespace Emby.Server.Implementations.Updates
         /// <returns>Task{IEnumerable{PackageVersionInfo}}.</returns>
         /// <returns>Task{IEnumerable{PackageVersionInfo}}.</returns>
         public async Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(Version applicationVersion, bool withAutoUpdateEnabled, CancellationToken cancellationToken)
         public async Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(Version applicationVersion, bool withAutoUpdateEnabled, CancellationToken cancellationToken)
         {
         {
-            var catalog = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
-
-            var plugins = _applicationHost.Plugins.ToList();
-
-            if (withAutoUpdateEnabled)
+            if (!_config.CommonConfiguration.EnableAutoUpdate)
             {
             {
-                plugins = plugins
-                    .Where(p => _config.CommonConfiguration.EnableAutoUpdate)
-                    .ToList();
+                return new PackageVersionInfo[] { };
             }
             }
 
 
+            var catalog = await GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
+
             var systemUpdateLevel = GetSystemUpdateLevel();
             var systemUpdateLevel = GetSystemUpdateLevel();
 
 
             // Figure out what needs to be installed
             // Figure out what needs to be installed
-            var packages = plugins.Select(p =>
+            return _applicationHost.Plugins.Select(p =>
             {
             {
                 var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Id.ToString(), applicationVersion, systemUpdateLevel);
                 var latestPluginInfo = GetLatestCompatibleVersion(catalog, p.Name, p.Id.ToString(), applicationVersion, systemUpdateLevel);
 
 
                 return latestPluginInfo != null && GetPackageVersion(latestPluginInfo) > p.Version ? latestPluginInfo : null;
                 return latestPluginInfo != null && GetPackageVersion(latestPluginInfo) > p.Version ? latestPluginInfo : null;
 
 
-            }).Where(i => i != null).ToList();
-
-            return packages
-                .Where(p => !string.IsNullOrWhiteSpace(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase)));
+            }).Where(i => i != null)
+            .Where(p => !string.IsNullOrWhiteSpace(p.sourceUrl) && !CompletedInstallations.Any(i => string.Equals(i.AssemblyGuid, p.guid, StringComparison.OrdinalIgnoreCase)));
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 1 - 1
Emby.Server.Implementations/packages.config

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
 <packages>
   <package id="Emby.XmlTv" version="1.0.10" targetFramework="net46" />
   <package id="Emby.XmlTv" version="1.0.10" targetFramework="net46" />
-  <package id="MediaBrowser.Naming" version="1.0.6" targetFramework="net46" />
+  <package id="MediaBrowser.Naming" version="1.0.7" targetFramework="net46" />
   <package id="ServiceStack.Text" version="4.5.8" targetFramework="net46" />
   <package id="ServiceStack.Text" version="4.5.8" targetFramework="net46" />
   <package id="SharpCompress" version="0.14.0" targetFramework="net46" />
   <package id="SharpCompress" version="0.14.0" targetFramework="net46" />
   <package id="SimpleInjector" version="4.0.8" targetFramework="net46" />
   <package id="SimpleInjector" version="4.0.8" targetFramework="net46" />

+ 20 - 4
MediaBrowser.Api/BaseApiService.cs

@@ -11,6 +11,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
+using MediaBrowser.Model.Extensions;
 
 
 namespace MediaBrowser.Api
 namespace MediaBrowser.Api
 {
 {
@@ -54,6 +55,17 @@ namespace MediaBrowser.Api
             return Request.Headers[name];
             return Request.Headers[name];
         }
         }
 
 
+        private static readonly string[] EmptyStringArray = new string[] { };
+        public static string[] SplitValue(string value, char delim)
+        {
+            if (string.IsNullOrWhiteSpace(value))
+            {
+                return EmptyStringArray;
+            }
+
+            return value.Split(new[] { delim }, StringSplitOptions.RemoveEmptyEntries);
+        }
+        
         /// <summary>
         /// <summary>
         /// To the optimized result.
         /// To the optimized result.
         /// </summary>
         /// </summary>
@@ -128,7 +140,7 @@ namespace MediaBrowser.Api
             var hasFields = request as IHasItemFields;
             var hasFields = request as IHasItemFields;
             if (hasFields != null)
             if (hasFields != null)
             {
             {
-                options.Fields = hasFields.GetItemFields().ToList();
+                options.Fields = hasFields.GetItemFields();
             }
             }
 
 
             var client = authInfo.Client ?? string.Empty;
             var client = authInfo.Client ?? string.Empty;
@@ -137,7 +149,9 @@ namespace MediaBrowser.Api
                 client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 ||
                 client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 ||
                 client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1)
                 client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1)
             {
             {
-                options.Fields.Add(Model.Querying.ItemFields.RecursiveItemCount);
+                var list = options.Fields.ToList();
+                list.Add(Model.Querying.ItemFields.RecursiveItemCount);
+                options.Fields = list.ToArray(list.Count);
             }
             }
 
 
             if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 ||
             if (client.IndexOf("kodi", StringComparison.OrdinalIgnoreCase) != -1 ||
@@ -148,7 +162,9 @@ namespace MediaBrowser.Api
                client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1 ||
                client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1 ||
                client.IndexOf("androidtv", StringComparison.OrdinalIgnoreCase) != -1)
                client.IndexOf("androidtv", StringComparison.OrdinalIgnoreCase) != -1)
             {
             {
-                options.Fields.Add(Model.Querying.ItemFields.ChildCount);
+                var list = options.Fields.ToList();
+                list.Add(Model.Querying.ItemFields.ChildCount);
+                options.Fields = list.ToArray(list.Count);
             }
             }
 
 
             var hasDtoOptions = request as IHasDtoOptions;
             var hasDtoOptions = request as IHasDtoOptions;
@@ -167,7 +183,7 @@ namespace MediaBrowser.Api
 
 
                 if (!string.IsNullOrWhiteSpace(hasDtoOptions.EnableImageTypes))
                 if (!string.IsNullOrWhiteSpace(hasDtoOptions.EnableImageTypes))
                 {
                 {
-                    options.ImageTypes = (hasDtoOptions.EnableImageTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToList();
+                    options.ImageTypes = (hasDtoOptions.EnableImageTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToArray();
                 }
                 }
             }
             }
 
 

+ 4 - 4
MediaBrowser.Api/ChannelService.cs

@@ -55,7 +55,7 @@ namespace MediaBrowser.Api
     }
     }
 
 
     [Route("/Channels/Features", "GET", Summary = "Gets features for a channel")]
     [Route("/Channels/Features", "GET", Summary = "Gets features for a channel")]
-    public class GetAllChannelFeatures : IReturn<List<ChannelFeatures>>
+    public class GetAllChannelFeatures : IReturn<ChannelFeatures[]>
     {
     {
     }
     }
 
 
@@ -187,7 +187,7 @@ namespace MediaBrowser.Api
 
 
         public object Get(GetAllChannelFeatures request)
         public object Get(GetAllChannelFeatures request)
         {
         {
-            var result = _channelManager.GetAllChannelFeatures().ToList();
+            var result = _channelManager.GetAllChannelFeatures();
 
 
             return ToOptimizedResult(result);
             return ToOptimizedResult(result);
         }
         }
@@ -231,7 +231,7 @@ namespace MediaBrowser.Api
                 SortOrder = request.SortOrder,
                 SortOrder = request.SortOrder,
                 SortBy = (request.SortBy ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
                 SortBy = (request.SortBy ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
                 Filters = request.GetFilters().ToArray(),
                 Filters = request.GetFilters().ToArray(),
-                Fields = request.GetItemFields().ToArray()
+                Fields = request.GetItemFields()
 
 
             }, CancellationToken.None).ConfigureAwait(false);
             }, CancellationToken.None).ConfigureAwait(false);
 
 
@@ -247,7 +247,7 @@ namespace MediaBrowser.Api
                 ChannelIds = (request.ChannelIds ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
                 ChannelIds = (request.ChannelIds ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(),
                 UserId = request.UserId,
                 UserId = request.UserId,
                 Filters = request.GetFilters().ToArray(),
                 Filters = request.GetFilters().ToArray(),
-                Fields = request.GetItemFields().ToList()
+                Fields = request.GetItemFields()
 
 
             }, CancellationToken.None).ConfigureAwait(false);
             }, CancellationToken.None).ConfigureAwait(false);
 
 

+ 2 - 3
MediaBrowser.Api/ConfigurationService.cs

@@ -6,7 +6,6 @@ using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Serialization;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
-using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.IO;
@@ -62,7 +61,7 @@ namespace MediaBrowser.Api
 
 
     [Route("/System/Configuration/MetadataPlugins", "GET", Summary = "Gets all available metadata plugins")]
     [Route("/System/Configuration/MetadataPlugins", "GET", Summary = "Gets all available metadata plugins")]
     [Authenticated(Roles = "Admin")]
     [Authenticated(Roles = "Admin")]
-    public class GetMetadataPlugins : IReturn<List<MetadataPluginSummary>>
+    public class GetMetadataPlugins : IReturn<MetadataPluginSummary[]>
     {
     {
 
 
     }
     }
@@ -170,7 +169,7 @@ namespace MediaBrowser.Api
 
 
         public object Get(GetMetadataPlugins request)
         public object Get(GetMetadataPlugins request)
         {
         {
-            return ToOptimizedSerializedResultUsingCache(_providerManager.GetAllMetadataPlugins().ToList());
+            return ToOptimizedSerializedResultUsingCache(_providerManager.GetAllMetadataPlugins());
         }
         }
     }
     }
 }
 }

+ 1 - 2
MediaBrowser.Api/Devices/DeviceService.cs

@@ -1,5 +1,4 @@
 using System;
 using System;
-using System.Linq;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Devices;
 using MediaBrowser.Model.Devices;
@@ -143,7 +142,7 @@ namespace MediaBrowser.Api.Devices
             }
             }
             else
             else
             {
             {
-                var file = Request.Files.First();
+                var file = Request.Files.Length == 0 ? null : Request.Files[0];
 
 
                 var task = _deviceManager.AcceptCameraUpload(deviceId, file.InputStream, new LocalFileInfo
                 var task = _deviceManager.AcceptCameraUpload(deviceId, file.InputStream, new LocalFileInfo
                 {
                 {

+ 1 - 3
MediaBrowser.Api/DisplayPreferencesService.cs

@@ -88,9 +88,7 @@ namespace MediaBrowser.Api
             // Serialize to json and then back so that the core doesn't see the request dto type
             // Serialize to json and then back so that the core doesn't see the request dto type
             var displayPreferences = _jsonSerializer.DeserializeFromString<DisplayPreferences>(_jsonSerializer.SerializeToString(request));
             var displayPreferences = _jsonSerializer.DeserializeFromString<DisplayPreferences>(_jsonSerializer.SerializeToString(request));
 
 
-            var task = _displayPreferencesManager.SaveDisplayPreferences(displayPreferences, request.UserId, request.Client, CancellationToken.None);
-
-            Task.WaitAll(task);
+            _displayPreferencesManager.SaveDisplayPreferences(displayPreferences, request.UserId, request.Client, CancellationToken.None);
         }
         }
     }
     }
 }
 }

+ 0 - 1
MediaBrowser.Api/Dlna/DlnaServerService.cs

@@ -3,7 +3,6 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
-using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
 using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.IO;

+ 4 - 5
MediaBrowser.Api/Dlna/DlnaService.cs

@@ -1,14 +1,13 @@
-using MediaBrowser.Controller.Dlna;
+using System.Linq;
+using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dlna;
-using System.Collections.Generic;
-using System.Linq;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
 
 
 namespace MediaBrowser.Api.Dlna
 namespace MediaBrowser.Api.Dlna
 {
 {
     [Route("/Dlna/ProfileInfos", "GET", Summary = "Gets a list of profiles")]
     [Route("/Dlna/ProfileInfos", "GET", Summary = "Gets a list of profiles")]
-    public class GetProfileInfos : IReturn<List<DeviceProfileInfo>>
+    public class GetProfileInfos : IReturn<DeviceProfileInfo[]>
     {
     {
     }
     }
 
 
@@ -53,7 +52,7 @@ namespace MediaBrowser.Api.Dlna
 
 
         public object Get(GetProfileInfos request)
         public object Get(GetProfileInfos request)
         {
         {
-            var result = _dlnaManager.GetProfileInfos().ToList();
+            var result = _dlnaManager.GetProfileInfos().ToArray();
 
 
             return ToOptimizedResult(result);
             return ToOptimizedResult(result);
         }
         }

+ 3 - 6
MediaBrowser.Api/EnvironmentService.cs

@@ -226,7 +226,7 @@ namespace MediaBrowser.Api
                 return ToOptimizedSerializedResultUsingCache(GetNetworkShares(path).OrderBy(i => i.Path).ToList());
                 return ToOptimizedSerializedResultUsingCache(GetNetworkShares(path).OrderBy(i => i.Path).ToList());
             }
             }
 
 
-            return ToOptimizedSerializedResultUsingCache(GetFileSystemEntries(request).OrderBy(i => i.Path).ToList());
+            return ToOptimizedSerializedResultUsingCache(GetFileSystemEntries(request).ToList());
         }
         }
 
 
         public object Get(GetNetworkShares request)
         public object Get(GetNetworkShares request)
@@ -271,9 +271,7 @@ namespace MediaBrowser.Api
         /// <returns>System.Object.</returns>
         /// <returns>System.Object.</returns>
         public object Get(GetNetworkDevices request)
         public object Get(GetNetworkDevices request)
         {
         {
-            var result = _networkManager.GetNetworkDevices()
-                .OrderBy(i => i.Path)
-                .ToList();
+            var result = _networkManager.GetNetworkDevices().ToList();
 
 
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
         }
         }
@@ -300,7 +298,6 @@ namespace MediaBrowser.Api
         /// <returns>IEnumerable{FileSystemEntryInfo}.</returns>
         /// <returns>IEnumerable{FileSystemEntryInfo}.</returns>
         private IEnumerable<FileSystemEntryInfo> GetFileSystemEntries(GetDirectoryContents request)
         private IEnumerable<FileSystemEntryInfo> GetFileSystemEntries(GetDirectoryContents request)
         {
         {
-            // using EnumerateFileSystemInfos doesn't handle reparse points (symlinks)
             var entries = _fileSystem.GetFileSystemEntries(request.Path).Where(i =>
             var entries = _fileSystem.GetFileSystemEntries(request.Path).Where(i =>
             {
             {
                 if (!request.IncludeHidden && i.IsHidden)
                 if (!request.IncludeHidden && i.IsHidden)
@@ -329,7 +326,7 @@ namespace MediaBrowser.Api
                 Path = f.FullName,
                 Path = f.FullName,
                 Type = f.IsDirectory ? FileSystemEntryType.Directory : FileSystemEntryType.File
                 Type = f.IsDirectory ? FileSystemEntryType.Directory : FileSystemEntryType.File
 
 
-            }).ToList();
+            });
         }
         }
 
 
         public object Get(GetParentPath request)
         public object Get(GetParentPath request)

+ 1 - 1
MediaBrowser.Api/FilterService.cs

@@ -108,7 +108,7 @@ namespace MediaBrowser.Api
                 EnableTotalRecordCount = false,
                 EnableTotalRecordCount = false,
                 DtoOptions = new Controller.Dto.DtoOptions
                 DtoOptions = new Controller.Dto.DtoOptions
                 {
                 {
-                    Fields = new List<ItemFields> { ItemFields.Genres, ItemFields.Tags },
+                    Fields = new ItemFields[] { ItemFields.Genres, ItemFields.Tags },
                     EnableImages = false,
                     EnableImages = false,
                     EnableUserData = false
                     EnableUserData = false
                 }
                 }

+ 8 - 52
MediaBrowser.Api/GamesService.cs

@@ -28,21 +28,7 @@ namespace MediaBrowser.Api
     /// Class GetGameSystemSummaries
     /// Class GetGameSystemSummaries
     /// </summary>
     /// </summary>
     [Route("/Games/SystemSummaries", "GET", Summary = "Finds games similar to a given game.")]
     [Route("/Games/SystemSummaries", "GET", Summary = "Finds games similar to a given game.")]
-    public class GetGameSystemSummaries : IReturn<List<GameSystemSummary>>
-    {
-        /// <summary>
-        /// Gets or sets the user id.
-        /// </summary>
-        /// <value>The user id.</value>
-        [ApiMember(Name = "UserId", Description = "Optional. Filter by user id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
-        public string UserId { get; set; }
-    }
-
-    /// <summary>
-    /// Class GetGameSystemSummaries
-    /// </summary>
-    [Route("/Games/PlayerIndex", "GET", Summary = "Gets an index of players (1-x) and the number of games listed under each")]
-    public class GetPlayerIndex : IReturn<List<ItemIndex>>
+    public class GetGameSystemSummaries : IReturn<GameSystemSummary[]>
     {
     {
         /// <summary>
         /// <summary>
         /// Gets or sets the user id.
         /// Gets or sets the user id.
@@ -117,47 +103,17 @@ namespace MediaBrowser.Api
                     EnableImages = false
                     EnableImages = false
                 }
                 }
             };
             };
-            var gameSystems = _libraryManager.GetItemList(query)
-                .Cast<GameSystem>()
-                .ToList();
 
 
-            var result = gameSystems
+            var result = _libraryManager.GetItemList(query)
+                .Cast<GameSystem>()
                 .Select(i => GetSummary(i, user))
                 .Select(i => GetSummary(i, user))
-                .ToList();
+                .ToArray();
 
 
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
         }
         }
 
 
         private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
         private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
 
 
-        public object Get(GetPlayerIndex request)
-        {
-            var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId);
-            var query = new InternalItemsQuery(user)
-            {
-                IncludeItemTypes = new[] { typeof(Game).Name },
-                DtoOptions = new DtoOptions(false)
-                {
-                    EnableImages = false
-                }
-            };
-            var games = _libraryManager.GetItemList(query)
-                .Cast<Game>()
-                .ToList();
-
-            var lookup = games
-                .ToLookup(i => i.PlayersSupported ?? -1)
-                .OrderBy(i => i.Key)
-                .Select(i => new ItemIndex
-                {
-                    ItemCount = i.Count(),
-                    Name = i.Key == -1 ? string.Empty : i.Key.ToString(UsCulture)
-                })
-                .ToList();
-
-            return ToOptimizedSerializedResultUsingCache(lookup);
-        }
-
         /// <summary>
         /// <summary>
         /// Gets the summary.
         /// Gets the summary.
         /// </summary>
         /// </summary>
@@ -183,15 +139,15 @@ namespace MediaBrowser.Api
                     }
                     }
                 });
                 });
 
 
-            var games = items.Cast<Game>().ToList();
+            var games = items.Cast<Game>().ToArray();
 
 
             summary.ClientInstalledGameCount = games.Count(i => i.IsPlaceHolder);
             summary.ClientInstalledGameCount = games.Count(i => i.IsPlaceHolder);
 
 
-            summary.GameCount = games.Count;
+            summary.GameCount = games.Length;
 
 
             summary.GameFileExtensions = games.Where(i => !i.IsPlaceHolder).Select(i => Path.GetExtension(i.Path))
             summary.GameFileExtensions = games.Where(i => !i.IsPlaceHolder).Select(i => Path.GetExtension(i.Path))
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
-                .ToList();
+                .ToArray();
 
 
             return summary;
             return summary;
         }
         }
@@ -234,7 +190,7 @@ namespace MediaBrowser.Api
 
 
             var result = new QueryResult<BaseItemDto>
             var result = new QueryResult<BaseItemDto>
             {
             {
-                Items = returnList.ToArray(returnList.Count),
+                Items = returnList,
 
 
                 TotalRecordCount = itemsResult.Count
                 TotalRecordCount = itemsResult.Count
             };
             };

+ 2 - 2
MediaBrowser.Api/IHasItemFields.cs

@@ -27,7 +27,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// </summary>
         /// <param name="request">The request.</param>
         /// <param name="request">The request.</param>
         /// <returns>IEnumerable{ItemFields}.</returns>
         /// <returns>IEnumerable{ItemFields}.</returns>
-        public static IEnumerable<ItemFields> GetItemFields(this IHasItemFields request)
+        public static ItemFields[] GetItemFields(this IHasItemFields request)
         {
         {
             var val = request.Fields;
             var val = request.Fields;
 
 
@@ -46,7 +46,7 @@ namespace MediaBrowser.Api
                 }
                 }
                 return null;
                 return null;
 
 
-            }).Where(i => i.HasValue).Select(i => i.Value);
+            }).Where(i => i.HasValue).Select(i => i.Value).ToArray();
         }
         }
     }
     }
 }
 }

+ 1 - 14
MediaBrowser.Api/Images/ImageService.cs

@@ -556,20 +556,7 @@ namespace MediaBrowser.Api.Images
                 throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type));
                 throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type));
             }
             }
 
 
-            var supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.ImageEnhancers.Where(i =>
-            {
-                try
-                {
-                    return i.Supports(item, request.Type);
-                }
-                catch (Exception ex)
-                {
-                    Logger.ErrorException("Error in image enhancer: {0}", ex, i.GetType().Name);
-
-                    return false;
-                }
-
-            }).ToList() : new List<IImageEnhancer>();
+            var supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.GetSupportedEnhancers(item, request.Type) : new List<IImageEnhancer>();
 
 
             var cropwhitespace = request.Type == ImageType.Logo || 
             var cropwhitespace = request.Type == ImageType.Logo || 
                 request.Type == ImageType.Art
                 request.Type == ImageType.Art

+ 5 - 5
MediaBrowser.Api/Images/RemoteImageService.cs

@@ -150,7 +150,7 @@ namespace MediaBrowser.Api.Images
 
 
             }, CancellationToken.None).ConfigureAwait(false);
             }, CancellationToken.None).ConfigureAwait(false);
 
 
-            var imagesList = images.ToList();
+            var imagesList = images.ToArray();
 
 
             var allProviders = _providerManager.GetRemoteImageProviderInfo(item);
             var allProviders = _providerManager.GetRemoteImageProviderInfo(item);
 
 
@@ -161,22 +161,22 @@ namespace MediaBrowser.Api.Images
 
 
             var result = new RemoteImageResult
             var result = new RemoteImageResult
             {
             {
-                TotalRecordCount = imagesList.Count,
+                TotalRecordCount = imagesList.Length,
                 Providers = allProviders.Select(i => i.Name)
                 Providers = allProviders.Select(i => i.Name)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
                 .Distinct(StringComparer.OrdinalIgnoreCase)
-                .ToList()
+                .ToArray()
             };
             };
 
 
             if (request.StartIndex.HasValue)
             if (request.StartIndex.HasValue)
             {
             {
                 imagesList = imagesList.Skip(request.StartIndex.Value)
                 imagesList = imagesList.Skip(request.StartIndex.Value)
-                    .ToList();
+                    .ToArray();
             }
             }
 
 
             if (request.Limit.HasValue)
             if (request.Limit.HasValue)
             {
             {
                 imagesList = imagesList.Take(request.Limit.Value)
                 imagesList = imagesList.Take(request.Limit.Value)
-                    .ToList();
+                    .ToArray();
             }
             }
 
 
             result.Images = imagesList;
             result.Images = imagesList;

+ 6 - 6
MediaBrowser.Api/ItemUpdateService.cs

@@ -64,8 +64,8 @@ namespace MediaBrowser.Api
 
 
             var info = new MetadataEditorInfo
             var info = new MetadataEditorInfo
             {
             {
-                ParentalRatingOptions = _localizationManager.GetParentalRatings().ToList(),
-                ExternalIdInfos = _providerManager.GetExternalIdInfos(item).ToList(),
+                ParentalRatingOptions = _localizationManager.GetParentalRatings(),
+                ExternalIdInfos = _providerManager.GetExternalIdInfos(item).ToArray(),
                 Countries = _localizationManager.GetCountries(),
                 Countries = _localizationManager.GetCountries(),
                 Cultures = _localizationManager.GetCultures()
                 Cultures = _localizationManager.GetCultures()
             };
             };
@@ -78,14 +78,14 @@ namespace MediaBrowser.Api
 
 
                 if (string.IsNullOrWhiteSpace(inheritedContentType) || !string.IsNullOrWhiteSpace(configuredContentType))
                 if (string.IsNullOrWhiteSpace(inheritedContentType) || !string.IsNullOrWhiteSpace(configuredContentType))
                 {
                 {
-                    info.ContentTypeOptions = GetContentTypeOptions(true);
+                    info.ContentTypeOptions = GetContentTypeOptions(true).ToArray();
                     info.ContentType = configuredContentType;
                     info.ContentType = configuredContentType;
 
 
                     if (string.IsNullOrWhiteSpace(inheritedContentType) || string.Equals(inheritedContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
                     if (string.IsNullOrWhiteSpace(inheritedContentType) || string.Equals(inheritedContentType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
                     {
                     {
                         info.ContentTypeOptions = info.ContentTypeOptions
                         info.ContentTypeOptions = info.ContentTypeOptions
                             .Where(i => string.IsNullOrWhiteSpace(i.Value) || string.Equals(i.Value, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
                             .Where(i => string.IsNullOrWhiteSpace(i.Value) || string.Equals(i.Value, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
-                            .ToList();
+                            .ToArray();
                     }
                     }
                 }
                 }
             }
             }
@@ -209,7 +209,7 @@ namespace MediaBrowser.Api
             // Do this first so that metadata savers can pull the updates from the database.
             // Do this first so that metadata savers can pull the updates from the database.
             if (request.People != null)
             if (request.People != null)
             {
             {
-                await _libraryManager.UpdatePeople(item, request.People.Select(x => new PersonInfo { Name = x.Name, Role = x.Role, Type = x.Type }).ToList());
+                _libraryManager.UpdatePeople(item, request.People.Select(x => new PersonInfo { Name = x.Name, Role = x.Role, Type = x.Type }).ToList());
             }
             }
 
 
             UpdateItem(request, item);
             UpdateItem(request, item);
@@ -352,7 +352,7 @@ namespace MediaBrowser.Api
                     hasArtists.Artists = request
                     hasArtists.Artists = request
                         .ArtistItems
                         .ArtistItems
                         .Select(i => i.Name)
                         .Select(i => i.Name)
-                        .ToList();
+                        .ToArray();
                 }
                 }
             }
             }
 
 

+ 8 - 8
MediaBrowser.Api/Library/LibraryService.cs

@@ -227,7 +227,7 @@ namespace MediaBrowser.Api.Library
 
 
     [Route("/Library/MediaFolders", "GET", Summary = "Gets all user media folders.")]
     [Route("/Library/MediaFolders", "GET", Summary = "Gets all user media folders.")]
     [Authenticated]
     [Authenticated]
-    public class GetMediaFolders : IReturn<ItemsResult>
+    public class GetMediaFolders : IReturn<QueryResult<BaseItemDto>>
     {
     {
         [ApiMember(Name = "IsHidden", Description = "Optional. Filter by folders that are marked hidden, or not.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
         [ApiMember(Name = "IsHidden", Description = "Optional. Filter by folders that are marked hidden, or not.", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
         public bool? IsHidden { get; set; }
         public bool? IsHidden { get; set; }
@@ -400,7 +400,7 @@ namespace MediaBrowser.Api.Library
                 });
                 });
             }
             }
 
 
-            return new ItemsResult();
+            return new QueryResult<BaseItemDto>();
         }
         }
 
 
         public object Get(GetMediaFolders request)
         public object Get(GetMediaFolders request)
@@ -416,7 +416,7 @@ namespace MediaBrowser.Api.Library
 
 
             var dtoOptions = GetDtoOptions(_authContext, request);
             var dtoOptions = GetDtoOptions(_authContext, request);
 
 
-            var result = new ItemsResult
+            var result = new QueryResult<BaseItemDto>
             {
             {
                 TotalRecordCount = items.Count,
                 TotalRecordCount = items.Count,
 
 
@@ -525,18 +525,18 @@ namespace MediaBrowser.Api.Library
             });
             });
         }
         }
 
 
-        private async void LogDownload(BaseItem item, User user, AuthorizationInfo auth)
+        private void LogDownload(BaseItem item, User user, AuthorizationInfo auth)
         {
         {
             try
             try
             {
             {
-                await _activityManager.Create(new ActivityLogEntry
+                _activityManager.Create(new ActivityLogEntry
                 {
                 {
                     Name = string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name),
                     Name = string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name),
                     Type = "UserDownloadingContent",
                     Type = "UserDownloadingContent",
                     ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), auth.Client, auth.Device),
                     ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), auth.Client, auth.Device),
                     UserId = auth.UserId
                     UserId = auth.UserId
 
 
-                }).ConfigureAwait(false);
+                });
             }
             }
             catch
             catch
             {
             {
@@ -615,7 +615,7 @@ namespace MediaBrowser.Api.Library
                 parent = parent.GetParent();
                 parent = parent.GetParent();
             }
             }
 
 
-            return baseItemDtos.ToList();
+            return baseItemDtos;
         }
         }
 
 
         private BaseItem TranslateParentItem(BaseItem item, User user)
         private BaseItem TranslateParentItem(BaseItem item, User user)
@@ -915,7 +915,7 @@ namespace MediaBrowser.Api.Library
              : request.IncludeItemTypes.Split(',');
              : request.IncludeItemTypes.Split(',');
 
 
             var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
             var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
-            
+
             var query = new InternalItemsQuery(user)
             var query = new InternalItemsQuery(user)
             {
             {
                 IncludeItemTypes = includeTypes,
                 IncludeItemTypes = includeTypes,

+ 11 - 9
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -648,7 +648,7 @@ namespace MediaBrowser.Api.LiveTv
     {
     {
         public List<TunerChannelMapping> TunerChannels { get; set; }
         public List<TunerChannelMapping> TunerChannels { get; set; }
         public List<NameIdPair> ProviderChannels { get; set; }
         public List<NameIdPair> ProviderChannels { get; set; }
-        public List<NameValuePair> Mappings { get; set; }
+        public NameValuePair[] Mappings { get; set; }
         public string ProviderName { get; set; }
         public string ProviderName { get; set; }
     }
     }
 
 
@@ -789,7 +789,7 @@ namespace MediaBrowser.Api.LiveTv
             var providerChannels = await _liveTvManager.GetChannelsFromListingsProviderData(request.ProviderId, CancellationToken.None)
             var providerChannels = await _liveTvManager.GetChannelsFromListingsProviderData(request.ProviderId, CancellationToken.None)
                      .ConfigureAwait(false);
                      .ConfigureAwait(false);
 
 
-            var mappings = listingsProviderInfo.ChannelMappings.ToList();
+            var mappings = listingsProviderInfo.ChannelMappings;
 
 
             var result = new ChannelMappingOptions
             var result = new ChannelMappingOptions
             {
             {
@@ -862,7 +862,7 @@ namespace MediaBrowser.Api.LiveTv
         {
         {
             var config = GetConfiguration();
             var config = GetConfiguration();
 
 
-            config.TunerHosts = config.TunerHosts.Where(i => !string.Equals(request.Id, i.Id, StringComparison.OrdinalIgnoreCase)).ToList();
+            config.TunerHosts = config.TunerHosts.Where(i => !string.Equals(request.Id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray();
 
 
             _config.SaveConfiguration("livetv", config);
             _config.SaveConfiguration("livetv", config);
         }
         }
@@ -922,9 +922,8 @@ namespace MediaBrowser.Api.LiveTv
 
 
             options.AddCurrentProgram = request.AddCurrentProgram;
             options.AddCurrentProgram = request.AddCurrentProgram;
 
 
-            var returnList = (await _dtoService.GetBaseItemDtos(channelResult.Items, options, user)
+            var returnArray = (await _dtoService.GetBaseItemDtos(channelResult.Items, options, user)
                 .ConfigureAwait(false));
                 .ConfigureAwait(false));
-            var returnArray = returnList.ToArray(returnList.Count);
 
 
             var result = new QueryResult<BaseItemDto>
             var result = new QueryResult<BaseItemDto>
             {
             {
@@ -937,10 +936,13 @@ namespace MediaBrowser.Api.LiveTv
 
 
         private void RemoveFields(DtoOptions options)
         private void RemoveFields(DtoOptions options)
         {
         {
-            options.Fields.Remove(ItemFields.CanDelete);
-            options.Fields.Remove(ItemFields.CanDownload);
-            options.Fields.Remove(ItemFields.DisplayPreferencesId);
-            options.Fields.Remove(ItemFields.Etag);
+            var fields = options.Fields.ToList();
+
+            fields.Remove(ItemFields.CanDelete);
+            fields.Remove(ItemFields.CanDownload);
+            fields.Remove(ItemFields.DisplayPreferencesId);
+            fields.Remove(ItemFields.Etag);
+            options.Fields = fields.ToArray(fields.Count);
         }
         }
 
 
         public object Get(GetChannel request)
         public object Get(GetChannel request)

+ 6 - 7
MediaBrowser.Api/LocalizationService.cs

@@ -2,7 +2,6 @@
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Linq;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
 
 
 namespace MediaBrowser.Api
 namespace MediaBrowser.Api
@@ -11,7 +10,7 @@ namespace MediaBrowser.Api
     /// Class GetCultures
     /// Class GetCultures
     /// </summary>
     /// </summary>
     [Route("/Localization/Cultures", "GET", Summary = "Gets known cultures")]
     [Route("/Localization/Cultures", "GET", Summary = "Gets known cultures")]
-    public class GetCultures : IReturn<List<CultureDto>>
+    public class GetCultures : IReturn<CultureDto[]>
     {
     {
     }
     }
 
 
@@ -19,7 +18,7 @@ namespace MediaBrowser.Api
     /// Class GetCountries
     /// Class GetCountries
     /// </summary>
     /// </summary>
     [Route("/Localization/Countries", "GET", Summary = "Gets known countries")]
     [Route("/Localization/Countries", "GET", Summary = "Gets known countries")]
-    public class GetCountries : IReturn<List<CountryInfo>>
+    public class GetCountries : IReturn<CountryInfo[]>
     {
     {
     }
     }
 
 
@@ -27,7 +26,7 @@ namespace MediaBrowser.Api
     /// Class ParentalRatings
     /// Class ParentalRatings
     /// </summary>
     /// </summary>
     [Route("/Localization/ParentalRatings", "GET", Summary = "Gets known parental ratings")]
     [Route("/Localization/ParentalRatings", "GET", Summary = "Gets known parental ratings")]
-    public class GetParentalRatings : IReturn<List<ParentalRating>>
+    public class GetParentalRatings : IReturn<ParentalRating[]>
     {
     {
     }
     }
 
 
@@ -35,7 +34,7 @@ namespace MediaBrowser.Api
     /// Class ParentalRatings
     /// Class ParentalRatings
     /// </summary>
     /// </summary>
     [Route("/Localization/Options", "GET", Summary = "Gets localization options")]
     [Route("/Localization/Options", "GET", Summary = "Gets localization options")]
-    public class GetLocalizationOptions : IReturn<List<LocalizatonOption>>
+    public class GetLocalizationOptions : IReturn<LocalizatonOption[]>
     {
     {
     }
     }
 
 
@@ -66,14 +65,14 @@ namespace MediaBrowser.Api
         /// <returns>System.Object.</returns>
         /// <returns>System.Object.</returns>
         public object Get(GetParentalRatings request)
         public object Get(GetParentalRatings request)
         {
         {
-            var result = _localization.GetParentalRatings().ToList();
+            var result = _localization.GetParentalRatings();
 
 
             return ToOptimizedResult(result);
             return ToOptimizedResult(result);
         }
         }
 
 
         public object Get(GetLocalizationOptions request)
         public object Get(GetLocalizationOptions request)
         {
         {
-            var result = _localization.GetLocalizationOptions().ToList();
+            var result = _localization.GetLocalizationOptions();
 
 
             return ToOptimizedResult(result);
             return ToOptimizedResult(result);
         }
         }

+ 0 - 1
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -97,7 +97,6 @@
     <Compile Include="Movies\MoviesService.cs" />
     <Compile Include="Movies\MoviesService.cs" />
     <Compile Include="NewsService.cs" />
     <Compile Include="NewsService.cs" />
     <Compile Include="NotificationsService.cs" />
     <Compile Include="NotificationsService.cs" />
-    <Compile Include="PackageReviewService.cs" />
     <Compile Include="PackageService.cs" />
     <Compile Include="PackageService.cs" />
     <Compile Include="PluginService.cs" />
     <Compile Include="PluginService.cs" />
     <Compile Include="Images\RemoteImageService.cs" />
     <Compile Include="Images\RemoteImageService.cs" />

+ 4 - 5
MediaBrowser.Api/Movies/CollectionService.cs

@@ -4,7 +4,6 @@ using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Collections;
 using MediaBrowser.Model.Collections;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
 
 
@@ -71,8 +70,8 @@ namespace MediaBrowser.Api.Movies
                 IsLocked = request.IsLocked,
                 IsLocked = request.IsLocked,
                 Name = request.Name,
                 Name = request.Name,
                 ParentId = parentId,
                 ParentId = parentId,
-                ItemIdList = (request.Ids ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(i => new Guid(i)).ToList(),
-                UserIds = new List<Guid> { new Guid(userId) }
+                ItemIdList = SplitValue(request.Ids, ','),
+                UserIds = new string[] { userId }
 
 
             }).ConfigureAwait(false);
             }).ConfigureAwait(false);
 
 
@@ -88,14 +87,14 @@ namespace MediaBrowser.Api.Movies
 
 
         public void Post(AddToCollection request)
         public void Post(AddToCollection request)
         {
         {
-            var task = _collectionManager.AddToCollection(new Guid(request.Id), request.Ids.Split(',').Select(i => new Guid(i)));
+            var task = _collectionManager.AddToCollection(new Guid(request.Id), SplitValue(request.Ids, ','));
 
 
             Task.WaitAll(task);
             Task.WaitAll(task);
         }
         }
 
 
         public void Delete(RemoveFromCollection request)
         public void Delete(RemoveFromCollection request)
         {
         {
-            var task = _collectionManager.RemoveFromCollection(new Guid(request.Id), request.Ids.Split(',').Select(i => new Guid(i)));
+            var task = _collectionManager.RemoveFromCollection(new Guid(request.Id), SplitValue(request.Ids, ','));
 
 
             Task.WaitAll(task);
             Task.WaitAll(task);
         }
         }

+ 5 - 5
MediaBrowser.Api/Movies/MoviesService.cs

@@ -170,7 +170,7 @@ namespace MediaBrowser.Api.Movies
 
 
             var result = new QueryResult<BaseItemDto>
             var result = new QueryResult<BaseItemDto>
             {
             {
-                Items = returnList.ToArray(returnList.Count),
+                Items = returnList,
 
 
                 TotalRecordCount = itemsResult.Count
                 TotalRecordCount = itemsResult.Count
             };
             };
@@ -320,7 +320,7 @@ namespace MediaBrowser.Api.Movies
                         BaselineItemName = name,
                         BaselineItemName = name,
                         CategoryId = name.GetMD5().ToString("N"),
                         CategoryId = name.GetMD5().ToString("N"),
                         RecommendationType = type,
                         RecommendationType = type,
-                        Items = returnItems.ToArray(returnItems.Count)
+                        Items = returnItems
                     };
                     };
                 }
                 }
             }
             }
@@ -360,7 +360,7 @@ namespace MediaBrowser.Api.Movies
                         BaselineItemName = name,
                         BaselineItemName = name,
                         CategoryId = name.GetMD5().ToString("N"),
                         CategoryId = name.GetMD5().ToString("N"),
                         RecommendationType = type,
                         RecommendationType = type,
-                        Items = returnItems.ToArray(returnItems.Count)
+                        Items = returnItems
                     };
                     };
                 }
                 }
             }
             }
@@ -397,7 +397,7 @@ namespace MediaBrowser.Api.Movies
                         BaselineItemName = item.Name,
                         BaselineItemName = item.Name,
                         CategoryId = item.Id.ToString("N"),
                         CategoryId = item.Id.ToString("N"),
                         RecommendationType = type,
                         RecommendationType = type,
-                        Items = returnItems.ToArray(returnItems.Count)
+                        Items = returnItems
                     };
                     };
                 }
                 }
             }
             }
@@ -426,7 +426,7 @@ namespace MediaBrowser.Api.Movies
         {
         {
             var people = _libraryManager.GetPeople(new InternalPeopleQuery
             var people = _libraryManager.GetPeople(new InternalPeopleQuery
             {
             {
-                PersonTypes = new List<string>
+                PersonTypes = new string[]
                 {
                 {
                     PersonType.Director
                     PersonType.Director
                 }
                 }

+ 2 - 1
MediaBrowser.Api/Movies/TrailersService.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Controller.Collections;
 using MediaBrowser.Controller.Collections;
+using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
@@ -11,7 +12,7 @@ using MediaBrowser.Model.Services;
 namespace MediaBrowser.Api.Movies
 namespace MediaBrowser.Api.Movies
 {
 {
     [Route("/Trailers", "GET", Summary = "Finds movies and trailers similar to a given trailer.")]
     [Route("/Trailers", "GET", Summary = "Finds movies and trailers similar to a given trailer.")]
-    public class Getrailers : BaseItemsRequest, IReturn<ItemsResult>
+    public class Getrailers : BaseItemsRequest, IReturn<QueryResult<BaseItemDto>>
     {
     {
     }
     }
 
 

+ 9 - 3
MediaBrowser.Api/Music/InstantMixService.cs

@@ -8,6 +8,7 @@ using MediaBrowser.Model.Querying;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Extensions;
 
 
@@ -185,15 +186,20 @@ namespace MediaBrowser.Api.Music
         {
         {
             var list = items;
             var list = items;
 
 
-            var result = new ItemsResult
+            var result = new QueryResult<BaseItemDto>
             {
             {
                 TotalRecordCount = list.Count
                 TotalRecordCount = list.Count
             };
             };
 
 
-            var returnList = (await _dtoService.GetBaseItemDtos(list.Take(request.Limit ?? list.Count), dtoOptions, user)
+            if (request.Limit.HasValue)
+            {
+                list = list.Take(request.Limit.Value).ToList();
+            }
+
+            var returnList = (await _dtoService.GetBaseItemDtos(list, dtoOptions, user)
                 .ConfigureAwait(false));
                 .ConfigureAwait(false));
 
 
-            result.Items = returnList.ToArray(returnList.Count);
+            result.Items = returnList;
 
 
             return ToOptimizedResult(result);
             return ToOptimizedResult(result);
         }
         }

+ 1 - 1
MediaBrowser.Api/NotificationsService.cs

@@ -99,7 +99,7 @@ namespace MediaBrowser.Api
 
 
         public object Get(GetNotificationTypes request)
         public object Get(GetNotificationTypes request)
         {
         {
-            var result = _notificationManager.GetNotificationTypes().ToList();
+            var result = _notificationManager.GetNotificationTypes();
 
 
             return ToOptimizedResult(result);
             return ToOptimizedResult(result);
         }
         }

+ 0 - 162
MediaBrowser.Api/PackageReviewService.cs

@@ -1,162 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Serialization;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Services;
-
-namespace MediaBrowser.Api
-{
-    /// <summary>
-    /// Class InstallPackage
-    /// </summary>
-    [Route("/Packages/Reviews/{Id}", "POST", Summary = "Creates or updates a package review")]
-    public class CreateReviewRequest : IReturnVoid
-    {
-        /// <summary>
-        /// Gets or sets the Id.
-        /// </summary>
-        /// <value>The Id.</value>
-        [ApiMember(Name = "Id", Description = "Package Id", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "POST")]
-        public int Id { get; set; }
-
-        /// <summary>
-        /// Gets or sets the rating.
-        /// </summary>
-        /// <value>The review.</value>
-        [ApiMember(Name = "Rating", Description = "The rating value (1-5)", IsRequired = true, DataType = "int", ParameterType = "query", Verb = "POST")]
-        public int Rating { get; set; }
-
-        /// <summary>
-        /// Gets or sets the recommend value.
-        /// </summary>
-        /// <value>Whether or not this review recommends this item.</value>
-        [ApiMember(Name = "Recommend", Description = "Whether or not this review recommends this item", IsRequired = true, DataType = "bool", ParameterType = "query", Verb = "POST")]
-        public bool Recommend { get; set; }
-
-        /// <summary>
-        /// Gets or sets the title.
-        /// </summary>
-        /// <value>The title.</value>
-        [ApiMember(Name = "Title", Description = "Optional short description of review.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
-        public string Title { get; set; }
-
-        /// <summary>
-        /// Gets or sets the full review.
-        /// </summary>
-        /// <value>The full review.</value>
-        [ApiMember(Name = "Review", Description = "Optional full review.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
-        public string Review { get; set; }
-    }
-
-    /// <summary>
-    /// Class InstallPackage
-    /// </summary>
-    [Route("/Packages/{Id}/Reviews", "GET", Summary = "Gets reviews for a package")]
-    public class ReviewRequest : IReturn<List<PackageReviewInfo>>
-    {
-        /// <summary>
-        /// Gets or sets the Id.
-        /// </summary>
-        /// <value>The Id.</value>
-        [ApiMember(Name = "Id", Description = "Package Id", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
-        public int Id { get; set; }
-
-        /// <summary>
-        /// Gets or sets the max rating.
-        /// </summary>
-        /// <value>The max rating.</value>
-        [ApiMember(Name = "MaxRating", Description = "Retrieve only reviews less than or equal to this", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
-        public int MaxRating { get; set; }
-
-        /// <summary>
-        /// Gets or sets the min rating.
-        /// </summary>
-        /// <value>The max rating.</value>
-        [ApiMember(Name = "MinRating", Description = "Retrieve only reviews greator than or equal to this", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
-        public int MinRating { get; set; }
-
-        /// <summary>
-        /// Only retrieve reviews with at least a short review.
-        /// </summary>
-        /// <value>True if should only get reviews with a title.</value>
-        [ApiMember(Name = "ForceTitle", Description = "Whether or not to restrict results to those with a title", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
-        public bool ForceTitle { get; set; }
-
-        /// <summary>
-        /// Gets or sets the limit for the query.
-        /// </summary>
-        /// <value>The max rating.</value>
-        [ApiMember(Name = "Limit", Description = "Limit the result to this many reviews (ordered by latest)", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
-        public int Limit { get; set; }
-
-    }
-
-    [Authenticated]
-    public class PackageReviewService : BaseApiService
-    {
-        private readonly IHttpClient _httpClient;
-        private readonly IJsonSerializer _serializer;
-        private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
-        private readonly IServerApplicationHost _appHost;
-
-        public PackageReviewService(IHttpClient httpClient, IJsonSerializer serializer, IServerApplicationHost appHost)
-        {
-            _httpClient = httpClient;
-            _serializer = serializer;
-            _appHost = appHost;
-        }
-
-        public async Task<object> Get(ReviewRequest request)
-        {
-            var parms = "?id=" + request.Id;
-
-            if (request.MaxRating > 0)
-            {
-                parms += "&max=" + request.MaxRating;
-            }
-            if (request.MinRating > 0)
-            {
-                parms += "&min=" + request.MinRating;
-            }
-            if (request.MinRating > 0)
-            {
-                parms += "&limit=" + request.Limit;
-            }
-            if (request.ForceTitle)
-            {
-                parms += "&title=true";
-            }
-
-            using (var result = await _httpClient.Get(MbAdminUrl + "/service/packageReview/retrieve" + parms, CancellationToken.None)
-                            .ConfigureAwait(false))
-            {
-                var reviews = _serializer.DeserializeFromStream<List<PackageReviewInfo>>(result);
-
-                return ToOptimizedResult(reviews);
-            }
-        }
-
-        public void Post(CreateReviewRequest request)
-        {
-            var reviewText = WebUtility.HtmlEncode(request.Review ?? string.Empty);
-            var title = WebUtility.HtmlEncode(request.Title ?? string.Empty);
-
-            var review = new Dictionary<string, string>
-                             { { "id", request.Id.ToString(CultureInfo.InvariantCulture) },
-                               { "mac", _appHost.SystemId },
-                               { "rating", request.Rating.ToString(CultureInfo.InvariantCulture) },
-                               { "recommend", request.Recommend.ToString() },
-                               { "title", title },
-                               { "review", reviewText },
-                             };
-
-            Task.WaitAll(_httpClient.Post(MbAdminUrl + "/service/packageReview/update", review, CancellationToken.None));
-        }
-    }
-}

+ 14 - 13
MediaBrowser.Api/PackageService.cs

@@ -40,7 +40,7 @@ namespace MediaBrowser.Api
     /// </summary>
     /// </summary>
     [Route("/Packages", "GET", Summary = "Gets available packages")]
     [Route("/Packages", "GET", Summary = "Gets available packages")]
     [Authenticated]
     [Authenticated]
-    public class GetPackages : IReturn<List<PackageInfo>>
+    public class GetPackages : IReturn<PackageInfo[]>
     {
     {
         /// <summary>
         /// <summary>
         /// Gets or sets the name.
         /// Gets or sets the name.
@@ -66,7 +66,7 @@ namespace MediaBrowser.Api
     /// </summary>
     /// </summary>
     [Route("/Packages/Updates", "GET", Summary = "Gets available package updates for currently installed packages")]
     [Route("/Packages/Updates", "GET", Summary = "Gets available package updates for currently installed packages")]
     [Authenticated(Roles = "Admin")]
     [Authenticated(Roles = "Admin")]
-    public class GetPackageVersionUpdates : IReturn<List<PackageVersionInfo>>
+    public class GetPackageVersionUpdates : IReturn<PackageVersionInfo[]>
     {
     {
         /// <summary>
         /// <summary>
         /// Gets or sets the name.
         /// Gets or sets the name.
@@ -148,24 +148,26 @@ namespace MediaBrowser.Api
         /// <returns>System.Object.</returns>
         /// <returns>System.Object.</returns>
         public object Get(GetPackageVersionUpdates request)
         public object Get(GetPackageVersionUpdates request)
         {
         {
-            var result = new List<PackageVersionInfo>();
+            PackageVersionInfo[] result = null;
 
 
             if (string.Equals(request.PackageType, "UserInstalled", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(request.PackageType, "UserInstalled", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase))
             {
             {
-                result.AddRange(_installationManager.GetAvailablePluginUpdates(_appHost.ApplicationVersion, false, CancellationToken.None).Result.ToList());
+                result = _installationManager.GetAvailablePluginUpdates(_appHost.ApplicationVersion, false, CancellationToken.None).Result.ToArray();
             }
             }
 
 
-            else if (string.Equals(request.PackageType, "System", StringComparison.OrdinalIgnoreCase) || string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase))
+            else if (string.Equals(request.PackageType, "System", StringComparison.OrdinalIgnoreCase) ||
+                     string.Equals(request.PackageType, "All", StringComparison.OrdinalIgnoreCase))
             {
             {
-                var updateCheckResult = _appHost.CheckForApplicationUpdate(CancellationToken.None, new SimpleProgress<double>()).Result;
+                var updateCheckResult = _appHost
+                    .CheckForApplicationUpdate(CancellationToken.None, new SimpleProgress<double>()).Result;
 
 
                 if (updateCheckResult.IsUpdateAvailable)
                 if (updateCheckResult.IsUpdateAvailable)
                 {
                 {
-                    result.Add(updateCheckResult.Package);
+                    result = new PackageVersionInfo[] {updateCheckResult.Package};
                 }
                 }
             }
             }
 
 
-            return ToOptimizedResult(result);
+            return ToOptimizedResult(result ?? new PackageVersionInfo[] { });
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -176,10 +178,9 @@ namespace MediaBrowser.Api
         public object Get(GetPackage request)
         public object Get(GetPackage request)
         {
         {
             var packages = _installationManager.GetAvailablePackages(CancellationToken.None, applicationVersion: _appHost.ApplicationVersion).Result;
             var packages = _installationManager.GetAvailablePackages(CancellationToken.None, applicationVersion: _appHost.ApplicationVersion).Result;
-            var list = packages.ToList();
 
 
-            var result = list.FirstOrDefault(p => string.Equals(p.guid, request.AssemblyGuid ?? "none", StringComparison.OrdinalIgnoreCase))
-                         ?? list.FirstOrDefault(p => p.name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
+            var result = packages.FirstOrDefault(p => string.Equals(p.guid, request.AssemblyGuid ?? "none", StringComparison.OrdinalIgnoreCase))
+                         ?? packages.FirstOrDefault(p => p.name.Equals(request.Name, StringComparison.OrdinalIgnoreCase));
 
 
             return ToOptimizedResult(result);
             return ToOptimizedResult(result);
         }
         }
@@ -191,7 +192,7 @@ namespace MediaBrowser.Api
         /// <returns>System.Object.</returns>
         /// <returns>System.Object.</returns>
         public async Task<object> Get(GetPackages request)
         public async Task<object> Get(GetPackages request)
         {
         {
-            var packages = await _installationManager.GetAvailablePackages(CancellationToken.None, false, request.PackageType, _appHost.ApplicationVersion).ConfigureAwait(false);
+            IEnumerable<PackageInfo> packages = await _installationManager.GetAvailablePackages(CancellationToken.None, false, request.PackageType, _appHost.ApplicationVersion).ConfigureAwait(false);
 
 
             if (!string.IsNullOrEmpty(request.TargetSystems))
             if (!string.IsNullOrEmpty(request.TargetSystems))
             {
             {
@@ -215,7 +216,7 @@ namespace MediaBrowser.Api
                 packages = packages.Where(p => p.enableInAppStore == request.IsAppStoreEnabled.Value);
                 packages = packages.Where(p => p.enableInAppStore == request.IsAppStoreEnabled.Value);
             }
             }
 
 
-            return ToOptimizedResult(packages.ToList());
+            return ToOptimizedResult(packages.ToArray());
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 5 - 7
MediaBrowser.Api/PlaylistService.cs

@@ -1,11 +1,11 @@
-using MediaBrowser.Controller.Dto;
+using System.Linq;
+using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Playlists;
 using MediaBrowser.Model.Playlists;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
-using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Extensions;
@@ -149,7 +149,7 @@ namespace MediaBrowser.Api
             var result = await _playlistManager.CreatePlaylist(new PlaylistCreationRequest
             var result = await _playlistManager.CreatePlaylist(new PlaylistCreationRequest
             {
             {
                 Name = request.Name,
                 Name = request.Name,
-                ItemIdList = (request.Ids ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(),
+                ItemIdList = SplitValue(request.Ids, ','),
                 UserId = request.UserId,
                 UserId = request.UserId,
                 MediaType = request.MediaType
                 MediaType = request.MediaType
 
 
@@ -193,10 +193,8 @@ namespace MediaBrowser.Api
 
 
             var dtoOptions = GetDtoOptions(_authContext, request);
             var dtoOptions = GetDtoOptions(_authContext, request);
 
 
-            var returnList = (await _dtoService.GetBaseItemDtos(items.Select(i => i.Item2), dtoOptions, user)
+            var dtos = (await _dtoService.GetBaseItemDtos(items.Select(i => i.Item2).ToList(), dtoOptions, user)
                 .ConfigureAwait(false));
                 .ConfigureAwait(false));
-            var dtos = returnList
-                   .ToArray(returnList.Count);
 
 
             var index = 0;
             var index = 0;
             foreach (var item in dtos)
             foreach (var item in dtos)
@@ -205,7 +203,7 @@ namespace MediaBrowser.Api
                 index++;
                 index++;
             }
             }
 
 
-            var result = new ItemsResult
+            var result = new QueryResult<BaseItemDto>
             {
             {
                 Items = dtos,
                 Items = dtos,
                 TotalRecordCount = count
                 TotalRecordCount = count

+ 5 - 6
MediaBrowser.Api/PluginService.cs

@@ -23,7 +23,7 @@ namespace MediaBrowser.Api
     /// </summary>
     /// </summary>
     [Route("/Plugins", "GET", Summary = "Gets a list of currently installed plugins")]
     [Route("/Plugins", "GET", Summary = "Gets a list of currently installed plugins")]
     [Authenticated]
     [Authenticated]
-    public class GetPlugins : IReturn<List<PluginInfo>>
+    public class GetPlugins : IReturn<PluginInfo[]>
     {
     {
         public bool? IsAppStoreEnabled { get; set; }
         public bool? IsAppStoreEnabled { get; set; }
     }
     }
@@ -195,14 +195,13 @@ namespace MediaBrowser.Api
         /// <returns>System.Object.</returns>
         /// <returns>System.Object.</returns>
         public async Task<object> Get(GetPlugins request)
         public async Task<object> Get(GetPlugins request)
         {
         {
-            var result = _appHost.Plugins.OrderBy(p => p.Name).Select(p => p.GetPluginInfo()).ToList();
+            var result = _appHost.Plugins.OrderBy(p => p.Name).Select(p => p.GetPluginInfo()).ToArray();
             var requireAppStoreEnabled = request.IsAppStoreEnabled.HasValue && request.IsAppStoreEnabled.Value;
             var requireAppStoreEnabled = request.IsAppStoreEnabled.HasValue && request.IsAppStoreEnabled.Value;
 
 
             // Don't fail just on account of image url's
             // Don't fail just on account of image url's
             try
             try
             {
             {
-                var packages = (await _installationManager.GetAvailablePackagesWithoutRegistrationInfo(CancellationToken.None))
-                    .ToList();
+                var packages = (await _installationManager.GetAvailablePackagesWithoutRegistrationInfo(CancellationToken.None));
 
 
                 foreach (var plugin in result)
                 foreach (var plugin in result)
                 {
                 {
@@ -223,7 +222,7 @@ namespace MediaBrowser.Api
                             return pkg != null && pkg.enableInAppStore;
                             return pkg != null && pkg.enableInAppStore;
                   
                   
                         })
                         })
-                        .ToList();
+                        .ToArray();
                 }
                 }
             }
             }
             catch
             catch
@@ -232,7 +231,7 @@ namespace MediaBrowser.Api
                 // Play it safe here
                 // Play it safe here
                 if (requireAppStoreEnabled)
                 if (requireAppStoreEnabled)
                 {
                 {
-                    result = new List<PluginInfo>();
+                    result = new PluginInfo[] { };
                 }
                 }
             }
             }
 
 

+ 1 - 1
MediaBrowser.Api/Reports/Data/ReportBuilder.cs

@@ -533,7 +533,7 @@ namespace MediaBrowser.Api.Reports
                     break;
                     break;
 
 
                 case HeaderMetadata.Tracks:
                 case HeaderMetadata.Tracks:
-                    option.Column = (i, r) => this.GetObject<MusicAlbum, List<Audio>>(i, (x) => x.Tracks.ToList(), new List<Audio>()).Count();
+                    option.Column = (i, r) => this.GetObject<MusicAlbum, List<Audio>>(i, (x) => x.Tracks.Cast<Audio>().ToList(), new List<Audio>()).Count();
                     break;
                     break;
 
 
                 case HeaderMetadata.Audio:
                 case HeaderMetadata.Audio:

+ 2 - 2
MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs

@@ -27,7 +27,7 @@ namespace MediaBrowser.Api.ScheduledTasks
     /// Class GetScheduledTasks
     /// Class GetScheduledTasks
     /// </summary>
     /// </summary>
     [Route("/ScheduledTasks", "GET", Summary = "Gets scheduled tasks")]
     [Route("/ScheduledTasks", "GET", Summary = "Gets scheduled tasks")]
-    public class GetScheduledTasks : IReturn<List<TaskInfo>>
+    public class GetScheduledTasks : IReturn<TaskInfo[]>
     {
     {
         [ApiMember(Name = "IsHidden", Description = "Optional filter tasks that are hidden, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
         [ApiMember(Name = "IsHidden", Description = "Optional filter tasks that are hidden, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
         public bool? IsHidden { get; set; }
         public bool? IsHidden { get; set; }
@@ -158,7 +158,7 @@ namespace MediaBrowser.Api.ScheduledTasks
             
             
             var infos = result
             var infos = result
                 .Select(ScheduledTaskHelpers.GetTaskInfo)
                 .Select(ScheduledTaskHelpers.GetTaskInfo)
-                .ToList();
+                .ToArray();
 
 
             return ToOptimizedResult(infos);
             return ToOptimizedResult(infos);
         }
         }

+ 4 - 4
MediaBrowser.Api/SearchService.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Drawing;
+using System.Linq;
+using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Audio;
@@ -7,7 +8,6 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Search;
 using MediaBrowser.Model.Search;
-using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
@@ -240,7 +240,7 @@ namespace MediaBrowser.Api
 
 
             if (album != null)
             if (album != null)
             {
             {
-                result.Artists = album.Artists.ToArray();
+                result.Artists = album.Artists;
                 result.AlbumArtist = album.AlbumArtist;
                 result.AlbumArtist = album.AlbumArtist;
             }
             }
 
 
@@ -250,7 +250,7 @@ namespace MediaBrowser.Api
             {
             {
                 result.Album = song.Album;
                 result.Album = song.Album;
                 result.AlbumArtist = song.AlbumArtists.FirstOrDefault();
                 result.AlbumArtist = song.AlbumArtists.FirstOrDefault();
-                result.Artists = song.Artists.ToArray();
+                result.Artists = song.Artists;
             }
             }
 
 
             if (!string.IsNullOrWhiteSpace(item.ChannelId))
             if (!string.IsNullOrWhiteSpace(item.ChannelId))

+ 6 - 9
MediaBrowser.Api/Session/SessionsService.cs

@@ -18,7 +18,7 @@ namespace MediaBrowser.Api.Session
     /// </summary>
     /// </summary>
     [Route("/Sessions", "GET", Summary = "Gets a list of sessions")]
     [Route("/Sessions", "GET", Summary = "Gets a list of sessions")]
     [Authenticated]
     [Authenticated]
-    public class GetSessions : IReturn<List<SessionInfoDto>>
+    public class GetSessions : IReturn<SessionInfoDto[]>
     {
     {
         [ApiMember(Name = "ControllableByUserId", Description = "Optional. Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         [ApiMember(Name = "ControllableByUserId", Description = "Optional. Filter by sessions that a given user is allowed to remote control.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string ControllableByUserId { get; set; }
         public string ControllableByUserId { get; set; }
@@ -313,14 +313,13 @@ namespace MediaBrowser.Api.Session
 
 
         public void Delete(RevokeKey request)
         public void Delete(RevokeKey request)
         {
         {
-            var task = _sessionManager.RevokeToken(request.Key);
+            _sessionManager.RevokeToken(request.Key);
 
 
-            Task.WaitAll(task);
         }
         }
 
 
         public void Post(CreateKey request)
         public void Post(CreateKey request)
         {
         {
-            var task = _authRepo.Create(new AuthenticationInfo
+            _authRepo.Create(new AuthenticationInfo
             {
             {
                 AppName = request.App,
                 AppName = request.App,
                 IsActive = true,
                 IsActive = true,
@@ -328,8 +327,6 @@ namespace MediaBrowser.Api.Session
                 DateCreated = DateTime.UtcNow
                 DateCreated = DateTime.UtcNow
 
 
             }, CancellationToken.None);
             }, CancellationToken.None);
-
-            Task.WaitAll(task);
         }
         }
 
 
         public void Post(ReportSessionEnded request)
         public void Post(ReportSessionEnded request)
@@ -396,7 +393,7 @@ namespace MediaBrowser.Api.Session
                 });
                 });
             }
             }
 
 
-            return ToOptimizedResult(result.Select(_sessionManager.GetSessionInfoDto).ToList());
+            return ToOptimizedResult(result.Select(_sessionManager.GetSessionInfoDto).ToArray());
         }
         }
 
 
         public void Post(SendPlaystateCommand request)
         public void Post(SendPlaystateCommand request)
@@ -532,9 +529,9 @@ namespace MediaBrowser.Api.Session
             }
             }
             _sessionManager.ReportCapabilities(request.Id, new ClientCapabilities
             _sessionManager.ReportCapabilities(request.Id, new ClientCapabilities
             {
             {
-                PlayableMediaTypes = (request.PlayableMediaTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
+                PlayableMediaTypes = SplitValue(request.PlayableMediaTypes, ','),
 
 
-                SupportedCommands = (request.SupportedCommands ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
+                SupportedCommands = SplitValue(request.SupportedCommands, ','),
 
 
                 SupportsMediaControl = request.SupportsMediaControl,
                 SupportsMediaControl = request.SupportsMediaControl,
 
 

+ 4 - 4
MediaBrowser.Api/SimilarItemsHelper.cs

@@ -30,7 +30,7 @@ namespace MediaBrowser.Api
         public string ExcludeArtistIds { get; set; }
         public string ExcludeArtistIds { get; set; }
     }
     }
 
 
-    public class BaseGetSimilarItems : IReturn<ItemsResult>, IHasDtoOptions
+    public class BaseGetSimilarItems : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions
     {
     {
         [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
         [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
         public bool? EnableImages { get; set; }
         public bool? EnableImages { get; set; }
@@ -97,18 +97,18 @@ namespace MediaBrowser.Api
             var items = GetSimilaritems(item, libraryManager, inputItems, getSimilarityScore)
             var items = GetSimilaritems(item, libraryManager, inputItems, getSimilarityScore)
                 .ToList();
                 .ToList();
 
 
-            IEnumerable<BaseItem> returnItems = items;
+            List<BaseItem> returnItems = items;
 
 
             if (request.Limit.HasValue)
             if (request.Limit.HasValue)
             {
             {
-                returnItems = returnItems.Take(request.Limit.Value);
+                returnItems = returnItems.Take(request.Limit.Value).ToList();
             }
             }
 
 
             var dtos = await dtoService.GetBaseItemDtos(returnItems, dtoOptions, user).ConfigureAwait(false);
             var dtos = await dtoService.GetBaseItemDtos(returnItems, dtoOptions, user).ConfigureAwait(false);
 
 
             return new QueryResult<BaseItemDto>
             return new QueryResult<BaseItemDto>
             {
             {
-                Items = dtos.ToArray(dtos.Count),
+                Items = dtos,
 
 
                 TotalRecordCount = items.Count
                 TotalRecordCount = items.Count
             };
             };

+ 2 - 3
MediaBrowser.Api/Social/SharingService.cs

@@ -121,8 +121,7 @@ namespace MediaBrowser.Api.Social
 
 
         public void Delete(DeleteShare request)
         public void Delete(DeleteShare request)
         {
         {
-            var task = _sharingManager.DeleteShare(request.Id);
-            Task.WaitAll(task);
+            _sharingManager.DeleteShare(request.Id);
         }
         }
 
 
         public async Task<object> Get(GetShareImage request)
         public async Task<object> Get(GetShareImage request)
@@ -157,7 +156,7 @@ namespace MediaBrowser.Api.Social
                 }
                 }
                 catch
                 catch
                 {
                 {
-                    
+
                 }
                 }
             }
             }
 
 

部分文件因为文件数量过多而无法显示