Преглед на файлове

Enable TreatWarningsAsErrors for MediaBrowser.Model

Bond_009 преди 4 години
родител
ревизия
141efafd3d
променени са 98 файла, в които са добавени 1908 реда и са изтрити 1892 реда
  1. 1 1
      Emby.Dlna/ContentDirectory/StubType.cs
  2. 1 1
      Emby.Dlna/DlnaManager.cs
  3. 1 1
      Emby.Dlna/PlayTo/TransportState.cs
  4. 3 3
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  5. 1 1
      Jellyfin.Api/Helpers/MediaInfoHelper.cs
  6. 1 1
      Jellyfin.Api/Helpers/StreamingHelpers.cs
  7. 3 3
      MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
  8. 9 9
      MediaBrowser.Model/Channels/ChannelFeatures.cs
  9. 3 3
      MediaBrowser.Model/Channels/ChannelQuery.cs
  10. 37 37
      MediaBrowser.Model/Configuration/EncodingOptions.cs
  11. 5 5
      MediaBrowser.Model/Configuration/ImageOption.cs
  12. 19 384
      MediaBrowser.Model/Configuration/LibraryOptions.cs
  13. 12 0
      MediaBrowser.Model/Configuration/MediaPathInfo.cs
  14. 2 2
      MediaBrowser.Model/Configuration/MetadataConfiguration.cs
  15. 10 10
      MediaBrowser.Model/Configuration/MetadataOptions.cs
  16. 6 6
      MediaBrowser.Model/Configuration/MetadataPluginSummary.cs
  17. 365 0
      MediaBrowser.Model/Configuration/TypeOptions.cs
  18. 18 18
      MediaBrowser.Model/Configuration/UserConfiguration.cs
  19. 8 8
      MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs
  20. 5 4
      MediaBrowser.Model/Dlna/AudioOptions.cs
  21. 6 6
      MediaBrowser.Model/Dlna/CodecProfile.cs
  22. 1 1
      MediaBrowser.Model/Dlna/ConditionProcessor.cs
  23. 5 5
      MediaBrowser.Model/Dlna/ContainerProfile.cs
  24. 19 17
      MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
  25. 1 0
      MediaBrowser.Model/Dlna/IDeviceDiscovery.cs
  26. 2 2
      MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
  27. 4 4
      MediaBrowser.Model/Dlna/ResolutionConfiguration.cs
  28. 5 5
      MediaBrowser.Model/Dlna/ResponseProfile.cs
  29. 27 27
      MediaBrowser.Model/Dlna/SearchCriteria.cs
  30. 2 2
      MediaBrowser.Model/Dlna/SortCriteria.cs
  31. 8 6
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  32. 596 594
      MediaBrowser.Model/Dlna/StreamInfo.cs
  33. 5 5
      MediaBrowser.Model/Dto/BaseItemDto.cs
  34. 34 33
      MediaBrowser.Model/Dto/MediaSourceInfo.cs
  35. 9 9
      MediaBrowser.Model/Dto/MetadataEditorInfo.cs
  36. 14 0
      MediaBrowser.Model/Dto/NameGuidPair.cs
  37. 0 7
      MediaBrowser.Model/Dto/NameIdPair.cs
  38. 9 9
      MediaBrowser.Model/Dto/UserDto.cs
  39. 0 32
      MediaBrowser.Model/Entities/CollectionType.cs
  40. 98 99
      MediaBrowser.Model/Entities/MediaStream.cs
  41. 0 40
      MediaBrowser.Model/Entities/PackageReviewInfo.cs
  42. 36 0
      MediaBrowser.Model/Entities/SpecialFolder.cs
  43. 8 8
      MediaBrowser.Model/Entities/VirtualFolderInfo.cs
  44. 6 6
      MediaBrowser.Model/Globalization/CultureDto.cs
  45. 5 2
      MediaBrowser.Model/IO/IFileSystem.cs
  46. 8 8
      MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs
  47. 58 0
      MediaBrowser.Model/LiveTv/ListingsProviderInfo.cs
  48. 13 13
      MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
  49. 8 89
      MediaBrowser.Model/LiveTv/LiveTvOptions.cs
  50. 5 5
      MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs
  51. 8 8
      MediaBrowser.Model/LiveTv/RecordingQuery.cs
  52. 8 8
      MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs
  53. 38 0
      MediaBrowser.Model/LiveTv/TunerHostInfo.cs
  54. 2 2
      MediaBrowser.Model/MediaBrowser.Model.csproj
  55. 11 11
      MediaBrowser.Model/MediaInfo/MediaInfo.cs
  56. 11 11
      MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
  57. 8 8
      MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs
  58. 2 2
      MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs
  59. 6 0
      MediaBrowser.Model/Net/ISocket.cs
  60. 3 0
      MediaBrowser.Model/Net/ISocketFactory.cs
  61. 12 9
      MediaBrowser.Model/Net/MimeTypes.cs
  62. 0 33
      MediaBrowser.Model/Net/NetworkShare.cs
  63. 2 2
      MediaBrowser.Model/Net/SocketReceiveResult.cs
  64. 1 1
      MediaBrowser.Model/Net/WebSocketMessage.cs
  65. 5 5
      MediaBrowser.Model/Notifications/NotificationOptions.cs
  66. 7 7
      MediaBrowser.Model/Notifications/NotificationRequest.cs
  67. 2 2
      MediaBrowser.Model/Providers/ExternalIdInfo.cs
  68. 1 1
      MediaBrowser.Model/Providers/RemoteImageInfo.cs
  69. 8 8
      MediaBrowser.Model/Providers/SubtitleOptions.cs
  70. 5 5
      MediaBrowser.Model/Querying/EpisodeQuery.cs
  71. 5 4
      MediaBrowser.Model/Querying/LatestItemsQuery.cs
  72. 7 7
      MediaBrowser.Model/Querying/MovieRecommendationQuery.cs
  73. 10 10
      MediaBrowser.Model/Querying/NextUpQuery.cs
  74. 3 22
      MediaBrowser.Model/Querying/QueryFilters.cs
  75. 26 0
      MediaBrowser.Model/Querying/QueryFiltersLegacy.cs
  76. 13 13
      MediaBrowser.Model/Querying/QueryResult.cs
  77. 8 8
      MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs
  78. 16 16
      MediaBrowser.Model/Search/SearchQuery.cs
  79. 1 0
      MediaBrowser.Model/Session/BrowseRequest.cs
  80. 7 7
      MediaBrowser.Model/Session/ClientCapabilities.cs
  81. 6 7
      MediaBrowser.Model/Session/GeneralCommand.cs
  82. 0 14
      MediaBrowser.Model/Session/PlaybackProgressInfo.cs
  83. 4 2
      MediaBrowser.Model/Session/PlaystateCommand.cs
  84. 14 0
      MediaBrowser.Model/Session/QueueItem.cs
  85. 11 0
      MediaBrowser.Model/Session/RepeatMode.cs
  86. 31 0
      MediaBrowser.Model/Session/TranscodeReason.cs
  87. 5 32
      MediaBrowser.Model/Session/TranscodingInfo.cs
  88. 5 5
      MediaBrowser.Model/Sync/SyncJob.cs
  89. 9 9
      MediaBrowser.Model/System/SystemInfo.cs
  90. 1 1
      MediaBrowser.Model/System/WakeOnLanInfo.cs
  91. 2 3
      MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs
  92. 12 8
      MediaBrowser.Model/Tasks/ITaskManager.cs
  93. 4 0
      MediaBrowser.Model/Tasks/ITaskTrigger.cs
  94. 8 8
      MediaBrowser.Model/Tasks/TaskInfo.cs
  95. 6 6
      MediaBrowser.Model/Tasks/TaskTriggerInfo.cs
  96. 50 50
      MediaBrowser.Model/Users/UserPolicy.cs
  97. 2 0
      jellyfin.ruleset
  98. 0 6
      tests/Jellyfin.Api.Tests/ModelBinders/TestType.cs

+ 1 - 1
Emby.Dlna/ContentDirectory/StubType.cs

@@ -1,5 +1,5 @@
 #pragma warning disable CS1591
-#pragma warning disable SA1602
+
 
 namespace Emby.Dlna.ContentDirectory
 {

+ 1 - 1
Emby.Dlna/DlnaManager.cs

@@ -553,7 +553,7 @@ namespace Emby.Dlna
 
         private void DumpProfiles()
         {
-            DeviceProfile[] list = new []
+            DeviceProfile[] list = new[]
             {
                 new SamsungSmartTvProfile(),
                 new XboxOneProfile(),

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

@@ -1,5 +1,5 @@
 #pragma warning disable CS1591
-#pragma warning disable SA1602
+
 
 namespace Emby.Dlna.PlayTo
 {

+ 3 - 3
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -6207,9 +6207,9 @@ AND Type = @InternalPersonType)");
 
             if (item.Type == MediaStreamType.Subtitle)
             {
-                item.localizedUndefined = _localization.GetLocalizedString("Undefined");
-                item.localizedDefault = _localization.GetLocalizedString("Default");
-                item.localizedForced = _localization.GetLocalizedString("Forced");
+                item.LocalizedUndefined = _localization.GetLocalizedString("Undefined");
+                item.LocalizedDefault = _localization.GetLocalizedString("Default");
+                item.LocalizedForced = _localization.GetLocalizedString("Forced");
             }
 
             return item;

+ 1 - 1
Jellyfin.Api/Helpers/MediaInfoHelper.cs

@@ -523,7 +523,7 @@ namespace Jellyfin.Api.Helpers
         /// <param name="type">Dlna profile type.</param>
         public void NormalizeMediaSourceContainer(MediaSourceInfo mediaSource, DeviceProfile profile, DlnaProfileType type)
         {
-            mediaSource.Container = StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(mediaSource.Container, mediaSource.Path, profile, type);
+            mediaSource.Container = StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(mediaSource.Container, profile, type);
         }
 
         private void SetDeviceSpecificSubtitleInfo(StreamInfo info, MediaSourceInfo mediaSource, string accessToken)

+ 1 - 1
Jellyfin.Api/Helpers/StreamingHelpers.cs

@@ -183,7 +183,7 @@ namespace Jellyfin.Api.Helpers
             if (string.IsNullOrEmpty(containerInternal))
             {
                 containerInternal = streamingRequest.Static ?
-                    StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(state.InputContainer, state.MediaPath, null, DlnaProfileType.Audio)
+                    StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(state.InputContainer, null, DlnaProfileType.Audio)
                     : GetOutputFileExtension(state);
             }
 

+ 3 - 3
MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs

@@ -681,9 +681,9 @@ namespace MediaBrowser.MediaEncoding.Probing
             {
                 stream.Type = MediaStreamType.Subtitle;
                 stream.Codec = NormalizeSubtitleCodec(stream.Codec);
-                stream.localizedUndefined = _localization.GetLocalizedString("Undefined");
-                stream.localizedDefault = _localization.GetLocalizedString("Default");
-                stream.localizedForced = _localization.GetLocalizedString("Forced");
+                stream.LocalizedUndefined = _localization.GetLocalizedString("Undefined");
+                stream.LocalizedDefault = _localization.GetLocalizedString("Default");
+                stream.LocalizedForced = _localization.GetLocalizedString("Forced");
             }
             else if (string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase))
             {

+ 9 - 9
MediaBrowser.Model/Channels/ChannelFeatures.cs

@@ -7,6 +7,13 @@ namespace MediaBrowser.Model.Channels
 {
     public class ChannelFeatures
     {
+        public ChannelFeatures()
+        {
+            MediaTypes = Array.Empty<ChannelMediaType>();
+            ContentTypes = Array.Empty<ChannelMediaContentType>();
+            DefaultSortFields = Array.Empty<ChannelItemSortField>();
+        }
+
         /// <summary>
         /// Gets or sets the name.
         /// </summary>
@@ -38,7 +45,7 @@ namespace MediaBrowser.Model.Channels
         public ChannelMediaContentType[] ContentTypes { get; set; }
 
         /// <summary>
-        /// Represents the maximum number of records the channel allows retrieving at a time.
+        /// Gets or sets the maximum number of records the channel allows retrieving at a time.
         /// </summary>
         public int? MaxPageSize { get; set; }
 
@@ -55,7 +62,7 @@ namespace MediaBrowser.Model.Channels
         public ChannelItemSortField[] DefaultSortFields { get; set; }
 
         /// <summary>
-        /// Indicates if a sort ascending/descending toggle is supported or not.
+        /// Gets or sets a value indicating whether a sort ascending/descending toggle is supported.
         /// </summary>
         public bool SupportsSortOrderToggle { get; set; }
 
@@ -76,12 +83,5 @@ namespace MediaBrowser.Model.Channels
         /// </summary>
         /// <value><c>true</c> if [supports content downloading]; otherwise, <c>false</c>.</value>
         public bool SupportsContentDownloading { get; set; }
-
-        public ChannelFeatures()
-        {
-            MediaTypes = Array.Empty<ChannelMediaType>();
-            ContentTypes = Array.Empty<ChannelMediaContentType>();
-            DefaultSortFields = Array.Empty<ChannelItemSortField>();
-        }
     }
 }

+ 3 - 3
MediaBrowser.Model/Channels/ChannelQuery.cs

@@ -10,7 +10,7 @@ namespace MediaBrowser.Model.Channels
     public class ChannelQuery
     {
         /// <summary>
-        /// Fields to return within the items, in addition to basic information.
+        /// Gets or sets the fields to return within the items, in addition to basic information.
         /// </summary>
         /// <value>The fields.</value>
         public ItemFields[] Fields { get; set; }
@@ -28,13 +28,13 @@ namespace MediaBrowser.Model.Channels
         public Guid UserId { get; set; }
 
         /// <summary>
-        /// Skips over a given number of items within the results. Use for paging.
+        /// Gets or sets the start index. Use for paging.
         /// </summary>
         /// <value>The start index.</value>
         public int? StartIndex { get; set; }
 
         /// <summary>
-        /// The maximum number of items to return.
+        /// Gets or sets the maximum number of items to return.
         /// </summary>
         /// <value>The limit.</value>
         public int? Limit { get; set; }

+ 37 - 37
MediaBrowser.Model/Configuration/EncodingOptions.cs

@@ -5,6 +5,41 @@ namespace MediaBrowser.Model.Configuration
 {
     public class EncodingOptions
     {
+        public EncodingOptions()
+        {
+            EnableFallbackFont = false;
+            DownMixAudioBoost = 2;
+            MaxMuxingQueueSize = 2048;
+            EnableThrottling = false;
+            ThrottleDelaySeconds = 180;
+            EncodingThreadCount = -1;
+            // This is a DRM device that is almost guaranteed to be there on every intel platform,
+            // plus it's the default one in ffmpeg if you don't specify anything
+            VaapiDevice = "/dev/dri/renderD128";
+            // This is the OpenCL device that is used for tonemapping.
+            // The left side of the dot is the platform number, and the right side is the device number on the platform.
+            OpenclDevice = "0.0";
+            EnableTonemapping = false;
+            EnableVppTonemapping = false;
+            TonemappingAlgorithm = "hable";
+            TonemappingRange = "auto";
+            TonemappingDesat = 0;
+            TonemappingThreshold = 0.8;
+            TonemappingPeak = 100;
+            TonemappingParam = 0;
+            H264Crf = 23;
+            H265Crf = 28;
+            DeinterlaceDoubleRate = false;
+            DeinterlaceMethod = "yadif";
+            EnableDecodingColorDepth10Hevc = true;
+            EnableDecodingColorDepth10Vp9 = true;
+            EnableEnhancedNvdecDecoder = true;
+            EnableHardwareEncoding = true;
+            AllowHevcEncoding = true;
+            EnableSubtitleExtraction = true;
+            HardwareDecodingCodecs = new string[] { "h264", "vc1" };
+        }
+
         public int EncodingThreadCount { get; set; }
 
         public string TranscodingTempPath { get; set; }
@@ -24,12 +59,12 @@ namespace MediaBrowser.Model.Configuration
         public string HardwareAccelerationType { get; set; }
 
         /// <summary>
-        /// FFmpeg path as set by the user via the UI.
+        /// Gets or sets the FFmpeg path as set by the user via the UI.
         /// </summary>
         public string EncoderAppPath { get; set; }
 
         /// <summary>
-        /// The current FFmpeg path being used by the system and displayed on the transcode page.
+        /// Gets or sets the current FFmpeg path being used by the system and displayed on the transcode page.
         /// </summary>
         public string EncoderAppPathDisplay { get; set; }
 
@@ -76,40 +111,5 @@ namespace MediaBrowser.Model.Configuration
         public bool EnableSubtitleExtraction { get; set; }
 
         public string[] HardwareDecodingCodecs { get; set; }
-
-        public EncodingOptions()
-        {
-            EnableFallbackFont = false;
-            DownMixAudioBoost = 2;
-            MaxMuxingQueueSize = 2048;
-            EnableThrottling = false;
-            ThrottleDelaySeconds = 180;
-            EncodingThreadCount = -1;
-            // This is a DRM device that is almost guaranteed to be there on every intel platform,
-            // plus it's the default one in ffmpeg if you don't specify anything
-            VaapiDevice = "/dev/dri/renderD128";
-            // This is the OpenCL device that is used for tonemapping.
-            // The left side of the dot is the platform number, and the right side is the device number on the platform.
-            OpenclDevice = "0.0";
-            EnableTonemapping = false;
-            EnableVppTonemapping = false;
-            TonemappingAlgorithm = "hable";
-            TonemappingRange = "auto";
-            TonemappingDesat = 0;
-            TonemappingThreshold = 0.8;
-            TonemappingPeak = 100;
-            TonemappingParam = 0;
-            H264Crf = 23;
-            H265Crf = 28;
-            DeinterlaceDoubleRate = false;
-            DeinterlaceMethod = "yadif";
-            EnableDecodingColorDepth10Hevc = true;
-            EnableDecodingColorDepth10Vp9 = true;
-            EnableEnhancedNvdecDecoder = true;
-            EnableHardwareEncoding = true;
-            AllowHevcEncoding = true;
-            EnableSubtitleExtraction = true;
-            HardwareDecodingCodecs = new string[] { "h264", "vc1" };
-        }
     }
 }

+ 5 - 5
MediaBrowser.Model/Configuration/ImageOption.cs

@@ -6,6 +6,11 @@ namespace MediaBrowser.Model.Configuration
 {
     public class ImageOption
     {
+        public ImageOption()
+        {
+            Limit = 1;
+        }
+
         /// <summary>
         /// Gets or sets the type.
         /// </summary>
@@ -23,10 +28,5 @@ namespace MediaBrowser.Model.Configuration
         /// </summary>
         /// <value>The minimum width.</value>
         public int MinWidth { get; set; }
-
-        public ImageOption()
-        {
-            Limit = 1;
-        }
     }
 }

+ 19 - 384
MediaBrowser.Model/Configuration/LibraryOptions.cs

@@ -2,13 +2,30 @@
 #pragma warning disable CS1591
 
 using System;
-using System.Collections.Generic;
-using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Model.Configuration
 {
     public class LibraryOptions
     {
+        public LibraryOptions()
+        {
+            TypeOptions = Array.Empty<TypeOptions>();
+            DisabledSubtitleFetchers = Array.Empty<string>();
+            SubtitleFetcherOrder = Array.Empty<string>();
+            DisabledLocalMetadataReaders = Array.Empty<string>();
+
+            SkipSubtitlesIfAudioTrackMatches = true;
+            RequirePerfectSubtitleMatch = true;
+
+            EnablePhotos = true;
+            SaveSubtitlesWithMedia = true;
+            EnableRealtimeMonitor = true;
+            PathInfos = Array.Empty<MediaPathInfo>();
+            EnableInternetProviders = true;
+            EnableAutomaticSeriesGrouping = true;
+            SeasonZeroDisplayName = "Specials";
+        }
+
         public bool EnablePhotos { get; set; }
 
         public bool EnableRealtimeMonitor { get; set; }
@@ -79,387 +96,5 @@ namespace MediaBrowser.Model.Configuration
 
             return null;
         }
-
-        public LibraryOptions()
-        {
-            TypeOptions = Array.Empty<TypeOptions>();
-            DisabledSubtitleFetchers = Array.Empty<string>();
-            SubtitleFetcherOrder = Array.Empty<string>();
-            DisabledLocalMetadataReaders = Array.Empty<string>();
-
-            SkipSubtitlesIfAudioTrackMatches = true;
-            RequirePerfectSubtitleMatch = true;
-
-            EnablePhotos = true;
-            SaveSubtitlesWithMedia = true;
-            EnableRealtimeMonitor = true;
-            PathInfos = Array.Empty<MediaPathInfo>();
-            EnableInternetProviders = true;
-            EnableAutomaticSeriesGrouping = true;
-            SeasonZeroDisplayName = "Specials";
-        }
-    }
-
-    public class MediaPathInfo
-    {
-        public string Path { get; set; }
-
-        public string NetworkPath { get; set; }
-    }
-
-    public class TypeOptions
-    {
-        public string Type { get; set; }
-
-        public string[] MetadataFetchers { get; set; }
-
-        public string[] MetadataFetcherOrder { get; set; }
-
-        public string[] ImageFetchers { get; set; }
-
-        public string[] ImageFetcherOrder { get; set; }
-
-        public ImageOption[] ImageOptions { get; set; }
-
-        public ImageOption GetImageOptions(ImageType type)
-        {
-            foreach (var i in ImageOptions)
-            {
-                if (i.Type == type)
-                {
-                    return i;
-                }
-            }
-
-            if (DefaultImageOptions.TryGetValue(Type, out ImageOption[] options))
-            {
-                foreach (var i in options)
-                {
-                    if (i.Type == type)
-                    {
-                        return i;
-                    }
-                }
-            }
-
-            return DefaultInstance;
-        }
-
-        public int GetLimit(ImageType type)
-        {
-            return GetImageOptions(type).Limit;
-        }
-
-        public int GetMinWidth(ImageType type)
-        {
-            return GetImageOptions(type).MinWidth;
-        }
-
-        public bool IsEnabled(ImageType type)
-        {
-            return GetLimit(type) > 0;
-        }
-
-        public TypeOptions()
-        {
-            MetadataFetchers = Array.Empty<string>();
-            MetadataFetcherOrder = Array.Empty<string>();
-            ImageFetchers = Array.Empty<string>();
-            ImageFetcherOrder = Array.Empty<string>();
-            ImageOptions = Array.Empty<ImageOption>();
-        }
-
-        public static Dictionary<string, ImageOption[]> DefaultImageOptions = new Dictionary<string, ImageOption[]>
-        {
-            {
-                "Movie", new []
-                {
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        MinWidth = 1280,
-                        Type = ImageType.Backdrop
-                    },
-
-                    // Don't download this by default as it's rarely used.
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Art
-                    },
-
-                    // Don't download this by default as it's rarely used.
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Disc
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Primary
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Banner
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Thumb
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Logo
-                    }
-                }
-            },
-            {
-                "MusicVideo", new []
-                {
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        MinWidth = 1280,
-                        Type = ImageType.Backdrop
-                    },
-
-                    // Don't download this by default as it's rarely used.
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Art
-                    },
-
-                    // Don't download this by default as it's rarely used.
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Disc
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Primary
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Banner
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Thumb
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Logo
-                    }
-                }
-            },
-            {
-                "Series", new []
-                {
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        MinWidth = 1280,
-                        Type = ImageType.Backdrop
-                    },
-
-                    // Don't download this by default as it's rarely used.
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Art
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Primary
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Banner
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Thumb
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Logo
-                    }
-                }
-            },
-            {
-                "MusicAlbum", new []
-                {
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        MinWidth = 1280,
-                        Type = ImageType.Backdrop
-                    },
-
-                    // Don't download this by default as it's rarely used.
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Disc
-                    }
-                }
-            },
-            {
-                "MusicArtist", new []
-                {
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        MinWidth = 1280,
-                        Type = ImageType.Backdrop
-                    },
-
-                    // Don't download this by default
-                    // They do look great, but most artists won't have them, which means a banner view isn't really possible
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Banner
-                    },
-
-                    // Don't download this by default
-                    // Generally not used
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Art
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Logo
-                    }
-                }
-            },
-            {
-                "BoxSet", new []
-                {
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        MinWidth = 1280,
-                        Type = ImageType.Backdrop
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Primary
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Thumb
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Logo
-                    },
-
-                    // Don't download this by default as it's rarely used.
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Art
-                    },
-
-                    // Don't download this by default as it's rarely used.
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Disc
-                    },
-
-                    // Don't download this by default as it's rarely used.
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Banner
-                    }
-                }
-            },
-            {
-                "Season", new []
-                {
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        MinWidth = 1280,
-                        Type = ImageType.Backdrop
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Primary
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Banner
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        Type = ImageType.Thumb
-                    }
-                }
-            },
-            {
-                "Episode", new []
-                {
-                    new ImageOption
-                    {
-                        Limit = 0,
-                        MinWidth = 1280,
-                        Type = ImageType.Backdrop
-                    },
-
-                    new ImageOption
-                    {
-                        Limit = 1,
-                        Type = ImageType.Primary
-                    }
-                }
-            }
-        };
-
-        public static ImageOption DefaultInstance = new ImageOption();
     }
 }

