From 65bab55ca09b09f5229d6e9d50f9cfccfeb8f3d0 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 13 Feb 2021 00:39:18 +0100 Subject: Minor improvements --- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs') diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index bd026bce1..b5291b560 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -1496,7 +1496,7 @@ namespace MediaBrowser.MediaEncoding.Probing video.IndexNumber = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[0], CultureInfo.InvariantCulture); int totalEpisodesInSeason = int.Parse(numbers[0].Replace(".", string.Empty, StringComparison.Ordinal).Split('/')[1], CultureInfo.InvariantCulture); - description = string.Join(" ", numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it + description = string.Join(' ', numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it } else { @@ -1508,7 +1508,7 @@ namespace MediaBrowser.MediaEncoding.Probing if (subtitle.Contains('.', StringComparison.Ordinal)) { // skip the comment, keep the subtitle - description = string.Join(".", subtitle.Split('.'), 1, subtitle.Split('.').Length - 1).Trim(); // skip the first + description = string.Join('.', subtitle.Split('.'), 1, subtitle.Split('.').Length - 1).Trim(); // skip the first } else { -- cgit v1.2.3 From 141efafd3dbbef3dc64824a89fd3fdc77da0b25e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 20 Feb 2021 23:13:04 +0100 Subject: Enable TreatWarningsAsErrors for MediaBrowser.Model --- Emby.Dlna/ContentDirectory/StubType.cs | 2 +- Emby.Dlna/DlnaManager.cs | 2 +- Emby.Dlna/PlayTo/TransportState.cs | 2 +- .../Data/SqliteItemRepository.cs | 6 +- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 2 +- Jellyfin.Api/Helpers/StreamingHelpers.cs | 2 +- .../Probing/ProbeResultNormalizer.cs | 6 +- MediaBrowser.Model/Channels/ChannelFeatures.cs | 18 +- MediaBrowser.Model/Channels/ChannelQuery.cs | 6 +- .../Configuration/EncodingOptions.cs | 74 +- MediaBrowser.Model/Configuration/ImageOption.cs | 10 +- MediaBrowser.Model/Configuration/LibraryOptions.cs | 403 +------ MediaBrowser.Model/Configuration/MediaPathInfo.cs | 12 + .../Configuration/MetadataConfiguration.cs | 4 +- .../Configuration/MetadataOptions.cs | 20 +- .../Configuration/MetadataPluginSummary.cs | 12 +- MediaBrowser.Model/Configuration/TypeOptions.cs | 365 ++++++ .../Configuration/UserConfiguration.cs | 36 +- .../Configuration/XbmcMetadataOptions.cs | 16 +- MediaBrowser.Model/Dlna/AudioOptions.cs | 9 +- MediaBrowser.Model/Dlna/CodecProfile.cs | 12 +- MediaBrowser.Model/Dlna/ConditionProcessor.cs | 2 +- MediaBrowser.Model/Dlna/ContainerProfile.cs | 10 +- MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs | 36 +- MediaBrowser.Model/Dlna/IDeviceDiscovery.cs | 1 + .../Dlna/MediaFormatProfileResolver.cs | 4 +- MediaBrowser.Model/Dlna/ResolutionConfiguration.cs | 8 +- MediaBrowser.Model/Dlna/ResponseProfile.cs | 10 +- MediaBrowser.Model/Dlna/SearchCriteria.cs | 54 +- MediaBrowser.Model/Dlna/SortCriteria.cs | 4 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 14 +- MediaBrowser.Model/Dlna/StreamInfo.cs | 1190 ++++++++++---------- MediaBrowser.Model/Dto/BaseItemDto.cs | 10 +- MediaBrowser.Model/Dto/MediaSourceInfo.cs | 67 +- MediaBrowser.Model/Dto/MetadataEditorInfo.cs | 18 +- MediaBrowser.Model/Dto/NameGuidPair.cs | 14 + MediaBrowser.Model/Dto/NameIdPair.cs | 7 - MediaBrowser.Model/Dto/UserDto.cs | 18 +- MediaBrowser.Model/Entities/CollectionType.cs | 32 - MediaBrowser.Model/Entities/MediaStream.cs | 197 ++-- MediaBrowser.Model/Entities/PackageReviewInfo.cs | 40 - MediaBrowser.Model/Entities/SpecialFolder.cs | 36 + MediaBrowser.Model/Entities/VirtualFolderInfo.cs | 16 +- MediaBrowser.Model/Globalization/CultureDto.cs | 12 +- MediaBrowser.Model/IO/IFileSystem.cs | 7 +- MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs | 16 +- MediaBrowser.Model/LiveTv/ListingsProviderInfo.cs | 58 + MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs | 26 +- MediaBrowser.Model/LiveTv/LiveTvOptions.cs | 97 +- MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs | 10 +- MediaBrowser.Model/LiveTv/RecordingQuery.cs | 16 +- MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs | 16 +- MediaBrowser.Model/LiveTv/TunerHostInfo.cs | 38 + MediaBrowser.Model/MediaBrowser.Model.csproj | 4 +- MediaBrowser.Model/MediaInfo/MediaInfo.cs | 22 +- .../MediaInfo/PlaybackInfoRequest.cs | 22 +- .../MediaInfo/PlaybackInfoResponse.cs | 16 +- MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs | 4 +- MediaBrowser.Model/Net/ISocket.cs | 6 + MediaBrowser.Model/Net/ISocketFactory.cs | 3 + MediaBrowser.Model/Net/MimeTypes.cs | 21 +- MediaBrowser.Model/Net/NetworkShare.cs | 33 - MediaBrowser.Model/Net/SocketReceiveResult.cs | 4 +- MediaBrowser.Model/Net/WebSocketMessage.cs | 2 +- .../Notifications/NotificationOptions.cs | 10 +- .../Notifications/NotificationRequest.cs | 14 +- MediaBrowser.Model/Providers/ExternalIdInfo.cs | 4 +- MediaBrowser.Model/Providers/RemoteImageInfo.cs | 2 +- MediaBrowser.Model/Providers/SubtitleOptions.cs | 16 +- MediaBrowser.Model/Querying/EpisodeQuery.cs | 10 +- MediaBrowser.Model/Querying/LatestItemsQuery.cs | 9 +- .../Querying/MovieRecommendationQuery.cs | 14 +- MediaBrowser.Model/Querying/NextUpQuery.cs | 20 +- MediaBrowser.Model/Querying/QueryFilters.cs | 25 +- MediaBrowser.Model/Querying/QueryFiltersLegacy.cs | 26 + MediaBrowser.Model/Querying/QueryResult.cs | 26 +- .../Querying/UpcomingEpisodesQuery.cs | 16 +- MediaBrowser.Model/Search/SearchQuery.cs | 32 +- MediaBrowser.Model/Session/BrowseRequest.cs | 1 + MediaBrowser.Model/Session/ClientCapabilities.cs | 14 +- MediaBrowser.Model/Session/GeneralCommand.cs | 13 +- MediaBrowser.Model/Session/PlaybackProgressInfo.cs | 14 - MediaBrowser.Model/Session/PlaystateCommand.cs | 6 +- MediaBrowser.Model/Session/QueueItem.cs | 14 + MediaBrowser.Model/Session/RepeatMode.cs | 11 + MediaBrowser.Model/Session/TranscodeReason.cs | 31 + MediaBrowser.Model/Session/TranscodingInfo.cs | 37 +- MediaBrowser.Model/Sync/SyncJob.cs | 10 +- MediaBrowser.Model/System/SystemInfo.cs | 18 +- MediaBrowser.Model/System/WakeOnLanInfo.cs | 2 +- MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs | 5 +- MediaBrowser.Model/Tasks/ITaskManager.cs | 20 +- MediaBrowser.Model/Tasks/ITaskTrigger.cs | 4 + MediaBrowser.Model/Tasks/TaskInfo.cs | 16 +- MediaBrowser.Model/Tasks/TaskTriggerInfo.cs | 12 +- MediaBrowser.Model/Users/UserPolicy.cs | 100 +- jellyfin.ruleset | 2 + tests/Jellyfin.Api.Tests/ModelBinders/TestType.cs | 6 - 98 files changed, 1908 insertions(+), 1892 deletions(-) create mode 100644 MediaBrowser.Model/Configuration/MediaPathInfo.cs create mode 100644 MediaBrowser.Model/Configuration/TypeOptions.cs create mode 100644 MediaBrowser.Model/Dto/NameGuidPair.cs delete mode 100644 MediaBrowser.Model/Entities/PackageReviewInfo.cs create mode 100644 MediaBrowser.Model/Entities/SpecialFolder.cs create mode 100644 MediaBrowser.Model/LiveTv/ListingsProviderInfo.cs create mode 100644 MediaBrowser.Model/LiveTv/TunerHostInfo.cs delete mode 100644 MediaBrowser.Model/Net/NetworkShare.cs create mode 100644 MediaBrowser.Model/Querying/QueryFiltersLegacy.cs create mode 100644 MediaBrowser.Model/Session/QueueItem.cs create mode 100644 MediaBrowser.Model/Session/RepeatMode.cs create mode 100644 MediaBrowser.Model/Session/TranscodeReason.cs (limited to 'MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs') diff --git a/Emby.Dlna/ContentDirectory/StubType.cs b/Emby.Dlna/ContentDirectory/StubType.cs index 982ae5d68..a5116055d 100644 --- a/Emby.Dlna/ContentDirectory/StubType.cs +++ b/Emby.Dlna/ContentDirectory/StubType.cs @@ -1,5 +1,5 @@ #pragma warning disable CS1591 -#pragma warning disable SA1602 + namespace Emby.Dlna.ContentDirectory { diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 21ba1c755..9ab324038 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -553,7 +553,7 @@ namespace Emby.Dlna private void DumpProfiles() { - DeviceProfile[] list = new [] + DeviceProfile[] list = new[] { new SamsungSmartTvProfile(), new XboxOneProfile(), diff --git a/Emby.Dlna/PlayTo/TransportState.cs b/Emby.Dlna/PlayTo/TransportState.cs index 7068a5d24..1ca71283a 100644 --- a/Emby.Dlna/PlayTo/TransportState.cs +++ b/Emby.Dlna/PlayTo/TransportState.cs @@ -1,5 +1,5 @@ #pragma warning disable CS1591 -#pragma warning disable SA1602 + namespace Emby.Dlna.PlayTo { diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index dad8bec7b..d78b93bd7 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/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; diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index 0d8315dee..ce6740fc9 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -523,7 +523,7 @@ namespace Jellyfin.Api.Helpers /// Dlna profile type. 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) diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 4957ee8b8..8df1f5c27 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/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); } diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index b5291b560..b9cb49cf2 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/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)) { diff --git a/MediaBrowser.Model/Channels/ChannelFeatures.cs b/MediaBrowser.Model/Channels/ChannelFeatures.cs index a55754edd..d925b78b6 100644 --- a/MediaBrowser.Model/Channels/ChannelFeatures.cs +++ b/MediaBrowser.Model/Channels/ChannelFeatures.cs @@ -7,6 +7,13 @@ namespace MediaBrowser.Model.Channels { public class ChannelFeatures { + public ChannelFeatures() + { + MediaTypes = Array.Empty(); + ContentTypes = Array.Empty(); + DefaultSortFields = Array.Empty(); + } + /// /// Gets or sets the name. /// @@ -38,7 +45,7 @@ namespace MediaBrowser.Model.Channels public ChannelMediaContentType[] ContentTypes { get; set; } /// - /// 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. /// public int? MaxPageSize { get; set; } @@ -55,7 +62,7 @@ namespace MediaBrowser.Model.Channels public ChannelItemSortField[] DefaultSortFields { get; set; } /// - /// 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. /// public bool SupportsSortOrderToggle { get; set; } @@ -76,12 +83,5 @@ namespace MediaBrowser.Model.Channels /// /// true if [supports content downloading]; otherwise, false. public bool SupportsContentDownloading { get; set; } - - public ChannelFeatures() - { - MediaTypes = Array.Empty(); - ContentTypes = Array.Empty(); - DefaultSortFields = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/Channels/ChannelQuery.cs b/MediaBrowser.Model/Channels/ChannelQuery.cs index fd90e7f06..59966127f 100644 --- a/MediaBrowser.Model/Channels/ChannelQuery.cs +++ b/MediaBrowser.Model/Channels/ChannelQuery.cs @@ -10,7 +10,7 @@ namespace MediaBrowser.Model.Channels public class ChannelQuery { /// - /// 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. /// /// The fields. public ItemFields[] Fields { get; set; } @@ -28,13 +28,13 @@ namespace MediaBrowser.Model.Channels public Guid UserId { get; set; } /// - /// Skips over a given number of items within the results. Use for paging. + /// Gets or sets the start index. Use for paging. /// /// The start index. public int? StartIndex { get; set; } /// - /// The maximum number of items to return. + /// Gets or sets the maximum number of items to return. /// /// The limit. public int? Limit { get; set; } diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index da467e133..a9b280301 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/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; } /// - /// FFmpeg path as set by the user via the UI. + /// Gets or sets the FFmpeg path as set by the user via the UI. /// public string EncoderAppPath { get; set; } /// - /// 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. /// 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" }; - } } } diff --git a/MediaBrowser.Model/Configuration/ImageOption.cs b/MediaBrowser.Model/Configuration/ImageOption.cs index 2b1268c74..0af7b7e14 100644 --- a/MediaBrowser.Model/Configuration/ImageOption.cs +++ b/MediaBrowser.Model/Configuration/ImageOption.cs @@ -6,6 +6,11 @@ namespace MediaBrowser.Model.Configuration { public class ImageOption { + public ImageOption() + { + Limit = 1; + } + /// /// Gets or sets the type. /// @@ -23,10 +28,5 @@ namespace MediaBrowser.Model.Configuration /// /// The minimum width. public int MinWidth { get; set; } - - public ImageOption() - { - Limit = 1; - } } } diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index 77ac11d69..24698360e 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/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(); + DisabledSubtitleFetchers = Array.Empty(); + SubtitleFetcherOrder = Array.Empty(); + DisabledLocalMetadataReaders = Array.Empty(); + + SkipSubtitlesIfAudioTrackMatches = true; + RequirePerfectSubtitleMatch = true; + + EnablePhotos = true; + SaveSubtitlesWithMedia = true; + EnableRealtimeMonitor = true; + PathInfos = Array.Empty(); + 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(); - DisabledSubtitleFetchers = Array.Empty(); - SubtitleFetcherOrder = Array.Empty(); - DisabledLocalMetadataReaders = Array.Empty(); - - SkipSubtitlesIfAudioTrackMatches = true; - RequirePerfectSubtitleMatch = true; - - EnablePhotos = true; - SaveSubtitlesWithMedia = true; - EnableRealtimeMonitor = true; - PathInfos = Array.Empty(); - 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(); - MetadataFetcherOrder = Array.Empty(); - ImageFetchers = Array.Empty(); - ImageFetcherOrder = Array.Empty(); - ImageOptions = Array.Empty(); - } - - public static Dictionary DefaultImageOptions = new Dictionary - { - { - "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(); } } diff --git a/MediaBrowser.Model/Configuration/MediaPathInfo.cs b/MediaBrowser.Model/Configuration/MediaPathInfo.cs new file mode 100644 index 000000000..4f311c58f --- /dev/null +++ b/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; } + } +} diff --git a/MediaBrowser.Model/Configuration/MetadataConfiguration.cs b/MediaBrowser.Model/Configuration/MetadataConfiguration.cs index 706831bdd..be044243d 100644 --- a/MediaBrowser.Model/Configuration/MetadataConfiguration.cs +++ b/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; } } } diff --git a/MediaBrowser.Model/Configuration/MetadataOptions.cs b/MediaBrowser.Model/Configuration/MetadataOptions.cs index e7dc3da3c..76b72bd08 100644 --- a/MediaBrowser.Model/Configuration/MetadataOptions.cs +++ b/MediaBrowser.Model/Configuration/MetadataOptions.cs @@ -10,6 +10,16 @@ namespace MediaBrowser.Model.Configuration /// public class MetadataOptions { + public MetadataOptions() + { + DisabledMetadataSavers = Array.Empty(); + LocalMetadataReaderOrder = Array.Empty(); + DisabledMetadataFetchers = Array.Empty(); + MetadataFetcherOrder = Array.Empty(); + DisabledImageFetchers = Array.Empty(); + ImageFetcherOrder = Array.Empty(); + } + 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(); - LocalMetadataReaderOrder = Array.Empty(); - DisabledMetadataFetchers = Array.Empty(); - MetadataFetcherOrder = Array.Empty(); - DisabledImageFetchers = Array.Empty(); - ImageFetcherOrder = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs b/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs index 0c197ee02..aa07d6623 100644 --- a/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs +++ b/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs @@ -8,6 +8,12 @@ namespace MediaBrowser.Model.Configuration { public class MetadataPluginSummary { + public MetadataPluginSummary() + { + SupportedImageTypes = Array.Empty(); + Plugins = Array.Empty(); + } + /// /// Gets or sets the type of the item. /// @@ -25,11 +31,5 @@ namespace MediaBrowser.Model.Configuration /// /// The supported image types. public ImageType[] SupportedImageTypes { get; set; } - - public MetadataPluginSummary() - { - SupportedImageTypes = Array.Empty(); - Plugins = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/Configuration/TypeOptions.cs b/MediaBrowser.Model/Configuration/TypeOptions.cs new file mode 100644 index 000000000..d0179e5aa --- /dev/null +++ b/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 DefaultImageOptions = new Dictionary + { + { + "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(); + MetadataFetcherOrder = Array.Empty(); + ImageFetchers = Array.Empty(); + ImageFetcherOrder = Array.Empty(); + ImageOptions = Array.Empty(); + } + + 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; + } + } +} diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index cc0e0c468..935e6cbe1 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -11,6 +11,24 @@ namespace MediaBrowser.Model.Configuration /// public class UserConfiguration { + /// + /// Initializes a new instance of the class. + /// + public UserConfiguration() + { + EnableNextEpisodeAutoPlay = true; + RememberAudioSelections = true; + RememberSubtitleSelections = true; + + HidePlayedInLatest = true; + PlayDefaultAudioTrack = true; + + LatestItemsExcludes = Array.Empty(); + OrderedViews = Array.Empty(); + MyMediaExcludes = Array.Empty(); + GroupedFolders = Array.Empty(); + } + /// /// Gets or sets the audio language preference. /// @@ -52,23 +70,5 @@ namespace MediaBrowser.Model.Configuration public bool RememberSubtitleSelections { get; set; } public bool EnableNextEpisodeAutoPlay { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public UserConfiguration() - { - EnableNextEpisodeAutoPlay = true; - RememberAudioSelections = true; - RememberSubtitleSelections = true; - - HidePlayedInLatest = true; - PlayDefaultAudioTrack = true; - - LatestItemsExcludes = Array.Empty(); - OrderedViews = Array.Empty(); - MyMediaExcludes = Array.Empty(); - GroupedFolders = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs index 4d5f996f8..8ad070dcb 100644 --- a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs +++ b/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; - } } } diff --git a/MediaBrowser.Model/Dlna/AudioOptions.cs b/MediaBrowser.Model/Dlna/AudioOptions.cs index bbb8bf426..4d4d8d78c 100644 --- a/MediaBrowser.Model/Dlna/AudioOptions.cs +++ b/MediaBrowser.Model/Dlna/AudioOptions.cs @@ -34,20 +34,20 @@ namespace MediaBrowser.Model.Dlna public DeviceProfile Profile { get; set; } /// - /// 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. /// public string MediaSourceId { get; set; } public string DeviceId { get; set; } /// - /// 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. /// public int? MaxAudioChannels { get; set; } /// - /// The application's configured quality setting. + /// Gets or sets the application's configured quality setting. /// public int? MaxBitrate { get; set; } @@ -66,6 +66,7 @@ namespace MediaBrowser.Model.Dlna /// /// Gets the maximum bitrate. /// + /// Whether or not this is audio. /// System.Nullable<System.Int32>. public int? GetMaxBitrate(bool isAudio) { diff --git a/MediaBrowser.Model/Dlna/CodecProfile.cs b/MediaBrowser.Model/Dlna/CodecProfile.cs index d4fd3e673..8343cf028 100644 --- a/MediaBrowser.Model/Dlna/CodecProfile.cs +++ b/MediaBrowser.Model/Dlna/CodecProfile.cs @@ -9,6 +9,12 @@ namespace MediaBrowser.Model.Dlna { public class CodecProfile { + public CodecProfile() + { + Conditions = Array.Empty(); + ApplyConditions = Array.Empty(); + } + [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(); - ApplyConditions = Array.Empty(); - } - public string[] GetCodecs() { return ContainerProfile.SplitValue(Codec); diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs index faf1ee41b..55c4dd074 100644 --- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs +++ b/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 diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs index 56c89d854..d83c8f2f3 100644 --- a/MediaBrowser.Model/Dlna/ContainerProfile.cs +++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs @@ -9,6 +9,11 @@ namespace MediaBrowser.Model.Dlna { public class ContainerProfile { + public ContainerProfile() + { + Conditions = Array.Empty(); + } + [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(); - } - public string[] GetContainers() { return SplitValue(Container); diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index 50e3374f7..ec106f105 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/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, diff --git a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs index 05209e53d..086088dea 100644 --- a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs +++ b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs @@ -8,6 +8,7 @@ namespace MediaBrowser.Model.Dlna public interface IDeviceDiscovery { event EventHandler> DeviceDiscovered; + event EventHandler> DeviceLeft; } } diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs index 3c955989a..f61b8d59e 100644 --- a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs +++ b/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)) { diff --git a/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs b/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs index 30c44fbe0..f8f76c69d 100644 --- a/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs +++ b/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; } } } diff --git a/MediaBrowser.Model/Dlna/ResponseProfile.cs b/MediaBrowser.Model/Dlna/ResponseProfile.cs index 48f53f06c..bf9661f7f 100644 --- a/MediaBrowser.Model/Dlna/ResponseProfile.cs +++ b/MediaBrowser.Model/Dlna/ResponseProfile.cs @@ -8,6 +8,11 @@ namespace MediaBrowser.Model.Dlna { public class ResponseProfile { + public ResponseProfile() + { + Conditions = Array.Empty(); + } + [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(); - } - public string[] GetContainers() { return ContainerProfile.SplitValue(Container); diff --git a/MediaBrowser.Model/Dlna/SearchCriteria.cs b/MediaBrowser.Model/Dlna/SearchCriteria.cs index 94f5bd3db..b1fc48c08 100644 --- a/MediaBrowser.Model/Dlna/SearchCriteria.cs +++ b/MediaBrowser.Model/Dlna/SearchCriteria.cs @@ -7,31 +7,6 @@ namespace MediaBrowser.Model.Dlna { public class SearchCriteria { - public SearchType SearchType { get; set; } - - /// - /// Splits the specified string. - /// - /// The string. - /// The term. - /// The limit. - /// System.String[]. - private static string[] RegexSplit(string str, string term, int limit) - { - return new Regex(term).Split(str, limit); - } - - /// - /// Splits the specified string. - /// - /// The string. - /// The term. - /// System.String[]. - 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; } + + /// + /// Splits the specified string. + /// + /// The string. + /// The term. + /// The limit. + /// System.String[]. + private static string[] RegexSplit(string str, string term, int limit) + { + return new Regex(term).Split(str, limit); + } + + /// + /// Splits the specified string. + /// + /// The string. + /// The term. + /// System.String[]. + private static string[] RegexSplit(string str, string term) + { + return Regex.Split(str, term, RegexOptions.IgnoreCase); + } } } diff --git a/MediaBrowser.Model/Dlna/SortCriteria.cs b/MediaBrowser.Model/Dlna/SortCriteria.cs index 53e4540cb..7769d0bd3 100644 --- a/MediaBrowser.Model/Dlna/SortCriteria.cs +++ b/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; } } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index a3983afe5..bf33691c7 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/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; } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 4765052d5..f7010dcd0 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -27,45 +27,6 @@ namespace MediaBrowser.Model.Dlna StreamOptions = new Dictionary(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) + /// + /// Gets the audio stream that will be used. + /// + public MediaStream TargetAudioStream { - if (PlayMethod == PlayMethod.DirectPlay) + get { - return MediaSource.Path; + if (MediaSource != null) + { + return MediaSource.GetDefaultAudioStream(AudioStreamIndex); + } + + return null; } + } - if (string.IsNullOrEmpty(baseUrl)) + /// + /// Gets the video stream that will be used. + /// + public MediaStream TargetVideoStream + { + get { - throw new ArgumentNullException(nameof(baseUrl)); + if (MediaSource != null) + { + return MediaSource.VideoStream; + } + + return null; } + } - var list = new List(); - foreach (NameValuePair pair in BuildParams(this, accessToken)) + /// + /// Gets the audio sample rate that will be in the output stream. + /// + public int? TargetAudioSampleRate + { + get { - if (string.IsNullOrEmpty(pair.Value)) + var stream = TargetAudioStream; + return AudioSampleRate.HasValue && !IsDirectStream + ? AudioSampleRate + : stream == null ? null : stream.SampleRate; + } + } + + /// + /// Gets the audio sample rate that will be in the output stream. + /// + 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; + } + } + + /// + /// Gets the audio sample rate that will be in the output stream. + /// + 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) + /// + /// Gets the target reference frames. + /// + /// The target reference frames. + 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)) + /// + /// Gets the audio sample rate that will be in the output stream. + /// + 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 BuildParams(StreamInfo item, string accessToken) + /// + /// Gets the audio sample rate that will be in the output stream. + /// + public double? TargetVideoLevel { - var list = new List(); - - 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 + } + + /// + /// Gets the audio sample rate that will be in the output stream. + /// + 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) + /// + /// Gets the audio sample rate that will be in the output stream. + /// + 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) + /// + /// Gets the target video codec tag. + /// + /// The target video codec tag. + 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 GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken) + /// + /// Gets the audio bitrate that will be in the output stream. + /// + 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 GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) + /// + /// Gets the audio channels that will be in the output stream. + /// + public int? TargetAudioChannels { - var list = GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken); - var newList = new List(); - - // 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 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 GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) + /// + /// Gets the audio codec that will be in the output stream. + /// + public string[] TargetAudioCodec { - var list = new List(); + 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() : 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() : new[] { codec }; } } - } - - return list; - } - - private void AddSubtitleProfiles(List 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() : 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() : new[] { codec }; } - - info.IsExternalUrl = false; } - else - { - info.Url = stream.Path; - info.IsExternalUrl = true; - } - } - return info; + return VideoCodecs; + } } /// - /// Returns the audio stream that will be used. + /// Gets the audio channels that will be in the output stream. /// - 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; } } - /// - /// Returns the video stream that will be used. - /// - 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; } } - /// - /// Predicts the audio sample rate that will be in the output stream. - /// - 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; } } - /// - /// Predicts the audio sample rate that will be in the output stream. - /// - 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; } } - /// - /// Predicts the audio sample rate that will be in the output stream. - /// - 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; } } - /// - /// Gets the target reference frames. - /// - /// The target reference frames. - 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; } } - /// - /// Predicts the audio sample rate that will be in the output stream. - /// - 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; } } - /// - /// Predicts the audio sample rate that will be in the output stream. - /// - 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; } - /// - /// Predicts the audio sample rate that will be in the output stream. - /// - 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; } - } - /// - /// Predicts the audio sample rate that will be in the output stream. - /// - public string TargetVideoProfile - { - get + if (string.IsNullOrEmpty(baseUrl)) { - if (IsDirectStream) + throw new ArgumentNullException(nameof(baseUrl)); + } + + var list = new List(); + 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; + } - /// - /// Gets the target video codec tag. - /// - /// The target video codec tag. - 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); } - /// - /// Predicts the audio bitrate that will be in the output stream. - /// - 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)); } - } - /// - /// Predicts the audio channels that will be in the output stream. - /// - 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 BuildParams(StreamInfo item, string accessToken) { - var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels; + var list = new List(); - 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)); - /// - /// Predicts the audio codec that will be in the output stream. - /// - 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() : 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() : 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() : 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() : 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())); } - } - /// - /// Predicts the audio channels that will be in the output stream. - /// - 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 GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken) + { + return GetExternalSubtitles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken); + } - public bool? IsTargetAnamorphic + public List GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) { - get + var list = GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken); + var newList = new List(); + + // 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 GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken) { - get + return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken); + } + + public List GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) + { + var list = new List(); + + // 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 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) diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index 2f9f9d3cd..a784025e3 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -294,13 +294,13 @@ namespace MediaBrowser.Model.Dto public NameGuidPair[] GenreItems { get; set; } /// - /// 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. /// /// The parent logo item id. public string ParentLogoItemId { get; set; } /// - /// 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. /// /// The parent backdrop item id. public string ParentBackdropItemId { get; set; } @@ -318,7 +318,7 @@ namespace MediaBrowser.Model.Dto public int? LocalTrailerCount { get; set; } /// - /// 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. /// /// The user data. public UserItemDataDto UserData { get; set; } @@ -506,7 +506,7 @@ namespace MediaBrowser.Model.Dto public string ParentLogoImageTag { get; set; } /// - /// 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. /// /// The parent art item id. public string ParentArtItemId { get; set; } @@ -695,7 +695,7 @@ namespace MediaBrowser.Model.Dto public string ChannelPrimaryImageTag { get; set; } /// - /// The start date of the recording, in UTC. + /// Gets or sets the start date of the recording, in UTC. /// public DateTime? StartDate { get; set; } diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index be682be23..ec3b37efa 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -12,6 +12,18 @@ namespace MediaBrowser.Model.Dto { public class MediaSourceInfo { + public MediaSourceInfo() + { + Formats = Array.Empty(); + MediaStreams = new List(); + MediaAttachments = Array.Empty(); + RequiredHttpHeaders = new Dictionary(); + 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; } /// + /// Gets or sets a value indicating whether the media is remote. /// Differentiate internet url vs local network. /// 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(); - MediaStreams = new List(); - MediaAttachments = Array.Empty(); - RequiredHttpHeaders = new Dictionary(); - 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) diff --git a/MediaBrowser.Model/Dto/MetadataEditorInfo.cs b/MediaBrowser.Model/Dto/MetadataEditorInfo.cs index e4f38d6af..e0e889f7d 100644 --- a/MediaBrowser.Model/Dto/MetadataEditorInfo.cs +++ b/MediaBrowser.Model/Dto/MetadataEditorInfo.cs @@ -10,6 +10,15 @@ namespace MediaBrowser.Model.Dto { public class MetadataEditorInfo { + public MetadataEditorInfo() + { + ParentalRatingOptions = Array.Empty(); + Countries = Array.Empty(); + Cultures = Array.Empty(); + ExternalIdInfos = Array.Empty(); + ContentTypeOptions = Array.Empty(); + } + 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(); - Countries = Array.Empty(); - Cultures = Array.Empty(); - ExternalIdInfos = Array.Empty(); - ContentTypeOptions = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/Dto/NameGuidPair.cs b/MediaBrowser.Model/Dto/NameGuidPair.cs new file mode 100644 index 000000000..71166df97 --- /dev/null +++ b/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; } + } +} diff --git a/MediaBrowser.Model/Dto/NameIdPair.cs b/MediaBrowser.Model/Dto/NameIdPair.cs index 45c2fb35d..7f18b4502 100644 --- a/MediaBrowser.Model/Dto/NameIdPair.cs +++ b/MediaBrowser.Model/Dto/NameIdPair.cs @@ -19,11 +19,4 @@ namespace MediaBrowser.Model.Dto /// The identifier. public string Id { get; set; } } - - public class NameGuidPair - { - public string Name { get; set; } - - public Guid Id { get; set; } - } } diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs index 40222c9dc..256d7b10f 100644 --- a/MediaBrowser.Model/Dto/UserDto.cs +++ b/MediaBrowser.Model/Dto/UserDto.cs @@ -10,6 +10,15 @@ namespace MediaBrowser.Model.Dto /// public class UserDto : IItemDto, IHasServerId { + /// + /// Initializes a new instance of the class. + /// + public UserDto() + { + Configuration = new UserConfiguration(); + Policy = new UserPolicy(); + } + /// /// Gets or sets the name. /// @@ -94,15 +103,6 @@ namespace MediaBrowser.Model.Dto /// The primary image aspect ratio. public double? PrimaryImageAspectRatio { get; set; } - /// - /// Initializes a new instance of the class. - /// - public UserDto() - { - Configuration = new UserConfiguration(); - Policy = new UserPolicy(); - } - /// public override string ToString() { diff --git a/MediaBrowser.Model/Entities/CollectionType.cs b/MediaBrowser.Model/Entities/CollectionType.cs index 354038712..60b69d4b0 100644 --- a/MediaBrowser.Model/Entities/CollectionType.cs +++ b/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"; - } } diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index d85a8cde9..ade9d7e8d 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -84,7 +84,7 @@ namespace MediaBrowser.Model.Entities public string Title { get; set; } /// - /// Gets or sets the video range. + /// Gets the video range. /// /// The video range. 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; } /// @@ -487,6 +426,96 @@ namespace MediaBrowser.Model.Entities } } + /// + /// Gets or sets a value indicating whether [supports external stream]. + /// + /// true if [supports external stream]; otherwise, false. + public bool SupportsExternalStream { get; set; } + + /// + /// Gets or sets the filename. + /// + /// The filename. + public string Path { get; set; } + + /// + /// Gets or sets the pixel format. + /// + /// The pixel format. + public string PixelFormat { get; set; } + + /// + /// Gets or sets the level. + /// + /// The level. + public double? Level { get; set; } + + /// + /// Gets or sets whether this instance is anamorphic. + /// + /// true if this instance is anamorphic; otherwise, false. + 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; } - - /// - /// Gets or sets a value indicating whether [supports external stream]. - /// - /// true if [supports external stream]; otherwise, false. - public bool SupportsExternalStream { get; set; } - - /// - /// Gets or sets the filename. - /// - /// The filename. - public string Path { get; set; } - - /// - /// Gets or sets the pixel format. - /// - /// The pixel format. - public string PixelFormat { get; set; } - - /// - /// Gets or sets the level. - /// - /// The level. - public double? Level { get; set; } - - /// - /// Gets a value indicating whether this instance is anamorphic. - /// - /// true if this instance is anamorphic; otherwise, false. - public bool? IsAnamorphic { get; set; } } } diff --git a/MediaBrowser.Model/Entities/PackageReviewInfo.cs b/MediaBrowser.Model/Entities/PackageReviewInfo.cs deleted file mode 100644 index 5b22b34ac..000000000 --- a/MediaBrowser.Model/Entities/PackageReviewInfo.cs +++ /dev/null @@ -1,40 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -using System; - -namespace MediaBrowser.Model.Entities -{ - public class PackageReviewInfo - { - /// - /// Gets or sets the package id (database key) for this review. - /// - public int id { get; set; } - - /// - /// Gets or sets the rating value. - /// - public int rating { get; set; } - - /// - /// Gets or sets whether or not this review recommends this item. - /// - public bool recommend { get; set; } - - /// - /// Gets or sets a short description of the review. - /// - public string title { get; set; } - - /// - /// Gets or sets the full review. - /// - public string review { get; set; } - - /// - /// Gets or sets the time of review. - /// - public DateTime timestamp { get; set; } - } -} diff --git a/MediaBrowser.Model/Entities/SpecialFolder.cs b/MediaBrowser.Model/Entities/SpecialFolder.cs new file mode 100644 index 000000000..2250c5dff --- /dev/null +++ b/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"; + } +} diff --git a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs index f2bc6f25e..1b0e59240 100644 --- a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs +++ b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs @@ -11,6 +11,14 @@ namespace MediaBrowser.Model.Entities /// public class VirtualFolderInfo { + /// + /// Initializes a new instance of the class. + /// + public VirtualFolderInfo() + { + Locations = Array.Empty(); + } + /// /// Gets or sets the name. /// @@ -31,14 +39,6 @@ namespace MediaBrowser.Model.Entities public LibraryOptions LibraryOptions { get; set; } - /// - /// Initializes a new instance of the class. - /// - public VirtualFolderInfo() - { - Locations = Array.Empty(); - } - /// /// Gets or sets the item identifier. /// diff --git a/MediaBrowser.Model/Globalization/CultureDto.cs b/MediaBrowser.Model/Globalization/CultureDto.cs index 6af4a872c..5246f87d9 100644 --- a/MediaBrowser.Model/Globalization/CultureDto.cs +++ b/MediaBrowser.Model/Globalization/CultureDto.cs @@ -10,6 +10,11 @@ namespace MediaBrowser.Model.Globalization /// public class CultureDto { + public CultureDto() + { + ThreeLetterISOLanguageNames = Array.Empty(); + } + /// /// Gets or sets the name. /// @@ -29,7 +34,7 @@ namespace MediaBrowser.Model.Globalization public string TwoLetterISOLanguageName { get; set; } /// - /// Gets or sets the name of the three letter ISO language. + /// Gets the name of the three letter ISO language. /// /// The name of the three letter ISO language. public string ThreeLetterISOLanguageName @@ -47,10 +52,5 @@ namespace MediaBrowser.Model.Globalization } public string[] ThreeLetterISOLanguageNames { get; set; } - - public CultureDto() - { - ThreeLetterISOLanguageNames = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs index dc6549787..ef08ecec6 100644 --- a/MediaBrowser.Model/IO/IFileSystem.cs +++ b/MediaBrowser.Model/IO/IFileSystem.cs @@ -155,13 +155,16 @@ namespace MediaBrowser.Model.IO /// Gets the directories. /// /// The path. - /// if set to true [recursive]. - /// IEnumerable<DirectoryInfo>. + /// If set to true also searches in subdirectiories. + /// All found directories. IEnumerable GetDirectories(string path, bool recursive = false); /// /// Gets the files. /// + /// The path in which to search. + /// If set to true also searches in subdirectiories. + /// All found files. IEnumerable GetFiles(string path, bool recursive = false); IEnumerable GetFiles(string path, IReadOnlyList extensions, bool enableCaseSensitiveExtensions, bool recursive); diff --git a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs index 07e76d960..c6de4c1ab 100644 --- a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Model.LiveTv public class BaseTimerInfoDto : IHasServerId { /// - /// Id of the recording. + /// Gets or sets the Id of the recording. /// public string Id { get; set; } @@ -28,7 +28,7 @@ namespace MediaBrowser.Model.LiveTv public string ExternalId { get; set; } /// - /// ChannelId of the recording. + /// Gets or sets the channel id of the recording. /// public Guid ChannelId { get; set; } @@ -39,7 +39,7 @@ namespace MediaBrowser.Model.LiveTv public string ExternalChannelId { get; set; } /// - /// ChannelName of the recording. + /// Gets or sets the channel name of the recording. /// public string ChannelName { get; set; } @@ -58,22 +58,22 @@ namespace MediaBrowser.Model.LiveTv public string ExternalProgramId { get; set; } /// - /// Name of the recording. + /// Gets or sets the name of the recording. /// public string Name { get; set; } /// - /// Description of the recording. + /// Gets or sets the description of the recording. /// public string Overview { get; set; } /// - /// The start date of the recording, in UTC. + /// Gets or sets the start date of the recording, in UTC. /// public DateTime StartDate { get; set; } /// - /// The end date of the recording, in UTC. + /// Gets or sets the end date of the recording, in UTC. /// public DateTime EndDate { get; set; } @@ -108,7 +108,7 @@ namespace MediaBrowser.Model.LiveTv public bool IsPrePaddingRequired { get; set; } /// - /// 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. /// /// The parent backdrop item id. public string ParentBackdropItemId { get; set; } diff --git a/MediaBrowser.Model/LiveTv/ListingsProviderInfo.cs b/MediaBrowser.Model/LiveTv/ListingsProviderInfo.cs new file mode 100644 index 000000000..082daeb51 --- /dev/null +++ b/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(); + EnableAllTuners = true; + ChannelMappings = Array.Empty(); + } + + 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; } + } +} diff --git a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs index bcba344cc..ca8defd8b 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs @@ -11,6 +11,12 @@ namespace MediaBrowser.Model.LiveTv /// public class LiveTvChannelQuery { + public LiveTvChannelQuery() + { + EnableUserData = true; + SortBy = Array.Empty(); + } + /// /// Gets or sets the type of the channel. /// @@ -48,13 +54,13 @@ namespace MediaBrowser.Model.LiveTv public Guid UserId { get; set; } /// - /// Skips over a given number of items within the results. Use for paging. + /// gets or sets the start index. Used for paging. /// /// The start index. public int? StartIndex { get; set; } /// - /// The maximum number of items to return. + /// Gets or sets the maximum number of items to return. /// /// The limit. public int? Limit { get; set; } @@ -68,15 +74,15 @@ namespace MediaBrowser.Model.LiveTv public bool EnableUserData { get; set; } /// - /// Used to specific whether to return news or not. + /// Gets or sets a value whether to return news or not. /// - /// If set to null, all programs will be returned + /// If set to null, all programs will be returned. public bool? IsNews { get; set; } /// - /// Used to specific whether to return movies or not. + /// Gets or sets a value whether to return movies or not. /// - /// If set to null, all programs will be returned + /// If set to null, all programs will be returned. public bool? IsMovie { get; set; } /// @@ -96,15 +102,9 @@ namespace MediaBrowser.Model.LiveTv public string[] SortBy { get; set; } /// - /// The sort order to return results with. + /// Gets or sets the sort order to return results with. /// /// The sort order. public SortOrder? SortOrder { get; set; } - - public LiveTvChannelQuery() - { - EnableUserData = true; - SortBy = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index 789de3198..4cece941c 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/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(); + ListingProviders = Array.Empty(); + MediaLocationsCreated = Array.Empty(); + 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(); - ListingProviders = Array.Empty(); - MediaLocationsCreated = Array.Empty(); - 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(); - EnableAllTuners = true; - ChannelMappings = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs b/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs index 856f638c5..ef5c5d2f3 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs @@ -10,6 +10,11 @@ namespace MediaBrowser.Model.LiveTv /// public class LiveTvServiceInfo { + public LiveTvServiceInfo() + { + Tuners = Array.Empty(); + } + /// /// Gets or sets the name. /// @@ -53,10 +58,5 @@ namespace MediaBrowser.Model.LiveTv public bool IsVisible { get; set; } public string[] Tuners { get; set; } - - public LiveTvServiceInfo() - { - Tuners = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/LiveTv/RecordingQuery.cs b/MediaBrowser.Model/LiveTv/RecordingQuery.cs index 69e7db470..99bb1603c 100644 --- a/MediaBrowser.Model/LiveTv/RecordingQuery.cs +++ b/MediaBrowser.Model/LiveTv/RecordingQuery.cs @@ -12,6 +12,11 @@ namespace MediaBrowser.Model.LiveTv /// public class RecordingQuery { + public RecordingQuery() + { + EnableTotalRecordCount = true; + } + /// /// Gets or sets the channel identifier. /// @@ -31,13 +36,13 @@ namespace MediaBrowser.Model.LiveTv public string Id { get; set; } /// - /// Skips over a given number of items within the results. Use for paging. + /// Gets or sets the start index. Use for paging. /// /// The start index. public int? StartIndex { get; set; } /// - /// The maximum number of items to return. + /// Gets or sets the maximum number of items to return. /// /// The limit. public int? Limit { get; set; } @@ -61,7 +66,7 @@ namespace MediaBrowser.Model.LiveTv public string SeriesTimerId { get; set; } /// - /// 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. /// /// The fields. 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; - } } } diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs index 90422d19c..b26f5f45f 100644 --- a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs @@ -7,6 +7,14 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Model.LiveTv { + public enum KeepUntil + { + UntilDeleted, + UntilSpaceNeeded, + UntilWatched, + UntilDate + } + /// /// Class SeriesTimerInfoDto. /// @@ -83,12 +91,4 @@ namespace MediaBrowser.Model.LiveTv /// The parent primary image tag. public string ParentPrimaryImageTag { get; set; } } - - public enum KeepUntil - { - UntilDeleted, - UntilSpaceNeeded, - UntilWatched, - UntilDate - } } diff --git a/MediaBrowser.Model/LiveTv/TunerHostInfo.cs b/MediaBrowser.Model/LiveTv/TunerHostInfo.cs new file mode 100644 index 000000000..7d4bbb2d0 --- /dev/null +++ b/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; } + } +} diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index c53428651..b6d916913 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -17,7 +17,7 @@ net5.0 false true - true + true enable latest true @@ -44,7 +44,7 @@ - + diff --git a/MediaBrowser.Model/MediaInfo/MediaInfo.cs b/MediaBrowser.Model/MediaInfo/MediaInfo.cs index 472055c22..a268a4fa6 100644 --- a/MediaBrowser.Model/MediaInfo/MediaInfo.cs +++ b/MediaBrowser.Model/MediaInfo/MediaInfo.cs @@ -10,6 +10,17 @@ namespace MediaBrowser.Model.MediaInfo { public class MediaInfo : MediaSourceInfo, IHasProviderIds { + public MediaInfo() + { + Chapters = Array.Empty(); + Artists = Array.Empty(); + AlbumArtists = Array.Empty(); + Studios = Array.Empty(); + Genres = Array.Empty(); + People = Array.Empty(); + ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + public ChapterInfo[] Chapters { get; set; } /// @@ -69,16 +80,5 @@ namespace MediaBrowser.Model.MediaInfo /// /// The overview. public string Overview { get; set; } - - public MediaInfo() - { - Chapters = Array.Empty(); - Artists = Array.Empty(); - AlbumArtists = Array.Empty(); - Studios = Array.Empty(); - Genres = Array.Empty(); - People = Array.Empty(); - ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); - } } } diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs index 321685677..ecd9b8834 100644 --- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs +++ b/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 }; - } } } diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs index 273350182..32971b108 100644 --- a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs +++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs @@ -10,6 +10,14 @@ namespace MediaBrowser.Model.MediaInfo /// public class PlaybackInfoResponse { + /// + /// Initializes a new instance of the class. + /// + public PlaybackInfoResponse() + { + MediaSources = Array.Empty(); + } + /// /// Gets or sets the media sources. /// @@ -27,13 +35,5 @@ namespace MediaBrowser.Model.MediaInfo /// /// The error code. public PlaybackErrorCode? ErrorCode { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public PlaybackInfoResponse() - { - MediaSources = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs b/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs index 37f5c55da..d5c3a6aec 100644 --- a/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs +++ b/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs @@ -7,11 +7,11 @@ namespace MediaBrowser.Model.MediaInfo { public class SubtitleTrackInfo { - public IReadOnlyList TrackEvents { get; set; } - public SubtitleTrackInfo() { TrackEvents = Array.Empty(); } + + public IReadOnlyList TrackEvents { get; set; } } } diff --git a/MediaBrowser.Model/Net/ISocket.cs b/MediaBrowser.Model/Net/ISocket.cs index 5b6ed92df..3de41d565 100644 --- a/MediaBrowser.Model/Net/ISocket.cs +++ b/MediaBrowser.Model/Net/ISocket.cs @@ -23,6 +23,12 @@ namespace MediaBrowser.Model.Net /// /// Sends a UDP message to a particular end point (uni or multicast). /// + /// An array of type that contains the data to send. + /// The zero-based position in buffer at which to begin sending data. + /// The number of bytes to send. + /// An that represents the remote device. + /// The cancellation token to cancel operation. + /// The task object representing the asynchronous operation. Task SendToAsync(byte[] buffer, int offset, int bytes, IPEndPoint endPoint, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs index 363abefc1..1527ef595 100644 --- a/MediaBrowser.Model/Net/ISocketFactory.cs +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -14,6 +14,9 @@ namespace MediaBrowser.Model.Net /// /// Creates a new unicast socket using the specified local port number. /// + /// The local IP address to bind to. + /// The local port to bind to. + /// A new unicast socket using the specified local port number. ISocket CreateSsdpUdpSocket(IPAddress localIp, int localPort); /// diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs index 902db1e9e..96f5ab51a 100644 --- a/MediaBrowser.Model/Net/MimeTypes.cs +++ b/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 /// /// Gets the type of the MIME. /// - public static string? GetMimeType(string path, bool enableStreamDefault) + /// The filename to find the MIME type of. + /// Whether of not to return a default value if no fitting MIME type is found. + /// The worrect MIME type for the given filename, or `null` if it wasn't found and is false. + 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)); } diff --git a/MediaBrowser.Model/Net/NetworkShare.cs b/MediaBrowser.Model/Net/NetworkShare.cs deleted file mode 100644 index 6344cbe21..000000000 --- a/MediaBrowser.Model/Net/NetworkShare.cs +++ /dev/null @@ -1,33 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -namespace MediaBrowser.Model.Net -{ - public class NetworkShare - { - /// - /// The name of the computer that this share belongs to. - /// - public string Server { get; set; } - - /// - /// Share name. - /// - public string Name { get; set; } - - /// - /// Local path. - /// - public string Path { get; set; } - - /// - /// Share type. - /// - public NetworkShareType ShareType { get; set; } - - /// - /// Comment. - /// - public string Remark { get; set; } - } -} diff --git a/MediaBrowser.Model/Net/SocketReceiveResult.cs b/MediaBrowser.Model/Net/SocketReceiveResult.cs index 54139fe9c..1524786ea 100644 --- a/MediaBrowser.Model/Net/SocketReceiveResult.cs +++ b/MediaBrowser.Model/Net/SocketReceiveResult.cs @@ -20,12 +20,12 @@ namespace MediaBrowser.Model.Net public int ReceivedBytes { get; set; } /// - /// The the data was received from. + /// Gets or sets the the data was received from. /// public IPEndPoint RemoteEndPoint { get; set; } /// - /// The local . + /// Gets or sets the local . /// public IPAddress LocalIPAddress { get; set; } } diff --git a/MediaBrowser.Model/Net/WebSocketMessage.cs b/MediaBrowser.Model/Net/WebSocketMessage.cs index bffbbe612..b00158cb3 100644 --- a/MediaBrowser.Model/Net/WebSocketMessage.cs +++ b/MediaBrowser.Model/Net/WebSocketMessage.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Model.Net /// /// Class WebSocketMessage. /// - /// + /// The type of the data. public class WebSocketMessage { /// diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs index 239a3777e..94bb5d6e3 100644 --- a/MediaBrowser.Model/Notifications/NotificationOptions.cs +++ b/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) diff --git a/MediaBrowser.Model/Notifications/NotificationRequest.cs b/MediaBrowser.Model/Notifications/NotificationRequest.cs index febc2bc09..622c50cd8 100644 --- a/MediaBrowser.Model/Notifications/NotificationRequest.cs +++ b/MediaBrowser.Model/Notifications/NotificationRequest.cs @@ -7,6 +7,12 @@ namespace MediaBrowser.Model.Notifications { public class NotificationRequest { + public NotificationRequest() + { + UserIds = Array.Empty(); + 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; } /// - /// The corresponding type name used in configuration. Not for display. + /// Gets or sets the corresponding type name used in configuration. Not for display. /// public string NotificationType { get; set; } public SendToUserType? SendToUserMode { get; set; } - - public NotificationRequest() - { - UserIds = Array.Empty(); - Date = DateTime.UtcNow; - } } } diff --git a/MediaBrowser.Model/Providers/ExternalIdInfo.cs b/MediaBrowser.Model/Providers/ExternalIdInfo.cs index afe95e6ee..0ea3e96ca 100644 --- a/MediaBrowser.Model/Providers/ExternalIdInfo.cs +++ b/MediaBrowser.Model/Providers/ExternalIdInfo.cs @@ -6,11 +6,11 @@ namespace MediaBrowser.Model.Providers public class ExternalIdInfo { /// - /// Represents the external id information for serialization to the client. + /// Initializes a new instance of the class. /// /// Name of the external id provider (IE: IMDB, MusicBrainz, etc). /// Key for this id. This key should be unique across all providers. - /// Specific media type for this id + /// Specific media type for this id. /// URL format string. public ExternalIdInfo(string name, string key, ExternalIdMediaType? type, string urlFormatString) { diff --git a/MediaBrowser.Model/Providers/RemoteImageInfo.cs b/MediaBrowser.Model/Providers/RemoteImageInfo.cs index fb25999e0..48207d2d4 100644 --- a/MediaBrowser.Model/Providers/RemoteImageInfo.cs +++ b/MediaBrowser.Model/Providers/RemoteImageInfo.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Model.Providers public string Url { get; set; } /// - /// Gets a url used for previewing a smaller version. + /// Gets or sets a url used for previewing a smaller version. /// public string ThumbnailUrl { get; set; } diff --git a/MediaBrowser.Model/Providers/SubtitleOptions.cs b/MediaBrowser.Model/Providers/SubtitleOptions.cs index 5702c460b..6ea1e1486 100644 --- a/MediaBrowser.Model/Providers/SubtitleOptions.cs +++ b/MediaBrowser.Model/Providers/SubtitleOptions.cs @@ -7,6 +7,14 @@ namespace MediaBrowser.Model.Providers { public class SubtitleOptions { + public SubtitleOptions() + { + DownloadLanguages = Array.Empty(); + + 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(); - - SkipIfAudioTrackMatches = true; - RequirePerfectMatch = true; - } } } diff --git a/MediaBrowser.Model/Querying/EpisodeQuery.cs b/MediaBrowser.Model/Querying/EpisodeQuery.cs index 13b1a0dcb..56a7f3320 100644 --- a/MediaBrowser.Model/Querying/EpisodeQuery.cs +++ b/MediaBrowser.Model/Querying/EpisodeQuery.cs @@ -7,6 +7,11 @@ namespace MediaBrowser.Model.Querying { public class EpisodeQuery { + public EpisodeQuery() + { + Fields = Array.Empty(); + } + /// /// Gets or sets the user identifier. /// @@ -66,10 +71,5 @@ namespace MediaBrowser.Model.Querying /// /// The start item identifier. public string StartItemId { get; set; } - - public EpisodeQuery() - { - Fields = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/Querying/LatestItemsQuery.cs b/MediaBrowser.Model/Querying/LatestItemsQuery.cs index 7954ef4b4..f555ffb36 100644 --- a/MediaBrowser.Model/Querying/LatestItemsQuery.cs +++ b/MediaBrowser.Model/Querying/LatestItemsQuery.cs @@ -14,31 +14,32 @@ namespace MediaBrowser.Model.Querying } /// - /// The user to localize search results for. + /// Gets or sets the user to localize search results for. /// /// The user id. public Guid UserId { get; set; } /// + /// Gets or sets the parent id. /// Specify this to localize the search to a specific item or folder. Omit to use the root. /// /// The parent id. public Guid ParentId { get; set; } /// - /// Skips over a given number of items within the results. Use for paging. + /// Gets or sets the start index. Used for paging. /// /// The start index. public int? StartIndex { get; set; } /// - /// The maximum number of items to return. + /// Gets or sets the maximum number of items to return. /// /// The limit. public int? Limit { get; set; } /// - /// 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. /// /// The fields. public ItemFields[] Fields { get; set; } diff --git a/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs b/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs index 1c8875890..b800f5de5 100644 --- a/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs +++ b/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(); + } + /// /// Gets or sets the user identifier. /// @@ -36,12 +43,5 @@ namespace MediaBrowser.Model.Querying /// /// The fields. public ItemFields[] Fields { get; set; } - - public MovieRecommendationQuery() - { - ItemLimit = 10; - CategoryLimit = 6; - Fields = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/Querying/NextUpQuery.cs b/MediaBrowser.Model/Querying/NextUpQuery.cs index 001d0623c..0555afc00 100644 --- a/MediaBrowser.Model/Querying/NextUpQuery.cs +++ b/MediaBrowser.Model/Querying/NextUpQuery.cs @@ -8,6 +8,13 @@ namespace MediaBrowser.Model.Querying { public class NextUpQuery { + public NextUpQuery() + { + EnableImageTypes = Array.Empty(); + EnableTotalRecordCount = true; + DisableFirstEpisode = false; + } + /// /// Gets or sets the user id. /// @@ -27,19 +34,19 @@ namespace MediaBrowser.Model.Querying public string SeriesId { get; set; } /// - /// Skips over a given number of items within the results. Use for paging. + /// Gets or sets the start index. Use for paging. /// /// The start index. public int? StartIndex { get; set; } /// - /// The maximum number of items to return. + /// Gets or sets the maximum number of items to return. /// /// The limit. public int? Limit { get; set; } /// - /// 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. /// /// The fields. 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. /// public bool DisableFirstEpisode { get; set; } - - public NextUpQuery() - { - EnableImageTypes = Array.Empty(); - EnableTotalRecordCount = true; - DisableFirstEpisode = false; - } } } diff --git a/MediaBrowser.Model/Querying/QueryFilters.cs b/MediaBrowser.Model/Querying/QueryFilters.cs index 6e4d25181..73b27a7b0 100644 --- a/MediaBrowser.Model/Querying/QueryFilters.cs +++ b/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(); Tags = Array.Empty(); - OfficialRatings = Array.Empty(); - Years = Array.Empty(); + Genres = Array.Empty(); } - } - public class QueryFilters - { public NameGuidPair[] Genres { get; set; } public string[] Tags { get; set; } - - public QueryFilters() - { - Tags = Array.Empty(); - Genres = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/Querying/QueryFiltersLegacy.cs b/MediaBrowser.Model/Querying/QueryFiltersLegacy.cs new file mode 100644 index 000000000..fcb450ed3 --- /dev/null +++ b/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(); + Tags = Array.Empty(); + OfficialRatings = Array.Empty(); + Years = Array.Empty(); + } + + public string[] Genres { get; set; } + + public string[] Tags { get; set; } + + public string[] OfficialRatings { get; set; } + + public int[] Years { get; set; } + } +} diff --git a/MediaBrowser.Model/Querying/QueryResult.cs b/MediaBrowser.Model/Querying/QueryResult.cs index 490f48b84..8ce794800 100644 --- a/MediaBrowser.Model/Querying/QueryResult.cs +++ b/MediaBrowser.Model/Querying/QueryResult.cs @@ -8,6 +8,17 @@ namespace MediaBrowser.Model.Querying { public class QueryResult { + public QueryResult() + { + Items = Array.Empty(); + } + + public QueryResult(IReadOnlyList items) + { + Items = items; + TotalRecordCount = items.Count; + } + /// /// Gets or sets the items. /// @@ -15,26 +26,15 @@ namespace MediaBrowser.Model.Querying public IReadOnlyList Items { get; set; } /// - /// The total number of records available. + /// Gets or sets the total number of records available. /// /// The total record count. public int TotalRecordCount { get; set; } /// - /// The index of the first record in Items. + /// Gets or sets the index of the first record in Items. /// /// First record index. public int StartIndex { get; set; } - - public QueryResult() - { - Items = Array.Empty(); - } - - public QueryResult(IReadOnlyList items) - { - Items = items; - TotalRecordCount = items.Count; - } } } diff --git a/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs b/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs index eb6239460..2cf0f0d5f 100644 --- a/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs +++ b/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs @@ -8,6 +8,11 @@ namespace MediaBrowser.Model.Querying { public class UpcomingEpisodesQuery { + public UpcomingEpisodesQuery() + { + EnableImageTypes = Array.Empty(); + } + /// /// Gets or sets the user id. /// @@ -21,19 +26,19 @@ namespace MediaBrowser.Model.Querying public string ParentId { get; set; } /// - /// Skips over a given number of items within the results. Use for paging. + /// Gets or sets the start index. Use for paging. /// /// The start index. public int? StartIndex { get; set; } /// - /// The maximum number of items to return. + /// Gets or sets the maximum number of items to return. /// /// The limit. public int? Limit { get; set; } /// - /// 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. /// /// The fields. public ItemFields[] Fields { get; set; } @@ -55,10 +60,5 @@ namespace MediaBrowser.Model.Querying /// /// The enable image types. public ImageType[] EnableImageTypes { get; set; } - - public UpcomingEpisodesQuery() - { - EnableImageTypes = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/Search/SearchQuery.cs b/MediaBrowser.Model/Search/SearchQuery.cs index ce60062cd..aedfa4d36 100644 --- a/MediaBrowser.Model/Search/SearchQuery.cs +++ b/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(); + IncludeItemTypes = Array.Empty(); + ExcludeItemTypes = Array.Empty(); + } + /// - /// The user to localize search results for. + /// Gets or sets the user to localize search results for. /// /// The user id. public Guid UserId { get; set; } @@ -20,13 +33,13 @@ namespace MediaBrowser.Model.Search public string SearchTerm { get; set; } /// - /// Skips over a given number of items within the results. Use for paging. + /// Gets or sets the start index. Used for paging. /// /// The start index. public int? StartIndex { get; set; } /// - /// The maximum number of items to return. + /// Gets or sets the maximum number of items to return. /// /// The limit. 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(); - IncludeItemTypes = Array.Empty(); - ExcludeItemTypes = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/Session/BrowseRequest.cs b/MediaBrowser.Model/Session/BrowseRequest.cs index 1c997d584..65afe5cf3 100644 --- a/MediaBrowser.Model/Session/BrowseRequest.cs +++ b/MediaBrowser.Model/Session/BrowseRequest.cs @@ -7,6 +7,7 @@ namespace MediaBrowser.Model.Session public class BrowseRequest { /// + /// Gets or sets the item type. /// Artist, Genre, Studio, Person, or any kind of BaseItem. /// /// The type of the item. diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index 5852f4e37..d692906c6 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -9,6 +9,13 @@ namespace MediaBrowser.Model.Session { public class ClientCapabilities { + public ClientCapabilities() + { + PlayableMediaTypes = Array.Empty(); + SupportedCommands = Array.Empty(); + SupportsPersistentIdentifier = true; + } + public IReadOnlyList PlayableMediaTypes { get; set; } public IReadOnlyList 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(); - SupportedCommands = Array.Empty(); - SupportsPersistentIdentifier = true; - } } } diff --git a/MediaBrowser.Model/Session/GeneralCommand.cs b/MediaBrowser.Model/Session/GeneralCommand.cs index 77bb6bcf7..29528c110 100644 --- a/MediaBrowser.Model/Session/GeneralCommand.cs +++ b/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 Arguments { get; set; } - public GeneralCommand() { Arguments = new Dictionary(); } + + public GeneralCommandType Name { get; set; } + + public Guid ControllingUserId { get; set; } + + public Dictionary Arguments { get; } } } diff --git a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs index 73dbe6a2d..a6e7efcb0 100644 --- a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs +++ b/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; } - } } diff --git a/MediaBrowser.Model/Session/PlaystateCommand.cs b/MediaBrowser.Model/Session/PlaystateCommand.cs index 3aa091f79..df47f3b73 100644 --- a/MediaBrowser.Model/Session/PlaystateCommand.cs +++ b/MediaBrowser.Model/Session/PlaystateCommand.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - namespace MediaBrowser.Model.Session { /// @@ -46,6 +44,10 @@ namespace MediaBrowser.Model.Session /// The fast forward. /// FastForward, + + /// + /// The play pause. + /// PlayPause } } diff --git a/MediaBrowser.Model/Session/QueueItem.cs b/MediaBrowser.Model/Session/QueueItem.cs new file mode 100644 index 000000000..32b19101b --- /dev/null +++ b/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; } + } +} diff --git a/MediaBrowser.Model/Session/RepeatMode.cs b/MediaBrowser.Model/Session/RepeatMode.cs new file mode 100644 index 000000000..c6e173d6b --- /dev/null +++ b/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 + } +} diff --git a/MediaBrowser.Model/Session/TranscodeReason.cs b/MediaBrowser.Model/Session/TranscodeReason.cs new file mode 100644 index 000000000..e93b5d288 --- /dev/null +++ b/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 + } +} diff --git a/MediaBrowser.Model/Session/TranscodingInfo.cs b/MediaBrowser.Model/Session/TranscodingInfo.cs index e832c2f6f..064a087d5 100644 --- a/MediaBrowser.Model/Session/TranscodingInfo.cs +++ b/MediaBrowser.Model/Session/TranscodingInfo.cs @@ -7,6 +7,11 @@ namespace MediaBrowser.Model.Session { public class TranscodingInfo { + public TranscodingInfo() + { + TranscodeReasons = Array.Empty(); + } + 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(); - } - } - - 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 } } diff --git a/MediaBrowser.Model/Sync/SyncJob.cs b/MediaBrowser.Model/Sync/SyncJob.cs index b9290b6e8..3e396e5d1 100644 --- a/MediaBrowser.Model/Sync/SyncJob.cs +++ b/MediaBrowser.Model/Sync/SyncJob.cs @@ -7,6 +7,11 @@ namespace MediaBrowser.Model.Sync { public class SyncJob { + public SyncJob() + { + RequestedItemIds = Array.Empty(); + } + /// /// Gets or sets the identifier. /// @@ -126,10 +131,5 @@ namespace MediaBrowser.Model.Sync public string PrimaryImageItemId { get; set; } public string PrimaryImageTag { get; set; } - - public SyncJob() - { - RequestedItemIds = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 4b83fb7e6..d75ae91c0 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -30,6 +30,14 @@ namespace MediaBrowser.Model.System /// public class SystemInfo : PublicSystemInfo { + /// + /// Initializes a new instance of the class. + /// + public SystemInfo() + { + CompletedInstallations = Array.Empty(); + } + /// /// Gets or sets the display name of the operating system. /// @@ -37,7 +45,7 @@ namespace MediaBrowser.Model.System public string OperatingSystemDisplayName { get; set; } /// - /// Get or sets the package name. + /// Gets or sets the package name. /// /// The value of the '-package' command line argument. public string PackageName { get; set; } @@ -127,13 +135,5 @@ namespace MediaBrowser.Model.System public FFmpegLocation EncoderLocation { get; set; } public Architecture SystemArchitecture { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public SystemInfo() - { - CompletedInstallations = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/System/WakeOnLanInfo.cs b/MediaBrowser.Model/System/WakeOnLanInfo.cs index b2cbe737d..aba19a6ba 100644 --- a/MediaBrowser.Model/System/WakeOnLanInfo.cs +++ b/MediaBrowser.Model/System/WakeOnLanInfo.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.Model.System /// Gets the MAC address of the device. /// /// The MAC address. - public string? MacAddress { get; set; } + public string? MacAddress { get; } /// /// Gets or sets the wake-on-LAN port. diff --git a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs index 2f05e08c5..ca769e26b 100644 --- a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs +++ b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Model.Tasks event EventHandler> TaskProgress; /// - /// Gets or sets the scheduled task. + /// Gets the scheduled task. /// /// The scheduled task. IScheduledTask ScheduledTask { get; } @@ -57,10 +57,9 @@ namespace MediaBrowser.Model.Tasks double? CurrentProgress { get; } /// - /// Gets the triggers that define when the task will run. + /// Gets or sets the triggers that define when the task will run. /// /// The triggers. - /// value TaskTriggerInfo[] Triggers { get; set; } /// diff --git a/MediaBrowser.Model/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs index 02b29074e..a86bf2a1c 100644 --- a/MediaBrowser.Model/Tasks/ITaskManager.cs +++ b/MediaBrowser.Model/Tasks/ITaskManager.cs @@ -9,6 +9,10 @@ namespace MediaBrowser.Model.Tasks { public interface ITaskManager : IDisposable { + event EventHandler> TaskExecuting; + + event EventHandler TaskCompleted; + /// /// Gets the list of Scheduled Tasks. /// @@ -18,7 +22,7 @@ namespace MediaBrowser.Model.Tasks /// /// Cancels if running and queue. /// - /// + /// An implementatin of . /// Task options. void CancelIfRunningAndQueue(TaskOptions options) where T : IScheduledTask; @@ -26,21 +30,21 @@ namespace MediaBrowser.Model.Tasks /// /// Cancels if running and queue. /// - /// + /// An implementatin of . void CancelIfRunningAndQueue() where T : IScheduledTask; /// /// Cancels if running. /// - /// + /// An implementatin of . void CancelIfRunning() where T : IScheduledTask; /// /// Queues the scheduled task. /// - /// + /// An implementatin of . /// Task options. void QueueScheduledTask(TaskOptions options) where T : IScheduledTask; @@ -48,7 +52,7 @@ namespace MediaBrowser.Model.Tasks /// /// Queues the scheduled task. /// - /// + /// An implementatin of . void QueueScheduledTask() where T : IScheduledTask; @@ -58,6 +62,8 @@ namespace MediaBrowser.Model.Tasks /// /// Queues the scheduled task. /// + /// The to queue. + /// The to use. void QueueScheduledTask(IScheduledTask task, TaskOptions options); /// @@ -67,12 +73,10 @@ namespace MediaBrowser.Model.Tasks void AddTasks(IEnumerable tasks); void Cancel(IScheduledTaskWorker task); + Task Execute(IScheduledTaskWorker task, TaskOptions options); void Execute() where T : IScheduledTask; - - event EventHandler> TaskExecuting; - event EventHandler TaskCompleted; } } diff --git a/MediaBrowser.Model/Tasks/ITaskTrigger.cs b/MediaBrowser.Model/Tasks/ITaskTrigger.cs index 5c30d6c22..cbd60cca1 100644 --- a/MediaBrowser.Model/Tasks/ITaskTrigger.cs +++ b/MediaBrowser.Model/Tasks/ITaskTrigger.cs @@ -21,6 +21,10 @@ namespace MediaBrowser.Model.Tasks /// /// Stars waiting for the trigger action. /// + /// Result of the last run triggerd task. + /// The . + /// The name of the task. + /// Wheter or not this is is fired during startup. void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup); /// diff --git a/MediaBrowser.Model/Tasks/TaskInfo.cs b/MediaBrowser.Model/Tasks/TaskInfo.cs index 77100dfe7..16de0b121 100644 --- a/MediaBrowser.Model/Tasks/TaskInfo.cs +++ b/MediaBrowser.Model/Tasks/TaskInfo.cs @@ -8,6 +8,14 @@ namespace MediaBrowser.Model.Tasks /// public class TaskInfo { + /// + /// Initializes a new instance of the class. + /// + public TaskInfo() + { + Triggers = Array.Empty(); + } + /// /// Gets or sets the name. /// @@ -67,13 +75,5 @@ namespace MediaBrowser.Model.Tasks /// /// The key. public string Key { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public TaskInfo() - { - Triggers = Array.Empty(); - } } } diff --git a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs index 5aeaffc2b..f8a8c727e 100644 --- a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs +++ b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs @@ -10,6 +10,12 @@ namespace MediaBrowser.Model.Tasks /// 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"; + /// /// Gets or sets the type. /// @@ -39,11 +45,5 @@ namespace MediaBrowser.Model.Tasks /// /// The maximum runtime ticks. 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"; } } diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index 37da04adf..111070d81 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/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(); + + 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(); + BlockUnratedItems = Array.Empty(); + + EnableUserPreferenceAccess = true; + + AccessSchedules = Array.Empty(); + + LoginAttemptsBeforeLockout = -1; + + MaxActiveSessions = 0; + + EnableAllChannels = true; + EnabledChannels = Array.Empty(); + + EnableAllFolders = true; + EnabledFolders = Array.Empty(); + + EnabledDevices = Array.Empty(); + EnableAllDevices = true; + + EnableContentDownloading = true; + EnablePublicSharing = true; + EnableRemoteAccess = true; + SyncPlayAccess = SyncPlayUserAccessType.CreateAndJoinGroups; + } + /// /// Gets or sets a value indicating whether this instance is administrator. /// @@ -112,55 +162,5 @@ namespace MediaBrowser.Model.Users /// /// Access level to SyncPlay features. public SyncPlayUserAccessType SyncPlayAccess { get; set; } - - public UserPolicy() - { - IsHidden = true; - - EnableContentDeletion = false; - EnableContentDeletionFromFolders = Array.Empty(); - - 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(); - BlockUnratedItems = Array.Empty(); - - EnableUserPreferenceAccess = true; - - AccessSchedules = Array.Empty(); - - LoginAttemptsBeforeLockout = -1; - - MaxActiveSessions = 0; - - EnableAllChannels = true; - EnabledChannels = Array.Empty(); - - EnableAllFolders = true; - EnabledFolders = Array.Empty(); - - EnabledDevices = Array.Empty(); - EnableAllDevices = true; - - EnableContentDownloading = true; - EnablePublicSharing = true; - EnableRemoteAccess = true; - SyncPlayAccess = SyncPlayUserAccessType.CreateAndJoinGroups; - } } } diff --git a/jellyfin.ruleset b/jellyfin.ruleset index fa09bfb66..81337390c 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -32,6 +32,8 @@ + + diff --git a/tests/Jellyfin.Api.Tests/ModelBinders/TestType.cs b/tests/Jellyfin.Api.Tests/ModelBinders/TestType.cs index 544a74637..92c534eae 100644 --- a/tests/Jellyfin.Api.Tests/ModelBinders/TestType.cs +++ b/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 } } -- cgit v1.2.3 From 9ed7f429c01c3f54a154442250d3447fd66d1b02 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 9 Mar 2021 03:04:47 +0100 Subject: FxCop -> Net Analyzers (part 1) --- Emby.Dlna/Emby.Dlna.csproj | 1 - Emby.Drawing/Emby.Drawing.csproj | 1 - Emby.Naming/Emby.Naming.csproj | 1 - Emby.Notifications/Emby.Notifications.csproj | 1 - Emby.Photos/Emby.Photos.csproj | 1 - .../Emby.Server.Implementations.csproj | 1 - Jellyfin.Api/Jellyfin.Api.csproj | 1 - Jellyfin.Data/Jellyfin.Data.csproj | 1 - Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 1 - Jellyfin.Networking/Jellyfin.Networking.csproj | 1 - .../Jellyfin.Server.Implementations.csproj | 1 - Jellyfin.Server/Jellyfin.Server.csproj | 1 - Jellyfin.sln | 14 +- MediaBrowser.Common/MediaBrowser.Common.csproj | 1 - .../MediaBrowser.Controller.csproj | 1 - .../MediaBrowser.LocalMetadata.csproj | 1 - .../BdInfo/BdInfoDirectoryInfo.cs | 2 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 92 +--- MediaBrowser.MediaEncoding/FfmpegException.cs | 39 ++ .../MediaBrowser.MediaEncoding.csproj | 8 +- .../Probing/ProbeResultNormalizer.cs | 18 +- .../Subtitles/SubtitleEncoder.cs | 6 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 9 +- .../MediaBrowser.Providers.csproj | 8 +- .../MediaBrowser.XbmcMetadata.csproj | 8 +- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 4 +- .../Providers/BaseVideoNfoProvider.cs | 2 +- MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs | 18 +- MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs | 13 +- MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs | 16 +- .../Savers/EpisodeNfoSaver.cs | 31 +- MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs | 17 +- MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs | 11 +- MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs | 21 +- jellyfin.ruleset | 8 +- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 8 +- .../Jellyfin.Common.Tests.csproj | 8 +- .../Jellyfin.Controller.Tests.csproj | 8 +- .../Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj | 8 +- .../Jellyfin.MediaEncoding.Tests.csproj | 8 +- .../Jellyfin.Model.Tests.csproj | 8 +- .../AudioBook/AudioBookResolverTests.cs | 4 +- .../Jellyfin.Naming.Tests.csproj | 10 +- .../Video/VideoResolverTests.cs | 4 +- .../Jellyfin.Networking.Tests.csproj | 42 ++ .../Jellyfin.Networking.Tests/NetworkParseTests.cs | 518 ++++++++++++++++++++ .../Jellyfin.Networking.Tests.csproj | 39 -- .../NetworkTesting/NetworkParseTests.cs | 519 --------------------- .../Jellyfin.Server.Implementations.Tests.csproj | 8 +- .../Jellyfin.XbmcMetadata.Tests.csproj | 8 +- 50 files changed, 750 insertions(+), 810 deletions(-) create mode 100644 MediaBrowser.MediaEncoding/FfmpegException.cs create mode 100644 tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj create mode 100644 tests/Jellyfin.Networking.Tests/NetworkParseTests.cs delete mode 100644 tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj delete mode 100644 tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs (limited to 'MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs') diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 8b057a095..480621dd7 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -25,7 +25,6 @@ - diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index 7d479a5c6..5c5afe1c6 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -25,7 +25,6 @@ - diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index b43203e9d..63116f368 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -44,7 +44,6 @@ - diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj index 16ee918c4..526a27229 100644 --- a/Emby.Notifications/Emby.Notifications.csproj +++ b/Emby.Notifications/Emby.Notifications.csproj @@ -25,7 +25,6 @@ - diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index 62e33e6c4..e64a658c5 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -28,7 +28,6 @@ - diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index f03f04e02..5a9792b51 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -49,7 +49,6 @@ - diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 67d0a3b5a..d5372d752 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -28,7 +28,6 @@ - diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index a8ac45645..42731bb11 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -34,7 +34,6 @@ - diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index 466a12e67..1a8415ae0 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -32,7 +32,6 @@ - diff --git a/Jellyfin.Networking/Jellyfin.Networking.csproj b/Jellyfin.Networking/Jellyfin.Networking.csproj index cbda74361..f89a18426 100644 --- a/Jellyfin.Networking/Jellyfin.Networking.csproj +++ b/Jellyfin.Networking/Jellyfin.Networking.csproj @@ -13,7 +13,6 @@ - diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 4f24da0ee..19c7ac567 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -14,7 +14,6 @@ - diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index bf4f80669..6bfb5b878 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -26,7 +26,6 @@ - diff --git a/Jellyfin.sln b/Jellyfin.sln index d83013dab..02ac1c7e9 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -68,14 +68,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Server.Implementat EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking", "Jellyfin.Networking\Jellyfin.Networking.csproj", "{0A3FCC4D-C714-4072-B90F-E374A15F9FF9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\NetworkTesting\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Dlna.Tests", "tests\Jellyfin.Dlna.Tests\Jellyfin.Dlna.Tests.csproj", "{B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.XbmcMetadata.Tests", "tests\Jellyfin.XbmcMetadata.Tests\Jellyfin.XbmcMetadata.Tests.csproj", "{30922383-D513-4F4D-B890-A940B57FA353}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Model.Tests", "tests\Jellyfin.Model.Tests\Jellyfin.Model.Tests.csproj", "{FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Networking.Tests", "tests\Jellyfin.Networking.Tests\Jellyfin.Networking.Tests.csproj", "{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -190,10 +190,6 @@ Global {0A3FCC4D-C714-4072-B90F-E374A15F9FF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {0A3FCC4D-C714-4072-B90F-E374A15F9FF9}.Release|Any CPU.ActiveCfg = Release|Any CPU {0A3FCC4D-C714-4072-B90F-E374A15F9FF9}.Release|Any CPU.Build.0 = Release|Any CPU - {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}.Debug|Any CPU.Build.0 = Debug|Any CPU {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -206,6 +202,10 @@ Global {FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D}.Release|Any CPU.Build.0 = Release|Any CPU + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -217,10 +217,10 @@ Global {A2FD0A10-8F62-4F9D-B171-FFDF9F0AFA9D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {2E3A1B4B-4225-4AAA-8B29-0181A84E7AEE} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {462584F7-5023-4019-9EAC-B98CA458C0A0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} - {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {B8AE4B9D-E8D3-4B03-A95E-7FD8CECECC50} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {30922383-D513-4F4D-B890-A940B57FA353} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {FC1BC0CE-E8D2-4AE9-A6AB-8A02143B335D} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 8bb30c565..34e1934e2 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -46,7 +46,6 @@ - diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 6b1c096ac..d487a324f 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -47,7 +47,6 @@ - diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index 3ce9ff4cc..1792f1d9b 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -24,7 +24,6 @@ - diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs index 4a54b677d..ef9943722 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs @@ -71,7 +71,7 @@ namespace MediaBrowser.MediaEncoding.BdInfo _impl.FullName, new[] { searchPattern }, false, - searchOption.HasFlag(System.IO.SearchOption.AllDirectories)).ToArray(), + (searchOption & System.IO.SearchOption.AllDirectories) == System.IO.SearchOption.AllDirectories).ToArray(), x => new BdInfoFileInfo(x)); } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index a52019384..8a25a64c7 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -448,7 +448,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (result == null || (result.Streams == null && result.Format == null)) { - throw new Exception("ffprobe failed - streams and format are both null."); + throw new FfmpegException("ffprobe failed - streams and format are both null."); } if (result.Streams != null) @@ -571,32 +571,18 @@ namespace MediaBrowser.MediaEncoding.Encoder // apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar. // This filter chain may have adverse effects on recorded tv thumbnails if ar changes during presentation ex. commercials @ diff ar - var vf = string.Empty; - - if (threedFormat.HasValue) + var vf = threedFormat switch { - switch (threedFormat.Value) - { - case Video3DFormat.HalfSideBySide: - vf = "-vf crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; - // hsbs crop width in half,scale to correct size, set the display aspect,crop out any black bars we may have made. Work out the correct height based on the display aspect it will maintain the aspect where -1 in this case (3d) may not. - break; - case Video3DFormat.FullSideBySide: - vf = "-vf crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; - // fsbs crop width in half,set the display aspect,crop out any black bars we may have made - break; - case Video3DFormat.HalfTopAndBottom: - vf = "-vf crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; - // htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made - break; - case Video3DFormat.FullTopAndBottom: - vf = "-vf crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1"; - // ftab crop heigt in half, set the display aspect,crop out any black bars we may have made - break; - default: - break; - } - } + // hsbs crop width in half,scale to correct size, set the display aspect,crop out any black bars we may have made. Work out the correct height based on the display aspect it will maintain the aspect where -1 in this case (3d) may not. + Video3DFormat.HalfSideBySide => "-vf crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1", + // fsbs crop width in half,set the display aspect,crop out any black bars we may have made + Video3DFormat.FullSideBySide => "-vf crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1", + // htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made + Video3DFormat.HalfTopAndBottom => "-vf crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1", + // ftab crop heigt in half, set the display aspect,crop out any black bars we may have made + Video3DFormat.FullTopAndBottom => "-vf crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1", + _ => string.Empty + }; var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty; @@ -604,7 +590,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (enableHdrExtraction) { string tonemapFilters = "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0:peak=100,zscale=t=bt709:m=bt709,format=yuv420p"; - if (string.IsNullOrEmpty(vf)) + if (vf.Length == 0) { vf = "-vf " + tonemapFilters; } @@ -633,35 +619,11 @@ namespace MediaBrowser.MediaEncoding.Encoder var args = string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, threads); - var probeSizeArgument = string.Empty; - var analyzeDurationArgument = string.Empty; - - if (!string.IsNullOrWhiteSpace(probeSizeArgument)) - { - args = probeSizeArgument + " " + args; - } - - if (!string.IsNullOrWhiteSpace(analyzeDurationArgument)) - { - args = analyzeDurationArgument + " " + args; - } - if (offset.HasValue) { args = string.Format(CultureInfo.InvariantCulture, "-ss {0} ", GetTimeParameter(offset.Value)) + args; } - if (videoStream != null) - { - /* fix - var decoder = encodinghelper.GetHardwareAcceleratedVideoDecoder(VideoType.VideoFile, videoStream, GetEncodingOptions()); - if (!string.IsNullOrWhiteSpace(decoder)) - { - args = decoder + " " + args; - } - */ - } - if (!string.IsNullOrWhiteSpace(container)) { var inputFormat = EncodingHelper.GetInputFormat(container); @@ -723,7 +685,7 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger.LogError(msg); - throw new Exception(msg); + throw new FfmpegException(msg); } return tempExtractPath; @@ -770,30 +732,6 @@ namespace MediaBrowser.MediaEncoding.Encoder var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, threads); - var probeSizeArgument = string.Empty; - var analyzeDurationArgument = string.Empty; - - if (!string.IsNullOrWhiteSpace(probeSizeArgument)) - { - args = probeSizeArgument + " " + args; - } - - if (!string.IsNullOrWhiteSpace(analyzeDurationArgument)) - { - args = analyzeDurationArgument + " " + args; - } - - if (videoStream != null) - { - /* fix - var decoder = encodinghelper.GetHardwareAcceleratedVideoDecoder(VideoType.VideoFile, videoStream, GetEncodingOptions()); - if (!string.IsNullOrWhiteSpace(decoder)) - { - args = decoder + " " + args; - } - */ - } - if (!string.IsNullOrWhiteSpace(container)) { var inputFormat = EncodingHelper.GetInputFormat(container); @@ -872,7 +810,7 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger.LogError(msg); - throw new Exception(msg); + throw new FfmpegException(msg); } } } diff --git a/MediaBrowser.MediaEncoding/FfmpegException.cs b/MediaBrowser.MediaEncoding/FfmpegException.cs new file mode 100644 index 000000000..1697fd33a --- /dev/null +++ b/MediaBrowser.MediaEncoding/FfmpegException.cs @@ -0,0 +1,39 @@ +using System; + +namespace MediaBrowser.MediaEncoding +{ + /// + /// Represents errors that occur during interaction with FFmpeg. + /// + public class FfmpegException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public FfmpegException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public FfmpegException(string message) : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message and a + /// reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if + /// no inner exception is specified. + /// + public FfmpegException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 3d6b4f98a..de00920ba 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -10,6 +10,9 @@ false true true + true + AllEnabledByDefault + ../jellyfin.ruleset @@ -30,13 +33,8 @@ - - ../jellyfin.ruleset - - - diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index b9cb49cf2..75067315f 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -640,7 +640,7 @@ namespace MediaBrowser.MediaEncoding.Probing } // Filter out junk - if (!string.IsNullOrWhiteSpace(streamInfo.CodecTagString) && streamInfo.CodecTagString.IndexOf("[0]", StringComparison.OrdinalIgnoreCase) == -1) + if (!string.IsNullOrWhiteSpace(streamInfo.CodecTagString) && !streamInfo.CodecTagString.Contains("[0]", StringComparison.OrdinalIgnoreCase)) { stream.CodecTag = streamInfo.CodecTagString; } @@ -1500,11 +1500,23 @@ namespace MediaBrowser.MediaEncoding.Probing } else { - throw new Exception(); // Switch to default parsing + // Switch to default parsing + if (subtitle.Contains('.', StringComparison.Ordinal)) + { + // skip the comment, keep the subtitle + description = string.Join('.', subtitle.Split('.'), 1, subtitle.Split('.').Length - 1).Trim(); // skip the first + } + else + { + description = subtitle.Trim(); // Clean up whitespaces and save it + } } } - catch // Default parsing + catch (Exception ex) { + _logger.LogError(ex, "Error while parsing subtitle field"); + + // Default parsing if (subtitle.Contains('.', StringComparison.Ordinal)) { // skip the comment, keep the subtitle diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index fbb1563bb..39bec8da1 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -25,7 +25,7 @@ using UtfUnknown; namespace MediaBrowser.MediaEncoding.Subtitles { - public class SubtitleEncoder : ISubtitleEncoder + public sealed class SubtitleEncoder : ISubtitleEncoder { private readonly ILogger _logger; private readonly IApplicationPaths _appPaths; @@ -484,7 +484,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { _logger.LogError("ffmpeg subtitle conversion failed for {Path}", inputPath); - throw new Exception( + throw new FfmpegException( string.Format(CultureInfo.InvariantCulture, "ffmpeg subtitle conversion failed for {0}", inputPath)); } @@ -637,7 +637,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles _logger.LogError(msg); - throw new Exception(msg); + throw new FfmpegException(msg); } else { diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index b6d916913..30403219f 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -19,7 +19,9 @@ true true enable - latest + true + + ../jellyfin.ruleset true true true @@ -44,7 +46,6 @@ - @@ -53,8 +54,4 @@ - - ../jellyfin.ruleset - - diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 071a149db..5e7b8043f 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -30,20 +30,18 @@ false true true + true + AllEnabledByDefault + ../jellyfin.ruleset - - - ../jellyfin.ruleset - - diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index 40f06c731..95327d3ae 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -20,18 +20,16 @@ true true enable + true + AllEnabledByDefault + ../jellyfin.ruleset - - - ../jellyfin.ruleset - - diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 6f164caf3..c4bbaf301 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -1168,11 +1168,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// IEnumerable{System.String}. private IEnumerable SplitNames(string value) { - value = value ?? string.Empty; - // Only split by comma if there is no pipe in the string // We have to be careful to not split names like Matthew, Jr. - var separator = value.IndexOf('|', StringComparison.Ordinal) == -1 && value.IndexOf(';', StringComparison.Ordinal) == -1 + var separator = !value.Contains('|', StringComparison.Ordinal) && !value.Contains(';', StringComparison.Ordinal) ? new[] { ',' } : new[] { '|', ';' }; diff --git a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs index af722748b..64cfc098f 100644 --- a/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs +++ b/MediaBrowser.XbmcMetadata/Providers/BaseVideoNfoProvider.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.XbmcMetadata.Providers private readonly IUserManager _userManager; private readonly IUserDataManager _userDataManager; - public BaseVideoNfoProvider( + protected BaseVideoNfoProvider( ILogger> logger, IFileSystem fileSystem, IConfigurationManager config, diff --git a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs index c22f77dcd..2385e7048 100644 --- a/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/AlbumNfoSaver.cs @@ -96,18 +96,16 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - protected override List GetTagsUsed(BaseItem item) + protected override IEnumerable GetTagsUsed(BaseItem item) { - var list = base.GetTagsUsed(item); - list.AddRange( - new string[] - { - "track", - "artist", - "albumartist" - }); + foreach (var tag in base.GetTagsUsed(item)) + { + yield return tag; + } - return list; + yield return "track"; + yield return "artist"; + yield return "albumartist"; } } } diff --git a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs index 6365cdecb..71b58cddb 100644 --- a/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs @@ -88,16 +88,15 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - protected override List GetTagsUsed(BaseItem item) + protected override IEnumerable GetTagsUsed(BaseItem item) { - var list = base.GetTagsUsed(item); - list.AddRange(new string[] + foreach (var tag in base.GetTagsUsed(item)) { - "album", - "disbanded" - }); + yield return tag; + } - return list; + yield return "album"; + yield return "disbanded"; } } } diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index 0edab3787..3be35e2d9 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -166,19 +166,16 @@ namespace MediaBrowser.XbmcMetadata.Savers /// public abstract bool IsEnabledFor(BaseItem item, ItemUpdateType updateType); - protected virtual List GetTagsUsed(BaseItem item) + protected virtual IEnumerable GetTagsUsed(BaseItem item) { - var list = new List(); foreach (var providerKey in item.ProviderIds.Keys) { var providerIdTagName = GetTagForProviderKey(providerKey); if (!_commonTags.Contains(providerIdTagName)) { - list.Add(providerIdTagName); + yield return providerIdTagName; } } - - return list; } /// @@ -261,7 +258,7 @@ namespace MediaBrowser.XbmcMetadata.Savers AddMediaInfo(hasMediaSources, writer); } - var tagsUsed = GetTagsUsed(item); + var tagsUsed = GetTagsUsed(item).ToList(); try { @@ -351,10 +348,7 @@ namespace MediaBrowser.XbmcMetadata.Savers } var scanType = stream.IsInterlaced ? "interlaced" : "progressive"; - if (!string.IsNullOrEmpty(scanType)) - { - writer.WriteElementString("scantype", scanType); - } + writer.WriteElementString("scantype", scanType); if (stream.Channels.HasValue) { @@ -968,7 +962,7 @@ namespace MediaBrowser.XbmcMetadata.Savers => string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase); - private void AddCustomTags(string path, List xmlTagsUsed, XmlWriter writer, ILogger logger) + private void AddCustomTags(string path, IReadOnlyCollection xmlTagsUsed, XmlWriter writer, ILogger logger) { var settings = new XmlReaderSettings() { diff --git a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs index 5d3d17893..62f80e81b 100644 --- a/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/EpisodeNfoSaver.cs @@ -111,24 +111,23 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - protected override List GetTagsUsed(BaseItem item) + protected override IEnumerable GetTagsUsed(BaseItem item) { - var list = base.GetTagsUsed(item); - list.AddRange(new string[] + foreach (var tag in base.GetTagsUsed(item)) { - "aired", - "season", - "episode", - "episodenumberend", - "airsafter_season", - "airsbefore_episode", - "airsbefore_season", - "displayseason", - "displayepisode", - "showtitle" - }); - - return list; + yield return tag; + } + + yield return "aired"; + yield return "season"; + yield return "episode"; + yield return "episodenumberend"; + yield return "airsafter_season"; + yield return "airsbefore_episode"; + yield return "airsbefore_season"; + yield return "displayseason"; + yield return "displayepisode"; + yield return "showtitle"; } } } diff --git a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs index 841121735..412e8031b 100644 --- a/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/MovieNfoSaver.cs @@ -123,18 +123,17 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - protected override List GetTagsUsed(BaseItem item) + protected override IEnumerable GetTagsUsed(BaseItem item) { - var list = base.GetTagsUsed(item); - list.AddRange(new string[] + foreach (var tag in base.GetTagsUsed(item)) { - "album", - "artist", - "set", - "id" - }); + yield return tag; + } - return list; + yield return "album"; + yield return "artist"; + yield return "set"; + yield return "id"; } } } diff --git a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs index 925a230bd..b9d73ba82 100644 --- a/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/SeasonNfoSaver.cs @@ -72,15 +72,14 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - protected override List GetTagsUsed(BaseItem item) + protected override IEnumerable GetTagsUsed(BaseItem item) { - var list = base.GetTagsUsed(item); - list.AddRange(new string[] + foreach (var tag in base.GetTagsUsed(item)) { - "seasonnumber" - }); + yield return tag; + } - return list; + yield return "seasonnumber"; } } } diff --git a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs index 42285db76..083f22e5d 100644 --- a/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/SeriesNfoSaver.cs @@ -90,20 +90,19 @@ namespace MediaBrowser.XbmcMetadata.Savers } /// - protected override List GetTagsUsed(BaseItem item) + protected override IEnumerable GetTagsUsed(BaseItem item) { - var list = base.GetTagsUsed(item); - list.AddRange(new string[] + foreach (var tag in base.GetTagsUsed(item)) { - "id", - "episodeguide", - "season", - "episode", - "status", - "displayorder" - }); + yield return tag; + } - return list; + yield return "id"; + yield return "episodeguide"; + yield return "season"; + yield return "episode"; + yield return "status"; + yield return "displayorder"; } } } diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 81337390c..b012d2b00 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -38,7 +38,7 @@ - + @@ -53,6 +53,8 @@ + + @@ -61,7 +63,11 @@ + + + + diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 873ff0ab4..0d8176bb2 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -10,6 +10,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -27,7 +30,6 @@ - @@ -37,10 +39,6 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index 278f34109..78e3061f7 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -10,6 +10,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -21,7 +24,6 @@ - @@ -32,8 +34,4 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj index b02a68a3d..df1eb8617 100644 --- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -10,6 +10,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -21,7 +24,6 @@ - @@ -31,8 +33,4 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj index 850db1c75..d173d5c93 100644 --- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj +++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj @@ -5,6 +5,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -16,7 +19,6 @@ - @@ -26,8 +28,4 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index e729dbb09..84306e0f7 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -10,6 +10,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -27,7 +30,6 @@ - @@ -37,8 +39,4 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj index b6d2c63bd..b458c06ff 100644 --- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj +++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj @@ -5,6 +5,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -16,7 +19,6 @@ - @@ -26,8 +28,4 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs index b3257ace3..ad63adadc 100644 --- a/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/AudioBook/AudioBookResolverTests.cs @@ -10,7 +10,7 @@ namespace Jellyfin.Naming.Tests.AudioBook { private readonly NamingOptions _namingOptions = new NamingOptions(); - public static IEnumerable GetResolveFileTestData() + public static IEnumerable Resolve_ValidFileNameTestData() { yield return new object[] { @@ -36,7 +36,7 @@ namespace Jellyfin.Naming.Tests.AudioBook } [Theory] - [MemberData(nameof(GetResolveFileTestData))] + [MemberData(nameof(Resolve_ValidFileNameTestData))] public void Resolve_ValidFileName_Success(AudioBookFileInfo expectedResult) { var result = new AudioBookResolver(_namingOptions).Resolve(expectedResult.Path); diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index 99185c975..0f8a0333a 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -8,8 +8,11 @@ net5.0 false - enable true + enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -25,14 +28,9 @@ - - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs index ba5eaf1af..9bbbe2970 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoResolverTests.cs @@ -11,7 +11,7 @@ namespace Jellyfin.Naming.Tests.Video { private readonly VideoResolver _videoResolver = new VideoResolver(new NamingOptions()); - public static IEnumerable GetResolveFileTestData() + public static IEnumerable ResolveFile_ValidFileNameTestData() { yield return new object[] { @@ -156,7 +156,7 @@ namespace Jellyfin.Naming.Tests.Video } [Theory] - [MemberData(nameof(GetResolveFileTestData))] + [MemberData(nameof(ResolveFile_ValidFileNameTestData))] public void ResolveFile_ValidFileName_Success(VideoFileInfo expectedResult) { var result = _videoResolver.ResolveFile(expectedResult.Path); diff --git a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj new file mode 100644 index 000000000..61eead0e9 --- /dev/null +++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj @@ -0,0 +1,42 @@ + + + + + {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} + + + + net5.0 + false + true + enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset + + + + + + + + + + + + + + + + + + + + + + + + DEBUG + + + diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs new file mode 100644 index 000000000..c3469035e --- /dev/null +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -0,0 +1,518 @@ +using System; +using System.Collections.ObjectModel; +using System.Net; +using Jellyfin.Networking.Configuration; +using Jellyfin.Networking.Manager; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Net; +using Microsoft.Extensions.Logging.Abstractions; +using Moq; +using Xunit; + +namespace Jellyfin.Networking.Tests +{ + public class NetworkParseTests + { + private static IConfigurationManager GetMockConfig(NetworkConfiguration conf) + { + var configManager = new Mock + { + CallBase = true + }; + configManager.Setup(x => x.GetConfiguration(It.IsAny())).Returns(conf); + return (IConfigurationManager)configManager.Object; + } + + /// + /// Checks the ability to ignore virtual interfaces. + /// + /// Mock network setup, in the format (IP address, interface index, interface name) | .... + /// LAN addresses. + /// Bind addresses that are excluded. + [Theory] + // All valid + [InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.0/24", "[192.168.1.208/24,200.200.200.200/24]")] + // eth16 only + [InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.208/24]")] + // All interfaces excluded. + [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[]")] + // vEthernet1 and vEthernet212 should be excluded. + [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24]")] + public void IgnoreVirtualInterfaces(string interfaces, string lan, string value) + { + var conf = new NetworkConfiguration() + { + EnableIPV6 = true, + EnableIPV4 = true, + LocalNetworkSubnets = lan?.Split(';') ?? throw new ArgumentNullException(nameof(lan)) + }; + + NetworkManager.MockNetworkSettings = interfaces; + using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + NetworkManager.MockNetworkSettings = string.Empty; + + Assert.Equal(nm.GetInternalBindAddresses().AsString(), value); + } + + /// + /// Check that the value given is in the network provided. + /// + /// Network address. + /// Value to check. + [Theory] + [InlineData("192.168.10.0/24, !192.168.10.60/32", "192.168.10.60")] + public void IsInNetwork(string network, string value) + { + if (network == null) + { + throw new ArgumentNullException(nameof(network)); + } + + var conf = new NetworkConfiguration() + { + EnableIPV6 = true, + EnableIPV4 = true, + LocalNetworkSubnets = network.Split(',') + }; + + using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + + Assert.False(nm.IsInLocalNetwork(value)); + } + + /// + /// Checks IP address formats. + /// + /// + [Theory] + [InlineData("127.0.0.1")] + [InlineData("127.0.0.1:123")] + [InlineData("localhost")] + [InlineData("localhost:1345")] + [InlineData("www.google.co.uk")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")] + [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]:124")] + [InlineData("fe80::7add:12ff:febb:c67b%16")] + [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")] + [InlineData("fe80::7add:12ff:febb:c67b%16:123")] + [InlineData("[fe80::7add:12ff:febb:c67b%16]")] + [InlineData("192.168.1.2/255.255.255.0")] + [InlineData("192.168.1.2/24")] + public void ValidHostStrings(string address) + { + Assert.True(IPHost.TryParse(address, out _)); + } + + /// + /// Checks IP address formats. + /// + /// + [Theory] + [InlineData("127.0.0.1")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")] + [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]")] + [InlineData("fe80::7add:12ff:febb:c67b%16")] + [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")] + [InlineData("fe80::7add:12ff:febb:c67b%16:123")] + [InlineData("[fe80::7add:12ff:febb:c67b%16]")] + [InlineData("192.168.1.2/255.255.255.0")] + [InlineData("192.168.1.2/24")] + public void ValidIPStrings(string address) + { + Assert.True(IPNetAddress.TryParse(address, out _)); + } + + /// + /// All should be invalid address strings. + /// + /// Invalid address strings. + [Theory] + [InlineData("256.128.0.0.0.1")] + [InlineData("127.0.0.1#")] + [InlineData("localhost!")] + [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")] + [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")] + public void InvalidAddressString(string address) + { + Assert.False(IPNetAddress.TryParse(address, out _)); + Assert.False(IPHost.TryParse(address, out _)); + } + + /// + /// Test collection parsing. + /// + /// Collection to parse. + /// Included addresses from the collection. + /// Included IP4 addresses from the collection. + /// Excluded addresses from the collection. + /// Excluded IP4 addresses from the collection. + /// Network addresses of the collection. + [Theory] + [InlineData( + "127.0.0.1#", + "[]", + "[]", + "[]", + "[]", + "[]")] + [InlineData( + "!127.0.0.1", + "[]", + "[]", + "[127.0.0.1/32]", + "[127.0.0.1/32]", + "[]")] + [InlineData( + "", + "[]", + "[]", + "[]", + "[]", + "[]")] + [InlineData( + "192.158.1.2/16, localhost, fd23:184f:2029:0:3139:7386:67d7:d517, !10.10.10.10", + "[192.158.1.2/16,[127.0.0.1/32,::1/128],fd23:184f:2029:0:3139:7386:67d7:d517/128]", + "[192.158.1.2/16,127.0.0.1/32]", + "[10.10.10.10/32]", + "[10.10.10.10/32]", + "[192.158.0.0/16,127.0.0.1/32,::1/128,fd23:184f:2029:0:3139:7386:67d7:d517/128]")] + [InlineData( + "192.158.1.2/255.255.0.0,192.169.1.2/8", + "[192.158.1.2/16,192.169.1.2/8]", + "[192.158.1.2/16,192.169.1.2/8]", + "[]", + "[]", + "[192.158.0.0/16,192.0.0.0/8]")] + public void TestCollections(string settings, string result1, string result2, string result3, string result4, string result5) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + var conf = new NetworkConfiguration() + { + EnableIPV6 = true, + EnableIPV4 = true, + }; + + using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + + // Test included. + Collection nc = nm.CreateIPCollection(settings.Split(","), false); + Assert.Equal(nc.AsString(), result1); + + // Test excluded. + nc = nm.CreateIPCollection(settings.Split(","), true); + Assert.Equal(nc.AsString(), result3); + + conf.EnableIPV6 = false; + nm.UpdateSettings(conf); + + // Test IP4 included. + nc = nm.CreateIPCollection(settings.Split(","), false); + Assert.Equal(nc.AsString(), result2); + + // Test IP4 excluded. + nc = nm.CreateIPCollection(settings.Split(","), true); + Assert.Equal(nc.AsString(), result4); + + conf.EnableIPV6 = true; + nm.UpdateSettings(conf); + + // Test network addresses of collection. + nc = nm.CreateIPCollection(settings.Split(","), false); + nc = nc.AsNetworks(); + Assert.Equal(nc.AsString(), result5); + } + + /// + /// Union two collections. + /// + /// Source. + /// Destination. + /// Result. + [Theory] + [InlineData("127.0.0.1", "fd23:184f:2029:0:3139:7386:67d7:d517/64,fd23:184f:2029:0:c0f0:8a8a:7605:fffa/128,fe80::3139:7386:67d7:d517%16/64,192.168.1.208/24,::1/128,127.0.0.1/8", "[127.0.0.1/32]")] + [InlineData("127.0.0.1", "127.0.0.1/8", "[127.0.0.1/32]")] + public void UnionCheck(string settings, string compare, string result) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + + if (compare == null) + { + throw new ArgumentNullException(nameof(compare)); + } + + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + var conf = new NetworkConfiguration() + { + EnableIPV6 = true, + EnableIPV4 = true, + }; + + using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + + Collection nc1 = nm.CreateIPCollection(settings.Split(","), false); + Collection nc2 = nm.CreateIPCollection(compare.Split(","), false); + + Assert.Equal(nc1.Union(nc2).AsString(), result); + } + + [Theory] + [InlineData("192.168.5.85/24", "192.168.5.1")] + [InlineData("192.168.5.85/24", "192.168.5.254")] + [InlineData("10.128.240.50/30", "10.128.240.48")] + [InlineData("10.128.240.50/30", "10.128.240.49")] + [InlineData("10.128.240.50/30", "10.128.240.50")] + [InlineData("10.128.240.50/30", "10.128.240.51")] + [InlineData("127.0.0.1/8", "127.0.0.1")] + public void IpV4SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress) + { + var ipAddressObj = IPNetAddress.Parse(netMask); + Assert.True(ipAddressObj.Contains(IPAddress.Parse(ipAddress))); + } + + [Theory] + [InlineData("192.168.5.85/24", "192.168.4.254")] + [InlineData("192.168.5.85/24", "191.168.5.254")] + [InlineData("10.128.240.50/30", "10.128.240.47")] + [InlineData("10.128.240.50/30", "10.128.240.52")] + [InlineData("10.128.240.50/30", "10.128.239.50")] + [InlineData("10.128.240.50/30", "10.127.240.51")] + public void IpV4SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress) + { + var ipAddressObj = IPNetAddress.Parse(netMask); + Assert.False(ipAddressObj.Contains(IPAddress.Parse(ipAddress))); + } + + [Theory] + [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")] + [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFFF")] + [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:0001:0000:0000:0000")] + [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFF0")] + [InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")] + public void IpV6SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress) + { + var ipAddressObj = IPNetAddress.Parse(netMask); + Assert.True(ipAddressObj.Contains(IPAddress.Parse(ipAddress))); + } + + [Theory] + [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0011:FFFF:FFFF:FFFF:FFFF")] + [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0013:0000:0000:0000:0000")] + [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0013:0001:0000:0000:0000")] + [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0011:FFFF:FFFF:FFFF:FFF0")] + [InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0001")] + public void IpV6SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress) + { + var ipAddressObj = IPNetAddress.Parse(netMask); + Assert.False(ipAddressObj.Contains(IPAddress.Parse(ipAddress))); + } + + [Theory] + [InlineData("10.0.0.0/255.0.0.0", "10.10.10.1/32")] + [InlineData("10.0.0.0/8", "10.10.10.1/32")] + [InlineData("10.0.0.0/255.0.0.0", "10.10.10.1")] + + [InlineData("10.10.0.0/255.255.0.0", "10.10.10.1/32")] + [InlineData("10.10.0.0/16", "10.10.10.1/32")] + [InlineData("10.10.0.0/255.255.0.0", "10.10.10.1")] + + [InlineData("10.10.10.0/255.255.255.0", "10.10.10.1/32")] + [InlineData("10.10.10.0/24", "10.10.10.1/32")] + [InlineData("10.10.10.0/255.255.255.0", "10.10.10.1")] + + public void TestSubnetContains(string network, string ip) + { + Assert.True(IPNetAddress.TryParse(network, out var networkObj)); + Assert.True(IPNetAddress.TryParse(ip, out var ipObj)); + Assert.True(networkObj.Contains(ipObj)); + } + + [Theory] + [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "172.168.1.2/24", "172.168.1.2/24")] + [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "172.168.1.2/24, 10.10.10.1", "172.168.1.2/24,10.10.10.1/24")] + [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "192.168.1.2/255.255.255.0, 10.10.10.1", "192.168.1.2/24,10.10.10.1/24")] + [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "192.168.1.2/24, 100.10.10.1", "192.168.1.2/24")] + [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "194.168.1.2/24, 100.10.10.1", "")] + + public void TestCollectionEquality(string source, string dest, string result) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (dest == null) + { + throw new ArgumentNullException(nameof(dest)); + } + + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + var conf = new NetworkConfiguration() + { + EnableIPV6 = true, + EnableIPV4 = true + }; + + using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + + // Test included, IP6. + Collection ncSource = nm.CreateIPCollection(source.Split(",")); + Collection ncDest = nm.CreateIPCollection(dest.Split(",")); + Collection ncResult = ncSource.Union(ncDest); + Collection resultCollection = nm.CreateIPCollection(result.Split(",")); + Assert.True(ncResult.Compare(resultCollection)); + } + + [Theory] + [InlineData("10.1.1.1/32", "10.1.1.1")] + [InlineData("192.168.1.254/32", "192.168.1.254/255.255.255.255")] + + public void TestEquals(string source, string dest) + { + Assert.True(IPNetAddress.Parse(source).Equals(IPNetAddress.Parse(dest))); + Assert.True(IPNetAddress.Parse(dest).Equals(IPNetAddress.Parse(source))); + } + + [Theory] + + // Testing bind interfaces. + // On my system eth16 is internal, eth11 external (Windows defines the indexes). + // + // This test is to replicate how DNLA requests work throughout the system. + + // User on internal network, we're bound internal and external - so result is internal. + [InlineData("192.168.1.1", "eth16,eth11", false, "eth16")] + // User on external network, we're bound internal and external - so result is external. + [InlineData("8.8.8.8", "eth16,eth11", false, "eth11")] + // User on internal network, we're bound internal only - so result is internal. + [InlineData("10.10.10.10", "eth16", false, "eth16")] + // User on internal network, no binding specified - so result is the 1st internal. + [InlineData("192.168.1.1", "", false, "eth16")] + // User on external network, internal binding only - so result is the 1st internal. + [InlineData("jellyfin.org", "eth16", false, "eth16")] + // User on external network, no binding - so result is the 1st external. + [InlineData("jellyfin.org", "", false, "eth11")] + // User assumed to be internal, no binding - so result is the 1st internal. + [InlineData("", "", false, "eth16")] + public void TestBindInterfaces(string source, string bindAddresses, bool ipv6enabled, string result) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (bindAddresses == null) + { + throw new ArgumentNullException(nameof(bindAddresses)); + } + + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } + + var conf = new NetworkConfiguration() + { + LocalNetworkAddresses = bindAddresses.Split(','), + EnableIPV6 = ipv6enabled, + EnableIPV4 = true + }; + + NetworkManager.MockNetworkSettings = "192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11"; + using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + NetworkManager.MockNetworkSettings = string.Empty; + + _ = nm.TryParseInterface(result, out Collection? resultObj); + + if (resultObj != null) + { + result = ((IPNetAddress)resultObj[0]).ToString(true); + var intf = nm.GetBindInterface(source, out int? _); + + Assert.Equal(intf, result); + } + } + + [Theory] + + // Testing bind interfaces. These are set for my system so won't work elsewhere. + // On my system eth16 is internal, eth11 external (Windows defines the indexes). + // + // This test is to replicate how subnet bound ServerPublisherUri work throughout the system. + + // User on internal network, we're bound internal and external - so result is internal override. + [InlineData("192.168.1.1", "192.168.1.0/24", "eth16,eth11", false, "192.168.1.0/24=internal.jellyfin", "internal.jellyfin")] + + // User on external network, we're bound internal and external - so result is override. + [InlineData("8.8.8.8", "192.168.1.0/24", "eth16,eth11", false, "0.0.0.0=http://helloworld.com", "http://helloworld.com")] + + // User on internal network, we're bound internal only, but the address isn't in the LAN - so return the override. + [InlineData("10.10.10.10", "192.168.1.0/24", "eth16", false, "0.0.0.0=http://internalButNotDefinedAsLan.com", "http://internalButNotDefinedAsLan.com")] + + // User on internal network, no binding specified - so result is the 1st internal. + [InlineData("192.168.1.1", "192.168.1.0/24", "", false, "0.0.0.0=http://helloworld.com", "eth16")] + + // User on external network, internal binding only - so assumption is a proxy forward, return external override. + [InlineData("jellyfin.org", "192.168.1.0/24", "eth16", false, "0.0.0.0=http://helloworld.com", "http://helloworld.com")] + + // User on external network, no binding - so result is the 1st external which is overriden. + [InlineData("jellyfin.org", "192.168.1.0/24", "", false, "0.0.0.0 = http://helloworld.com", "http://helloworld.com")] + + // User assumed to be internal, no binding - so result is the 1st internal. + [InlineData("", "192.168.1.0/24", "", false, "0.0.0.0=http://helloworld.com", "eth16")] + + // User is internal, no binding - so result is the 1st internal, which is then overridden. + [InlineData("192.168.1.1", "192.168.1.0/24", "", false, "eth16=http://helloworld.com", "http://helloworld.com")] + public void TestBindInterfaceOverrides(string source, string lan, string bindAddresses, bool ipv6enabled, string publishedServers, string result) + { + if (lan == null) + { + throw new ArgumentNullException(nameof(lan)); + } + + if (bindAddresses == null) + { + throw new ArgumentNullException(nameof(bindAddresses)); + } + + var conf = new NetworkConfiguration() + { + LocalNetworkSubnets = lan.Split(','), + LocalNetworkAddresses = bindAddresses.Split(','), + EnableIPV6 = ipv6enabled, + EnableIPV4 = true, + PublishedServerUriBySubnet = new string[] { publishedServers } + }; + + NetworkManager.MockNetworkSettings = "192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11"; + using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); + NetworkManager.MockNetworkSettings = string.Empty; + + if (nm.TryParseInterface(result, out Collection? resultObj) && resultObj != null) + { + // Parse out IPAddresses so we can do a string comparison. (Ignore subnet masks). + result = ((IPNetAddress)resultObj[0]).ToString(true); + } + + var intf = nm.GetBindInterface(source, out int? _); + + Assert.Equal(intf, result); + } + } +} diff --git a/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj deleted file mode 100644 index fd77397ba..000000000 --- a/tests/Jellyfin.Networking.Tests/NetworkTesting/Jellyfin.Networking.Tests.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - - - {42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} - - - - net5.0 - false - enable - true - - - - - - - - - - - - - - - - - - - - - - ../../jellyfin-tests.ruleset - - - DEBUG - - diff --git a/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs deleted file mode 100644 index 9f928ded1..000000000 --- a/tests/Jellyfin.Networking.Tests/NetworkTesting/NetworkParseTests.cs +++ /dev/null @@ -1,519 +0,0 @@ -using System; -using System.Net; -using Jellyfin.Networking.Configuration; -using Jellyfin.Networking.Manager; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Net; -using Moq; -using Microsoft.Extensions.Logging.Abstractions; -using Xunit; -using System.Collections.ObjectModel; - -namespace Jellyfin.Networking.Tests -{ - public class NetworkParseTests - { - private static IConfigurationManager GetMockConfig(NetworkConfiguration conf) - { - var configManager = new Mock - { - CallBase = true - }; - configManager.Setup(x => x.GetConfiguration(It.IsAny())).Returns(conf); - return (IConfigurationManager)configManager.Object; - } - - /// - /// Checks the ability to ignore virtual interfaces. - /// - /// Mock network setup, in the format (IP address, interface index, interface name) | .... - /// LAN addresses. - /// Bind addresses that are excluded. - [Theory] - // All valid - [InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.0/24", "[192.168.1.208/24,200.200.200.200/24]")] - // eth16 only - [InlineData("192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[192.168.1.208/24]")] - // All interfaces excluded. - [InlineData("192.168.1.208/24,-16,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24", "[]")] - // vEthernet1 and vEthernet212 should be excluded. - [InlineData("192.168.1.200/24,-20,vEthernet1|192.168.2.208/24,-16,vEthernet212|200.200.200.200/24,11,eth11", "192.168.1.0/24;200.200.200.200/24", "[200.200.200.200/24]")] - public void IgnoreVirtualInterfaces(string interfaces, string lan, string value) - { - var conf = new NetworkConfiguration() - { - EnableIPV6 = true, - EnableIPV4 = true, - LocalNetworkSubnets = lan?.Split(';') ?? throw new ArgumentNullException(nameof(lan)) - }; - - NetworkManager.MockNetworkSettings = interfaces; - using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); - NetworkManager.MockNetworkSettings = string.Empty; - - Assert.Equal(nm.GetInternalBindAddresses().AsString(), value); - } - - /// - /// Check that the value given is in the network provided. - /// - /// Network address. - /// Value to check. - [Theory] - [InlineData("192.168.10.0/24, !192.168.10.60/32", "192.168.10.60")] - public void IsInNetwork(string network, string value) - { - if (network == null) - { - throw new ArgumentNullException(nameof(network)); - } - - var conf = new NetworkConfiguration() - { - EnableIPV6 = true, - EnableIPV4 = true, - LocalNetworkSubnets = network.Split(',') - }; - - using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); - - Assert.False(nm.IsInLocalNetwork(value)); - } - - /// - /// Checks IP address formats. - /// - /// - [Theory] - [InlineData("127.0.0.1")] - [InlineData("127.0.0.1:123")] - [InlineData("localhost")] - [InlineData("localhost:1345")] - [InlineData("www.google.co.uk")] - [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")] - [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")] - [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]:124")] - [InlineData("fe80::7add:12ff:febb:c67b%16")] - [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")] - [InlineData("fe80::7add:12ff:febb:c67b%16:123")] - [InlineData("[fe80::7add:12ff:febb:c67b%16]")] - [InlineData("192.168.1.2/255.255.255.0")] - [InlineData("192.168.1.2/24")] - public void ValidHostStrings(string address) - { - Assert.True(IPHost.TryParse(address, out _)); - } - - /// - /// Checks IP address formats. - /// - /// - [Theory] - [InlineData("127.0.0.1")] - [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517")] - [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517/56")] - [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517]")] - [InlineData("fe80::7add:12ff:febb:c67b%16")] - [InlineData("[fe80::7add:12ff:febb:c67b%16]:123")] - [InlineData("fe80::7add:12ff:febb:c67b%16:123")] - [InlineData("[fe80::7add:12ff:febb:c67b%16]")] - [InlineData("192.168.1.2/255.255.255.0")] - [InlineData("192.168.1.2/24")] - public void ValidIPStrings(string address) - { - Assert.True(IPNetAddress.TryParse(address, out _)); - } - - - /// - /// All should be invalid address strings. - /// - /// Invalid address strings. - [Theory] - [InlineData("256.128.0.0.0.1")] - [InlineData("127.0.0.1#")] - [InlineData("localhost!")] - [InlineData("fd23:184f:2029:0:3139:7386:67d7:d517:1231")] - [InlineData("[fd23:184f:2029:0:3139:7386:67d7:d517:1231]")] - public void InvalidAddressString(string address) - { - Assert.False(IPNetAddress.TryParse(address, out _)); - Assert.False(IPHost.TryParse(address, out _)); - } - - - /// - /// Test collection parsing. - /// - /// Collection to parse. - /// Included addresses from the collection. - /// Included IP4 addresses from the collection. - /// Excluded addresses from the collection. - /// Excluded IP4 addresses from the collection. - /// Network addresses of the collection. - [Theory] - [InlineData("127.0.0.1#", - "[]", - "[]", - "[]", - "[]", - "[]")] - [InlineData("!127.0.0.1", - "[]", - "[]", - "[127.0.0.1/32]", - "[127.0.0.1/32]", - "[]")] - [InlineData("", - "[]", - "[]", - "[]", - "[]", - "[]")] - [InlineData( - "192.158.1.2/16, localhost, fd23:184f:2029:0:3139:7386:67d7:d517, !10.10.10.10", - "[192.158.1.2/16,[127.0.0.1/32,::1/128],fd23:184f:2029:0:3139:7386:67d7:d517/128]", - "[192.158.1.2/16,127.0.0.1/32]", - "[10.10.10.10/32]", - "[10.10.10.10/32]", - "[192.158.0.0/16,127.0.0.1/32,::1/128,fd23:184f:2029:0:3139:7386:67d7:d517/128]")] - [InlineData("192.158.1.2/255.255.0.0,192.169.1.2/8", - "[192.158.1.2/16,192.169.1.2/8]", - "[192.158.1.2/16,192.169.1.2/8]", - "[]", - "[]", - "[192.158.0.0/16,192.0.0.0/8]")] - public void TestCollections(string settings, string result1, string result2, string result3, string result4, string result5) - { - if (settings == null) - { - throw new ArgumentNullException(nameof(settings)); - } - - var conf = new NetworkConfiguration() - { - EnableIPV6 = true, - EnableIPV4 = true, - }; - - using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); - - // Test included. - Collection nc = nm.CreateIPCollection(settings.Split(","), false); - Assert.Equal(nc.AsString(), result1); - - // Test excluded. - nc = nm.CreateIPCollection(settings.Split(","), true); - Assert.Equal(nc.AsString(), result3); - - conf.EnableIPV6 = false; - nm.UpdateSettings(conf); - - // Test IP4 included. - nc = nm.CreateIPCollection(settings.Split(","), false); - Assert.Equal(nc.AsString(), result2); - - // Test IP4 excluded. - nc = nm.CreateIPCollection(settings.Split(","), true); - Assert.Equal(nc.AsString(), result4); - - conf.EnableIPV6 = true; - nm.UpdateSettings(conf); - - // Test network addresses of collection. - nc = nm.CreateIPCollection(settings.Split(","), false); - nc = nc.AsNetworks(); - Assert.Equal(nc.AsString(), result5); - } - - /// - /// Union two collections. - /// - /// Source. - /// Destination. - /// Result. - [Theory] - [InlineData("127.0.0.1", "fd23:184f:2029:0:3139:7386:67d7:d517/64,fd23:184f:2029:0:c0f0:8a8a:7605:fffa/128,fe80::3139:7386:67d7:d517%16/64,192.168.1.208/24,::1/128,127.0.0.1/8", "[127.0.0.1/32]")] - [InlineData("127.0.0.1", "127.0.0.1/8", "[127.0.0.1/32]")] - public void UnionCheck(string settings, string compare, string result) - { - if (settings == null) - { - throw new ArgumentNullException(nameof(settings)); - } - - if (compare == null) - { - throw new ArgumentNullException(nameof(compare)); - } - - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - - var conf = new NetworkConfiguration() - { - EnableIPV6 = true, - EnableIPV4 = true, - }; - - using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); - - Collection nc1 = nm.CreateIPCollection(settings.Split(","), false); - Collection nc2 = nm.CreateIPCollection(compare.Split(","), false); - - Assert.Equal(nc1.Union(nc2).AsString(), result); - } - - [Theory] - [InlineData("192.168.5.85/24", "192.168.5.1")] - [InlineData("192.168.5.85/24", "192.168.5.254")] - [InlineData("10.128.240.50/30", "10.128.240.48")] - [InlineData("10.128.240.50/30", "10.128.240.49")] - [InlineData("10.128.240.50/30", "10.128.240.50")] - [InlineData("10.128.240.50/30", "10.128.240.51")] - [InlineData("127.0.0.1/8", "127.0.0.1")] - public void IpV4SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress) - { - var ipAddressObj = IPNetAddress.Parse(netMask); - Assert.True(ipAddressObj.Contains(IPAddress.Parse(ipAddress))); - } - - [Theory] - [InlineData("192.168.5.85/24", "192.168.4.254")] - [InlineData("192.168.5.85/24", "191.168.5.254")] - [InlineData("10.128.240.50/30", "10.128.240.47")] - [InlineData("10.128.240.50/30", "10.128.240.52")] - [InlineData("10.128.240.50/30", "10.128.239.50")] - [InlineData("10.128.240.50/30", "10.127.240.51")] - public void IpV4SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress) - { - var ipAddressObj = IPNetAddress.Parse(netMask); - Assert.False(ipAddressObj.Contains(IPAddress.Parse(ipAddress))); - } - - [Theory] - [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")] - [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFFF")] - [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:0001:0000:0000:0000")] - [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFF0")] - [InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0000")] - public void IpV6SubnetMaskMatchesValidIpAddress(string netMask, string ipAddress) - { - var ipAddressObj = IPNetAddress.Parse(netMask); - Assert.True(ipAddressObj.Contains(IPAddress.Parse(ipAddress))); - } - - [Theory] - [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0011:FFFF:FFFF:FFFF:FFFF")] - [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0013:0000:0000:0000:0000")] - [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0013:0001:0000:0000:0000")] - [InlineData("2001:db8:abcd:0012::0/64", "2001:0DB8:ABCD:0011:FFFF:FFFF:FFFF:FFF0")] - [InlineData("2001:db8:abcd:0012::0/128", "2001:0DB8:ABCD:0012:0000:0000:0000:0001")] - public void IpV6SubnetMaskDoesNotMatchInvalidIpAddress(string netMask, string ipAddress) - { - var ipAddressObj = IPNetAddress.Parse(netMask); - Assert.False(ipAddressObj.Contains(IPAddress.Parse(ipAddress))); - } - - [Theory] - [InlineData("10.0.0.0/255.0.0.0", "10.10.10.1/32")] - [InlineData("10.0.0.0/8", "10.10.10.1/32")] - [InlineData("10.0.0.0/255.0.0.0", "10.10.10.1")] - - [InlineData("10.10.0.0/255.255.0.0", "10.10.10.1/32")] - [InlineData("10.10.0.0/16", "10.10.10.1/32")] - [InlineData("10.10.0.0/255.255.0.0", "10.10.10.1")] - - [InlineData("10.10.10.0/255.255.255.0", "10.10.10.1/32")] - [InlineData("10.10.10.0/24", "10.10.10.1/32")] - [InlineData("10.10.10.0/255.255.255.0", "10.10.10.1")] - - public void TestSubnetContains(string network, string ip) - { - Assert.True(IPNetAddress.TryParse(network, out var networkObj)); - Assert.True(IPNetAddress.TryParse(ip, out var ipObj)); - Assert.True(networkObj.Contains(ipObj)); - } - - [Theory] - [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "172.168.1.2/24", "172.168.1.2/24")] - [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "172.168.1.2/24, 10.10.10.1", "172.168.1.2/24,10.10.10.1/24")] - [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "192.168.1.2/255.255.255.0, 10.10.10.1", "192.168.1.2/24,10.10.10.1/24")] - [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "192.168.1.2/24, 100.10.10.1", "192.168.1.2/24")] - [InlineData("192.168.1.2/24,10.10.10.1/24,172.168.1.2/24", "194.168.1.2/24, 100.10.10.1", "")] - - public void TestCollectionEquality(string source, string dest, string result) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - if (dest == null) - { - throw new ArgumentNullException(nameof(dest)); - } - - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - var conf = new NetworkConfiguration() - { - EnableIPV6 = true, - EnableIPV4 = true - }; - - using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); - - // Test included, IP6. - Collection ncSource = nm.CreateIPCollection(source.Split(",")); - Collection ncDest = nm.CreateIPCollection(dest.Split(",")); - Collection ncResult = ncSource.Union(ncDest); - Collection resultCollection = nm.CreateIPCollection(result.Split(",")); - Assert.True(ncResult.Compare(resultCollection)); - } - - - [Theory] - [InlineData("10.1.1.1/32", "10.1.1.1")] - [InlineData("192.168.1.254/32", "192.168.1.254/255.255.255.255")] - - public void TestEquals(string source, string dest) - { - Assert.True(IPNetAddress.Parse(source).Equals(IPNetAddress.Parse(dest))); - Assert.True(IPNetAddress.Parse(dest).Equals(IPNetAddress.Parse(source))); - } - - [Theory] - - // Testing bind interfaces. - // On my system eth16 is internal, eth11 external (Windows defines the indexes). - // - // This test is to replicate how DNLA requests work throughout the system. - - // User on internal network, we're bound internal and external - so result is internal. - [InlineData("192.168.1.1", "eth16,eth11", false, "eth16")] - // User on external network, we're bound internal and external - so result is external. - [InlineData("8.8.8.8", "eth16,eth11", false, "eth11")] - // User on internal network, we're bound internal only - so result is internal. - [InlineData("10.10.10.10", "eth16", false, "eth16")] - // User on internal network, no binding specified - so result is the 1st internal. - [InlineData("192.168.1.1", "", false, "eth16")] - // User on external network, internal binding only - so result is the 1st internal. - [InlineData("jellyfin.org", "eth16", false, "eth16")] - // User on external network, no binding - so result is the 1st external. - [InlineData("jellyfin.org", "", false, "eth11")] - // User assumed to be internal, no binding - so result is the 1st internal. - [InlineData("", "", false, "eth16")] - public void TestBindInterfaces(string source, string bindAddresses, bool ipv6enabled, string result) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - if (bindAddresses == null) - { - throw new ArgumentNullException(nameof(bindAddresses)); - } - - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } - - var conf = new NetworkConfiguration() - { - LocalNetworkAddresses = bindAddresses.Split(','), - EnableIPV6 = ipv6enabled, - EnableIPV4 = true - }; - - NetworkManager.MockNetworkSettings = "192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11"; - using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); - NetworkManager.MockNetworkSettings = string.Empty; - - _ = nm.TryParseInterface(result, out Collection? resultObj); - - if (resultObj != null) - { - result = ((IPNetAddress)resultObj[0]).ToString(true); - var intf = nm.GetBindInterface(source, out int? _); - - Assert.Equal(intf, result); - } - } - - [Theory] - - // Testing bind interfaces. These are set for my system so won't work elsewhere. - // On my system eth16 is internal, eth11 external (Windows defines the indexes). - // - // This test is to replicate how subnet bound ServerPublisherUri work throughout the system. - - // User on internal network, we're bound internal and external - so result is internal override. - [InlineData("192.168.1.1", "192.168.1.0/24", "eth16,eth11", false, "192.168.1.0/24=internal.jellyfin", "internal.jellyfin")] - - // User on external network, we're bound internal and external - so result is override. - [InlineData("8.8.8.8", "192.168.1.0/24", "eth16,eth11", false, "0.0.0.0=http://helloworld.com", "http://helloworld.com")] - - // User on internal network, we're bound internal only, but the address isn't in the LAN - so return the override. - [InlineData("10.10.10.10", "192.168.1.0/24", "eth16", false, "0.0.0.0=http://internalButNotDefinedAsLan.com", "http://internalButNotDefinedAsLan.com")] - - // User on internal network, no binding specified - so result is the 1st internal. - [InlineData("192.168.1.1", "192.168.1.0/24", "", false, "0.0.0.0=http://helloworld.com", "eth16")] - - // User on external network, internal binding only - so assumption is a proxy forward, return external override. - [InlineData("jellyfin.org", "192.168.1.0/24", "eth16", false, "0.0.0.0=http://helloworld.com", "http://helloworld.com")] - - // User on external network, no binding - so result is the 1st external which is overriden. - [InlineData("jellyfin.org", "192.168.1.0/24", "", false, "0.0.0.0 = http://helloworld.com", "http://helloworld.com")] - - // User assumed to be internal, no binding - so result is the 1st internal. - [InlineData("", "192.168.1.0/24", "", false, "0.0.0.0=http://helloworld.com", "eth16")] - - // User is internal, no binding - so result is the 1st internal, which is then overridden. - [InlineData("192.168.1.1", "192.168.1.0/24", "", false, "eth16=http://helloworld.com", "http://helloworld.com")] - - public void TestBindInterfaceOverrides(string source, string lan, string bindAddresses, bool ipv6enabled, string publishedServers, string result) - { - if (lan == null) - { - throw new ArgumentNullException(nameof(lan)); - } - - if (bindAddresses == null) - { - throw new ArgumentNullException(nameof(bindAddresses)); - } - - var conf = new NetworkConfiguration() - { - LocalNetworkSubnets = lan.Split(','), - LocalNetworkAddresses = bindAddresses.Split(','), - EnableIPV6 = ipv6enabled, - EnableIPV4 = true, - PublishedServerUriBySubnet = new string[] { publishedServers } - }; - - NetworkManager.MockNetworkSettings = "192.168.1.208/24,-16,eth16|200.200.200.200/24,11,eth11"; - using var nm = new NetworkManager(GetMockConfig(conf), new NullLogger()); - NetworkManager.MockNetworkSettings = string.Empty; - - if (nm.TryParseInterface(result, out Collection? resultObj) && resultObj != null) - { - // Parse out IPAddresses so we can do a string comparison. (Ignore subnet masks). - result = ((IPNetAddress)resultObj[0]).ToString(true); - } - - var intf = nm.GetBindInterface(source, out int? _); - - Assert.Equal(intf, result); - } - } -} diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index 1ad8171be..8debb08c5 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -10,6 +10,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset Jellyfin.Server.Implementations.Tests @@ -31,7 +34,6 @@ - @@ -42,8 +44,4 @@ - - ../jellyfin-tests.ruleset - - diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj index d6aab3f85..2bb94c81f 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj +++ b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj @@ -5,6 +5,9 @@ false true enable + true + AllEnabledByDefault + ../jellyfin-tests.ruleset @@ -23,7 +26,6 @@ - @@ -34,8 +36,4 @@ - - ../jellyfin-tests.ruleset - - -- cgit v1.2.3 From 873ad72c1893e87d1f8a0a744ebfe53b87781521 Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Sun, 4 Apr 2021 15:08:08 +0200 Subject: Support MKV DATE_RELEASED tag for PremiereDate https://www.matroska.org/technical/tagging.html#temporal-information --- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs') diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 75067315f..a87104cd6 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -131,6 +131,7 @@ namespace MediaBrowser.MediaEncoding.Probing info.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ?? + FFProbeHelpers.GetDictionaryDateTime(tags, "date_released") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "date"); if (isAudio) -- cgit v1.2.3 From 24ac8a12233936955e31a409c1a131473041846e Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Sun, 4 Apr 2021 23:34:29 +0200 Subject: Improve metadata probing to better support music videos --- .../Probing/ProbeResultNormalizer.cs | 95 +++++++++++++--------- MediaBrowser.Model/MediaInfo/MediaInfo.cs | 2 + .../MediaInfo/FFProbeAudioInfo.cs | 5 ++ .../MediaInfo/FFProbeVideoInfo.cs | 11 +++ 4 files changed, 74 insertions(+), 39 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs') diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index a87104cd6..22624dfd7 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -121,19 +121,67 @@ namespace MediaBrowser.MediaEncoding.Probing { info.Name = title; } + else + { + title = FFProbeHelpers.GetDictionaryValue(tags, "title-eng"); + if (!string.IsNullOrWhiteSpace(title)) + { + info.Name = title; + } + } + + var titleSort = FFProbeHelpers.GetDictionaryValue(tags, "titlesort"); + if (!string.IsNullOrWhiteSpace(titleSort)) + { + info.ForcedSortName = titleSort; + } info.IndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "episode_sort"); info.ParentIndexNumber = FFProbeHelpers.GetDictionaryNumericValue(tags, "season_number"); info.ShowName = FFProbeHelpers.GetDictionaryValue(tags, "show_name"); info.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date"); - // Several different forms of retaildate - info.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ?? + // Several different forms of retail/premiere date + info.PremiereDate = + FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "date_released") ?? FFProbeHelpers.GetDictionaryDateTime(tags, "date"); + // Set common metadata for music (audio) and music videos (video) + info.Album = FFProbeHelpers.GetDictionaryValue(tags, "album"); + + var artists = FFProbeHelpers.GetDictionaryValue(tags, "artists"); + + if (!string.IsNullOrWhiteSpace(artists)) + { + info.Artists = SplitArtists(artists, new[] { '/', ';' }, false) + .DistinctNames() + .ToArray(); + } + else + { + var artist = FFProbeHelpers.GetDictionaryValue(tags, "artist"); + if (string.IsNullOrWhiteSpace(artist)) + { + info.Artists = Array.Empty(); + } + else + { + info.Artists = SplitArtists(artist, _nameDelimiters, true) + .DistinctNames() + .ToArray(); + } + } + + // If we don't have a ProductionYear try and get it from PremiereDate + if (!info.ProductionYear.HasValue && info.PremiereDate.HasValue) + { + info.ProductionYear = info.PremiereDate.Value.Year; + } + + // Set mediaType-specific metadata if (isAudio) { SetAudioRuntimeTicks(data, info); @@ -1076,13 +1124,13 @@ namespace MediaBrowser.MediaEncoding.Probing private void SetAudioInfoFromTags(MediaInfo audio, Dictionary tags) { - var peoples = new List(); + var people = new List(); var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer"); if (!string.IsNullOrWhiteSpace(composer)) { foreach (var person in Split(composer, false)) { - peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Composer }); + people.Add(new BaseItemPerson { Name = person, Type = PersonType.Composer }); } } @@ -1091,7 +1139,7 @@ namespace MediaBrowser.MediaEncoding.Probing { foreach (var person in Split(conductor, false)) { - peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Conductor }); + people.Add(new BaseItemPerson { Name = person, Type = PersonType.Conductor }); } } @@ -1100,46 +1148,21 @@ namespace MediaBrowser.MediaEncoding.Probing { foreach (var person in Split(lyricist, false)) { - peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Lyricist }); + people.Add(new BaseItemPerson { Name = person, Type = PersonType.Lyricist }); } } // Check for writer some music is tagged that way as alternative to composer/lyricist var writer = FFProbeHelpers.GetDictionaryValue(tags, "writer"); - if (!string.IsNullOrWhiteSpace(writer)) { foreach (var person in Split(writer, false)) { - peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Writer }); + people.Add(new BaseItemPerson { Name = person, Type = PersonType.Writer }); } } - audio.People = peoples.ToArray(); - audio.Album = FFProbeHelpers.GetDictionaryValue(tags, "album"); - - var artists = FFProbeHelpers.GetDictionaryValue(tags, "artists"); - - if (!string.IsNullOrWhiteSpace(artists)) - { - audio.Artists = SplitArtists(artists, new[] { '/', ';' }, false) - .DistinctNames() - .ToArray(); - } - else - { - var artist = FFProbeHelpers.GetDictionaryValue(tags, "artist"); - if (string.IsNullOrWhiteSpace(artist)) - { - audio.Artists = Array.Empty(); - } - else - { - audio.Artists = SplitArtists(artist, _nameDelimiters, true) - .DistinctNames() - .ToArray(); - } - } + audio.People = people.ToArray(); var albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "albumartist"); if (string.IsNullOrWhiteSpace(albumArtist)) @@ -1174,12 +1197,6 @@ namespace MediaBrowser.MediaEncoding.Probing // Disc number audio.ParentIndexNumber = GetDictionaryDiscValue(tags, "disc"); - // If we don't have a ProductionYear try and get it from PremiereDate - if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue) - { - audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year; - } - // There's several values in tags may or may not be present FetchStudios(audio, tags, "organization"); FetchStudios(audio, tags, "ensemble"); diff --git a/MediaBrowser.Model/MediaInfo/MediaInfo.cs b/MediaBrowser.Model/MediaInfo/MediaInfo.cs index a268a4fa6..453aeb028 100644 --- a/MediaBrowser.Model/MediaInfo/MediaInfo.cs +++ b/MediaBrowser.Model/MediaInfo/MediaInfo.cs @@ -51,6 +51,8 @@ namespace MediaBrowser.Model.MediaInfo public string ShowName { get; set; } + public string ForcedSortName { get; set; } + public int? IndexNumber { get; set; } public int? ParentIndexNumber { get; set; } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index 945463666..cf271e7db 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -111,6 +111,11 @@ namespace MediaBrowser.Providers.MediaInfo audio.Name = data.Name; } + if (!string.IsNullOrEmpty(data.ForcedSortName)) + { + audio.ForcedSortName = data.ForcedSortName; + } + if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast)) { var people = new List(); diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 74849a522..e7f9cf314 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -394,6 +394,12 @@ namespace MediaBrowser.Providers.MediaInfo } } + if (video is MusicVideo musicVideo) + { + musicVideo.Album = data.Album; + musicVideo.Artists = data.Artists; + } + if (data.ProductionYear.HasValue) { if (!video.ProductionYear.HasValue || isFullRefresh) @@ -436,6 +442,11 @@ namespace MediaBrowser.Providers.MediaInfo video.Name = data.Name; } } + + if (!string.IsNullOrWhiteSpace(data.ForcedSortName)) + { + video.ForcedSortName = data.ForcedSortName; + } } // If we don't have a ProductionYear try and get it from PremiereDate -- cgit v1.2.3 From bb6fddde9ac48905b876717806754a052bf0ad24 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 17 Apr 2021 11:19:09 +0100 Subject: Group Methods --- Emby.Server.Implementations/Playlists/PlaylistManager.cs | 2 +- Jellyfin.Api/Helpers/StreamingHelpers.cs | 4 ++-- Jellyfin.Networking/Manager/NetworkManager.cs | 2 +- Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 2 +- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 2 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs') diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 932f721ab..2d1a559f1 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -215,7 +215,7 @@ namespace Emby.Server.Implementations.Playlists // Create a list of the new linked children to add to the playlist var childrenToAdd = newItems - .Select(i => LinkedChild.Create(i)) + .Select(LinkedChild.Create) .ToList(); // Log duplicates that have been ignored, if any diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index e2306aa27..404cb3a46 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -121,14 +121,14 @@ namespace Jellyfin.Api.Helpers if (!string.IsNullOrWhiteSpace(streamingRequest.AudioCodec)) { state.SupportedAudioCodecs = streamingRequest.AudioCodec.Split(',', StringSplitOptions.RemoveEmptyEntries); - state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToAudioCodec(i)) + state.Request.AudioCodec = state.SupportedAudioCodecs.FirstOrDefault(mediaEncoder.CanEncodeToAudioCodec) ?? state.SupportedAudioCodecs.FirstOrDefault(); } if (!string.IsNullOrWhiteSpace(streamingRequest.SubtitleCodec)) { state.SupportedSubtitleCodecs = streamingRequest.SubtitleCodec.Split(',', StringSplitOptions.RemoveEmptyEntries); - state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(i => mediaEncoder.CanEncodeToSubtitleCodec(i)) + state.Request.SubtitleCodec = state.SupportedSubtitleCodecs.FirstOrDefault(mediaEncoder.CanEncodeToSubtitleCodec) ?? state.SupportedSubtitleCodecs.FirstOrDefault(); } diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 73e8b2cd7..4078fd126 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -1067,7 +1067,7 @@ namespace Jellyfin.Networking.Manager } // Internal interfaces must be private, not excluded and part of the LocalNetworkSubnet. - _internalInterfaces = CreateCollection(_interfaceAddresses.Where(i => IsInLocalNetwork(i))); + _internalInterfaces = CreateCollection(_interfaceAddresses.Where(IsInLocalNetwork)); } _logger.LogInformation("Defined LAN addresses : {0}", _lanSubnets.AsString()); diff --git a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs index 6821630db..ee4f8b0ba 100644 --- a/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs +++ b/Jellyfin.Server/Migrations/Routines/CreateUserLoggingConfigFile.cs @@ -75,7 +75,7 @@ namespace Jellyfin.Server.Migrations.Routines { var existingConfigJson = JToken.Parse(File.ReadAllText(oldConfigPath)); return _defaultConfigHistory - .Select(historicalConfigText => JToken.Parse(historicalConfigText)) + .Select(JToken.Parse) .Any(historicalConfigJson => JToken.DeepEquals(existingConfigJson, historicalConfigJson)); } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 53d45261e..1b69c6646 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2324,7 +2324,7 @@ namespace MediaBrowser.Controller.Entities .Where(i => i.IsLocalFile) .Select(i => System.IO.Path.GetDirectoryName(i.Path)) .Distinct(StringComparer.OrdinalIgnoreCase) - .SelectMany(i => directoryService.GetFilePaths(i)) + .SelectMany(directoryService.GetFilePaths) .ToList(); var deletedImages = ImageInfos diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index a87104cd6..ee2e5fcde 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.MediaEncoding.Probing .Where(i => i.Type != MediaStreamType.Subtitle || !string.IsNullOrWhiteSpace(i.Codec)) .ToList(); - info.MediaAttachments = internalStreams.Select(s => GetMediaAttachment(s)) + info.MediaAttachments = internalStreams.Select(GetMediaAttachment) .Where(i => i != null) .ToList(); diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 398d47d5f..f4c69fe8f 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -297,7 +297,7 @@ namespace MediaBrowser.Model.Dlna int? inputAudioSampleRate = audioStream?.SampleRate; int? inputAudioBitDepth = audioStream?.BitDepth; - if (directPlayMethods.Count() > 0) + if (directPlayMethods.Any()) { string audioCodec = audioStream?.Codec; -- cgit v1.2.3 From 2e98de90628e9a4e42fb182f2d5a2a296acfd827 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 5 May 2021 12:51:14 +0100 Subject: Code Clean up: Convert to null-coalescing operator ?? (#5845) Co-authored-by: Cody Robibero Co-authored-by: Patrick Barron <18354464+barronpm@users.noreply.github.com> --- Emby.Server.Implementations/ApplicationHost.cs | 10 +---- Emby.Server.Implementations/Dto/DtoService.cs | 15 ++------ .../Library/LibraryManager.cs | 5 +-- .../Library/Resolvers/TV/EpisodeResolver.cs | 14 ++----- .../LiveTv/EmbyTV/EmbyTV.cs | 10 ++--- .../LiveTv/Listings/SchedulesDirect.cs | 7 +--- .../LiveTv/LiveTvManager.cs | 15 ++------ .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 5 +-- .../Plugins/PluginManager.cs | 13 +------ .../ScheduledTasks/ScheduledTaskWorker.cs | 7 +--- .../Session/SessionManager.cs | 5 +-- Jellyfin.Api/Controllers/PluginsController.cs | 7 +--- Jellyfin.Api/Controllers/SearchController.cs | 5 +-- Jellyfin.Api/Helpers/StreamingHelpers.cs | 5 +-- MediaBrowser.Common/Net/IPHost.cs | 5 +-- MediaBrowser.Common/Plugins/BasePluginOfT.cs | 5 +-- MediaBrowser.Controller/Entities/BaseItem.cs | 26 ++++--------- MediaBrowser.Controller/Entities/UserView.cs | 5 +-- MediaBrowser.Controller/Library/ItemResolveArgs.cs | 5 +-- MediaBrowser.Controller/Playlists/Playlist.cs | 5 +-- .../Providers/MetadataRefreshOptions.cs | 5 +-- .../Providers/MetadataResult.cs | 16 +++----- .../Probing/ProbeResultNormalizer.cs | 45 ++++++---------------- .../Entities/ProviderIdsExtensions.cs | 5 +-- .../MediaInfo/FFProbeVideoInfo.cs | 5 +-- .../Subtitles/SubtitleManager.cs | 5 +-- RSSDP/SsdpCommunicationsServer.cs | 5 +-- 27 files changed, 60 insertions(+), 200 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 703f8d20d..75d8fc113 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -335,10 +335,7 @@ namespace Emby.Server.Implementations { get { - if (_deviceId == null) - { - _deviceId = new DeviceId(ApplicationPaths, LoggerFactory); - } + _deviceId ??= new DeviceId(ApplicationPaths, LoggerFactory); return _deviceId.Value; } @@ -370,10 +367,7 @@ namespace Emby.Server.Implementations /// System.Object. protected object CreateInstanceSafe(Type type) { - if (_creatingInstances == null) - { - _creatingInstances = new List(); - } + _creatingInstances ??= new List(); if (_creatingInstances.IndexOf(type) != -1) { diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 54b18a8c8..4ae35039a 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -665,10 +665,7 @@ namespace Emby.Server.Implementations.Dto var tag = GetImageCacheTag(item, image); if (!string.IsNullOrEmpty(image.BlurHash)) { - if (dto.ImageBlurHashes == null) - { - dto.ImageBlurHashes = new Dictionary>(); - } + dto.ImageBlurHashes ??= new Dictionary>(); if (!dto.ImageBlurHashes.ContainsKey(image.Type)) { @@ -702,10 +699,7 @@ namespace Emby.Server.Implementations.Dto if (hashes.Count > 0) { - if (dto.ImageBlurHashes == null) - { - dto.ImageBlurHashes = new Dictionary>(); - } + dto.ImageBlurHashes ??= new Dictionary>(); dto.ImageBlurHashes[imageType] = hashes; } @@ -898,10 +892,7 @@ namespace Emby.Server.Implementations.Dto dto.Taglines = new string[] { item.Tagline }; } - if (dto.Taglines == null) - { - dto.Taglines = Array.Empty(); - } + dto.Taglines ??= Array.Empty(); } dto.Type = item.GetBaseItemKind(); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index a44edad16..4d207471a 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -176,10 +176,7 @@ namespace Emby.Server.Implementations.Library { lock (_rootFolderSyncLock) { - if (_rootFolder == null) - { - _rootFolder = CreateRootFolder(); - } + _rootFolder ??= CreateRootFolder(); } } diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index 9b4cd7a3d..6f29bc649 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -35,14 +35,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV return null; } - var season = parent as Season; - // Just in case the user decided to nest episodes. // Not officially supported but in some cases we can handle it. - if (season == null) - { - season = parent.GetParents().OfType().FirstOrDefault(); - } + + var season = parent as Season ?? parent.GetParents().OfType().FirstOrDefault(); // If the parent is a Season or Series and the parent is not an extras folder, then this is an Episode if the VideoResolver returns something // Also handle flat tv folders @@ -55,11 +51,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV if (episode != null) { - var series = parent as Series; - if (series == null) - { - series = parent.GetParents().OfType().FirstOrDefault(); - } + var series = parent as Series ?? parent.GetParents().OfType().FirstOrDefault(); if (series != null) { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 665fbfa0f..28a2095e1 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2237,14 +2237,10 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var enabledTimersForSeries = new List(); foreach (var timer in allTimers) { - var existingTimer = _timerProvider.GetTimer(timer.Id); - - if (existingTimer == null) - { - existingTimer = string.IsNullOrWhiteSpace(timer.ProgramId) + var existingTimer = _timerProvider.GetTimer(timer.Id) + ?? (string.IsNullOrWhiteSpace(timer.ProgramId) ? null - : _timerProvider.GetTimerByProgramId(timer.ProgramId); - } + : _timerProvider.GetTimerByProgramId(timer.ProgramId)); if (existingTimer == null) { diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 1926e738f..9af65cabb 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -787,14 +787,11 @@ namespace Emby.Server.Implementations.LiveTv.Listings { var channelNumber = GetChannelNumber(channel); - var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase)); - if (station == null) - { - station = new ScheduleDirect.Station + var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase)) + ?? new ScheduleDirect.Station { stationID = channel.stationID }; - } var channelInfo = new ChannelInfo { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 63a3146aa..1145d8aa1 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -987,10 +987,7 @@ namespace Emby.Server.Implementations.LiveTv var externalProgramId = programTuple.Item2; string externalSeriesId = programTuple.Item3; - if (timerList == null) - { - timerList = (await GetTimersInternal(new TimerQuery(), cancellationToken).ConfigureAwait(false)).Items; - } + timerList ??= (await GetTimersInternal(new TimerQuery(), cancellationToken).ConfigureAwait(false)).Items; var timer = timerList.FirstOrDefault(i => string.Equals(i.ProgramId, externalProgramId, StringComparison.OrdinalIgnoreCase)); var foundSeriesTimer = false; @@ -1018,10 +1015,7 @@ namespace Emby.Server.Implementations.LiveTv continue; } - if (seriesTimerList == null) - { - seriesTimerList = (await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false)).Items; - } + seriesTimerList ??= (await GetSeriesTimersInternal(new SeriesTimerQuery(), cancellationToken).ConfigureAwait(false)).Items; var seriesTimer = seriesTimerList.FirstOrDefault(i => string.Equals(i.SeriesId, externalSeriesId, StringComparison.OrdinalIgnoreCase)); @@ -1974,10 +1968,7 @@ namespace Emby.Server.Implementations.LiveTv }; } - if (service == null) - { - service = _services[0]; - } + service ??= _services[0]; var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index c32ca2fb6..4aa5832b1 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -421,10 +421,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun string audioCodec = channelInfo.AudioCodec; - if (!videoBitrate.HasValue) - { - videoBitrate = isHd ? 15000000 : 2000000; - } + videoBitrate ??= isHd ? 15000000 : 2000000; int? audioBitrate = isHd ? 448000 : 192000; diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index fd2ee6b7a..14df20936 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -44,12 +44,7 @@ namespace Emby.Server.Implementations.Plugins { get { - if (_httpClientFactory == null) - { - _httpClientFactory = _appHost.Resolve(); - } - - return _httpClientFactory; + return _httpClientFactory ?? (_httpClientFactory = _appHost.Resolve()); } } @@ -276,11 +271,7 @@ namespace Emby.Server.Implementations.Plugins // If no version is given, return the current instance. var plugins = _plugins.Where(p => p.Id.Equals(id)).ToList(); - plugin = plugins.FirstOrDefault(p => p.Instance != null); - if (plugin == null) - { - plugin = plugins.OrderByDescending(p => p.Version).FirstOrDefault(); - } + plugin = plugins.FirstOrDefault(p => p.Instance != null) ?? plugins.OrderByDescending(p => p.Version).FirstOrDefault(); } else { diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 61dccaa19..101d9b537 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -301,12 +301,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { get { - if (_id == null) - { - _id = ScheduledTask.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture); - } - - return _id; + return _id ??= ScheduledTask.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture); } } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 6f21ec31e..6844152ea 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1475,10 +1475,7 @@ namespace Emby.Server.Implementations.Session user = _userManager.GetUserById(request.UserId); } - if (user == null) - { - user = _userManager.GetUserByName(request.Username); - } + user ??= _userManager.GetUserByName(request.Username); if (enforcePassword) { diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs index adec86a10..7a6130719 100644 --- a/Jellyfin.Api/Controllers/PluginsController.cs +++ b/Jellyfin.Api/Controllers/PluginsController.cs @@ -207,12 +207,7 @@ namespace Jellyfin.Api.Controllers var plugins = _pluginManager.Plugins.Where(p => p.Id.Equals(pluginId)); // Select the un-instanced one first. - var plugin = plugins.FirstOrDefault(p => p.Instance == null); - if (plugin == null) - { - // Then by the status. - plugin = plugins.OrderBy(p => p.Manifest.Status).FirstOrDefault(); - } + var plugin = plugins.FirstOrDefault(p => p.Instance == null) ?? plugins.OrderBy(p => p.Manifest.Status).FirstOrDefault(); if (plugin != null) { diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs index 6c22050a7..73bdf9018 100644 --- a/Jellyfin.Api/Controllers/SearchController.cs +++ b/Jellyfin.Api/Controllers/SearchController.cs @@ -228,10 +228,7 @@ namespace Jellyfin.Api.Controllers itemWithImage = GetParentWithImage(item, ImageType.Thumb); } - if (itemWithImage == null) - { - itemWithImage = GetParentWithImage(item, ImageType.Thumb); - } + itemWithImage ??= GetParentWithImage(item, ImageType.Thumb); if (itemWithImage != null) { diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index 583e613b4..8cffe9c4c 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -292,10 +292,7 @@ namespace Jellyfin.Api.Helpers } } - if (profile == null) - { - profile = dlnaManager.GetDefaultProfile(); - } + profile ??= dlnaManager.GetDefaultProfile(); var audioCodec = state.ActualOutputAudioCodec; diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs index fb3ef9b12..7156ce618 100644 --- a/MediaBrowser.Common/Net/IPHost.cs +++ b/MediaBrowser.Common/Net/IPHost.cs @@ -400,10 +400,7 @@ namespace MediaBrowser.Common.Net private bool ResolveHost() { // When was the last time we resolved? - if (_lastResolved == null) - { - _lastResolved = DateTime.UtcNow; - } + _lastResolved ??= DateTime.UtcNow; // If we haven't resolved before, or our timer has run out... if ((_addresses.Length == 0 && !Resolved) || (DateTime.UtcNow > _lastResolved.Value.AddMinutes(Timeout))) diff --git a/MediaBrowser.Common/Plugins/BasePluginOfT.cs b/MediaBrowser.Common/Plugins/BasePluginOfT.cs index 99c226f50..e074cc6a0 100644 --- a/MediaBrowser.Common/Plugins/BasePluginOfT.cs +++ b/MediaBrowser.Common/Plugins/BasePluginOfT.cs @@ -105,10 +105,7 @@ namespace MediaBrowser.Common.Plugins { lock (_configurationSyncLock) { - if (_configuration == null) - { - _configuration = LoadConfiguration(); - } + _configuration ??= LoadConfiguration(); } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 1b69c6646..32ae15498 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -106,15 +106,10 @@ namespace MediaBrowser.Controller.Entities { get { - if (_themeSongIds == null) - { - _themeSongIds = GetExtras() - .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeSong) - .Select(song => song.Id) - .ToArray(); - } - - return _themeSongIds; + return _themeSongIds ??= GetExtras() + .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeSong) + .Select(song => song.Id) + .ToArray(); } private set @@ -128,15 +123,10 @@ namespace MediaBrowser.Controller.Entities { get { - if (_themeVideoIds == null) - { - _themeVideoIds = GetExtras() - .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeVideo) - .Select(song => song.Id) - .ToArray(); - } - - return _themeVideoIds; + return _themeVideoIds ??= GetExtras() + .Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeVideo) + .Select(song => song.Id) + .ToArray(); } private set diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index b1da4d64c..fec83dd94 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -75,10 +75,7 @@ namespace MediaBrowser.Controller.Entities public override List GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query) { - if (query == null) - { - query = new InternalItemsQuery(user); - } + query ??= new InternalItemsQuery(user); query.EnableTotalRecordCount = false; var result = GetItemList(query); diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index f86f7df25..f9086066d 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -147,10 +147,7 @@ namespace MediaBrowser.Controller.Library throw new ArgumentException("The path was empty or null.", nameof(path)); } - if (AdditionalLocations == null) - { - AdditionalLocations = new List(); - } + AdditionalLocations ??= new List(); AdditionalLocations.Add(path); } diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index c9c168c4c..3c93cfc79 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -126,10 +126,7 @@ namespace MediaBrowser.Controller.Playlists private List GetPlayableItems(User user, InternalItemsQuery query) { - if (query == null) - { - query = new InternalItemsQuery(user); - } + query ??= new InternalItemsQuery(user); query.IsFolder = false; diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs index b92b83701..db0ef7072 100644 --- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs +++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs @@ -45,10 +45,7 @@ namespace MediaBrowser.Controller.Providers if (copy.RefreshPaths != null && copy.RefreshPaths.Length > 0) { - if (RefreshPaths == null) - { - RefreshPaths = Array.Empty(); - } + RefreshPaths ??= Array.Empty(); RefreshPaths = copy.RefreshPaths.ToArray(); } diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs index 864cb3050..98c7eadfe 100644 --- a/MediaBrowser.Controller/Providers/MetadataResult.cs +++ b/MediaBrowser.Controller/Providers/MetadataResult.cs @@ -37,10 +37,7 @@ namespace MediaBrowser.Controller.Providers public void AddPerson(PersonInfo p) { - if (People == null) - { - People = new List(); - } + People ??= new List(); PeopleHelper.AddPerson(People, p); } @@ -54,16 +51,15 @@ namespace MediaBrowser.Controller.Providers { People = new List(); } - - People.Clear(); + else + { + People.Clear(); + } } public UserItemData GetOrAddUserData(string userId) { - if (UserDataList == null) - { - UserDataList = new List(); - } + UserDataList ??= new List(); UserItemData userData = null; diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index ee2e5fcde..2e96f8cb0 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -1187,43 +1187,28 @@ namespace MediaBrowser.MediaEncoding.Probing FetchStudios(audio, tags, "label"); // These support mulitple values, but for now we only store the first. - var mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id")); - if (mb == null) - { - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID")); - } + var mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id")) + ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID")); audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, mb); - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id")); - if (mb == null) - { - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID")); - } + mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id")) + ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID")); audio.SetProviderId(MetadataProvider.MusicBrainzArtist, mb); - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id")); - if (mb == null) - { - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID")); - } + mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id")) + ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID")); audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, mb); - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id")); - if (mb == null) - { - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID")); - } + mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id")) + ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID")); audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, mb); - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id")); - if (mb == null) - { - mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID")); - } + mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id")) + ?? GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID")); audio.SetProviderId(MetadataProvider.MusicBrainzTrack, mb); } @@ -1290,15 +1275,7 @@ namespace MediaBrowser.MediaEncoding.Probing private IEnumerable GetSplitWhitelist() { - if (_splitWhiteList == null) - { - _splitWhiteList = new List - { - "AC/DC" - }; - } - - return _splitWhiteList; + return _splitWhiteList ??= new List { "AC/DC" }; } /// diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs index 09d14dc6a..ce4b0ec92 100644 --- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs +++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs @@ -123,10 +123,7 @@ namespace MediaBrowser.Model.Entities else { // Ensure it exists - if (instance.ProviderIds == null) - { - instance.ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); - } + instance.ProviderIds ??= new Dictionary(StringComparer.OrdinalIgnoreCase); instance.ProviderIds[name] = value; } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 74849a522..f049cc81f 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -111,10 +111,7 @@ namespace MediaBrowser.Providers.MediaInfo } } - if (streamFileNames == null) - { - streamFileNames = Array.Empty(); - } + streamFileNames ??= Array.Empty(); mediaInfoResult = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 1f3d9acff..8d62343cb 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -256,10 +256,7 @@ namespace MediaBrowser.Providers.Subtitles } catch (Exception ex) { - if (exceptionToThrow == null) - { - exceptionToThrow = ex; - } + exceptionToThrow ??= ex; } finally { diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 8f1f0fa61..f448ad38b 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -415,10 +415,7 @@ namespace Rssdp.Infrastructure { lock (_SendSocketSynchroniser) { - if (_sendSockets == null) - { - _sendSockets = CreateSocketAndListenForResponsesAsync(); - } + _sendSockets ??= CreateSocketAndListenForResponsesAsync(); } } } -- cgit v1.2.3 From e432796f6f0f500830b1c90c233c4e4c07287190 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 5 May 2021 14:39:50 +0200 Subject: Minor improvements --- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 11 ++++------- MediaBrowser.Providers/Subtitles/SubtitleManager.cs | 12 ++++++------ 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs') diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 2e96f8cb0..884ec0a29 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -29,7 +29,7 @@ namespace MediaBrowser.MediaEncoding.Probing private readonly ILogger _logger; private readonly ILocalizationManager _localization; - private List _splitWhiteList = null; + private string[] _splitWhiteList; public ProbeResultNormalizer(ILogger logger, ILocalizationManager localization) { @@ -37,6 +37,8 @@ namespace MediaBrowser.MediaEncoding.Probing _localization = localization; } + private IReadOnlyList SplitWhitelist => _splitWhiteList ??= new string[] { "AC/DC" }; + public MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType? videoType, bool isAudio, string path, MediaProtocol protocol) { var info = new MediaInfo @@ -1254,7 +1256,7 @@ namespace MediaBrowser.MediaEncoding.Probing var artistsFound = new List(); - foreach (var whitelistArtist in GetSplitWhitelist()) + foreach (var whitelistArtist in SplitWhitelist) { var originalVal = val; val = val.Replace(whitelistArtist, "|", StringComparison.OrdinalIgnoreCase); @@ -1273,11 +1275,6 @@ namespace MediaBrowser.MediaEncoding.Probing return artistsFound; } - private IEnumerable GetSplitWhitelist() - { - return _splitWhiteList ??= new List { "AC/DC" }; - } - /// /// Gets the studios from the tags collection. /// diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 8d62343cb..bf0c853ae 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -207,7 +207,7 @@ namespace MediaBrowser.Providers.Subtitles { var mediaFolderPath = Path.GetFullPath(Path.Combine(video.ContainingFolderPath, saveFileName)); // TODO: Add some error handling to the API user: return BadRequest("Could not save subtitle, bad path."); - if (mediaFolderPath.StartsWith(video.ContainingFolderPath)) + if (mediaFolderPath.StartsWith(video.ContainingFolderPath, StringComparison.Ordinal)) { savePaths.Add(mediaFolderPath); } @@ -216,7 +216,7 @@ namespace MediaBrowser.Providers.Subtitles var internalPath = Path.GetFullPath(Path.Combine(video.GetInternalMetadataPath(), saveFileName)); // TODO: Add some error to the user: return BadRequest("Could not save subtitle, bad path."); - if (internalPath.StartsWith(video.GetInternalMetadataPath())) + if (internalPath.StartsWith(video.GetInternalMetadataPath(), StringComparison.Ordinal)) { savePaths.Add(internalPath); } @@ -234,7 +234,7 @@ namespace MediaBrowser.Providers.Subtitles private async Task TrySaveToFiles(Stream stream, List savePaths) { - Exception exceptionToThrow = null; + List exs = null; foreach (var savePath in savePaths) { @@ -256,7 +256,7 @@ namespace MediaBrowser.Providers.Subtitles } catch (Exception ex) { - exceptionToThrow ??= ex; + (exs ??= new List()).Add(ex); } finally { @@ -266,9 +266,9 @@ namespace MediaBrowser.Providers.Subtitles stream.Position = 0; } - if (exceptionToThrow != null) + if (exs != null) { - throw exceptionToThrow; + throw new AggregateException(exs); } } -- cgit v1.2.3 From 26d7fc828075dbaa3068ac9c323ebef3370fd023 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 20 May 2021 22:10:19 +0200 Subject: Enable nullable reference types for MediaBrowser.MediaEncoding.Subtitles --- .../Attachments/AttachmentExtractor.cs | 1 + .../BdInfo/BdInfoDirectoryInfo.cs | 6 +- .../BdInfo/BdInfoFileInfo.cs | 2 +- .../Encoder/EncoderValidator.cs | 15 +++-- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 1 + .../MediaBrowser.MediaEncoding.csproj | 1 + .../Probing/FFProbeHelpers.cs | 2 + .../Probing/InternalMediaInfoResult.cs | 2 + MediaBrowser.MediaEncoding/Probing/MediaChapter.cs | 1 + .../Probing/MediaFormatInfo.cs | 2 + .../Probing/MediaStreamInfo.cs | 2 + .../Probing/ProbeResultNormalizer.cs | 1 + MediaBrowser.MediaEncoding/Subtitles/AssParser.cs | 2 - .../Subtitles/ParserValues.cs | 1 - MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs | 2 - MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs | 2 - .../Subtitles/SubtitleEditParser.cs | 2 - .../Subtitles/SubtitleEncoder.cs | 66 ++++++++++++---------- 18 files changed, 62 insertions(+), 49 deletions(-) (limited to 'MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs') diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index e8aeabf9d..a0ec3bd90 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -1,3 +1,4 @@ +#nullable disable #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs index ef9943722..e86e518be 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs @@ -9,9 +9,9 @@ namespace MediaBrowser.MediaEncoding.BdInfo { public class BdInfoDirectoryInfo : IDirectoryInfo { - private readonly IFileSystem _fileSystem = null; + private readonly IFileSystem _fileSystem; - private readonly FileSystemMetadata _impl = null; + private readonly FileSystemMetadata _impl; public BdInfoDirectoryInfo(IFileSystem fileSystem, string path) { @@ -29,7 +29,7 @@ namespace MediaBrowser.MediaEncoding.BdInfo public string FullName => _impl.FullName; - public IDirectoryInfo Parent + public IDirectoryInfo? Parent { get { diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs index 0a8af8e9c..41143c259 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs @@ -7,7 +7,7 @@ namespace MediaBrowser.MediaEncoding.BdInfo { public class BdInfoFileInfo : BDInfo.IO.IFileInfo { - private FileSystemMetadata _impl = null; + private FileSystemMetadata _impl; public BdInfoFileInfo(FileSystemMetadata impl) { diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 9e2417603..f782e65bd 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -121,11 +121,11 @@ namespace MediaBrowser.MediaEncoding.Encoder // When changing this, also change the minimum library versions in _ffmpegMinimumLibraryVersions public static Version MinVersion { get; } = new Version(4, 0); - public static Version MaxVersion { get; } = null; + public static Version? MaxVersion { get; } = null; public bool ValidateVersion() { - string output = null; + string output; try { output = GetProcessOutput(_encoderPath, "-version"); @@ -133,6 +133,7 @@ namespace MediaBrowser.MediaEncoding.Encoder catch (Exception ex) { _logger.LogError(ex, "Error validating encoder"); + return false; } if (string.IsNullOrWhiteSpace(output)) @@ -207,7 +208,7 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// The output from "ffmpeg -version". /// The FFmpeg version. - internal Version GetFFmpegVersion(string output) + internal Version? GetFFmpegVersion(string output) { // For pre-built binaries the FFmpeg version should be mentioned at the very start of the output var match = Regex.Match(output, @"^ffmpeg version n?((?:[0-9]+\.?)+)"); @@ -275,7 +276,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private IEnumerable GetHwaccelTypes() { - string output = null; + string? output = null; try { output = GetProcessOutput(_encoderPath, "-hwaccels"); @@ -303,7 +304,7 @@ namespace MediaBrowser.MediaEncoding.Encoder return false; } - string output = null; + string output; try { output = GetProcessOutput(_encoderPath, "-h filter=" + filter); @@ -311,6 +312,7 @@ namespace MediaBrowser.MediaEncoding.Encoder catch (Exception ex) { _logger.LogError(ex, "Error detecting the given filter"); + return false; } if (output.Contains("Filter " + filter, StringComparison.Ordinal)) @@ -331,7 +333,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private IEnumerable GetCodecs(Codec codec) { string codecstr = codec == Codec.Encoder ? "encoders" : "decoders"; - string output = null; + string output; try { output = GetProcessOutput(_encoderPath, "-" + codecstr); @@ -339,6 +341,7 @@ namespace MediaBrowser.MediaEncoding.Encoder catch (Exception ex) { _logger.LogError(ex, "Error detecting available {Codec}", codecstr); + return Enumerable.Empty(); } if (string.IsNullOrWhiteSpace(output)) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 62c0c0bb1..cdb778bf2 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -1,3 +1,4 @@ +#nullable disable #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 39fb0b47c..7733e715f 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -10,6 +10,7 @@ false true true + enable AllEnabledByDefault ../jellyfin.ruleset diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs index da37687e8..1fa90bb21 100644 --- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs +++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs @@ -1,3 +1,5 @@ +#nullable disable + using System; using System.Collections.Generic; using System.Globalization; diff --git a/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs b/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs index 0e319c1a8..d4d153b08 100644 --- a/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs +++ b/MediaBrowser.MediaEncoding/Probing/InternalMediaInfoResult.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Collections.Generic; using System.Text.Json.Serialization; diff --git a/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs index de062d06b..a1cef7a9f 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs @@ -1,3 +1,4 @@ +#nullable disable #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs index 8af122ef9..d50da37b8 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaFormatInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Collections.Generic; using System.Text.Json.Serialization; diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs index 7b7744163..c9c8c34c2 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs @@ -1,3 +1,5 @@ +#nullable disable + using System.Collections.Generic; using System.Text.Json.Serialization; diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 884ec0a29..2ec9dc346 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -1,3 +1,4 @@ +#nullable disable #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs index 8219aa7b4..08ee5c72e 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs @@ -1,5 +1,3 @@ -#nullable enable - using Microsoft.Extensions.Logging; using Nikse.SubtitleEdit.Core.SubtitleFormats; diff --git a/MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs b/MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs index dca5c1e8a..cec1aaf08 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs @@ -1,4 +1,3 @@ -#nullable enable #pragma warning disable CS1591 namespace MediaBrowser.MediaEncoding.Subtitles diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs index 19fb951dc..78d54ca51 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs @@ -1,5 +1,3 @@ -#nullable enable - using Microsoft.Extensions.Logging; using Nikse.SubtitleEdit.Core.SubtitleFormats; diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs index 36dc2e01f..17c2ae40e 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs @@ -1,5 +1,3 @@ -#nullable enable - using Microsoft.Extensions.Logging; using Nikse.SubtitleEdit.Core.SubtitleFormats; diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index 82ec6ca21..639a34d99 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -1,5 +1,3 @@ -#nullable enable - using System.Globalization; using System.IO; using System.Linq; diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 39bec8da1..608ebf443 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Concurrent; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; @@ -71,8 +72,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles try { - var reader = GetReader(inputFormat, true); - + var reader = GetReader(inputFormat); var trackInfo = reader.Parse(stream, cancellationToken); FilterEvents(trackInfo, startTimeTicks, endTimeTicks, preserveOriginalTimestamps); @@ -139,10 +139,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles .ConfigureAwait(false); var inputFormat = subtitle.format; - var writer = TryGetWriter(outputFormat); // Return the original if we don't have any way of converting it - if (writer == null) + if (!TryGetWriter(outputFormat, out var writer)) { return subtitle.stream; } @@ -239,7 +238,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec) .TrimStart('.'); - if (GetReader(currentFormat, false) == null) + if (TryGetReader(currentFormat, out _)) { // Convert var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, ".srt"); @@ -257,37 +256,41 @@ namespace MediaBrowser.MediaEncoding.Subtitles return new SubtitleInfo(subtitleStream.Path, mediaSource.Protocol, currentFormat, true); } - private ISubtitleParser GetReader(string format, bool throwIfMissing) + private bool TryGetReader(string format, [NotNullWhen(true)] out ISubtitleParser? value) { - if (string.IsNullOrEmpty(format)) - { - throw new ArgumentNullException(nameof(format)); - } - if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase)) { - return new SrtParser(_logger); + value = new SrtParser(_logger); + return true; } if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase)) { - return new SsaParser(_logger); + value = new SsaParser(_logger); + return true; } if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase)) { - return new AssParser(_logger); + value = new AssParser(_logger); + return true; } - if (throwIfMissing) + value = null; + return false; + } + + private ISubtitleParser GetReader(string format) + { + if (TryGetReader(format, out var reader)) { - throw new ArgumentException("Unsupported format: " + format); + return reader; } - return null; + throw new ArgumentException("Unsupported format: " + format); } - private ISubtitleWriter TryGetWriter(string format) + private bool TryGetWriter(string format, [NotNullWhen(true)] out ISubtitleWriter? value) { if (string.IsNullOrEmpty(format)) { @@ -296,32 +299,35 @@ namespace MediaBrowser.MediaEncoding.Subtitles if (string.Equals(format, "json", StringComparison.OrdinalIgnoreCase)) { - return new JsonWriter(); + value = new JsonWriter(); + return true; } if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase)) { - return new SrtWriter(); + value = new SrtWriter(); + return true; } if (string.Equals(format, SubtitleFormat.VTT, StringComparison.OrdinalIgnoreCase)) { - return new VttWriter(); + value = new VttWriter(); + return true; } if (string.Equals(format, SubtitleFormat.TTML, StringComparison.OrdinalIgnoreCase)) { - return new TtmlWriter(); + value = new TtmlWriter(); + return true; } - return null; + value = null; + return false; } private ISubtitleWriter GetWriter(string format) { - var writer = TryGetWriter(format); - - if (writer != null) + if (TryGetWriter(format, out var writer)) { return writer; } @@ -391,7 +397,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles throw new ArgumentNullException(nameof(outputPath)); } - Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + Directory.CreateDirectory(Path.GetDirectoryName(outputPath) ?? throw new ArgumentException($"Provided path ({outputPath}) is not valid.", nameof(outputPath))); var encodingParam = await GetSubtitleFileCharacterSet(inputPath, language, mediaSource.Protocol, cancellationToken).ConfigureAwait(false); @@ -549,7 +555,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles throw new ArgumentNullException(nameof(outputPath)); } - Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); + Directory.CreateDirectory(Path.GetDirectoryName(outputPath) ?? throw new ArgumentException($"Provided path ({outputPath}) is not valid.", nameof(outputPath))); var processArgs = string.Format( CultureInfo.InvariantCulture, @@ -715,7 +721,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { using (var stream = await GetStream(path, protocol, cancellationToken).ConfigureAwait(false)) { - var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName; + var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName ?? string.Empty; // UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding if ((path.EndsWith(".ass", StringComparison.Ordinal) || path.EndsWith(".ssa", StringComparison.Ordinal) || path.EndsWith(".srt", StringComparison.Ordinal)) @@ -725,7 +731,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles charset = string.Empty; } - _logger.LogDebug("charset {0} detected for {Path}", charset ?? "null", path); + _logger.LogDebug("charset {0} detected for {Path}", charset, path); return charset; } -- cgit v1.2.3