+ 12 - 0
MediaBrowser.Model/Configuration/MediaPathInfo.cs

@@ -0,0 +1,12 @@
+#nullable disable
+#pragma warning disable CS1591
+
+namespace MediaBrowser.Model.Configuration
+{
+    public class MediaPathInfo
+    {
+        public string Path { get; set; }
+
+        public string NetworkPath { get; set; }
+    }
+}

+ 2 - 2
MediaBrowser.Model/Configuration/MetadataConfiguration.cs

@@ -4,11 +4,11 @@ namespace MediaBrowser.Model.Configuration
 {
     public class MetadataConfiguration
     {
-        public bool UseFileCreationTimeForDateAdded { get; set; }
-
         public MetadataConfiguration()
         {
             UseFileCreationTimeForDateAdded = true;
         }
+
+        public bool UseFileCreationTimeForDateAdded { get; set; }
     }
 }

+ 10 - 10
MediaBrowser.Model/Configuration/MetadataOptions.cs

@@ -10,6 +10,16 @@ namespace MediaBrowser.Model.Configuration
     /// </summary>
     public class MetadataOptions
     {
+        public MetadataOptions()
+        {
+            DisabledMetadataSavers = Array.Empty<string>();
+            LocalMetadataReaderOrder = Array.Empty<string>();
+            DisabledMetadataFetchers = Array.Empty<string>();
+            MetadataFetcherOrder = Array.Empty<string>();
+            DisabledImageFetchers = Array.Empty<string>();
+            ImageFetcherOrder = Array.Empty<string>();
+        }
+
         public string ItemType { get; set; }
 
         public string[] DisabledMetadataSavers { get; set; }
@@ -23,15 +33,5 @@ namespace MediaBrowser.Model.Configuration
         public string[] DisabledImageFetchers { get; set; }
 
         public string[] ImageFetcherOrder { get; set; }
-
-        public MetadataOptions()
-        {
-            DisabledMetadataSavers = Array.Empty<string>();
-            LocalMetadataReaderOrder = Array.Empty<string>();
-            DisabledMetadataFetchers = Array.Empty<string>();
-            MetadataFetcherOrder = Array.Empty<string>();
-            DisabledImageFetchers = Array.Empty<string>();
-            ImageFetcherOrder = Array.Empty<string>();
-        }
     }
 }

+ 6 - 6
MediaBrowser.Model/Configuration/MetadataPluginSummary.cs

@@ -8,6 +8,12 @@ namespace MediaBrowser.Model.Configuration
 {
     public class MetadataPluginSummary
     {
+        public MetadataPluginSummary()
+        {
+            SupportedImageTypes = Array.Empty<ImageType>();
+            Plugins = Array.Empty<MetadataPlugin>();
+        }
+
         /// <summary>
         /// Gets or sets the type of the item.
         /// </summary>
@@ -25,11 +31,5 @@ namespace MediaBrowser.Model.Configuration
         /// </summary>
         /// <value>The supported image types.</value>
         public ImageType[] SupportedImageTypes { get; set; }
-
-        public MetadataPluginSummary()
-        {
-            SupportedImageTypes = Array.Empty<ImageType>();
-            Plugins = Array.Empty<MetadataPlugin>();
-        }
     }
 }

+ 365 - 0
MediaBrowser.Model/Configuration/TypeOptions.cs

@@ -0,0 +1,365 @@
+#nullable disable
+#pragma warning disable CS1591
+
+using System;
+using System.Collections.Generic;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Model.Configuration
+{
+    public class TypeOptions
+    {
+        public static readonly ImageOption DefaultInstance = new ImageOption();
+
+        public static readonly Dictionary<string, ImageOption[]> DefaultImageOptions = new Dictionary<string, ImageOption[]>
+        {
+            {
+                "Movie", new[]
+                {
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        MinWidth = 1280,
+                        Type = ImageType.Backdrop
+                    },
+
+                    // Don't download this by default as it's rarely used.
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Art
+                    },
+
+                    // Don't download this by default as it's rarely used.
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Disc
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Primary
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Banner
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Thumb
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Logo
+                    }
+                }
+            },
+            {
+                "MusicVideo", new[]
+                {
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        MinWidth = 1280,
+                        Type = ImageType.Backdrop
+                    },
+
+                    // Don't download this by default as it's rarely used.
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Art
+                    },
+
+                    // Don't download this by default as it's rarely used.
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Disc
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Primary
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Banner
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Thumb
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Logo
+                    }
+                }
+            },
+            {
+                "Series", new[]
+                {
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        MinWidth = 1280,
+                        Type = ImageType.Backdrop
+                    },
+
+                    // Don't download this by default as it's rarely used.
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Art
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Primary
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Banner
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Thumb
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Logo
+                    }
+                }
+            },
+            {
+                "MusicAlbum", new[]
+                {
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        MinWidth = 1280,
+                        Type = ImageType.Backdrop
+                    },
+
+                    // Don't download this by default as it's rarely used.
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Disc
+                    }
+                }
+            },
+            {
+                "MusicArtist", new[]
+                {
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        MinWidth = 1280,
+                        Type = ImageType.Backdrop
+                    },
+
+                    // Don't download this by default
+                    // They do look great, but most artists won't have them, which means a banner view isn't really possible
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Banner
+                    },
+
+                    // Don't download this by default
+                    // Generally not used
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Art
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Logo
+                    }
+                }
+            },
+            {
+                "BoxSet", new[]
+                {
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        MinWidth = 1280,
+                        Type = ImageType.Backdrop
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Primary
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Thumb
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Logo
+                    },
+
+                    // Don't download this by default as it's rarely used.
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Art
+                    },
+
+                    // Don't download this by default as it's rarely used.
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Disc
+                    },
+
+                    // Don't download this by default as it's rarely used.
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Banner
+                    }
+                }
+            },
+            {
+                "Season", new[]
+                {
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        MinWidth = 1280,
+                        Type = ImageType.Backdrop
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Primary
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Banner
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        Type = ImageType.Thumb
+                    }
+                }
+            },
+            {
+                "Episode", new[]
+                {
+                    new ImageOption
+                    {
+                        Limit = 0,
+                        MinWidth = 1280,
+                        Type = ImageType.Backdrop
+                    },
+
+                    new ImageOption
+                    {
+                        Limit = 1,
+                        Type = ImageType.Primary
+                    }
+                }
+            }
+        };
+
+        public TypeOptions()
+        {
+            MetadataFetchers = Array.Empty<string>();
+            MetadataFetcherOrder = Array.Empty<string>();
+            ImageFetchers = Array.Empty<string>();
+            ImageFetcherOrder = Array.Empty<string>();
+            ImageOptions = Array.Empty<ImageOption>();
+        }
+
+        public string Type { get; set; }
+
+        public string[] MetadataFetchers { get; set; }
+
+        public string[] MetadataFetcherOrder { get; set; }
+
+        public string[] ImageFetchers { get; set; }
+
+        public string[] ImageFetcherOrder { get; set; }
+
+        public ImageOption[] ImageOptions { get; set; }
+
+        public ImageOption GetImageOptions(ImageType type)
+        {
+            foreach (var i in ImageOptions)
+            {
+                if (i.Type == type)
+                {
+                    return i;
+                }
+            }
+
+            if (DefaultImageOptions.TryGetValue(Type, out ImageOption[] options))
+            {
+                foreach (var i in options)
+                {
+                    if (i.Type == type)
+                    {
+                        return i;
+                    }
+                }
+            }
+
+            return DefaultInstance;
+        }
+
+        public int GetLimit(ImageType type)
+        {
+            return GetImageOptions(type).Limit;
+        }
+
+        public int GetMinWidth(ImageType type)
+        {
+            return GetImageOptions(type).MinWidth;
+        }
+
+        public bool IsEnabled(ImageType type)
+        {
+            return GetLimit(type) > 0;
+        }
+    }
+}

+ 18 - 18
MediaBrowser.Model/Configuration/UserConfiguration.cs

@@ -11,6 +11,24 @@ namespace MediaBrowser.Model.Configuration
     /// </summary>
     public class UserConfiguration
     {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="UserConfiguration" /> class.
+        /// </summary>
+        public UserConfiguration()
+        {
+            EnableNextEpisodeAutoPlay = true;
+            RememberAudioSelections = true;
+            RememberSubtitleSelections = true;
+
+            HidePlayedInLatest = true;
+            PlayDefaultAudioTrack = true;
+
+            LatestItemsExcludes = Array.Empty<string>();
+            OrderedViews = Array.Empty<string>();
+            MyMediaExcludes = Array.Empty<string>();
+            GroupedFolders = Array.Empty<string>();
+        }
+
         /// <summary>
         /// Gets or sets the audio language preference.
         /// </summary>
@@ -52,23 +70,5 @@ namespace MediaBrowser.Model.Configuration
         public bool RememberSubtitleSelections { get; set; }
 
         public bool EnableNextEpisodeAutoPlay { get; set; }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="UserConfiguration" /> class.
-        /// </summary>
-        public UserConfiguration()
-        {
-            EnableNextEpisodeAutoPlay = true;
-            RememberAudioSelections = true;
-            RememberSubtitleSelections = true;
-
-            HidePlayedInLatest = true;
-            PlayDefaultAudioTrack = true;
-
-            LatestItemsExcludes = Array.Empty<string>();
-            OrderedViews = Array.Empty<string>();
-            MyMediaExcludes = Array.Empty<string>();
-            GroupedFolders = Array.Empty<string>();
-        }
     }
 }

+ 8 - 8
MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs

@@ -5,6 +5,14 @@ namespace MediaBrowser.Model.Configuration
 {
     public class XbmcMetadataOptions
     {
+        public XbmcMetadataOptions()
+        {
+            ReleaseDateFormat = "yyyy-MM-dd";
+
+            SaveImagePathsInNfo = true;
+            EnablePathSubstitution = true;
+        }
+
         public string UserId { get; set; }
 
         public string ReleaseDateFormat { get; set; }
@@ -14,13 +22,5 @@ namespace MediaBrowser.Model.Configuration
         public bool EnablePathSubstitution { get; set; }
 
         public bool EnableExtraThumbsDuplication { get; set; }
-
-        public XbmcMetadataOptions()
-        {
-            ReleaseDateFormat = "yyyy-MM-dd";
-
-            SaveImagePathsInNfo = true;
-            EnablePathSubstitution = true;
-        }
     }
 }

+ 5 - 4
MediaBrowser.Model/Dlna/AudioOptions.cs

@@ -34,20 +34,20 @@ namespace MediaBrowser.Model.Dlna
         public DeviceProfile Profile { get; set; }
 
         /// <summary>
-        /// Optional. Only needed if a specific AudioStreamIndex or SubtitleStreamIndex are requested.
+        /// Gets or sets a media source id. Optional. Only needed if a specific AudioStreamIndex or SubtitleStreamIndex are requested.
         /// </summary>
         public string MediaSourceId { get; set; }
 
         public string DeviceId { get; set; }
 
         /// <summary>
-        /// Allows an override of supported number of audio channels
-        /// Example: DeviceProfile supports five channel, but user only has stereo speakers
+        /// Gets or sets an override of supported number of audio channels
+        /// Example: DeviceProfile supports five channel, but user only has stereo speakers.
         /// </summary>
         public int? MaxAudioChannels { get; set; }
 
         /// <summary>
-        /// The application's configured quality setting.
+        /// Gets or sets the application's configured quality setting.
         /// </summary>
         public int? MaxBitrate { get; set; }
 
@@ -66,6 +66,7 @@ namespace MediaBrowser.Model.Dlna
         /// <summary>
         /// Gets the maximum bitrate.
         /// </summary>
+        /// <param name="isAudio">Whether or not this is audio.</param>
         /// <returns>System.Nullable&lt;System.Int32&gt;.</returns>
         public int? GetMaxBitrate(bool isAudio)
         {

+ 6 - 6
MediaBrowser.Model/Dlna/CodecProfile.cs

@@ -9,6 +9,12 @@ namespace MediaBrowser.Model.Dlna
 {
     public class CodecProfile
     {
+        public CodecProfile()
+        {
+            Conditions = Array.Empty<ProfileCondition>();
+            ApplyConditions = Array.Empty<ProfileCondition>();
+        }
+
         [XmlAttribute("type")]
         public CodecType Type { get; set; }
 
@@ -22,12 +28,6 @@ namespace MediaBrowser.Model.Dlna
         [XmlAttribute("container")]
         public string Container { get; set; }
 
-        public CodecProfile()
-        {
-            Conditions = Array.Empty<ProfileCondition>();
-            ApplyConditions = Array.Empty<ProfileCondition>();
-        }
-
         public string[] GetCodecs()
         {
             return ContainerProfile.SplitValue(Codec);

+ 1 - 1
MediaBrowser.Model/Dlna/ConditionProcessor.cs

@@ -1,8 +1,8 @@
 #pragma warning disable CS1591
 
 using System;
-using System.Linq;
 using System.Globalization;
+using System.Linq;
 using MediaBrowser.Model.MediaInfo;
 
 namespace MediaBrowser.Model.Dlna

+ 5 - 5
MediaBrowser.Model/Dlna/ContainerProfile.cs

@@ -9,6 +9,11 @@ namespace MediaBrowser.Model.Dlna
 {
     public class ContainerProfile
     {
+        public ContainerProfile()
+        {
+            Conditions = Array.Empty<ProfileCondition>();
+        }
+
         [XmlAttribute("type")]
         public DlnaProfileType Type { get; set; }
 
@@ -17,11 +22,6 @@ namespace MediaBrowser.Model.Dlna
         [XmlAttribute("container")]
         public string Container { get; set; }
 
-        public ContainerProfile()
-        {
-            Conditions = Array.Empty<ProfileCondition>();
-        }
-
         public string[] GetContainers()
         {
             return SplitValue(Container);

+ 19 - 17
MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs

@@ -81,13 +81,13 @@ namespace MediaBrowser.Model.Dlna
                             DlnaFlags.DlnaV15;
 
             // if (isDirectStream)
-            //{
-            //    flagValue = flagValue | DlnaFlags.ByteBasedSeek;
-            //}
-            // else if (runtimeTicks.HasValue)
-            //{
-            //    flagValue = flagValue | DlnaFlags.TimeBasedSeek;
-            //}
+            // {
+            //     flagValue = flagValue | DlnaFlags.ByteBasedSeek;
+            // }
+            //  else if (runtimeTicks.HasValue)
+            // {
+            //     flagValue = flagValue | DlnaFlags.TimeBasedSeek;
+            // }
 
             string dlnaflags = string.Format(
                 CultureInfo.InvariantCulture,
@@ -150,16 +150,18 @@ namespace MediaBrowser.Model.Dlna
                             DlnaFlags.DlnaV15;
 
             // if (isDirectStream)
-            //{
-            //    flagValue = flagValue | DlnaFlags.ByteBasedSeek;
-            //}
-            // else if (runtimeTicks.HasValue)
-            //{
-            //    flagValue = flagValue | DlnaFlags.TimeBasedSeek;
-            //}
-
-            string dlnaflags = string.Format(CultureInfo.InvariantCulture, ";DLNA.ORG_FLAGS={0}",
-             DlnaMaps.FlagsToString(flagValue));
+            // {
+            //     flagValue = flagValue | DlnaFlags.ByteBasedSeek;
+            // }
+            //  else if (runtimeTicks.HasValue)
+            // {
+            //     flagValue = flagValue | DlnaFlags.TimeBasedSeek;
+            // }
+
+            string dlnaflags = string.Format(
+                CultureInfo.InvariantCulture,
+                ";DLNA.ORG_FLAGS={0}",
+                DlnaMaps.FlagsToString(flagValue));
 
             ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(
                 container,

+ 1 - 0
MediaBrowser.Model/Dlna/IDeviceDiscovery.cs

@@ -8,6 +8,7 @@ namespace MediaBrowser.Model.Dlna
     public interface IDeviceDiscovery
     {
         event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered;
+
         event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
     }
 }

+ 2 - 2
MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs

@@ -57,7 +57,6 @@ namespace MediaBrowser.Model.Dlna
                 string.Equals(container, "mpegts", StringComparison.OrdinalIgnoreCase) ||
                 string.Equals(container, "m2ts", StringComparison.OrdinalIgnoreCase))
             {
-
                 return ResolveVideoMPEG2TSFormat(videoCodec, audioCodec, width, height, timestampType);
             }
 
@@ -323,7 +322,6 @@ namespace MediaBrowser.Model.Dlna
             if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase) &&
                 (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "wmapro", StringComparison.OrdinalIgnoreCase)))
             {
-
                 if (width.HasValue && height.HasValue)
                 {
                     if ((width.Value <= 720) && (height.Value <= 576))
@@ -479,7 +477,9 @@ namespace MediaBrowser.Model.Dlna
         {
             if (string.Equals(container, "jpeg", StringComparison.OrdinalIgnoreCase) ||
                 string.Equals(container, "jpg", StringComparison.OrdinalIgnoreCase))
+            {
                 return ResolveImageJPGFormat(width, height);
+            }
 
             if (string.Equals(container, "png", StringComparison.OrdinalIgnoreCase))
             {

+ 4 - 4
MediaBrowser.Model/Dlna/ResolutionConfiguration.cs

@@ -4,14 +4,14 @@ namespace MediaBrowser.Model.Dlna
 {
     public class ResolutionConfiguration
     {
-        public int MaxWidth { get; set; }
-
-        public int MaxBitrate { get; set; }
-
         public ResolutionConfiguration(int maxWidth, int maxBitrate)
         {
             MaxWidth = maxWidth;
             MaxBitrate = maxBitrate;
         }
+
+        public int MaxWidth { get; set; }
+
+        public int MaxBitrate { get; set; }
     }
 }

+ 5 - 5
MediaBrowser.Model/Dlna/ResponseProfile.cs

@@ -8,6 +8,11 @@ namespace MediaBrowser.Model.Dlna
 {
     public class ResponseProfile
     {
+        public ResponseProfile()
+        {
+            Conditions = Array.Empty<ProfileCondition>();
+        }
+
         [XmlAttribute("container")]
         public string Container { get; set; }
 
@@ -28,11 +33,6 @@ namespace MediaBrowser.Model.Dlna
 
         public ProfileCondition[] Conditions { get; set; }
 
-        public ResponseProfile()
-        {
-            Conditions = Array.Empty<ProfileCondition>();
-        }
-
         public string[] GetContainers()
         {
             return ContainerProfile.SplitValue(Container);

+ 27 - 27
MediaBrowser.Model/Dlna/SearchCriteria.cs

@@ -7,31 +7,6 @@ namespace MediaBrowser.Model.Dlna
 {
     public class SearchCriteria
     {
-        public SearchType SearchType { get; set; }
-
-        /// <summary>
-        /// Splits the specified string.
-        /// </summary>
-        /// <param name="str">The string.</param>
-        /// <param name="term">The term.</param>
-        /// <param name="limit">The limit.</param>
-        /// <returns>System.String[].</returns>
-        private static string[] RegexSplit(string str, string term, int limit)
-        {
-            return new Regex(term).Split(str, limit);
-        }
-
-        /// <summary>
-        /// Splits the specified string.
-        /// </summary>
-        /// <param name="str">The string.</param>
-        /// <param name="term">The term.</param>
-        /// <returns>System.String[].</returns>
-        private static string[] RegexSplit(string str, string term)
-        {
-            return Regex.Split(str, term, RegexOptions.IgnoreCase);
-        }
-
         public SearchCriteria(string search)
         {
             if (search.Length == 0)
@@ -48,8 +23,8 @@ namespace MediaBrowser.Model.Dlna
 
                 if (subFactors.Length == 3)
                 {
-                    if (string.Equals("upnp:class", subFactors[0], StringComparison.OrdinalIgnoreCase) &&
-                        (string.Equals("=", subFactors[1], StringComparison.Ordinal) || string.Equals("derivedfrom", subFactors[1], StringComparison.OrdinalIgnoreCase)))
+                    if (string.Equals("upnp:class", subFactors[0], StringComparison.OrdinalIgnoreCase)
+                        && (string.Equals("=", subFactors[1], StringComparison.Ordinal) || string.Equals("derivedfrom", subFactors[1], StringComparison.OrdinalIgnoreCase)))
                     {
                         if (string.Equals("\"object.item.imageItem\"", subFactors[2], StringComparison.Ordinal) || string.Equals("\"object.item.imageItem.photo\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
                         {
@@ -71,5 +46,30 @@ namespace MediaBrowser.Model.Dlna
                 }
             }
         }
+
+        public SearchType SearchType { get; set; }
+
+        /// <summary>
+        /// Splits the specified string.
+        /// </summary>
+        /// <param name="str">The string.</param>
+        /// <param name="term">The term.</param>
+        /// <param name="limit">The limit.</param>
+        /// <returns>System.String[].</returns>
+        private static string[] RegexSplit(string str, string term, int limit)
+        {
+            return new Regex(term).Split(str, limit);
+        }
+
+        /// <summary>
+        /// Splits the specified string.
+        /// </summary>
+        /// <param name="str">The string.</param>
+        /// <param name="term">The term.</param>
+        /// <returns>System.String[].</returns>
+        private static string[] RegexSplit(string str, string term)
+        {
+            return Regex.Split(str, term, RegexOptions.IgnoreCase);
+        }
     }
 }

+ 2 - 2
MediaBrowser.Model/Dlna/SortCriteria.cs

@@ -6,10 +6,10 @@ namespace MediaBrowser.Model.Dlna
 {
     public class SortCriteria
     {
-        public SortOrder SortOrder => SortOrder.Ascending;
-
         public SortCriteria(string value)
         {
         }
+
+        public SortOrder SortOrder => SortOrder.Ascending;
     }
 }

+ 8 - 6
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -227,7 +227,7 @@ namespace MediaBrowser.Model.Dlna
             }
         }
 
-        public static string NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, string _, DeviceProfile profile, DlnaProfileType type)
+        public static string NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, DeviceProfile profile, DlnaProfileType type)
         {
             if (string.IsNullOrEmpty(inputContainer))
             {
@@ -274,14 +274,14 @@ namespace MediaBrowser.Model.Dlna
             if (options.ForceDirectPlay)
             {
                 playlistItem.PlayMethod = PlayMethod.DirectPlay;
-                playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, item.Path, options.Profile, DlnaProfileType.Audio);
+                playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
                 return playlistItem;
             }
 
             if (options.ForceDirectStream)
             {
                 playlistItem.PlayMethod = PlayMethod.DirectStream;
-                playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, item.Path, options.Profile, DlnaProfileType.Audio);
+                playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
                 return playlistItem;
             }
 
@@ -349,7 +349,7 @@ namespace MediaBrowser.Model.Dlna
                         playlistItem.PlayMethod = PlayMethod.DirectStream;
                     }
 
-                    playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, item.Path, options.Profile, DlnaProfileType.Audio);
+                    playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
 
                     return playlistItem;
                 }
@@ -698,7 +698,7 @@ namespace MediaBrowser.Model.Dlna
                 if (directPlay != null)
                 {
                     playlistItem.PlayMethod = directPlay.Value;
-                    playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, item.Path, options.Profile, DlnaProfileType.Video);
+                    playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Video);
 
                     if (subtitleStream != null)
                     {
@@ -1404,7 +1404,9 @@ namespace MediaBrowser.Model.Dlna
             {
                 _logger.LogInformation(
                     "Bitrate exceeds {PlayBackMethod} limit: media bitrate: {MediaBitrate}, max bitrate: {MaxBitrate}",
-                    playMethod, itemBitrate, requestedMaxBitrate);
+                    playMethod,
+                    itemBitrate,
+                    requestedMaxBitrate);
                 return false;
             }
 

+ 596 - 594
MediaBrowser.Model/Dlna/StreamInfo.cs

@@ -27,45 +27,6 @@ namespace MediaBrowser.Model.Dlna
             StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
         }
 
-        public void SetOption(string qualifier, string name, string value)
-        {
-            if (string.IsNullOrEmpty(qualifier))
-            {
-                SetOption(name, value);
-            }
-            else
-            {
-                SetOption(qualifier + "-" + name, value);
-            }
-        }
-
-        public void SetOption(string name, string value)
-        {
-            StreamOptions[name] = value;
-        }
-
-        public string GetOption(string qualifier, string name)
-        {
-            var value = GetOption(qualifier + "-" + name);
-
-            if (string.IsNullOrEmpty(value))
-            {
-                value = GetOption(name);
-            }
-
-            return value;
-        }
-
-        public string GetOption(string name)
-        {
-            if (StreamOptions.TryGetValue(name, out var value))
-            {
-                return value;
-            }
-
-            return null;
-        }
-
         public Guid ItemId { get; set; }
 
         public PlayMethod PlayMethod { get; set; }
@@ -152,887 +113,928 @@ namespace MediaBrowser.Model.Dlna
             PlayMethod == PlayMethod.DirectStream ||
             PlayMethod == PlayMethod.DirectPlay;
 
-        public string ToUrl(string baseUrl, string accessToken)
+        /// <summary>
+        /// Gets the audio stream that will be used.
+        /// </summary>
+        public MediaStream TargetAudioStream
         {
-            if (PlayMethod == PlayMethod.DirectPlay)
+            get
             {
-                return MediaSource.Path;
+                if (MediaSource != null)
+                {
+                    return MediaSource.GetDefaultAudioStream(AudioStreamIndex);
+                }
+
+                return null;
             }
+        }
 
-            if (string.IsNullOrEmpty(baseUrl))
+        /// <summary>
+        /// Gets the video stream that will be used.
+        /// </summary>
+        public MediaStream TargetVideoStream
+        {
+            get
             {
-                throw new ArgumentNullException(nameof(baseUrl));
+                if (MediaSource != null)
+                {
+                    return MediaSource.VideoStream;
+                }
+
+                return null;
             }
+        }
 
-            var list = new List<string>();
-            foreach (NameValuePair pair in BuildParams(this, accessToken))
+        /// <summary>
+        /// Gets the audio sample rate that will be in the output stream.
+        /// </summary>
+        public int? TargetAudioSampleRate
+        {
+            get
             {
-                if (string.IsNullOrEmpty(pair.Value))
+                var stream = TargetAudioStream;
+                return AudioSampleRate.HasValue && !IsDirectStream
+                    ? AudioSampleRate
+                    : stream == null ? null : stream.SampleRate;
+            }
+        }
+
+        /// <summary>
+        /// Gets the audio sample rate that will be in the output stream.
+        /// </summary>
+        public int? TargetAudioBitDepth
+        {
+            get
+            {
+                if (IsDirectStream)
                 {
-                    continue;
+                    return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth;
                 }
 
-                // Try to keep the url clean by omitting defaults
-                if (string.Equals(pair.Name, "StartTimeTicks", StringComparison.OrdinalIgnoreCase) &&
-                    string.Equals(pair.Value, "0", StringComparison.OrdinalIgnoreCase))
+                var targetAudioCodecs = TargetAudioCodec;
+                var audioCodec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0];
+                if (!string.IsNullOrEmpty(audioCodec))
                 {
-                    continue;
+                    return GetTargetAudioBitDepth(audioCodec);
                 }
 
-                if (string.Equals(pair.Name, "SubtitleStreamIndex", StringComparison.OrdinalIgnoreCase) &&
-                    string.Equals(pair.Value, "-1", StringComparison.OrdinalIgnoreCase))
+                return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth;
+            }
+        }
+
+        /// <summary>
+        /// Gets the audio sample rate that will be in the output stream.
+        /// </summary>
+        public int? TargetVideoBitDepth
+        {
+            get
+            {
+                if (IsDirectStream)
                 {
-                    continue;
+                    return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth;
                 }
 
-                // Be careful, IsDirectStream==true by default (Static != false or not in query).
-                // See initialization of StreamingRequestDto in AudioController.GetAudioStream() method : Static = @static ?? true.
-                if (string.Equals(pair.Name, "Static", StringComparison.OrdinalIgnoreCase) &&
-                    string.Equals(pair.Value, "true", StringComparison.OrdinalIgnoreCase))
+                var targetVideoCodecs = TargetVideoCodec;
+                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
+                if (!string.IsNullOrEmpty(videoCodec))
                 {
-                    continue;
+                    return GetTargetVideoBitDepth(videoCodec);
                 }
 
-                var encodedValue = pair.Value.Replace(" ", "%20", StringComparison.Ordinal);
-
-                list.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", pair.Name, encodedValue));
+                return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth;
             }
-
-            string queryString = string.Join('&', list);
-
-            return GetUrl(baseUrl, queryString);
         }
 
-        private string GetUrl(string baseUrl, string queryString)
+        /// <summary>
+        /// Gets the target reference frames.
+        /// </summary>
+        /// <value>The target reference frames.</value>
+        public int? TargetRefFrames
         {
-            if (string.IsNullOrEmpty(baseUrl))
+            get
             {
-                throw new ArgumentNullException(nameof(baseUrl));
-            }
-
-            string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container;
-
-            baseUrl = baseUrl.TrimEnd('/');
+                if (IsDirectStream)
+                {
+                    return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames;
+                }
 
-            if (MediaType == DlnaProfileType.Audio)
-            {
-                if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
+                var targetVideoCodecs = TargetVideoCodec;
+                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
+                if (!string.IsNullOrEmpty(videoCodec))
                 {
-                    return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
+                    return GetTargetRefFrames(videoCodec);
                 }
 
-                return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
+                return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames;
             }
+        }
 
-            if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
+        /// <summary>
+        /// Gets the audio sample rate that will be in the output stream.
+        /// </summary>
+        public float? TargetFramerate
+        {
+            get
             {
-                return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
+                var stream = TargetVideoStream;
+                return MaxFramerate.HasValue && !IsDirectStream
+                    ? MaxFramerate
+                    : stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate;
             }
-
-            return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
         }
 
-        private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken)
+        /// <summary>
+        /// Gets the audio sample rate that will be in the output stream.
+        /// </summary>
+        public double? TargetVideoLevel
         {
-            var list = new List<NameValuePair>();
-
-            string audioCodecs = item.AudioCodecs.Length == 0 ?
-                string.Empty :
-                string.Join(',', item.AudioCodecs);
-
-            string videoCodecs = item.VideoCodecs.Length == 0 ?
-                string.Empty :
-                string.Join(',', item.VideoCodecs);
-
-            list.Add(new NameValuePair("DeviceProfileId", item.DeviceProfileId ?? string.Empty));
-            list.Add(new NameValuePair("DeviceId", item.DeviceId ?? string.Empty));
-            list.Add(new NameValuePair("MediaSourceId", item.MediaSourceId ?? string.Empty));
-            list.Add(new NameValuePair("Static", item.IsDirectStream.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
-            list.Add(new NameValuePair("VideoCodec", videoCodecs));
-            list.Add(new NameValuePair("AudioCodec", audioCodecs));
-            list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
-            list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
-            list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
-            list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
-            list.Add(new NameValuePair("AudioSampleRate", item.AudioSampleRate.HasValue ? item.AudioSampleRate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
-
-            list.Add(new NameValuePair("MaxFramerate", item.MaxFramerate.HasValue ? item.MaxFramerate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
-            list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
-            list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
-
-            long startPositionTicks = item.StartPositionTicks;
+            get
+            {
+                if (IsDirectStream)
+                {
+                    return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level;
+                }
 
-            var isHls = string.Equals(item.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase);
+                var targetVideoCodecs = TargetVideoCodec;
+                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
+                if (!string.IsNullOrEmpty(videoCodec))
+                {
+                    return GetTargetVideoLevel(videoCodec);
+                }
 
-            if (isHls)
-            {
-                list.Add(new NameValuePair("StartTimeTicks", string.Empty));
+                return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level;
             }
-            else
+        }
+
+        /// <summary>
+        /// Gets the audio sample rate that will be in the output stream.
+        /// </summary>
+        public int? TargetPacketLength
+        {
+            get
             {
-                list.Add(new NameValuePair("StartTimeTicks", startPositionTicks.ToString(CultureInfo.InvariantCulture)));
+                var stream = TargetVideoStream;
+                return !IsDirectStream
+                    ? null
+                    : stream == null ? null : stream.PacketLength;
             }
+        }
 
-            list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty));
-            list.Add(new NameValuePair("api_key", accessToken ?? string.Empty));
-
-            string liveStreamId = item.MediaSource?.LiveStreamId;
-            list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty));
-
-            list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty));
-
-            if (!item.IsDirectStream)
+        /// <summary>
+        /// Gets the audio sample rate that will be in the output stream.
+        /// </summary>
+        public string TargetVideoProfile
+        {
+            get
             {
-                if (item.RequireNonAnamorphic)
+                if (IsDirectStream)
                 {
-                    list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
+                    return TargetVideoStream == null ? null : TargetVideoStream.Profile;
                 }
 
-                list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? item.TranscodingMaxAudioChannels.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
-
-                if (item.EnableSubtitlesInManifest)
+                var targetVideoCodecs = TargetVideoCodec;
+                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
+                if (!string.IsNullOrEmpty(videoCodec))
                 {
-                    list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
-                }
-
-                if (item.EnableMpegtsM2TsMode)
-                {
-                    list.Add(new NameValuePair("EnableMpegtsM2TsMode", item.EnableMpegtsM2TsMode.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
-                }
-
-                if (item.EstimateContentLength)
-                {
-                    list.Add(new NameValuePair("EstimateContentLength", item.EstimateContentLength.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
-                }
-
-                if (item.TranscodeSeekInfo != TranscodeSeekInfo.Auto)
-                {
-                    list.Add(new NameValuePair("TranscodeSeekInfo", item.TranscodeSeekInfo.ToString().ToLowerInvariant()));
-                }
-
-                if (item.CopyTimestamps)
-                {
-                    list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
-                }
-
-                list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
-            }
-
-            list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty));
-
-            string subtitleCodecs = item.SubtitleCodecs.Length == 0 ?
-               string.Empty :
-               string.Join(',', item.SubtitleCodecs);
-
-            list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty));
-
-            if (isHls)
-            {
-                list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty));
-
-                if (item.SegmentLength.HasValue)
-                {
-                    list.Add(new NameValuePair("SegmentLength", item.SegmentLength.Value.ToString(CultureInfo.InvariantCulture)));
-                }
-
-                if (item.MinSegments.HasValue)
-                {
-                    list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture)));
-                }
-
-                list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString(CultureInfo.InvariantCulture)));
-            }
-
-            foreach (var pair in item.StreamOptions)
-            {
-                if (string.IsNullOrEmpty(pair.Value))
-                {
-                    continue;
+                    return GetOption(videoCodec, "profile");
                 }
 
-                // strip spaces to avoid having to encode h264 profile names
-                list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", string.Empty, StringComparison.Ordinal)));
+                return TargetVideoStream == null ? null : TargetVideoStream.Profile;
             }
+        }
 
-            if (!item.IsDirectStream)
+        /// <summary>
+        /// Gets the target video codec tag.
+        /// </summary>
+        /// <value>The target video codec tag.</value>
+        public string TargetVideoCodecTag
+        {
+            get
             {
-                list.Add(new NameValuePair("TranscodeReasons", string.Join(',', item.TranscodeReasons.Distinct().Select(i => i.ToString()))));
+                var stream = TargetVideoStream;
+                return !IsDirectStream
+                    ? null
+                    : stream == null ? null : stream.CodecTag;
             }
-
-            return list;
         }
 
-        public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
+        /// <summary>
+        /// Gets the audio bitrate that will be in the output stream.
+        /// </summary>
+        public int? TargetAudioBitrate
         {
-            return GetExternalSubtitles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
+            get
+            {
+                var stream = TargetAudioStream;
+                return AudioBitrate.HasValue && !IsDirectStream
+                    ? AudioBitrate
+                    : stream == null ? null : stream.BitRate;
+            }
         }
 
-        public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
+        /// <summary>
+        /// Gets the audio channels that will be in the output stream.
+        /// </summary>
+        public int? TargetAudioChannels
         {
-            var list = GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken);
-            var newList = new List<SubtitleStreamInfo>();
-
-            // First add the selected track
-            foreach (SubtitleStreamInfo stream in list)
+            get
             {
-                if (stream.DeliveryMethod == SubtitleDeliveryMethod.External)
+                if (IsDirectStream)
                 {
-                    newList.Add(stream);
+                    return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels;
                 }
-            }
 
-            return newList;
-        }
+                var targetAudioCodecs = TargetAudioCodec;
+                var codec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0];
+                if (!string.IsNullOrEmpty(codec))
+                {
+                    return GetTargetRefFrames(codec);
+                }
 
-        public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
-        {
-            return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
+                return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels;
+            }
         }
 
-        public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
+        /// <summary>
+        /// Gets the audio codec that will be in the output stream.
+        /// </summary>
+        public string[] TargetAudioCodec
         {
-            var list = new List<SubtitleStreamInfo>();
+            get
+            {
+                var stream = TargetAudioStream;
 
-            // HLS will preserve timestamps so we can just grab the full subtitle stream
-            long startPositionTicks = string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase)
-                ? 0
-                : (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0);
+                string inputCodec = stream?.Codec;
 
-            // First add the selected track
-            if (SubtitleStreamIndex.HasValue)
-            {
-                foreach (var stream in MediaSource.MediaStreams)
+                if (IsDirectStream)
                 {
-                    if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value)
-                    {
-                        AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
-                    }
+                    return string.IsNullOrEmpty(inputCodec) ? Array.Empty<string>() : new[] { inputCodec };
                 }
-            }
 
-            if (!includeSelectedTrackOnly)
-            {
-                foreach (var stream in MediaSource.MediaStreams)
+                foreach (string codec in AudioCodecs)
                 {
-                    if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value))
+                    if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase))
                     {
-                        AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
+                        return string.IsNullOrEmpty(codec) ? Array.Empty<string>() : new[] { codec };
                     }
                 }
-            }
-
-            return list;
-        }
-
-        private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks)
-        {
-            if (enableAllProfiles)
-            {
-                foreach (var profile in DeviceProfile.SubtitleProfiles)
-                {
-                    var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }, transcoderSupport);
-
-                    list.Add(info);
-                }
-            }
-            else
-            {
-                var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles, transcoderSupport);
 
-                list.Add(info);
+                return AudioCodecs;
             }
         }
 
-        private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport)
+        public string[] TargetVideoCodec
         {
-            var subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, Container, SubProtocol);
-            var info = new SubtitleStreamInfo
+            get
             {
-                IsForced = stream.IsForced,
-                Language = stream.Language,
-                Name = stream.Language ?? "Unknown",
-                Format = subtitleProfile.Format,
-                Index = stream.Index,
-                DeliveryMethod = subtitleProfile.Method,
-                DisplayTitle = stream.DisplayTitle
-            };
+                var stream = TargetVideoStream;
 
-            if (info.DeliveryMethod == SubtitleDeliveryMethod.External)
-            {
-                if (MediaSource.Protocol == MediaProtocol.File || !string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) || !stream.IsExternal)
+                string inputCodec = stream?.Codec;
+
+                if (IsDirectStream)
                 {
-                    info.Url = string.Format(CultureInfo.InvariantCulture, "{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
-                        baseUrl,
-                        ItemId,
-                        MediaSourceId,
-                        stream.Index.ToString(CultureInfo.InvariantCulture),
-                        startPositionTicks.ToString(CultureInfo.InvariantCulture),
-                        subtitleProfile.Format);
+                    return string.IsNullOrEmpty(inputCodec) ? Array.Empty<string>() : new[] { inputCodec };
+                }
 
-                    if (!string.IsNullOrEmpty(accessToken))
+                foreach (string codec in VideoCodecs)
+                {
+                    if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase))
                     {
-                        info.Url += "?api_key=" + accessToken;
+                        return string.IsNullOrEmpty(codec) ? Array.Empty<string>() : new[] { codec };
                     }
-
-                    info.IsExternalUrl = false;
                 }
-                else
-                {
-                    info.Url = stream.Path;
-                    info.IsExternalUrl = true;
-                }
-            }
 
-            return info;
+                return VideoCodecs;
+            }
         }
 
         /// <summary>
-        /// Returns the audio stream that will be used.
+        /// Gets the audio channels that will be in the output stream.
         /// </summary>
-        public MediaStream TargetAudioStream
+        public long? TargetSize
         {
             get
             {
-                if (MediaSource != null)
+                if (IsDirectStream)
                 {
-                    return MediaSource.GetDefaultAudioStream(AudioStreamIndex);
+                    return MediaSource.Size;
+                }
+
+                if (RunTimeTicks.HasValue)
+                {
+                    int? totalBitrate = TargetTotalBitrate;
+
+                    double totalSeconds = RunTimeTicks.Value;
+                    // Convert to ms
+                    totalSeconds /= 10000;
+                    // Convert to seconds
+                    totalSeconds /= 1000;
+
+                    return totalBitrate.HasValue ?
+                        Convert.ToInt64(totalBitrate.Value * totalSeconds) :
+                        (long?)null;
                 }
 
                 return null;
             }
         }
 
-        /// <summary>
-        /// Returns the video stream that will be used.
-        /// </summary>
-        public MediaStream TargetVideoStream
+        public int? TargetVideoBitrate
         {
             get
             {
-                if (MediaSource != null)
-                {
-                    return MediaSource.VideoStream;
-                }
+                var stream = TargetVideoStream;
 
-                return null;
+                return VideoBitrate.HasValue && !IsDirectStream
+                    ? VideoBitrate
+                    : stream == null ? null : stream.BitRate;
             }
         }
 
-        /// <summary>
-        /// Predicts the audio sample rate that will be in the output stream.
-        /// </summary>
-        public int? TargetAudioSampleRate
+        public TransportStreamTimestamp TargetTimestamp
         {
             get
             {
-                var stream = TargetAudioStream;
-                return AudioSampleRate.HasValue && !IsDirectStream
-                    ? AudioSampleRate
-                    : stream == null ? null : stream.SampleRate;
+                var defaultValue = string.Equals(Container, "m2ts", StringComparison.OrdinalIgnoreCase)
+                    ? TransportStreamTimestamp.Valid
+                    : TransportStreamTimestamp.None;
+
+                return !IsDirectStream
+                    ? defaultValue
+                    : MediaSource == null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None;
             }
         }
 
-        /// <summary>
-        /// Predicts the audio sample rate that will be in the output stream.
-        /// </summary>
-        public int? TargetAudioBitDepth
+        public int? TargetTotalBitrate => (TargetAudioBitrate ?? 0) + (TargetVideoBitrate ?? 0);
+
+        public bool? IsTargetAnamorphic
         {
             get
             {
                 if (IsDirectStream)
                 {
-                    return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth;
-                }
-
-                var targetAudioCodecs = TargetAudioCodec;
-                var audioCodec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0];
-                if (!string.IsNullOrEmpty(audioCodec))
-                {
-                    return GetTargetAudioBitDepth(audioCodec);
+                    return TargetVideoStream == null ? null : TargetVideoStream.IsAnamorphic;
                 }
 
-                return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth;
+                return false;
             }
         }
 
-        /// <summary>
-        /// Predicts the audio sample rate that will be in the output stream.
-        /// </summary>
-        public int? TargetVideoBitDepth
+        public bool? IsTargetInterlaced
         {
             get
             {
                 if (IsDirectStream)
                 {
-                    return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth;
+                    return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
                 }
 
                 var targetVideoCodecs = TargetVideoCodec;
                 var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
                 if (!string.IsNullOrEmpty(videoCodec))
                 {
-                    return GetTargetVideoBitDepth(videoCodec);
+                    if (string.Equals(GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
+                    {
+                        return false;
+                    }
                 }
 
-                return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth;
+                return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
             }
         }
 
-        /// <summary>
-        /// Gets the target reference frames.
-        /// </summary>
-        /// <value>The target reference frames.</value>
-        public int? TargetRefFrames
+        public bool? IsTargetAVC
         {
             get
             {
                 if (IsDirectStream)
                 {
-                    return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames;
-                }
-
-                var targetVideoCodecs = TargetVideoCodec;
-                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
-                if (!string.IsNullOrEmpty(videoCodec))
-                {
-                    return GetTargetRefFrames(videoCodec);
+                    return TargetVideoStream == null ? null : TargetVideoStream.IsAVC;
                 }
 
-                return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames;
+                return true;
             }
         }
 
-        /// <summary>
-        /// Predicts the audio sample rate that will be in the output stream.
-        /// </summary>
-        public float? TargetFramerate
+        public int? TargetWidth
         {
             get
             {
-                var stream = TargetVideoStream;
-                return MaxFramerate.HasValue && !IsDirectStream
-                    ? MaxFramerate
-                    : stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate;
+                var videoStream = TargetVideoStream;
+
+                if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
+                {
+                    ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value);
+
+                    size = DrawingUtils.Resize(size, 0, 0, MaxWidth ?? 0, MaxHeight ?? 0);
+
+                    return size.Width;
+                }
+
+                return MaxWidth;
             }
         }
 
-        /// <summary>
-        /// Predicts the audio sample rate that will be in the output stream.
-        /// </summary>
-        public double? TargetVideoLevel
+        public int? TargetHeight
         {
             get
             {
-                if (IsDirectStream)
-                {
-                    return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level;
-                }
+                var videoStream = TargetVideoStream;
 
-                var targetVideoCodecs = TargetVideoCodec;
-                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
-                if (!string.IsNullOrEmpty(videoCodec))
+                if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
                 {
-                    return GetTargetVideoLevel(videoCodec);
+                    ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value);
+
+                    size = DrawingUtils.Resize(size, 0, 0, MaxWidth ?? 0, MaxHeight ?? 0);
+
+                    return size.Height;
                 }
 
-                return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level;
+                return MaxHeight;
             }
         }
 
-        public int? GetTargetVideoBitDepth(string codec)
+        public int? TargetVideoStreamCount
         {
-            var value = GetOption(codec, "videobitdepth");
-            if (string.IsNullOrEmpty(value))
+            get
             {
-                return null;
-            }
+                if (IsDirectStream)
+                {
+                    return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
+                }
 
-            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
-            {
-                return result;
+                return GetMediaStreamCount(MediaStreamType.Video, 1);
             }
-
-            return null;
         }
 
-        public int? GetTargetAudioBitDepth(string codec)
+        public int? TargetAudioStreamCount
         {
-            var value = GetOption(codec, "audiobitdepth");
-            if (string.IsNullOrEmpty(value))
-            {
-                return null;
-            }
-
-            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
+            get
             {
-                return result;
-            }
+                if (IsDirectStream)
+                {
+                    return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue);
+                }
 
-            return null;
+                return GetMediaStreamCount(MediaStreamType.Audio, 1);
+            }
         }
 
-        public double? GetTargetVideoLevel(string codec)
+        public void SetOption(string qualifier, string name, string value)
         {
-            var value = GetOption(codec, "level");
-            if (string.IsNullOrEmpty(value))
+            if (string.IsNullOrEmpty(qualifier))
             {
-                return null;
+                SetOption(name, value);
             }
-
-            if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+            else
             {
-                return result;
+                SetOption(qualifier + "-" + name, value);
             }
+        }
 
-            return null;
+        public void SetOption(string name, string value)
+        {
+            StreamOptions[name] = value;
         }
 
-        public int? GetTargetRefFrames(string codec)
+        public string GetOption(string qualifier, string name)
         {
-            var value = GetOption(codec, "maxrefframes");
+            var value = GetOption(qualifier + "-" + name);
+
             if (string.IsNullOrEmpty(value))
             {
-                return null;
+                value = GetOption(name);
             }
 
-            if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+            return value;
+        }
+
+        public string GetOption(string name)
+        {
+            if (StreamOptions.TryGetValue(name, out var value))
             {
-                return result;
+                return value;
             }
 
             return null;
         }
 
-        /// <summary>
-        /// Predicts the audio sample rate that will be in the output stream.
-        /// </summary>
-        public int? TargetPacketLength
+        public string ToUrl(string baseUrl, string accessToken)
         {
-            get
+            if (PlayMethod == PlayMethod.DirectPlay)
             {
-                var stream = TargetVideoStream;
-                return !IsDirectStream
-                    ? null
-                    : stream == null ? null : stream.PacketLength;
+                return MediaSource.Path;
             }
-        }
 
-        /// <summary>
-        /// Predicts the audio sample rate that will be in the output stream.
-        /// </summary>
-        public string TargetVideoProfile
-        {
-            get
+            if (string.IsNullOrEmpty(baseUrl))
             {
-                if (IsDirectStream)
+                throw new ArgumentNullException(nameof(baseUrl));
+            }
+
+            var list = new List<string>();
+            foreach (NameValuePair pair in BuildParams(this, accessToken))
+            {
+                if (string.IsNullOrEmpty(pair.Value))
                 {
-                    return TargetVideoStream == null ? null : TargetVideoStream.Profile;
+                    continue;
                 }
 
-                var targetVideoCodecs = TargetVideoCodec;
-                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
-                if (!string.IsNullOrEmpty(videoCodec))
+                // Try to keep the url clean by omitting defaults
+                if (string.Equals(pair.Name, "StartTimeTicks", StringComparison.OrdinalIgnoreCase) &&
+                    string.Equals(pair.Value, "0", StringComparison.OrdinalIgnoreCase))
                 {
-                    return GetOption(videoCodec, "profile");
+                    continue;
                 }
 
-                return TargetVideoStream == null ? null : TargetVideoStream.Profile;
-            }
-        }
+                if (string.Equals(pair.Name, "SubtitleStreamIndex", StringComparison.OrdinalIgnoreCase) &&
+                    string.Equals(pair.Value, "-1", StringComparison.OrdinalIgnoreCase))
+                {
+                    continue;
+                }
 
-        /// <summary>
-        /// Gets the target video codec tag.
-        /// </summary>
-        /// <value>The target video codec tag.</value>
-        public string TargetVideoCodecTag
-        {
-            get
-            {
-                var stream = TargetVideoStream;
-                return !IsDirectStream
-                    ? null
-                    : stream == null ? null : stream.CodecTag;
+                // Be careful, IsDirectStream==true by default (Static != false or not in query).
+                // See initialization of StreamingRequestDto in AudioController.GetAudioStream() method : Static = @static ?? true.
+                if (string.Equals(pair.Name, "Static", StringComparison.OrdinalIgnoreCase) &&
+                    string.Equals(pair.Value, "true", StringComparison.OrdinalIgnoreCase))
+                {
+                    continue;
+                }
+
+                var encodedValue = pair.Value.Replace(" ", "%20");
+
+                list.Add(string.Format(CultureInfo.InvariantCulture, "{0}={1}", pair.Name, encodedValue));
             }
+
+            string queryString = string.Join("&", list.ToArray());
+
+            return GetUrl(baseUrl, queryString);
         }
 
-        /// <summary>
-        /// Predicts the audio bitrate that will be in the output stream.
-        /// </summary>
-        public int? TargetAudioBitrate
+        private string GetUrl(string baseUrl, string queryString)
         {
-            get
+            if (string.IsNullOrEmpty(baseUrl))
             {
-                var stream = TargetAudioStream;
-                return AudioBitrate.HasValue && !IsDirectStream
-                    ? AudioBitrate
-                    : stream == null ? null : stream.BitRate;
+                throw new ArgumentNullException(nameof(baseUrl));
             }
-        }
 
-        /// <summary>
-        /// Predicts the audio channels that will be in the output stream.
-        /// </summary>
-        public int? TargetAudioChannels
-        {
-            get
+            string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container;
+
+            baseUrl = baseUrl.TrimEnd('/');
+
+            if (MediaType == DlnaProfileType.Audio)
             {
-                if (IsDirectStream)
+                if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
                 {
-                    return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels;
+                    return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
                 }
 
-                var targetAudioCodecs = TargetAudioCodec;
-                var codec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0];
-                if (!string.IsNullOrEmpty(codec))
-                {
-                    return GetTargetRefFrames(codec);
-                }
+                return string.Format(CultureInfo.InvariantCulture, "{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
+            }
 
-                return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels;
+            if (string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
+            {
+                return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString);
             }
+
+            return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString);
         }
 
-        public int? GetTargetAudioChannels(string codec)
+        private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken)
         {
-            var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels;
+            var list = new List<NameValuePair>();
 
-            var value = GetOption(codec, "audiochannels");
-            if (string.IsNullOrEmpty(value))
+            string audioCodecs = item.AudioCodecs.Length == 0 ?
+                string.Empty :
+                string.Join(",", item.AudioCodecs);
+
+            string videoCodecs = item.VideoCodecs.Length == 0 ?
+                string.Empty :
+                string.Join(",", item.VideoCodecs);
+
+            list.Add(new NameValuePair("DeviceProfileId", item.DeviceProfileId ?? string.Empty));
+            list.Add(new NameValuePair("DeviceId", item.DeviceId ?? string.Empty));
+            list.Add(new NameValuePair("MediaSourceId", item.MediaSourceId ?? string.Empty));
+            list.Add(new NameValuePair("Static", item.IsDirectStream.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
+            list.Add(new NameValuePair("VideoCodec", videoCodecs));
+            list.Add(new NameValuePair("AudioCodec", audioCodecs));
+            list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
+            list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
+            list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
+            list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
+            list.Add(new NameValuePair("AudioSampleRate", item.AudioSampleRate.HasValue ? item.AudioSampleRate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
+
+            list.Add(new NameValuePair("MaxFramerate", item.MaxFramerate.HasValue ? item.MaxFramerate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
+            list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
+            list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
+
+            long startPositionTicks = item.StartPositionTicks;
+
+            var isHls = string.Equals(item.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase);
+
+            if (isHls)
             {
-                return defaultValue;
+                list.Add(new NameValuePair("StartTimeTicks", string.Empty));
             }
-
-            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
+            else
             {
-                return Math.Min(result, defaultValue ?? result);
+                list.Add(new NameValuePair("StartTimeTicks", startPositionTicks.ToString(CultureInfo.InvariantCulture)));
             }
 
-            return defaultValue;
-        }
+            list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty));
+            list.Add(new NameValuePair("api_key", accessToken ?? string.Empty));
 
-        /// <summary>
-        /// Predicts the audio codec that will be in the output stream.
-        /// </summary>
-        public string[] TargetAudioCodec
-        {
-            get
-            {
-                var stream = TargetAudioStream;
+            string liveStreamId = item.MediaSource?.LiveStreamId;
+            list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty));
 
-                string inputCodec = stream?.Codec;
+            list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty));
 
-                if (IsDirectStream)
+            if (!item.IsDirectStream)
+            {
+                if (item.RequireNonAnamorphic)
                 {
-                    return string.IsNullOrEmpty(inputCodec) ? Array.Empty<string>() : new[] { inputCodec };
+                    list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
                 }
 
-                foreach (string codec in AudioCodecs)
+                list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? item.TranscodingMaxAudioChannels.Value.ToString(CultureInfo.InvariantCulture) : string.Empty));
+
+                if (item.EnableSubtitlesInManifest)
                 {
-                    if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase))
-                    {
-                        return string.IsNullOrEmpty(codec) ? Array.Empty<string>() : new[] { codec };
-                    }
+                    list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
                 }
 
-                return AudioCodecs;
-            }
-        }
-
-        public string[] TargetVideoCodec
-        {
-            get
-            {
-                var stream = TargetVideoStream;
+                if (item.EnableMpegtsM2TsMode)
+                {
+                    list.Add(new NameValuePair("EnableMpegtsM2TsMode", item.EnableMpegtsM2TsMode.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
+                }
 
-                string inputCodec = stream?.Codec;
+                if (item.EstimateContentLength)
+                {
+                    list.Add(new NameValuePair("EstimateContentLength", item.EstimateContentLength.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
+                }
 
-                if (IsDirectStream)
+                if (item.TranscodeSeekInfo != TranscodeSeekInfo.Auto)
                 {
-                    return string.IsNullOrEmpty(inputCodec) ? Array.Empty<string>() : new[] { inputCodec };
+                    list.Add(new NameValuePair("TranscodeSeekInfo", item.TranscodeSeekInfo.ToString().ToLowerInvariant()));
                 }
 
-                foreach (string codec in VideoCodecs)
+                if (item.CopyTimestamps)
                 {
-                    if (string.Equals(codec, inputCodec, StringComparison.OrdinalIgnoreCase))
-                    {
-                        return string.IsNullOrEmpty(codec) ? Array.Empty<string>() : new[] { codec };
-                    }
+                    list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
                 }
 
-                return VideoCodecs;
+                list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()));
             }
-        }
 
-        /// <summary>
-        /// Predicts the audio channels that will be in the output stream.
-        /// </summary>
-        public long? TargetSize
-        {
-            get
+            list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty));
+
+            string subtitleCodecs = item.SubtitleCodecs.Length == 0 ?
+               string.Empty :
+               string.Join(",", item.SubtitleCodecs);
+
+            list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty));
+
+            if (isHls)
             {
-                if (IsDirectStream)
+                list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty));
+
+                if (item.SegmentLength.HasValue)
                 {
-                    return MediaSource.Size;
+                    list.Add(new NameValuePair("SegmentLength", item.SegmentLength.Value.ToString(CultureInfo.InvariantCulture)));
                 }
 
-                if (RunTimeTicks.HasValue)
+                if (item.MinSegments.HasValue)
                 {
-                    int? totalBitrate = TargetTotalBitrate;
-
-                    double totalSeconds = RunTimeTicks.Value;
-                    // Convert to ms
-                    totalSeconds /= 10000;
-                    // Convert to seconds
-                    totalSeconds /= 1000;
-
-                    return totalBitrate.HasValue ?
-                        Convert.ToInt64(totalBitrate.Value * totalSeconds) :
-                        (long?)null;
+                    list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture)));
                 }
 
-                return null;
+                list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString(CultureInfo.InvariantCulture)));
             }
-        }
 
-        public int? TargetVideoBitrate
-        {
-            get
+            foreach (var pair in item.StreamOptions)
             {
-                var stream = TargetVideoStream;
+                if (string.IsNullOrEmpty(pair.Value))
+                {
+                    continue;
+                }
 
-                return VideoBitrate.HasValue && !IsDirectStream
-                    ? VideoBitrate
-                    : stream == null ? null : stream.BitRate;
+                // strip spaces to avoid having to encode h264 profile names
+                list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", string.Empty)));
             }
-        }
 
-        public TransportStreamTimestamp TargetTimestamp
-        {
-            get
+            if (!item.IsDirectStream)
             {
-                var defaultValue = string.Equals(Container, "m2ts", StringComparison.OrdinalIgnoreCase)
-                    ? TransportStreamTimestamp.Valid
-                    : TransportStreamTimestamp.None;
-
-                return !IsDirectStream
-                    ? defaultValue
-                    : MediaSource == null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None;
+                list.Add(new NameValuePair("TranscodeReasons", string.Join(',', item.TranscodeReasons.Distinct())));
             }
+
+            return list;
         }
 
-        public int? TargetTotalBitrate => (TargetAudioBitrate ?? 0) + (TargetVideoBitrate ?? 0);
+        public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
+        {
+            return GetExternalSubtitles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
+        }
 
-        public bool? IsTargetAnamorphic
+        public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
         {
-            get
+            var list = GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken);
+            var newList = new List<SubtitleStreamInfo>();
+
+            // First add the selected track
+            foreach (SubtitleStreamInfo stream in list)
             {
-                if (IsDirectStream)
+                if (stream.DeliveryMethod == SubtitleDeliveryMethod.External)
                 {
-                    return TargetVideoStream == null ? null : TargetVideoStream.IsAnamorphic;
+                    newList.Add(stream);
                 }
-
-                return false;
             }
+
+            return newList;
         }
 
-        public bool? IsTargetInterlaced
+        public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
         {
-            get
+            return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
+        }
+
+        public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
+        {
+            var list = new List<SubtitleStreamInfo>();
+
+            // HLS will preserve timestamps so we can just grab the full subtitle stream
+            long startPositionTicks = string.Equals(SubProtocol, "hls", StringComparison.OrdinalIgnoreCase)
+                ? 0
+                : (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0);
+
+            // First add the selected track
+            if (SubtitleStreamIndex.HasValue)
             {
-                if (IsDirectStream)
+                foreach (var stream in MediaSource.MediaStreams)
                 {
-                    return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
+                    if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value)
+                    {
+                        AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
+                    }
                 }
+            }
 
-                var targetVideoCodecs = TargetVideoCodec;
-                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
-                if (!string.IsNullOrEmpty(videoCodec))
+            if (!includeSelectedTrackOnly)
+            {
+                foreach (var stream in MediaSource.MediaStreams)
                 {
-                    if (string.Equals(GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
+                    if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value))
                     {
-                        return false;
+                        AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks);
                     }
                 }
-
-                return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
             }
+
+            return list;
         }
 
-        public bool? IsTargetAVC
+        private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks)
         {
-            get
+            if (enableAllProfiles)
             {
-                if (IsDirectStream)
+                foreach (var profile in DeviceProfile.SubtitleProfiles)
                 {
-                    return TargetVideoStream == null ? null : TargetVideoStream.IsAVC;
+                    var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }, transcoderSupport);
+
+                    list.Add(info);
                 }
+            }
+            else
+            {
+                var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles, transcoderSupport);
 
-                return true;
+                list.Add(info);
             }
         }
 
-        public int? TargetWidth
+        private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport)
         {
-            get
+            var subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, Container, SubProtocol);
+            var info = new SubtitleStreamInfo
             {
-                var videoStream = TargetVideoStream;
+                IsForced = stream.IsForced,
+                Language = stream.Language,
+                Name = stream.Language ?? "Unknown",
+                Format = subtitleProfile.Format,
+                Index = stream.Index,
+                DeliveryMethod = subtitleProfile.Method,
+                DisplayTitle = stream.DisplayTitle
+            };
 
-                if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
+            if (info.DeliveryMethod == SubtitleDeliveryMethod.External)
+            {
+                if (MediaSource.Protocol == MediaProtocol.File || !string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) || !stream.IsExternal)
                 {
-                    ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value);
+                    info.Url = string.Format(
+                        CultureInfo.InvariantCulture,
+                        "{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
+                        baseUrl,
+                        ItemId,
+                        MediaSourceId,
+                        stream.Index.ToString(CultureInfo.InvariantCulture),
+                        startPositionTicks.ToString(CultureInfo.InvariantCulture),
+                        subtitleProfile.Format);
 
-                    size = DrawingUtils.Resize(size, 0, 0, MaxWidth ?? 0, MaxHeight ?? 0);
+                    if (!string.IsNullOrEmpty(accessToken))
+                    {
+                        info.Url += "?api_key=" + accessToken;
+                    }
 
-                    return size.Width;
+                    info.IsExternalUrl = false;
+                }
+                else
+                {
+                    info.Url = stream.Path;
+                    info.IsExternalUrl = true;
                 }
+            }
 
-                return MaxWidth;
+            return info;
+        }
+
+        public int? GetTargetVideoBitDepth(string codec)
+        {
+            var value = GetOption(codec, "videobitdepth");
+            if (string.IsNullOrEmpty(value))
+            {
+                return null;
+            }
+
+            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
+            {
+                return result;
             }
+
+            return null;
         }
 
-        public int? TargetHeight
+        public int? GetTargetAudioBitDepth(string codec)
         {
-            get
+            var value = GetOption(codec, "audiobitdepth");
+            if (string.IsNullOrEmpty(value))
             {
-                var videoStream = TargetVideoStream;
+                return null;
+            }
 
-                if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
-                {
-                    ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value);
+            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
+            {
+                return result;
+            }
 
-                    size = DrawingUtils.Resize(size, 0, 0, MaxWidth ?? 0, MaxHeight ?? 0);
+            return null;
+        }
 
-                    return size.Height;
-                }
+        public double? GetTargetVideoLevel(string codec)
+        {
+            var value = GetOption(codec, "level");
+            if (string.IsNullOrEmpty(value))
+            {
+                return null;
+            }
 
-                return MaxHeight;
+            if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+            {
+                return result;
             }
+
+            return null;
         }
 
-        public int? TargetVideoStreamCount
+        public int? GetTargetRefFrames(string codec)
         {
-            get
+            var value = GetOption(codec, "maxrefframes");
+            if (string.IsNullOrEmpty(value))
             {
-                if (IsDirectStream)
-                {
-                    return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue);
-                }
+                return null;
+            }
 
-                return GetMediaStreamCount(MediaStreamType.Video, 1);
+            if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+            {
+                return result;
             }
+
+            return null;
         }
 
-        public int? TargetAudioStreamCount
+        public int? GetTargetAudioChannels(string codec)
         {
-            get
+            var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels;
+
+            var value = GetOption(codec, "audiochannels");
+            if (string.IsNullOrEmpty(value))
             {
-                if (IsDirectStream)
-                {
-                    return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue);
-                }
+                return defaultValue;
+            }
 
-                return GetMediaStreamCount(MediaStreamType.Audio, 1);
+            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
+            {
+                return Math.Min(result, defaultValue ?? result);
             }
+
+            return defaultValue;
         }
 
         private int? GetMediaStreamCount(MediaStreamType type, int limit)

+ 5 - 5
MediaBrowser.Model/Dto/BaseItemDto.cs

@@ -294,13 +294,13 @@ namespace MediaBrowser.Model.Dto
         public NameGuidPair[] GenreItems { get; set; }
 
         /// <summary>
-        /// If the item does not have a logo, this will hold the Id of the Parent that has one.
+        /// Gets or sets wether the item has a logo, this will hold the Id of the Parent that has one.
         /// </summary>
         /// <value>The parent logo item id.</value>
         public string ParentLogoItemId { get; set; }
 
         /// <summary>
-        /// If the item does not have any backdrops, this will hold the Id of the Parent that has one.
+        /// Gets or sets wether the item has any backdrops, this will hold the Id of the Parent that has one.
         /// </summary>
         /// <value>The parent backdrop item id.</value>
         public string ParentBackdropItemId { get; set; }
@@ -318,7 +318,7 @@ namespace MediaBrowser.Model.Dto
         public int? LocalTrailerCount { get; set; }
 
         /// <summary>
-        /// User data for this item based on the user it's being requested for.
+        /// Gets or sets the user data for this item based on the user it's being requested for.
         /// </summary>
         /// <value>The user data.</value>
         public UserItemDataDto UserData { get; set; }
@@ -506,7 +506,7 @@ namespace MediaBrowser.Model.Dto
         public string ParentLogoImageTag { get; set; }
 
         /// <summary>
-        /// If the item does not have a art, this will hold the Id of the Parent that has one.
+        /// Gets or sets wether the item has fan art, this will hold the Id of the Parent that has one.
         /// </summary>
         /// <value>The parent art item id.</value>
         public string ParentArtItemId { get; set; }
@@ -695,7 +695,7 @@ namespace MediaBrowser.Model.Dto
         public string ChannelPrimaryImageTag { get; set; }
 
         /// <summary>
-        /// The start date of the recording, in UTC.
+        /// Gets or sets the start date of the recording, in UTC.
         /// </summary>
         public DateTime? StartDate { get; set; }
 

+ 34 - 33
MediaBrowser.Model/Dto/MediaSourceInfo.cs

@@ -12,6 +12,18 @@ namespace MediaBrowser.Model.Dto
 {
     public class MediaSourceInfo
     {
+        public MediaSourceInfo()
+        {
+            Formats = Array.Empty<string>();
+            MediaStreams = new List<MediaStream>();
+            MediaAttachments = Array.Empty<MediaAttachment>();
+            RequiredHttpHeaders = new Dictionary<string, string>();
+            SupportsTranscoding = true;
+            SupportsDirectStream = true;
+            SupportsDirectPlay = true;
+            SupportsProbing = true;
+        }
+
         public MediaProtocol Protocol { get; set; }
 
         public string Id { get; set; }
@@ -31,6 +43,7 @@ namespace MediaBrowser.Model.Dto
         public string Name { get; set; }
 
         /// <summary>
+        /// Gets or sets a value indicating whether the media is remote.
         /// Differentiate internet url vs local network.
         /// </summary>
         public bool IsRemote { get; set; }
@@ -95,16 +108,28 @@ namespace MediaBrowser.Model.Dto
 
         public int? AnalyzeDurationMs { get; set; }
 
-        public MediaSourceInfo()
+        [JsonIgnore]
+        public TranscodeReason[] TranscodeReasons { get; set; }
+
+        public int? DefaultAudioStreamIndex { get; set; }
+
+        public int? DefaultSubtitleStreamIndex { get; set; }
+
+        [JsonIgnore]
+        public MediaStream VideoStream
         {
-            Formats = Array.Empty<string>();
-            MediaStreams = new List<MediaStream>();
-            MediaAttachments = Array.Empty<MediaAttachment>();
-            RequiredHttpHeaders = new Dictionary<string, string>();
-            SupportsTranscoding = true;
-            SupportsDirectStream = true;
-            SupportsDirectPlay = true;
-            SupportsProbing = true;
+            get
+            {
+                foreach (var i in MediaStreams)
+                {
+                    if (i.Type == MediaStreamType.Video)
+                    {
+                        return i;
+                    }
+                }
+
+                return null;
+            }
         }
 
         public void InferTotalBitrate(bool force = false)
@@ -134,13 +159,6 @@ namespace MediaBrowser.Model.Dto
             }
         }
 
-        [JsonIgnore]
-        public TranscodeReason[] TranscodeReasons { get; set; }
-
-        public int? DefaultAudioStreamIndex { get; set; }
-
-        public int? DefaultSubtitleStreamIndex { get; set; }
-
         public MediaStream GetDefaultAudioStream(int? defaultIndex)
         {
             if (defaultIndex.HasValue)
@@ -175,23 +193,6 @@ namespace MediaBrowser.Model.Dto
             return null;
         }
 
-        [JsonIgnore]
-        public MediaStream VideoStream
-        {
-            get
-            {
-                foreach (var i in MediaStreams)
-                {
-                    if (i.Type == MediaStreamType.Video)
-                    {
-                        return i;
-                    }
-                }
-
-                return null;
-            }
-        }
-
         public MediaStream GetMediaStream(MediaStreamType type, int index)
         {
             foreach (var i in MediaStreams)

+ 9 - 9
MediaBrowser.Model/Dto/MetadataEditorInfo.cs

@@ -10,6 +10,15 @@ namespace MediaBrowser.Model.Dto
 {
     public class MetadataEditorInfo
     {
+        public MetadataEditorInfo()
+        {
+            ParentalRatingOptions = Array.Empty<ParentalRating>();
+            Countries = Array.Empty<CountryInfo>();
+            Cultures = Array.Empty<CultureDto>();
+            ExternalIdInfos = Array.Empty<ExternalIdInfo>();
+            ContentTypeOptions = Array.Empty<NameValuePair>();
+        }
+
         public ParentalRating[] ParentalRatingOptions { get; set; }
 
         public CountryInfo[] Countries { get; set; }
@@ -21,14 +30,5 @@ namespace MediaBrowser.Model.Dto
         public string ContentType { get; set; }
 
         public NameValuePair[] ContentTypeOptions { get; set; }
-
-        public MetadataEditorInfo()
-        {
-            ParentalRatingOptions = Array.Empty<ParentalRating>();
-            Countries = Array.Empty<CountryInfo>();
-            Cultures = Array.Empty<CultureDto>();
-            ExternalIdInfos = Array.Empty<ExternalIdInfo>();
-            ContentTypeOptions = Array.Empty<NameValuePair>();
-        }
     }
 }

+ 14 - 0
MediaBrowser.Model/Dto/NameGuidPair.cs

@@ -0,0 +1,14 @@
+#nullable disable
+#pragma warning disable CS1591
+
+using System;
+
+namespace MediaBrowser.Model.Dto
+{
+    public class NameGuidPair
+    {
+        public string Name { get; set; }
+
+        public Guid Id { get; set; }
+    }
+}

+ 0 - 7
MediaBrowser.Model/Dto/NameIdPair.cs

@@ -19,11 +19,4 @@ namespace MediaBrowser.Model.Dto
         /// <value>The identifier.</value>
         public string Id { get; set; }
     }
-
-    public class NameGuidPair
-    {
-        public string Name { get; set; }
-
-        public Guid Id { get; set; }
-    }
 }

+ 9 - 9
MediaBrowser.Model/Dto/UserDto.cs

@@ -10,6 +10,15 @@ namespace MediaBrowser.Model.Dto
     /// </summary>
     public class UserDto : IItemDto, IHasServerId
     {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="UserDto"/> class.
+        /// </summary>
+        public UserDto()
+        {
+            Configuration = new UserConfiguration();
+            Policy = new UserPolicy();
+        }
+
         /// <summary>
         /// Gets or sets the name.
         /// </summary>
@@ -94,15 +103,6 @@ namespace MediaBrowser.Model.Dto
         /// <value>The primary image aspect ratio.</value>
         public double? PrimaryImageAspectRatio { get; set; }
 
-        /// <summary>
-        /// Initializes a new instance of the <see cref="UserDto"/> class.
-        /// </summary>
-        public UserDto()
-        {
-            Configuration = new UserConfiguration();
-            Policy = new UserPolicy();
-        }
-
         /// <inheritdoc />
         public override string ToString()
         {

+ 0 - 32
MediaBrowser.Model/Entities/CollectionType.cs

@@ -24,36 +24,4 @@ namespace MediaBrowser.Model.Entities
         public const string Playlists = "playlists";
         public const string Folders = "folders";
     }
-
-    public static class SpecialFolder
-    {
-        public const string TvShowSeries = "TvShowSeries";
-        public const string TvGenres = "TvGenres";
-        public const string TvGenre = "TvGenre";
-        public const string TvLatest = "TvLatest";
-        public const string TvNextUp = "TvNextUp";
-        public const string TvResume = "TvResume";
-        public const string TvFavoriteSeries = "TvFavoriteSeries";
-        public const string TvFavoriteEpisodes = "TvFavoriteEpisodes";
-
-        public const string MovieLatest = "MovieLatest";
-        public const string MovieResume = "MovieResume";
-        public const string MovieMovies = "MovieMovies";
-        public const string MovieCollections = "MovieCollections";
-        public const string MovieFavorites = "MovieFavorites";
-        public const string MovieGenres = "MovieGenres";
-        public const string MovieGenre = "MovieGenre";
-
-        public const string MusicArtists = "MusicArtists";
-        public const string MusicAlbumArtists = "MusicAlbumArtists";
-        public const string MusicAlbums = "MusicAlbums";
-        public const string MusicGenres = "MusicGenres";
-        public const string MusicLatest = "MusicLatest";
-        public const string MusicPlaylists = "MusicPlaylists";
-        public const string MusicSongs = "MusicSongs";
-        public const string MusicFavorites = "MusicFavorites";
-        public const string MusicFavoriteArtists = "MusicFavoriteArtists";
-        public const string MusicFavoriteAlbums = "MusicFavoriteAlbums";
-        public const string MusicFavoriteSongs = "MusicFavoriteSongs";
-    }
 }

+ 98 - 99
MediaBrowser.Model/Entities/MediaStream.cs

@@ -84,7 +84,7 @@ namespace MediaBrowser.Model.Entities
         public string Title { get; set; }
 
         /// <summary>
-        /// Gets or sets the video range.
+        /// Gets the video range.
         /// </summary>
         /// <value>The video range.</value>
         public string VideoRange
@@ -108,11 +108,11 @@ namespace MediaBrowser.Model.Entities
             }
         }
 
-        public string localizedUndefined { get; set; }
+        public string LocalizedUndefined { get; set; }
 
-        public string localizedDefault { get; set; }
+        public string LocalizedDefault { get; set; }
 
-        public string localizedForced { get; set; }
+        public string LocalizedForced { get; set; }
 
         public string DisplayTitle
         {
@@ -154,7 +154,7 @@ namespace MediaBrowser.Model.Entities
 
                         if (IsDefault)
                         {
-                            attributes.Add(string.IsNullOrEmpty(localizedDefault) ? "Default" : localizedDefault);
+                            attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault);
                         }
 
                         if (!string.IsNullOrEmpty(Title))
@@ -229,17 +229,17 @@ namespace MediaBrowser.Model.Entities
                         }
                         else
                         {
-                            attributes.Add(string.IsNullOrEmpty(localizedUndefined) ? "Und" : localizedUndefined);
+                            attributes.Add(string.IsNullOrEmpty(LocalizedUndefined) ? "Und" : LocalizedUndefined);
                         }
 
                         if (IsDefault)
                         {
-                            attributes.Add(string.IsNullOrEmpty(localizedDefault) ? "Default" : localizedDefault);
+                            attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault);
                         }
 
                         if (IsForced)
                         {
-                            attributes.Add(string.IsNullOrEmpty(localizedForced) ? "Forced" : localizedForced);
+                            attributes.Add(string.IsNullOrEmpty(LocalizedForced) ? "Forced" : LocalizedForced);
                         }
 
                         if (!string.IsNullOrEmpty(Title))
@@ -266,67 +266,6 @@ namespace MediaBrowser.Model.Entities
             }
         }
 
-        private string GetResolutionText()
-        {
-            var i = this;
-
-            if (i.Width.HasValue && i.Height.HasValue)
-            {
-                var width = i.Width.Value;
-                var height = i.Height.Value;
-
-                if (width >= 3800 || height >= 2000)
-                {
-                    return "4K";
-                }
-
-                if (width >= 2500)
-                {
-                    if (i.IsInterlaced)
-                    {
-                        return "1440i";
-                    }
-
-                    return "1440p";
-                }
-
-                if (width >= 1900 || height >= 1000)
-                {
-                    if (i.IsInterlaced)
-                    {
-                        return "1080i";
-                    }
-
-                    return "1080p";
-                }
-
-                if (width >= 1260 || height >= 700)
-                {
-                    if (i.IsInterlaced)
-                    {
-                        return "720i";
-                    }
-
-                    return "720p";
-                }
-
-                if (width >= 700 || height >= 440)
-                {
-
-                    if (i.IsInterlaced)
-                    {
-                        return "480i";
-                    }
-
-                    return "480p";
-                }
-
-                return "SD";
-            }
-
-            return null;
-        }
-
         public string NalLengthSize { get; set; }
 
         /// <summary>
@@ -487,6 +426,96 @@ namespace MediaBrowser.Model.Entities
             }
         }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether [supports external stream].
+        /// </summary>
+        /// <value><c>true</c> if [supports external stream]; otherwise, <c>false</c>.</value>
+        public bool SupportsExternalStream { get; set; }
+
+        /// <summary>
+        /// Gets or sets the filename.
+        /// </summary>
+        /// <value>The filename.</value>
+        public string Path { get; set; }
+
+        /// <summary>
+        /// Gets or sets the pixel format.
+        /// </summary>
+        /// <value>The pixel format.</value>
+        public string PixelFormat { get; set; }
+
+        /// <summary>
+        /// Gets or sets the level.
+        /// </summary>
+        /// <value>The level.</value>
+        public double? Level { get; set; }
+
+        /// <summary>
+        /// Gets or sets whether this instance is anamorphic.
+        /// </summary>
+        /// <value><c>true</c> if this instance is anamorphic; otherwise, <c>false</c>.</value>
+        public bool? IsAnamorphic { get; set; }
+
+        private string GetResolutionText()
+        {
+            var i = this;
+
+            if (i.Width.HasValue && i.Height.HasValue)
+            {
+                var width = i.Width.Value;
+                var height = i.Height.Value;
+
+                if (width >= 3800 || height >= 2000)
+                {
+                    return "4K";
+                }
+
+                if (width >= 2500)
+                {
+                    if (i.IsInterlaced)
+                    {
+                        return "1440i";
+                    }
+
+                    return "1440p";
+                }
+
+                if (width >= 1900 || height >= 1000)
+                {
+                    if (i.IsInterlaced)
+                    {
+                        return "1080i";
+                    }
+
+                    return "1080p";
+                }
+
+                if (width >= 1260 || height >= 700)
+                {
+                    if (i.IsInterlaced)
+                    {
+                        return "720i";
+                    }
+
+                    return "720p";
+                }
+
+                if (width >= 700 || height >= 440)
+                {
+                    if (i.IsInterlaced)
+                    {
+                        return "480i";
+                    }
+
+                    return "480p";
+                }
+
+                return "SD";
+            }
+
+            return null;
+        }
+
         public static bool IsTextFormat(string format)
         {
             string codec = format ?? string.Empty;
@@ -533,35 +562,5 @@ namespace MediaBrowser.Model.Entities
 
             return true;
         }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether [supports external stream].
-        /// </summary>
-        /// <value><c>true</c> if [supports external stream]; otherwise, <c>false</c>.</value>
-        public bool SupportsExternalStream { get; set; }
-
-        /// <summary>
-        /// Gets or sets the filename.
-        /// </summary>
-        /// <value>The filename.</value>
-        public string Path { get; set; }
-
-        /// <summary>
-        /// Gets or sets the pixel format.
-        /// </summary>
-        /// <value>The pixel format.</value>
-        public string PixelFormat { get; set; }
-
-        /// <summary>
-        /// Gets or sets the level.
-        /// </summary>
-        /// <value>The level.</value>
-        public double? Level { get; set; }
-
-        /// <summary>
-        /// Gets a value indicating whether this instance is anamorphic.
-        /// </summary>
-        /// <value><c>true</c> if this instance is anamorphic; otherwise, <c>false</c>.</value>
-        public bool? IsAnamorphic { get; set; }
     }
 }

+ 0 - 40
MediaBrowser.Model/Entities/PackageReviewInfo.cs

@@ -1,40 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System;
-
-namespace MediaBrowser.Model.Entities
-{
-    public class PackageReviewInfo
-    {
-        /// <summary>
-        /// Gets or sets the package id (database key) for this review.
-        /// </summary>
-        public int id { get; set; }
-
-        /// <summary>
-        /// Gets or sets the rating value.
-        /// </summary>
-        public int rating { get; set; }
-
-        /// <summary>
-        /// Gets or sets whether or not this review recommends this item.
-        /// </summary>
-        public bool recommend { get; set; }
-
-        /// <summary>
-        /// Gets or sets a short description of the review.
-        /// </summary>
-        public string title { get; set; }
-
-        /// <summary>
-        /// Gets or sets the full review.
-        /// </summary>
-        public string review { get; set; }
-
-        /// <summary>
-        /// Gets or sets the time of review.
-        /// </summary>
-        public DateTime timestamp { get; set; }
-    }
-}

+ 36 - 0
MediaBrowser.Model/Entities/SpecialFolder.cs

@@ -0,0 +1,36 @@
+#pragma warning disable CS1591
+
+namespace MediaBrowser.Model.Entities
+{
+    public static class SpecialFolder
+    {
+        public const string TvShowSeries = "TvShowSeries";
+        public const string TvGenres = "TvGenres";
+        public const string TvGenre = "TvGenre";
+        public const string TvLatest = "TvLatest";
+        public const string TvNextUp = "TvNextUp";
+        public const string TvResume = "TvResume";
+        public const string TvFavoriteSeries = "TvFavoriteSeries";
+        public const string TvFavoriteEpisodes = "TvFavoriteEpisodes";
+
+        public const string MovieLatest = "MovieLatest";
+        public const string MovieResume = "MovieResume";
+        public const string MovieMovies = "MovieMovies";
+        public const string MovieCollections = "MovieCollections";
+        public const string MovieFavorites = "MovieFavorites";
+        public const string MovieGenres = "MovieGenres";
+        public const string MovieGenre = "MovieGenre";
+
+        public const string MusicArtists = "MusicArtists";
+        public const string MusicAlbumArtists = "MusicAlbumArtists";
+        public const string MusicAlbums = "MusicAlbums";
+        public const string MusicGenres = "MusicGenres";
+        public const string MusicLatest = "MusicLatest";
+        public const string MusicPlaylists = "MusicPlaylists";
+        public const string MusicSongs = "MusicSongs";
+        public const string MusicFavorites = "MusicFavorites";
+        public const string MusicFavoriteArtists = "MusicFavoriteArtists";
+        public const string MusicFavoriteAlbums = "MusicFavoriteAlbums";
+        public const string MusicFavoriteSongs = "MusicFavoriteSongs";
+    }
+}

+ 8 - 8
MediaBrowser.Model/Entities/VirtualFolderInfo.cs

@@ -11,6 +11,14 @@ namespace MediaBrowser.Model.Entities
     /// </summary>
     public class VirtualFolderInfo
     {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="VirtualFolderInfo"/> class.
+        /// </summary>
+        public VirtualFolderInfo()
+        {
+            Locations = Array.Empty<string>();
+        }
+
         /// <summary>
         /// Gets or sets the name.
         /// </summary>
@@ -31,14 +39,6 @@ namespace MediaBrowser.Model.Entities
 
         public LibraryOptions LibraryOptions { get; set; }
 
-        /// <summary>
-        /// Initializes a new instance of the <see cref="VirtualFolderInfo"/> class.
-        /// </summary>
-        public VirtualFolderInfo()
-        {
-            Locations = Array.Empty<string>();
-        }
-
         /// <summary>
         /// Gets or sets the item identifier.
         /// </summary>

+ 6 - 6
MediaBrowser.Model/Globalization/CultureDto.cs

@@ -10,6 +10,11 @@ namespace MediaBrowser.Model.Globalization
     /// </summary>
     public class CultureDto
     {
+        public CultureDto()
+        {
+            ThreeLetterISOLanguageNames = Array.Empty<string>();
+        }
+
         /// <summary>
         /// Gets or sets the name.
         /// </summary>
@@ -29,7 +34,7 @@ namespace MediaBrowser.Model.Globalization
         public string TwoLetterISOLanguageName { get; set; }
 
         /// <summary>
-        /// Gets or sets the name of the three letter ISO language.
+        /// Gets the name of the three letter ISO language.
         /// </summary>
         /// <value>The name of the three letter ISO language.</value>
         public string ThreeLetterISOLanguageName
@@ -47,10 +52,5 @@ namespace MediaBrowser.Model.Globalization
         }
 
         public string[] ThreeLetterISOLanguageNames { get; set; }
-
-        public CultureDto()
-        {
-            ThreeLetterISOLanguageNames = Array.Empty<string>();
-        }
     }
 }

+ 5 - 2
MediaBrowser.Model/IO/IFileSystem.cs

@@ -155,13 +155,16 @@ namespace MediaBrowser.Model.IO
         /// Gets the directories.
         /// </summary>
         /// <param name="path">The path.</param>
-        /// <param name="recursive">if set to <c>true</c> [recursive].</param>
-        /// <returns>IEnumerable&lt;DirectoryInfo&gt;.</returns>
+        /// <param name="recursive">If set to <c>true</c> also searches in subdirectiories.</param>
+        /// <returns>All found directories.</returns>
         IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false);
 
         /// <summary>
         /// Gets the files.
         /// </summary>
+        /// <param name="path">The path in which to search.</param>
+        /// <param name="recursive">If set to <c>true</c> also searches in subdirectiories.</param>
+        /// <returns>All found files.</returns>
         IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false);
 
         IEnumerable<FileSystemMetadata> GetFiles(string path, IReadOnlyList<string> extensions, bool enableCaseSensitiveExtensions, bool recursive);

+ 8 - 8
MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs

@@ -9,7 +9,7 @@ namespace MediaBrowser.Model.LiveTv
     public class BaseTimerInfoDto : IHasServerId
     {
         /// <summary>
-        /// Id of the recording.
+        /// Gets or sets the Id of the recording.
         /// </summary>
         public string Id { get; set; }
 
@@ -28,7 +28,7 @@ namespace MediaBrowser.Model.LiveTv
         public string ExternalId { get; set; }
 
         /// <summary>
-        /// ChannelId of the recording.
+        /// Gets or sets the channel id of the recording.
         /// </summary>
         public Guid ChannelId { get; set; }
 
@@ -39,7 +39,7 @@ namespace MediaBrowser.Model.LiveTv
         public string ExternalChannelId { get; set; }
 
         /// <summary>
-        /// ChannelName of the recording.
+        /// Gets or sets the channel name of the recording.
         /// </summary>
         public string ChannelName { get; set; }
 
@@ -58,22 +58,22 @@ namespace MediaBrowser.Model.LiveTv
         public string ExternalProgramId { get; set; }
 
         /// <summary>
-        /// Name of the recording.
+        /// Gets or sets the name of the recording.
         /// </summary>
         public string Name { get; set; }
 
         /// <summary>
-        /// Description of the recording.
+        /// Gets or sets the description of the recording.
         /// </summary>
         public string Overview { get; set; }
 
         /// <summary>
-        /// The start date of the recording, in UTC.
+        /// Gets or sets the start date of the recording, in UTC.
         /// </summary>
         public DateTime StartDate { get; set; }
 
         /// <summary>
-        /// The end date of the recording, in UTC.
+        /// Gets or sets the end date of the recording, in UTC.
         /// </summary>
         public DateTime EndDate { get; set; }
 
@@ -108,7 +108,7 @@ namespace MediaBrowser.Model.LiveTv
         public bool IsPrePaddingRequired { get; set; }
 
         /// <summary>
-        /// If the item does not have any backdrops, this will hold the Id of the Parent that has one.
+        /// Gets or sets the Id of the Parent that has a backdrop if the item does not have one.
         /// </summary>
         /// <value>The parent backdrop item id.</value>
         public string ParentBackdropItemId { get; set; }

+ 58 - 0
MediaBrowser.Model/LiveTv/ListingsProviderInfo.cs

@@ -0,0 +1,58 @@
+#nullable disable
+#pragma warning disable CS1591
+
+using System;
+using MediaBrowser.Model.Dto;
+
+namespace MediaBrowser.Model.LiveTv
+{
+    public class ListingsProviderInfo
+    {
+        public ListingsProviderInfo()
+        {
+            NewsCategories = new[] { "news", "journalism", "documentary", "current affairs" };
+            SportsCategories = new[] { "sports", "basketball", "baseball", "football" };
+            KidsCategories = new[] { "kids", "family", "children", "childrens", "disney" };
+            MovieCategories = new[] { "movie" };
+            EnabledTuners = Array.Empty<string>();
+            EnableAllTuners = true;
+            ChannelMappings = Array.Empty<NameValuePair>();
+        }
+
+        public string Id { get; set; }
+
+        public string Type { get; set; }
+
+        public string Username { get; set; }
+
+        public string Password { get; set; }
+
+        public string ListingsId { get; set; }
+
+        public string ZipCode { get; set; }
+
+        public string Country { get; set; }
+
+        public string Path { get; set; }
+
+        public string[] EnabledTuners { get; set; }
+
+        public bool EnableAllTuners { get; set; }
+
+        public string[] NewsCategories { get; set; }
+
+        public string[] SportsCategories { get; set; }
+
+        public string[] KidsCategories { get; set; }
+
+        public string[] MovieCategories { get; set; }
+
+        public NameValuePair[] ChannelMappings { get; set; }
+
+        public string MoviePrefix { get; set; }
+
+        public string PreferredLanguage { get; set; }
+
+        public string UserAgent { get; set; }
+    }
+}

+ 13 - 13
MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs

@@ -11,6 +11,12 @@ namespace MediaBrowser.Model.LiveTv
     /// </summary>
     public class LiveTvChannelQuery
     {
+        public LiveTvChannelQuery()
+        {
+            EnableUserData = true;
+            SortBy = Array.Empty<string>();
+        }
+
         /// <summary>
         /// Gets or sets the type of the channel.
         /// </summary>
@@ -48,13 +54,13 @@ namespace MediaBrowser.Model.LiveTv
         public Guid UserId { get; set; }
 
         /// <summary>
-        /// Skips over a given number of items within the results. Use for paging.
+        /// gets or sets the start index. Used for paging.
         /// </summary>
         /// <value>The start index.</value>
         public int? StartIndex { get; set; }
 
         /// <summary>
-        /// The maximum number of items to return.
+        /// Gets or sets the maximum number of items to return.
         /// </summary>
         /// <value>The limit.</value>
         public int? Limit { get; set; }
@@ -68,15 +74,15 @@ namespace MediaBrowser.Model.LiveTv
         public bool EnableUserData { get; set; }
 
         /// <summary>
-        /// Used to specific whether to return news or not.
+        /// Gets or sets a value whether to return news or not.
         /// </summary>
-        /// <remarks>If set to null, all programs will be returned</remarks>
+        /// <remarks>If set to <c>null</c>, all programs will be returned.</remarks>
         public bool? IsNews { get; set; }
 
         /// <summary>
-        /// Used to specific whether to return movies or not.
+        /// Gets or sets a value whether to return movies or not.
         /// </summary>
-        /// <remarks>If set to null, all programs will be returned</remarks>
+        /// <remarks>If set to <c>null</c>, all programs will be returned.</remarks>
         public bool? IsMovie { get; set; }
 
         /// <summary>
@@ -96,15 +102,9 @@ namespace MediaBrowser.Model.LiveTv
         public string[] SortBy { get; set; }
 
         /// <summary>
-        /// The sort order to return results with.
+        /// Gets or sets the sort order to return results with.
         /// </summary>
         /// <value>The sort order.</value>
         public SortOrder? SortOrder { get; set; }
-
-        public LiveTvChannelQuery()
-        {
-            EnableUserData = true;
-            SortBy = Array.Empty<string>();
-        }
     }
 }

+ 8 - 89
MediaBrowser.Model/LiveTv/LiveTvOptions.cs

@@ -2,12 +2,19 @@
 #pragma warning disable CS1591
 
 using System;
-using MediaBrowser.Model.Dto;
 
 namespace MediaBrowser.Model.LiveTv
 {
     public class LiveTvOptions
     {
+        public LiveTvOptions()
+        {
+            TunerHosts = Array.Empty<TunerHostInfo>();
+            ListingProviders = Array.Empty<ListingsProviderInfo>();
+            MediaLocationsCreated = Array.Empty<string>();
+            RecordingPostProcessorArguments = "\"{path}\"";
+        }
+
         public int? GuideDays { get; set; }
 
         public string RecordingPath { get; set; }
@@ -33,93 +40,5 @@ namespace MediaBrowser.Model.LiveTv
         public string RecordingPostProcessor { get; set; }
 
         public string RecordingPostProcessorArguments { get; set; }
-
-        public LiveTvOptions()
-        {
-            TunerHosts = Array.Empty<TunerHostInfo>();
-            ListingProviders = Array.Empty<ListingsProviderInfo>();
-            MediaLocationsCreated = Array.Empty<string>();
-            RecordingPostProcessorArguments = "\"{path}\"";
-        }
-    }
-
-    public class TunerHostInfo
-    {
-        public string Id { get; set; }
-
-        public string Url { get; set; }
-
-        public string Type { get; set; }
-
-        public string DeviceId { get; set; }
-
-        public string FriendlyName { get; set; }
-
-        public bool ImportFavoritesOnly { get; set; }
-
-        public bool AllowHWTranscoding { get; set; }
-
-        public bool EnableStreamLooping { get; set; }
-
-        public string Source { get; set; }
-
-        public int TunerCount { get; set; }
-
-        public string UserAgent { get; set; }
-
-        public TunerHostInfo()
-        {
-            AllowHWTranscoding = true;
-        }
-    }
-
-    public class ListingsProviderInfo
-    {
-        public string Id { get; set; }
-
-        public string Type { get; set; }
-
-        public string Username { get; set; }
-
-        public string Password { get; set; }
-
-        public string ListingsId { get; set; }
-
-        public string ZipCode { get; set; }
-
-        public string Country { get; set; }
-
-        public string Path { get; set; }
-
-        public string[] EnabledTuners { get; set; }
-
-        public bool EnableAllTuners { get; set; }
-
-        public string[] NewsCategories { get; set; }
-
-        public string[] SportsCategories { get; set; }
-
-        public string[] KidsCategories { get; set; }
-
-        public string[] MovieCategories { get; set; }
-
-        public NameValuePair[] ChannelMappings { get; set; }
-
-        public string MoviePrefix { get; set; }
-
-        public string PreferredLanguage { get; set; }
-
-        public string UserAgent { get; set; }
-
-        public ListingsProviderInfo()
-        {
-            NewsCategories = new[] { "news", "journalism", "documentary", "current affairs" };
-            SportsCategories = new[] { "sports", "basketball", "baseball", "football" };
-            KidsCategories = new[] { "kids", "family", "children", "childrens", "disney" };
-            MovieCategories = new[] { "movie" };
-            EnabledTuners = Array.Empty<string>();
-            EnableAllTuners = true;
-            ChannelMappings = Array.Empty<NameValuePair>();
-        }
     }
 }

+ 5 - 5
MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs

@@ -10,6 +10,11 @@ namespace MediaBrowser.Model.LiveTv
     /// </summary>
     public class LiveTvServiceInfo
     {
+        public LiveTvServiceInfo()
+        {
+            Tuners = Array.Empty<string>();
+        }
+
         /// <summary>
         /// Gets or sets the name.
         /// </summary>
@@ -53,10 +58,5 @@ namespace MediaBrowser.Model.LiveTv
         public bool IsVisible { get; set; }
 
         public string[] Tuners { get; set; }
-
-        public LiveTvServiceInfo()
-        {
-            Tuners = Array.Empty<string>();
-        }
     }
 }

+ 8 - 8
MediaBrowser.Model/LiveTv/RecordingQuery.cs

@@ -12,6 +12,11 @@ namespace MediaBrowser.Model.LiveTv
     /// </summary>
     public class RecordingQuery
     {
+        public RecordingQuery()
+        {
+            EnableTotalRecordCount = true;
+        }
+
         /// <summary>
         /// Gets or sets the channel identifier.
         /// </summary>
@@ -31,13 +36,13 @@ namespace MediaBrowser.Model.LiveTv
         public string Id { get; set; }
 
         /// <summary>
-        /// Skips over a given number of items within the results. Use for paging.
+        /// Gets or sets the start index. Use for paging.
         /// </summary>
         /// <value>The start index.</value>
         public int? StartIndex { get; set; }
 
         /// <summary>
-        /// The maximum number of items to return.
+        /// Gets or sets the maximum number of items to return.
         /// </summary>
         /// <value>The limit.</value>
         public int? Limit { get; set; }
@@ -61,7 +66,7 @@ namespace MediaBrowser.Model.LiveTv
         public string SeriesTimerId { get; set; }
 
         /// <summary>
-        /// Fields to return within the items, in addition to basic information.
+        /// Gets or sets the fields to return within the items, in addition to basic information.
         /// </summary>
         /// <value>The fields.</value>
         public ItemFields[] Fields { get; set; }
@@ -85,10 +90,5 @@ namespace MediaBrowser.Model.LiveTv
         public ImageType[] EnableImageTypes { get; set; }
 
         public bool EnableTotalRecordCount { get; set; }
-
-        public RecordingQuery()
-        {
-            EnableTotalRecordCount = true;
-        }
     }
 }

+ 8 - 8
MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs

@@ -7,6 +7,14 @@ using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Model.LiveTv
 {
+    public enum KeepUntil
+    {
+        UntilDeleted,
+        UntilSpaceNeeded,
+        UntilWatched,
+        UntilDate
+    }
+
     /// <summary>
     /// Class SeriesTimerInfoDto.
     /// </summary>
@@ -83,12 +91,4 @@ namespace MediaBrowser.Model.LiveTv
         /// <value>The parent primary image tag.</value>
         public string ParentPrimaryImageTag { get; set; }
     }
-
-    public enum KeepUntil
-    {
-        UntilDeleted,
-        UntilSpaceNeeded,
-        UntilWatched,
-        UntilDate
-    }
 }

+ 38 - 0
MediaBrowser.Model/LiveTv/TunerHostInfo.cs

@@ -0,0 +1,38 @@
+#nullable disable
+#pragma warning disable CS1591
+
+using System;
+using MediaBrowser.Model.Dto;
+
+namespace MediaBrowser.Model.LiveTv
+{
+    public class TunerHostInfo
+    {
+        public TunerHostInfo()
+        {
+            AllowHWTranscoding = true;
+        }
+
+        public string Id { get; set; }
+
+        public string Url { get; set; }
+
+        public string Type { get; set; }
+
+        public string DeviceId { get; set; }
+
+        public string FriendlyName { get; set; }
+
+        public bool ImportFavoritesOnly { get; set; }
+
+        public bool AllowHWTranscoding { get; set; }
+
+        public bool EnableStreamLooping { get; set; }
+
+        public string Source { get; set; }
+
+        public int TunerCount { get; set; }
+
+        public string UserAgent { get; set; }
+    }
+}

+ 2 - 2
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -17,7 +17,7 @@
     <TargetFramework>net5.0</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    <TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release' ">true</TreatWarningsAsErrors>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
     <Nullable>enable</Nullable>
     <LangVersion>latest</LangVersion>
     <PublishRepositoryUrl>true</PublishRepositoryUrl>
@@ -44,7 +44,7 @@
 
   <!-- Code Analyzers-->
   <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+    <!-- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> -->
     <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
     <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
     <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />

+ 11 - 11
MediaBrowser.Model/MediaInfo/MediaInfo.cs

@@ -10,6 +10,17 @@ namespace MediaBrowser.Model.MediaInfo
 {
     public class MediaInfo : MediaSourceInfo, IHasProviderIds
     {
+        public MediaInfo()
+        {
+            Chapters = Array.Empty<ChapterInfo>();
+            Artists = Array.Empty<string>();
+            AlbumArtists = Array.Empty<string>();
+            Studios = Array.Empty<string>();
+            Genres = Array.Empty<string>();
+            People = Array.Empty<BaseItemPerson>();
+            ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+        }
+
         public ChapterInfo[] Chapters { get; set; }
 
         /// <summary>
@@ -69,16 +80,5 @@ namespace MediaBrowser.Model.MediaInfo
         /// </summary>
         /// <value>The overview.</value>
         public string Overview { get; set; }
-
-        public MediaInfo()
-        {
-            Chapters = Array.Empty<ChapterInfo>();
-            Artists = Array.Empty<string>();
-            AlbumArtists = Array.Empty<string>();
-            Studios = Array.Empty<string>();
-            Genres = Array.Empty<string>();
-            People = Array.Empty<BaseItemPerson>();
-            ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-        }
     }
 }

+ 11 - 11
MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs

@@ -8,6 +8,17 @@ namespace MediaBrowser.Model.MediaInfo
 {
     public class PlaybackInfoRequest
     {
+        public PlaybackInfoRequest()
+        {
+            EnableDirectPlay = true;
+            EnableDirectStream = true;
+            EnableTranscoding = true;
+            AllowVideoStreamCopy = true;
+            AllowAudioStreamCopy = true;
+            IsPlayback = true;
+            DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http };
+        }
+
         public Guid Id { get; set; }
 
         public Guid UserId { get; set; }
@@ -43,16 +54,5 @@ namespace MediaBrowser.Model.MediaInfo
         public bool AutoOpenLiveStream { get; set; }
 
         public MediaProtocol[] DirectPlayProtocols { get; set; }
-
-        public PlaybackInfoRequest()
-        {
-            EnableDirectPlay = true;
-            EnableDirectStream = true;
-            EnableTranscoding = true;
-            AllowVideoStreamCopy = true;
-            AllowAudioStreamCopy = true;
-            IsPlayback = true;
-            DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http };
-        }
     }
 }

+ 8 - 8
MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs

@@ -10,6 +10,14 @@ namespace MediaBrowser.Model.MediaInfo
     /// </summary>
     public class PlaybackInfoResponse
     {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PlaybackInfoResponse" /> class.
+        /// </summary>
+        public PlaybackInfoResponse()
+        {
+            MediaSources = Array.Empty<MediaSourceInfo>();
+        }
+
         /// <summary>
         /// Gets or sets the media sources.
         /// </summary>
@@ -27,13 +35,5 @@ namespace MediaBrowser.Model.MediaInfo
         /// </summary>
         /// <value>The error code.</value>
         public PlaybackErrorCode? ErrorCode { get; set; }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="PlaybackInfoResponse" /> class.
-        /// </summary>
-        public PlaybackInfoResponse()
-        {
-            MediaSources = Array.Empty<MediaSourceInfo>();
-        }
     }
 }

+ 2 - 2
MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs

@@ -7,11 +7,11 @@ namespace MediaBrowser.Model.MediaInfo
 {
     public class SubtitleTrackInfo
     {
-        public IReadOnlyList<SubtitleTrackEvent> TrackEvents { get; set; }
-
         public SubtitleTrackInfo()
         {
             TrackEvents = Array.Empty<SubtitleTrackEvent>();
         }
+
+        public IReadOnlyList<SubtitleTrackEvent> TrackEvents { get; set; }
     }
 }

+ 6 - 0
MediaBrowser.Model/Net/ISocket.cs

@@ -23,6 +23,12 @@ namespace MediaBrowser.Model.Net
         /// <summary>
         /// Sends a UDP message to a particular end point (uni or multicast).
         /// </summary>
+        /// <param name="buffer">An array of type <see cref="byte" /> that contains the data to send.</param>
+        /// <param name="offset">The zero-based position in buffer at which to begin sending data.</param>
+        /// <param name="bytes">The number of bytes to send.</param>
+        /// <param name="endPoint">An <see cref="IPEndPoint" /> that represents the remote device.</param>
+        /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
+        /// <returns>The task object representing the asynchronous operation.</returns>
         Task SendToAsync(byte[] buffer, int offset, int bytes, IPEndPoint endPoint, CancellationToken cancellationToken);
     }
 }

+ 3 - 0
MediaBrowser.Model/Net/ISocketFactory.cs

@@ -14,6 +14,9 @@ namespace MediaBrowser.Model.Net
         /// <summary>
         /// Creates a new unicast socket using the specified local port number.
         /// </summary>
+        /// <param name="localIp">The local IP address to bind to.</param>
+        /// <param name="localPort">The local port to bind to.</param>
+        /// <returns>A new unicast socket using the specified local port number.</returns>
         ISocket CreateSsdpUdpSocket(IPAddress localIp, int localPort);
 
         /// <summary>

+ 12 - 9
MediaBrowser.Model/Net/MimeTypes.cs

@@ -91,9 +91,9 @@ namespace MediaBrowser.Model.Net
             { ".webp", "image/webp" },
 
             // Type font
-            { ".ttf" , "font/ttf" },
-            { ".woff" , "font/woff" },
-            { ".woff2" , "font/woff2" },
+            { ".ttf", "font/ttf" },
+            { ".woff", "font/woff" },
+            { ".woff2", "font/woff2" },
 
             // Type text
             { ".ass", "text/x-ssa" },
@@ -168,14 +168,17 @@ namespace MediaBrowser.Model.Net
         /// <summary>
         /// Gets the type of the MIME.
         /// </summary>
-        public static string? GetMimeType(string path, bool enableStreamDefault)
+        /// <param name="filename">The filename to find the MIME type of.</param>
+        /// <param name="enableStreamDefault">Whether of not to return a default value if no fitting MIME type is found.</param>
+        /// <returns>The worrect MIME type for the given filename, or `null` if it wasn't found and <paramref name="enableStreamDefault"/> is false.</returns>
+        public static string? GetMimeType(string filename, bool enableStreamDefault)
         {
-            if (path.Length == 0)
+            if (filename.Length == 0)
             {
-                throw new ArgumentException("String can't be empty.", nameof(path));
+                throw new ArgumentException("String can't be empty.", nameof(filename));
             }
 
-            var ext = Path.GetExtension(path);
+            var ext = Path.GetExtension(filename);
 
             if (_mimeTypeLookup.TryGetValue(ext, out string? result))
             {
@@ -210,9 +213,9 @@ namespace MediaBrowser.Model.Net
             return enableStreamDefault ? "application/octet-stream" : null;
         }
 
-        public static string? ToExtension(string? mimeType)
+        public static string? ToExtension(string mimeType)
         {
-            if (string.IsNullOrEmpty(mimeType))
+            if (mimeType.Length == 0)
             {
                 throw new ArgumentException("String can't be empty.", nameof(mimeType));
             }

+ 0 - 33
MediaBrowser.Model/Net/NetworkShare.cs

@@ -1,33 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.Net
-{
-    public class NetworkShare
-    {
-        /// <summary>
-        /// The name of the computer that this share belongs to.
-        /// </summary>
-        public string Server { get; set; }
-
-        /// <summary>
-        /// Share name.
-        /// </summary>
-        public string Name { get; set; }
-
-        /// <summary>
-        /// Local path.
-        /// </summary>
-        public string Path { get; set; }
-
-        /// <summary>
-        /// Share type.
-        /// </summary>
-        public NetworkShareType ShareType { get; set; }
-
-        /// <summary>
-        /// Comment.
-        /// </summary>
-        public string Remark { get; set; }
-    }
-}

+ 2 - 2
MediaBrowser.Model/Net/SocketReceiveResult.cs

@@ -20,12 +20,12 @@ namespace MediaBrowser.Model.Net
         public int ReceivedBytes { get; set; }
 
         /// <summary>
-        /// The <see cref="IPEndPoint"/> the data was received from.
+        /// Gets or sets the <see cref="IPEndPoint"/> the data was received from.
         /// </summary>
         public IPEndPoint RemoteEndPoint { get; set; }
 
         /// <summary>
-        /// The local <see cref="IPAddress"/>.
+        /// Gets or sets the local <see cref="IPAddress"/>.
         /// </summary>
         public IPAddress LocalIPAddress { get; set; }
     }

+ 1 - 1
MediaBrowser.Model/Net/WebSocketMessage.cs

@@ -9,7 +9,7 @@ namespace MediaBrowser.Model.Net
     /// <summary>
     /// Class WebSocketMessage.
     /// </summary>
-    /// <typeparam name="T"></typeparam>
+    /// <typeparam name="T">The type of the data.</typeparam>
     public class WebSocketMessage<T>
     {
         /// <summary>

+ 5 - 5
MediaBrowser.Model/Notifications/NotificationOptions.cs

@@ -2,18 +2,16 @@
 #pragma warning disable CS1591
 
 using System;
-using Jellyfin.Data.Enums;
-using MediaBrowser.Model.Extensions;
 using System.Linq;
 using Jellyfin.Data.Entities;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Users;
 
 namespace MediaBrowser.Model.Notifications
 {
     public class NotificationOptions
     {
-        public NotificationOption[] Options { get; set; }
-
         public NotificationOptions()
         {
             Options = new[]
@@ -71,6 +69,8 @@ namespace MediaBrowser.Model.Notifications
             };
         }
 
+        public NotificationOption[] Options { get; set; }
+
         public NotificationOption GetOptions(string type)
         {
             foreach (NotificationOption i in Options)
@@ -104,7 +104,7 @@ namespace MediaBrowser.Model.Notifications
             NotificationOption opt = GetOptions(type);
 
             return opt != null && opt.Enabled &&
-                   !opt.DisabledMonitorUsers.Contains(userId.ToString(""), StringComparer.OrdinalIgnoreCase);
+                   !opt.DisabledMonitorUsers.Contains(userId.ToString(string.Empty), StringComparer.OrdinalIgnoreCase);
         }
 
         public bool IsEnabledToSendToUser(string type, string userId, User user)

+ 7 - 7
MediaBrowser.Model/Notifications/NotificationRequest.cs

@@ -7,6 +7,12 @@ namespace MediaBrowser.Model.Notifications
 {
     public class NotificationRequest
     {
+        public NotificationRequest()
+        {
+            UserIds = Array.Empty<Guid>();
+            Date = DateTime.UtcNow;
+        }
+
         public string Name { get; set; }
 
         public string Description { get; set; }
@@ -20,16 +26,10 @@ namespace MediaBrowser.Model.Notifications
         public DateTime Date { get; set; }
 
         /// <summary>
-        /// The corresponding type name used in configuration. Not for display.
+        /// Gets or sets the corresponding type name used in configuration. Not for display.
         /// </summary>
         public string NotificationType { get; set; }
 
         public SendToUserType? SendToUserMode { get; set; }
-
-        public NotificationRequest()
-        {
-            UserIds = Array.Empty<Guid>();
-            Date = DateTime.UtcNow;
-        }
     }
 }

+ 2 - 2
MediaBrowser.Model/Providers/ExternalIdInfo.cs

@@ -6,11 +6,11 @@ namespace MediaBrowser.Model.Providers
     public class ExternalIdInfo
     {
         /// <summary>
-        /// Represents the external id information for serialization to the client.
+        /// Initializes a new instance of the <see cref="ExternalIdInfo"/> class.
         /// </summary>
         /// <param name="name">Name of the external id provider (IE: IMDB, MusicBrainz, etc).</param>
         /// <param name="key">Key for this id. This key should be unique across all providers.</param>
-        /// <param name="type">Specific media type for this id</param>
+        /// <param name="type">Specific media type for this id.</param>
         /// <param name="urlFormatString">URL format string.</param>
         public ExternalIdInfo(string name, string key, ExternalIdMediaType? type, string urlFormatString)
         {

+ 1 - 1
MediaBrowser.Model/Providers/RemoteImageInfo.cs

@@ -22,7 +22,7 @@ namespace MediaBrowser.Model.Providers
         public string Url { get; set; }
 
         /// <summary>
-        /// Gets a url used for previewing a smaller version.
+        /// Gets or sets a url used for previewing a smaller version.
         /// </summary>
         public string ThumbnailUrl { get; set; }
 

+ 8 - 8
MediaBrowser.Model/Providers/SubtitleOptions.cs

@@ -7,6 +7,14 @@ namespace MediaBrowser.Model.Providers
 {
     public class SubtitleOptions
     {
+        public SubtitleOptions()
+        {
+            DownloadLanguages = Array.Empty<string>();
+
+            SkipIfAudioTrackMatches = true;
+            RequirePerfectMatch = true;
+        }
+
         public bool SkipIfEmbeddedSubtitlesPresent { get; set; }
 
         public bool SkipIfAudioTrackMatches { get; set; }
@@ -24,13 +32,5 @@ namespace MediaBrowser.Model.Providers
         public bool IsOpenSubtitleVipAccount { get; set; }
 
         public bool RequirePerfectMatch { get; set; }
-
-        public SubtitleOptions()
-        {
-            DownloadLanguages = Array.Empty<string>();
-
-            SkipIfAudioTrackMatches = true;
-            RequirePerfectMatch = true;
-        }
     }
 }

+ 5 - 5
MediaBrowser.Model/Querying/EpisodeQuery.cs

@@ -7,6 +7,11 @@ namespace MediaBrowser.Model.Querying
 {
     public class EpisodeQuery
     {
+        public EpisodeQuery()
+        {
+            Fields = Array.Empty<ItemFields>();
+        }
+
         /// <summary>
         /// Gets or sets the user identifier.
         /// </summary>
@@ -66,10 +71,5 @@ namespace MediaBrowser.Model.Querying
         /// </summary>
         /// <value>The start item identifier.</value>
         public string StartItemId { get; set; }
-
-        public EpisodeQuery()
-        {
-            Fields = Array.Empty<ItemFields>();
-        }
     }
 }

+ 5 - 4
MediaBrowser.Model/Querying/LatestItemsQuery.cs

@@ -14,31 +14,32 @@ namespace MediaBrowser.Model.Querying
         }
 
         /// <summary>
-        /// The user to localize search results for.
+        /// Gets or sets the user to localize search results for.
         /// </summary>
         /// <value>The user id.</value>
         public Guid UserId { get; set; }
 
         /// <summary>
+        /// Gets or sets the parent id.
         /// Specify this to localize the search to a specific item or folder. Omit to use the root.
         /// </summary>
         /// <value>The parent id.</value>
         public Guid ParentId { get; set; }
 
         /// <summary>
-        /// Skips over a given number of items within the results. Use for paging.
+        /// Gets or sets the start index. Used for paging.
         /// </summary>
         /// <value>The start index.</value>
         public int? StartIndex { get; set; }
 
         /// <summary>
-        /// The maximum number of items to return.
+        /// Gets or sets the maximum number of items to return.
         /// </summary>
         /// <value>The limit.</value>
         public int? Limit { get; set; }
 
         /// <summary>
-        /// Fields to return within the items, in addition to basic information.
+        /// Gets or sets the fields to return within the items, in addition to basic information.
         /// </summary>
         /// <value>The fields.</value>
         public ItemFields[] Fields { get; set; }

+ 7 - 7
MediaBrowser.Model/Querying/MovieRecommendationQuery.cs

@@ -7,6 +7,13 @@ namespace MediaBrowser.Model.Querying
 {
     public class MovieRecommendationQuery
     {
+        public MovieRecommendationQuery()
+        {
+            ItemLimit = 10;
+            CategoryLimit = 6;
+            Fields = Array.Empty<ItemFields>();
+        }
+
         /// <summary>
         /// Gets or sets the user identifier.
         /// </summary>
@@ -36,12 +43,5 @@ namespace MediaBrowser.Model.Querying
         /// </summary>
         /// <value>The fields.</value>
         public ItemFields[] Fields { get; set; }
-
-        public MovieRecommendationQuery()
-        {
-            ItemLimit = 10;
-            CategoryLimit = 6;
-            Fields = Array.Empty<ItemFields>();
-        }
     }
 }

+ 10 - 10
MediaBrowser.Model/Querying/NextUpQuery.cs

@@ -8,6 +8,13 @@ namespace MediaBrowser.Model.Querying
 {
     public class NextUpQuery
     {
+        public NextUpQuery()
+        {
+            EnableImageTypes = Array.Empty<ImageType>();
+            EnableTotalRecordCount = true;
+            DisableFirstEpisode = false;
+        }
+
         /// <summary>
         /// Gets or sets the user id.
         /// </summary>
@@ -27,19 +34,19 @@ namespace MediaBrowser.Model.Querying
         public string SeriesId { get; set; }
 
         /// <summary>
-        /// Skips over a given number of items within the results. Use for paging.
+        /// Gets or sets the start index. Use for paging.
         /// </summary>
         /// <value>The start index.</value>
         public int? StartIndex { get; set; }
 
         /// <summary>
-        /// The maximum number of items to return.
+        /// Gets or sets the maximum number of items to return.
         /// </summary>
         /// <value>The limit.</value>
         public int? Limit { get; set; }
 
         /// <summary>
-        /// Fields to return within the items, in addition to basic information.
+        /// gets or sets the fields to return within the items, in addition to basic information.
         /// </summary>
         /// <value>The fields.</value>
         public ItemFields[] Fields { get; set; }
@@ -68,12 +75,5 @@ namespace MediaBrowser.Model.Querying
         /// Gets or sets a value indicating whether do disable sending first episode as next up.
         /// </summary>
         public bool DisableFirstEpisode { get; set; }
-
-        public NextUpQuery()
-        {
-            EnableImageTypes = Array.Empty<ImageType>();
-            EnableTotalRecordCount = true;
-            DisableFirstEpisode = false;
-        }
     }
 }

+ 3 - 22
MediaBrowser.Model/Querying/QueryFilters.cs

@@ -6,35 +6,16 @@ using MediaBrowser.Model.Dto;
 
 namespace MediaBrowser.Model.Querying
 {
-    public class QueryFiltersLegacy
+    public class QueryFilters
     {
-        public string[] Genres { get; set; }
-
-        public string[] Tags { get; set; }
-
-        public string[] OfficialRatings { get; set; }
-
-        public int[] Years { get; set; }
-
-        public QueryFiltersLegacy()
+        public QueryFilters()
         {
-            Genres = Array.Empty<string>();
             Tags = Array.Empty<string>();
-            OfficialRatings = Array.Empty<string>();
-            Years = Array.Empty<int>();
+            Genres = Array.Empty<NameGuidPair>();
         }
-    }
 
-    public class QueryFilters
-    {
         public NameGuidPair[] Genres { get; set; }
 
         public string[] Tags { get; set; }
-
-        public QueryFilters()
-        {
-            Tags = Array.Empty<string>();
-            Genres = Array.Empty<NameGuidPair>();
-        }
     }
 }

+ 26 - 0
MediaBrowser.Model/Querying/QueryFiltersLegacy.cs

@@ -0,0 +1,26 @@
+#nullable disable
+#pragma warning disable CS1591
+
+using System;
+
+namespace MediaBrowser.Model.Querying
+{
+    public class QueryFiltersLegacy
+    {
+        public QueryFiltersLegacy()
+        {
+            Genres = Array.Empty<string>();
+            Tags = Array.Empty<string>();
+            OfficialRatings = Array.Empty<string>();
+            Years = Array.Empty<int>();
+        }
+
+        public string[] Genres { get; set; }
+
+        public string[] Tags { get; set; }
+
+        public string[] OfficialRatings { get; set; }
+
+        public int[] Years { get; set; }
+    }
+}

+ 13 - 13
MediaBrowser.Model/Querying/QueryResult.cs

@@ -8,6 +8,17 @@ namespace MediaBrowser.Model.Querying
 {
     public class QueryResult<T>
     {
+        public QueryResult()
+        {
+            Items = Array.Empty<T>();
+        }
+
+        public QueryResult(IReadOnlyList<T> items)
+        {
+            Items = items;
+            TotalRecordCount = items.Count;
+        }
+
         /// <summary>
         /// Gets or sets the items.
         /// </summary>
@@ -15,26 +26,15 @@ namespace MediaBrowser.Model.Querying
         public IReadOnlyList<T> Items { get; set; }
 
         /// <summary>
-        /// The total number of records available.
+        /// Gets or sets the total number of records available.
         /// </summary>
         /// <value>The total record count.</value>
         public int TotalRecordCount { get; set; }
 
         /// <summary>
-        /// The index of the first record in Items.
+        /// Gets or sets the index of the first record in Items.
         /// </summary>
         /// <value>First record index.</value>
         public int StartIndex { get; set; }
-
-        public QueryResult()
-        {
-            Items = Array.Empty<T>();
-        }
-
-        public QueryResult(IReadOnlyList<T> items)
-        {
-            Items = items;
-            TotalRecordCount = items.Count;
-        }
     }
 }

+ 8 - 8
MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs

@@ -8,6 +8,11 @@ namespace MediaBrowser.Model.Querying
 {
     public class UpcomingEpisodesQuery
     {
+        public UpcomingEpisodesQuery()
+        {
+            EnableImageTypes = Array.Empty<ImageType>();
+        }
+
         /// <summary>
         /// Gets or sets the user id.
         /// </summary>
@@ -21,19 +26,19 @@ namespace MediaBrowser.Model.Querying
         public string ParentId { get; set; }
 
         /// <summary>
-        /// Skips over a given number of items within the results. Use for paging.
+        /// Gets or sets the start index. Use for paging.
         /// </summary>
         /// <value>The start index.</value>
         public int? StartIndex { get; set; }
 
         /// <summary>
-        /// The maximum number of items to return.
+        /// Gets or sets the maximum number of items to return.
         /// </summary>
         /// <value>The limit.</value>
         public int? Limit { get; set; }
 
         /// <summary>
-        /// Fields to return within the items, in addition to basic information.
+        /// Gets or sets the fields to return within the items, in addition to basic information.
         /// </summary>
         /// <value>The fields.</value>
         public ItemFields[] Fields { get; set; }
@@ -55,10 +60,5 @@ namespace MediaBrowser.Model.Querying
         /// </summary>
         /// <value>The enable image types.</value>
         public ImageType[] EnableImageTypes { get; set; }
-
-        public UpcomingEpisodesQuery()
-        {
-            EnableImageTypes = Array.Empty<ImageType>();
-        }
     }
 }

+ 16 - 16
MediaBrowser.Model/Search/SearchQuery.cs

@@ -7,8 +7,21 @@ namespace MediaBrowser.Model.Search
 {
     public class SearchQuery
     {
+        public SearchQuery()
+        {
+            IncludeArtists = true;
+            IncludeGenres = true;
+            IncludeMedia = true;
+            IncludePeople = true;
+            IncludeStudios = true;
+
+            MediaTypes = Array.Empty<string>();
+            IncludeItemTypes = Array.Empty<string>();
+            ExcludeItemTypes = Array.Empty<string>();
+        }
+
         /// <summary>
-        /// The user to localize search results for.
+        /// Gets or sets the user to localize search results for.
         /// </summary>
         /// <value>The user id.</value>
         public Guid UserId { get; set; }
@@ -20,13 +33,13 @@ namespace MediaBrowser.Model.Search
         public string SearchTerm { get; set; }
 
         /// <summary>
-        /// Skips over a given number of items within the results. Use for paging.
+        /// Gets or sets the start index. Used for paging.
         /// </summary>
         /// <value>The start index.</value>
         public int? StartIndex { get; set; }
 
         /// <summary>
-        /// The maximum number of items to return.
+        /// Gets or sets the maximum number of items to return.
         /// </summary>
         /// <value>The limit.</value>
         public int? Limit { get; set; }
@@ -58,18 +71,5 @@ namespace MediaBrowser.Model.Search
         public bool? IsKids { get; set; }
 
         public bool? IsSports { get; set; }
-
-        public SearchQuery()
-        {
-            IncludeArtists = true;
-            IncludeGenres = true;
-            IncludeMedia = true;
-            IncludePeople = true;
-            IncludeStudios = true;
-
-            MediaTypes = Array.Empty<string>();
-            IncludeItemTypes = Array.Empty<string>();
-            ExcludeItemTypes = Array.Empty<string>();
-        }
     }
 }

+ 1 - 0
MediaBrowser.Model/Session/BrowseRequest.cs

@@ -7,6 +7,7 @@ namespace MediaBrowser.Model.Session
     public class BrowseRequest
     {
         /// <summary>
+        /// Gets or sets the item type.
         /// Artist, Genre, Studio, Person, or any kind of BaseItem.
         /// </summary>
         /// <value>The type of the item.</value>

+ 7 - 7
MediaBrowser.Model/Session/ClientCapabilities.cs

@@ -9,6 +9,13 @@ namespace MediaBrowser.Model.Session
 {
     public class ClientCapabilities
     {
+        public ClientCapabilities()
+        {
+            PlayableMediaTypes = Array.Empty<string>();
+            SupportedCommands = Array.Empty<GeneralCommandType>();
+            SupportsPersistentIdentifier = true;
+        }
+
         public IReadOnlyList<string> PlayableMediaTypes { get; set; }
 
         public IReadOnlyList<GeneralCommandType> SupportedCommands { get; set; }
@@ -28,12 +35,5 @@ namespace MediaBrowser.Model.Session
         public string AppStoreUrl { get; set; }
 
         public string IconUrl { get; set; }
-
-        public ClientCapabilities()
-        {
-            PlayableMediaTypes = Array.Empty<string>();
-            SupportedCommands = Array.Empty<GeneralCommandType>();
-            SupportsPersistentIdentifier = true;
-        }
     }
 }

+ 6 - 7
MediaBrowser.Model/Session/GeneralCommand.cs

@@ -1,4 +1,3 @@
-#nullable disable
 #pragma warning disable CS1591
 
 using System;
@@ -8,15 +7,15 @@ namespace MediaBrowser.Model.Session
 {
     public class GeneralCommand
     {
-        public GeneralCommandType Name { get; set; }
-
-        public Guid ControllingUserId { get; set; }
-
-        public Dictionary<string, string> Arguments { get; set; }
-
         public GeneralCommand()
         {
             Arguments = new Dictionary<string, string>();
         }
+
+        public GeneralCommandType Name { get; set; }
+
+        public Guid ControllingUserId { get; set; }
+
+        public Dictionary<string, string> Arguments { get; }
     }
 }

+ 0 - 14
MediaBrowser.Model/Session/PlaybackProgressInfo.cs

@@ -111,18 +111,4 @@ namespace MediaBrowser.Model.Session
 
         public string PlaylistItemId { get; set; }
     }
-
-    public enum RepeatMode
-    {
-        RepeatNone = 0,
-        RepeatAll = 1,
-        RepeatOne = 2
-    }
-
-    public class QueueItem
-    {
-        public Guid Id { get; set; }
-
-        public string PlaylistItemId { get; set; }
-    }
 }

+ 4 - 2
MediaBrowser.Model/Session/PlaystateCommand.cs

@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
 namespace MediaBrowser.Model.Session
 {
     /// <summary>
@@ -46,6 +44,10 @@ namespace MediaBrowser.Model.Session
         /// The fast forward.
         /// </summary>
         FastForward,
+
+        /// <summary>
+        /// The play pause.
+        /// </summary>
         PlayPause
     }
 }

+ 14 - 0
MediaBrowser.Model/Session/QueueItem.cs

@@ -0,0 +1,14 @@
+#nullable disable
+#pragma warning disable CS1591
+
+using System;
+
+namespace MediaBrowser.Model.Session
+{
+    public class QueueItem
+    {
+        public Guid Id { get; set; }
+
+        public string PlaylistItemId { get; set; }
+    }
+}

+ 11 - 0
MediaBrowser.Model/Session/RepeatMode.cs

@@ -0,0 +1,11 @@
+#pragma warning disable CS1591
+
+namespace MediaBrowser.Model.Session
+{
+    public enum RepeatMode
+    {
+        RepeatNone = 0,
+        RepeatAll = 1,
+        RepeatOne = 2
+    }
+}

+ 31 - 0
MediaBrowser.Model/Session/TranscodeReason.cs

@@ -0,0 +1,31 @@
+#pragma warning disable CS1591
+
+namespace MediaBrowser.Model.Session
+{
+    public enum TranscodeReason
+    {
+        ContainerNotSupported = 0,
+        VideoCodecNotSupported = 1,
+        AudioCodecNotSupported = 2,
+        ContainerBitrateExceedsLimit = 3,
+        AudioBitrateNotSupported = 4,
+        AudioChannelsNotSupported = 5,
+        VideoResolutionNotSupported = 6,
+        UnknownVideoStreamInfo = 7,
+        UnknownAudioStreamInfo = 8,
+        AudioProfileNotSupported = 9,
+        AudioSampleRateNotSupported = 10,
+        AnamorphicVideoNotSupported = 11,
+        InterlacedVideoNotSupported = 12,
+        SecondaryAudioNotSupported = 13,
+        RefFramesNotSupported = 14,
+        VideoBitDepthNotSupported = 15,
+        VideoBitrateNotSupported = 16,
+        VideoFramerateNotSupported = 17,
+        VideoLevelNotSupported = 18,
+        VideoProfileNotSupported = 19,
+        AudioBitDepthNotSupported = 20,
+        SubtitleCodecNotSupported = 21,
+        DirectPlayError = 22
+    }
+}

+ 5 - 32
MediaBrowser.Model/Session/TranscodingInfo.cs

@@ -7,6 +7,11 @@ namespace MediaBrowser.Model.Session
 {
     public class TranscodingInfo
     {
+        public TranscodingInfo()
+        {
+            TranscodeReasons = Array.Empty<TranscodeReason>();
+        }
+
         public string AudioCodec { get; set; }
 
         public string VideoCodec { get; set; }
@@ -30,37 +35,5 @@ namespace MediaBrowser.Model.Session
         public int? AudioChannels { get; set; }
 
         public TranscodeReason[] TranscodeReasons { get; set; }
-
-        public TranscodingInfo()
-        {
-            TranscodeReasons = Array.Empty<TranscodeReason>();
-        }
-    }
-
-    public enum TranscodeReason
-    {
-        ContainerNotSupported = 0,
-        VideoCodecNotSupported = 1,
-        AudioCodecNotSupported = 2,
-        ContainerBitrateExceedsLimit = 3,
-        AudioBitrateNotSupported = 4,
-        AudioChannelsNotSupported = 5,
-        VideoResolutionNotSupported = 6,
-        UnknownVideoStreamInfo = 7,
-        UnknownAudioStreamInfo = 8,
-        AudioProfileNotSupported = 9,
-        AudioSampleRateNotSupported = 10,
-        AnamorphicVideoNotSupported = 11,
-        InterlacedVideoNotSupported = 12,
-        SecondaryAudioNotSupported = 13,
-        RefFramesNotSupported = 14,
-        VideoBitDepthNotSupported = 15,
-        VideoBitrateNotSupported = 16,
-        VideoFramerateNotSupported = 17,
-        VideoLevelNotSupported = 18,
-        VideoProfileNotSupported = 19,
-        AudioBitDepthNotSupported = 20,
-        SubtitleCodecNotSupported = 21,
-        DirectPlayError = 22
     }
 }

+ 5 - 5
MediaBrowser.Model/Sync/SyncJob.cs

@@ -7,6 +7,11 @@ namespace MediaBrowser.Model.Sync
 {
     public class SyncJob
     {
+        public SyncJob()
+        {
+            RequestedItemIds = Array.Empty<Guid>();
+        }
+
         /// <summary>
         /// Gets or sets the identifier.
         /// </summary>
@@ -126,10 +131,5 @@ namespace MediaBrowser.Model.Sync
         public string PrimaryImageItemId { get; set; }
 
         public string PrimaryImageTag { get; set; }
-
-        public SyncJob()
-        {
-            RequestedItemIds = Array.Empty<Guid>();
-        }
     }
 }

+ 9 - 9
MediaBrowser.Model/System/SystemInfo.cs

@@ -30,6 +30,14 @@ namespace MediaBrowser.Model.System
     /// </summary>
     public class SystemInfo : PublicSystemInfo
     {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SystemInfo" /> class.
+        /// </summary>
+        public SystemInfo()
+        {
+            CompletedInstallations = Array.Empty<InstallationInfo>();
+        }
+
         /// <summary>
         /// Gets or sets the display name of the operating system.
         /// </summary>
@@ -37,7 +45,7 @@ namespace MediaBrowser.Model.System
         public string OperatingSystemDisplayName { get; set; }
 
         /// <summary>
-        /// Get or sets the package name.
+        /// Gets or sets the package name.
         /// </summary>
         /// <value>The value of the '-package' command line argument.</value>
         public string PackageName { get; set; }
@@ -127,13 +135,5 @@ namespace MediaBrowser.Model.System
         public FFmpegLocation EncoderLocation { get; set; }
 
         public Architecture SystemArchitecture { get; set; }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="SystemInfo" /> class.
-        /// </summary>
-        public SystemInfo()
-        {
-            CompletedInstallations = Array.Empty<InstallationInfo>();
-        }
     }
 }

+ 1 - 1
MediaBrowser.Model/System/WakeOnLanInfo.cs

@@ -36,7 +36,7 @@ namespace MediaBrowser.Model.System
         /// Gets the MAC address of the device.
         /// </summary>
         /// <value>The MAC address.</value>
-        public string? MacAddress { get; set; }
+        public string? MacAddress { get; }
 
         /// <summary>
         /// Gets or sets the wake-on-LAN port.

+ 2 - 3
MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs

@@ -15,7 +15,7 @@ namespace MediaBrowser.Model.Tasks
         event EventHandler<GenericEventArgs<double>> TaskProgress;
 
         /// <summary>
-        /// Gets or sets the scheduled task.
+        /// Gets the scheduled task.
         /// </summary>
         /// <value>The scheduled task.</value>
         IScheduledTask ScheduledTask { get; }
@@ -57,10 +57,9 @@ namespace MediaBrowser.Model.Tasks
         double? CurrentProgress { get; }
 
         /// <summary>
-        /// Gets the triggers that define when the task will run.
+        /// Gets or sets the triggers that define when the task will run.
         /// </summary>
         /// <value>The triggers.</value>
-        /// <exception cref="ArgumentNullException">value</exception>
         TaskTriggerInfo[] Triggers { get; set; }
 
         /// <summary>

+ 12 - 8
MediaBrowser.Model/Tasks/ITaskManager.cs

@@ -9,6 +9,10 @@ namespace MediaBrowser.Model.Tasks
 {
     public interface ITaskManager : IDisposable
     {
+        event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting;
+
+        event EventHandler<TaskCompletionEventArgs> TaskCompleted;
+
         /// <summary>
         /// Gets the list of Scheduled Tasks.
         /// </summary>
@@ -18,7 +22,7 @@ namespace MediaBrowser.Model.Tasks
         /// <summary>
         /// Cancels if running and queue.
         /// </summary>
-        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="T">An implementatin of <see cref="IScheduledTask" />.</typeparam>
         /// <param name="options">Task options.</param>
         void CancelIfRunningAndQueue<T>(TaskOptions options)
             where T : IScheduledTask;
@@ -26,21 +30,21 @@ namespace MediaBrowser.Model.Tasks
         /// <summary>
         /// Cancels if running and queue.
         /// </summary>
-        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="T">An implementatin of <see cref="IScheduledTask" />.</typeparam>
         void CancelIfRunningAndQueue<T>()
             where T : IScheduledTask;
 
         /// <summary>
         /// Cancels if running.
         /// </summary>
-        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="T">An implementatin of <see cref="IScheduledTask" />.</typeparam>
         void CancelIfRunning<T>()
             where T : IScheduledTask;
 
         /// <summary>
         /// Queues the scheduled task.
         /// </summary>
-        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="T">An implementatin of <see cref="IScheduledTask" />.</typeparam>
         /// <param name="options">Task options.</param>
         void QueueScheduledTask<T>(TaskOptions options)
             where T : IScheduledTask;
@@ -48,7 +52,7 @@ namespace MediaBrowser.Model.Tasks
         /// <summary>
         /// Queues the scheduled task.
         /// </summary>
-        /// <typeparam name="T"></typeparam>
+        /// <typeparam name="T">An implementatin of <see cref="IScheduledTask" />.</typeparam>
         void QueueScheduledTask<T>()
             where T : IScheduledTask;
 
@@ -58,6 +62,8 @@ namespace MediaBrowser.Model.Tasks
         /// <summary>
         /// Queues the scheduled task.
         /// </summary>
+        /// <param name="task">The <see cref="IScheduledTask" /> to queue.</param>
+        /// <param name="options">The <see cref="TaskOptions" /> to use.</param>
         void QueueScheduledTask(IScheduledTask task, TaskOptions options);
 
         /// <summary>
@@ -67,12 +73,10 @@ namespace MediaBrowser.Model.Tasks
         void AddTasks(IEnumerable<IScheduledTask> tasks);
 
         void Cancel(IScheduledTaskWorker task);
+
         Task Execute(IScheduledTaskWorker task, TaskOptions options);
 
         void Execute<T>()
             where T : IScheduledTask;
-
-        event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting;
-        event EventHandler<TaskCompletionEventArgs> TaskCompleted;
     }
 }

+ 4 - 0
MediaBrowser.Model/Tasks/ITaskTrigger.cs

@@ -21,6 +21,10 @@ namespace MediaBrowser.Model.Tasks
         /// <summary>
         /// Stars waiting for the trigger action.
         /// </summary>
+        /// <param name="lastResult">Result of the last run triggerd task.</param>
+        /// <param name="logger">The <see cref="ILogger"/>.</param>
+        /// <param name="taskName">The name of the task.</param>
+        /// <param name="isApplicationStartup">Wheter or not this is is fired during startup.</param>
         void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup);
 
         /// <summary>

+ 8 - 8
MediaBrowser.Model/Tasks/TaskInfo.cs

@@ -8,6 +8,14 @@ namespace MediaBrowser.Model.Tasks
     /// </summary>
     public class TaskInfo
     {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="TaskInfo"/> class.
+        /// </summary>
+        public TaskInfo()
+        {
+            Triggers = Array.Empty<TaskTriggerInfo>();
+        }
+
         /// <summary>
         /// Gets or sets the name.
         /// </summary>
@@ -67,13 +75,5 @@ namespace MediaBrowser.Model.Tasks
         /// </summary>
         /// <value>The key.</value>
         public string Key { get; set; }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="TaskInfo"/> class.
-        /// </summary>
-        public TaskInfo()
-        {
-            Triggers = Array.Empty<TaskTriggerInfo>();
-        }
     }
 }

+ 6 - 6
MediaBrowser.Model/Tasks/TaskTriggerInfo.cs

@@ -10,6 +10,12 @@ namespace MediaBrowser.Model.Tasks
     /// </summary>
     public class TaskTriggerInfo
     {
+        public const string TriggerDaily = "DailyTrigger";
+        public const string TriggerWeekly = "WeeklyTrigger";
+        public const string TriggerInterval = "IntervalTrigger";
+        public const string TriggerSystemEvent = "SystemEventTrigger";
+        public const string TriggerStartup = "StartupTrigger";
+
         /// <summary>
         /// Gets or sets the type.
         /// </summary>
@@ -39,11 +45,5 @@ namespace MediaBrowser.Model.Tasks
         /// </summary>
         /// <value>The maximum runtime ticks.</value>
         public long? MaxRuntimeTicks { get; set; }
-
-        public const string TriggerDaily = "DailyTrigger";
-        public const string TriggerWeekly = "WeeklyTrigger";
-        public const string TriggerInterval = "IntervalTrigger";
-        public const string TriggerSystemEvent = "SystemEventTrigger";
-        public const string TriggerStartup = "StartupTrigger";
     }
 }

+ 50 - 50
MediaBrowser.Model/Users/UserPolicy.cs

@@ -10,6 +10,56 @@ namespace MediaBrowser.Model.Users
 {
     public class UserPolicy
     {
+        public UserPolicy()
+        {
+            IsHidden = true;
+
+            EnableContentDeletion = false;
+            EnableContentDeletionFromFolders = Array.Empty<string>();
+
+            EnableSyncTranscoding = true;
+            EnableMediaConversion = true;
+
+            EnableMediaPlayback = true;
+            EnableAudioPlaybackTranscoding = true;
+            EnableVideoPlaybackTranscoding = true;
+            EnablePlaybackRemuxing = true;
+            ForceRemoteSourceTranscoding = false;
+            EnableLiveTvManagement = true;
+            EnableLiveTvAccess = true;
+
+            // Without this on by default, admins won't be able to do this
+            // Improve in the future
+            EnableLiveTvManagement = true;
+
+            EnableSharedDeviceControl = true;
+
+            BlockedTags = Array.Empty<string>();
+            BlockUnratedItems = Array.Empty<UnratedItem>();
+
+            EnableUserPreferenceAccess = true;
+
+            AccessSchedules = Array.Empty<AccessSchedule>();
+
+            LoginAttemptsBeforeLockout = -1;
+
+            MaxActiveSessions = 0;
+
+            EnableAllChannels = true;
+            EnabledChannels = Array.Empty<Guid>();
+
+            EnableAllFolders = true;
+            EnabledFolders = Array.Empty<Guid>();
+
+            EnabledDevices = Array.Empty<string>();
+            EnableAllDevices = true;
+
+            EnableContentDownloading = true;
+            EnablePublicSharing = true;
+            EnableRemoteAccess = true;
+            SyncPlayAccess = SyncPlayUserAccessType.CreateAndJoinGroups;
+        }
+
         /// <summary>
         /// Gets or sets a value indicating whether this instance is administrator.
         /// </summary>
@@ -112,55 +162,5 @@ namespace MediaBrowser.Model.Users
         /// </summary>
         /// <value>Access level to SyncPlay features.</value>
         public SyncPlayUserAccessType SyncPlayAccess { get; set; }
-
-        public UserPolicy()
-        {
-            IsHidden = true;
-
-            EnableContentDeletion = false;
-            EnableContentDeletionFromFolders = Array.Empty<string>();
-
-            EnableSyncTranscoding = true;
-            EnableMediaConversion = true;
-
-            EnableMediaPlayback = true;
-            EnableAudioPlaybackTranscoding = true;
-            EnableVideoPlaybackTranscoding = true;
-            EnablePlaybackRemuxing = true;
-            ForceRemoteSourceTranscoding = false;
-            EnableLiveTvManagement = true;
-            EnableLiveTvAccess = true;
-
-            // Without this on by default, admins won't be able to do this
-            // Improve in the future
-            EnableLiveTvManagement = true;
-
-            EnableSharedDeviceControl = true;
-
-            BlockedTags = Array.Empty<string>();
-            BlockUnratedItems = Array.Empty<UnratedItem>();
-
-            EnableUserPreferenceAccess = true;
-
-            AccessSchedules = Array.Empty<AccessSchedule>();
-
-            LoginAttemptsBeforeLockout = -1;
-
-            MaxActiveSessions = 0;
-
-            EnableAllChannels = true;
-            EnabledChannels = Array.Empty<Guid>();
-
-            EnableAllFolders = true;
-            EnabledFolders = Array.Empty<Guid>();
-
-            EnabledDevices = Array.Empty<string>();
-            EnableAllDevices = true;
-
-            EnableContentDownloading = true;
-            EnablePublicSharing = true;
-            EnableRemoteAccess = true;
-            SyncPlayAccess = SyncPlayUserAccessType.CreateAndJoinGroups;
-        }
     }
 }

+ 2 - 0
jellyfin.ruleset

@@ -32,6 +32,8 @@
     <Rule Id="SA1515" Action="None" />
     <!-- disable warning SA1600: Elements should be documented -->
     <Rule Id="SA1600" Action="None" />
+    <!-- disable warning SA1602: Enumeration items should be documented -->
+    <Rule Id="SA1602" Action="None" />
     <!-- disable warning SA1633: The file header is missing or not located at the top of the file -->
     <Rule Id="SA1633" Action="None" />
   </Rules>

+ 0 - 6
tests/Jellyfin.Api.Tests/ModelBinders/TestType.cs

@@ -1,17 +1,11 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
 namespace Jellyfin.Api.Tests.ModelBinders
 {
     public enum TestType
     {
-#pragma warning disable SA1602 // Enumeration items should be documented
         How,
         Much,
         Is,
         The,
         Fish
-#pragma warning restore SA1602 // Enumeration items should be documented
     }
 }