aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Model
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Model')
-rw-r--r--MediaBrowser.Model/Branding/BrandingOptions.cs2
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs388
-rw-r--r--MediaBrowser.Model/Configuration/LibraryOptions.cs4
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs22
-rw-r--r--MediaBrowser.Model/Cryptography/PasswordHash.cs10
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs39
-rw-r--r--MediaBrowser.Model/Dlna/ContainerProfile.cs6
-rw-r--r--MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs2
-rw-r--r--MediaBrowser.Model/Dlna/DirectPlayProfile.cs6
-rw-r--r--MediaBrowser.Model/Dlna/ITranscoderSupport.cs18
-rw-r--r--MediaBrowser.Model/Dlna/MediaOptions.cs (renamed from MediaBrowser.Model/Dlna/AudioOptions.cs)66
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionNormalizer.cs2
-rw-r--r--MediaBrowser.Model/Dlna/SearchCriteria.cs5
-rw-r--r--MediaBrowser.Model/Dlna/SortCriteria.cs2
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs660
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs49
-rw-r--r--MediaBrowser.Model/Dlna/VideoOptions.cs16
-rw-r--r--MediaBrowser.Model/Drawing/DrawingUtils.cs8
-rw-r--r--MediaBrowser.Model/Drawing/ImageResolution.cs52
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs6
-rw-r--r--MediaBrowser.Model/Dto/BaseItemPerson.cs5
-rw-r--r--MediaBrowser.Model/Dto/ImageOptions.cs111
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceInfo.cs2
-rw-r--r--MediaBrowser.Model/Dto/UserDto.cs1
-rw-r--r--MediaBrowser.Model/Entities/DownMixStereoAlgorithms.cs23
-rw-r--r--MediaBrowser.Model/Entities/IHasShares.cs12
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs11
-rw-r--r--MediaBrowser.Model/Entities/MetadataProvider.cs52
-rw-r--r--MediaBrowser.Model/Entities/ParentalRating.cs4
-rw-r--r--MediaBrowser.Model/Entities/ProviderIdsExtensions.cs2
-rw-r--r--MediaBrowser.Model/Entities/Share.cs17
-rw-r--r--MediaBrowser.Model/Extensions/EnumerableExtensions.cs17
-rw-r--r--MediaBrowser.Model/Globalization/ILocalizationManager.cs3
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvOptions.cs4
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj22
-rw-r--r--MediaBrowser.Model/MediaInfo/AudioCodec.cs6
-rw-r--r--MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs56
-rw-r--r--MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs21
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs15
-rw-r--r--MediaBrowser.Model/Net/WebSocketMessage.cs31
-rw-r--r--MediaBrowser.Model/Notifications/NotificationLevel.cs11
-rw-r--r--MediaBrowser.Model/Notifications/NotificationOption.cs55
-rw-r--r--MediaBrowser.Model/Notifications/NotificationOptions.cs131
-rw-r--r--MediaBrowser.Model/Notifications/NotificationRequest.cs35
-rw-r--r--MediaBrowser.Model/Notifications/NotificationTypeInfo.cs18
-rw-r--r--MediaBrowser.Model/Notifications/SendToUserType.cs11
-rw-r--r--MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs39
-rw-r--r--MediaBrowser.Model/Querying/ItemFields.cs2
-rw-r--r--MediaBrowser.Model/Querying/ItemSortBy.cs5
-rw-r--r--MediaBrowser.Model/SyncPlay/GroupUpdate.cs54
-rw-r--r--MediaBrowser.Model/SyncPlay/GroupUpdateOfT.cs31
-rw-r--r--MediaBrowser.Model/SyncPlay/PlayQueueUpdate.cs4
-rw-r--r--MediaBrowser.Model/SyncPlay/SyncPlayQueueItem.cs (renamed from MediaBrowser.Model/SyncPlay/QueueItem.cs)6
-rw-r--r--MediaBrowser.Model/System/OperatingSystemId.cs12
-rw-r--r--MediaBrowser.Model/System/PublicSystemInfo.cs5
-rw-r--r--MediaBrowser.Model/System/SystemInfo.cs11
-rw-r--r--MediaBrowser.Model/Tasks/ITaskManager.cs4
-rw-r--r--MediaBrowser.Model/Updates/VersionInfo.cs2
-rw-r--r--MediaBrowser.Model/Users/UserPolicy.cs11
59 files changed, 1127 insertions, 1098 deletions
diff --git a/MediaBrowser.Model/Branding/BrandingOptions.cs b/MediaBrowser.Model/Branding/BrandingOptions.cs
index 695267d46..c6580598b 100644
--- a/MediaBrowser.Model/Branding/BrandingOptions.cs
+++ b/MediaBrowser.Model/Branding/BrandingOptions.cs
@@ -22,7 +22,7 @@ public class BrandingOptions
/// <summary>
/// Gets or sets a value indicating whether to enable the splashscreen.
/// </summary>
- public bool SplashscreenEnabled { get; set; } = true;
+ public bool SplashscreenEnabled { get; set; } = false;
/// <summary>
/// Gets or sets the splashscreen location on disk.
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index 008050602..b5b6e86c8 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -1,134 +1,266 @@
#nullable disable
-#pragma warning disable CS1591
+using MediaBrowser.Model.Entities;
-namespace MediaBrowser.Model.Configuration
+namespace MediaBrowser.Model.Configuration;
+
+/// <summary>
+/// Class EncodingOptions.
+/// </summary>
+public class EncodingOptions
{
- public class EncodingOptions
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EncodingOptions" /> class.
+ /// </summary>
+ public EncodingOptions()
{
- public EncodingOptions()
- {
- EnableFallbackFont = false;
- DownMixAudioBoost = 2;
- MaxMuxingQueueSize = 2048;
- EnableThrottling = false;
- EnableSegmentDeletion = false;
- ThrottleDelaySeconds = 180;
- SegmentKeepSeconds = 360;
- 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";
- EnableTonemapping = false;
- EnableVppTonemapping = false;
- TonemappingAlgorithm = "bt2390";
- TonemappingRange = "auto";
- TonemappingDesat = 0;
- TonemappingThreshold = 0.8;
- TonemappingPeak = 100;
- TonemappingParam = 0;
- VppTonemappingBrightness = 0;
- VppTonemappingContrast = 1.2;
- H264Crf = 23;
- H265Crf = 28;
- DeinterlaceDoubleRate = false;
- DeinterlaceMethod = "yadif";
- EnableDecodingColorDepth10Hevc = true;
- EnableDecodingColorDepth10Vp9 = true;
- EnableEnhancedNvdecDecoder = false;
- PreferSystemNativeHwDecoder = true;
- EnableIntelLowPowerH264HwEncoder = false;
- EnableIntelLowPowerHevcHwEncoder = false;
- EnableHardwareEncoding = true;
- AllowHevcEncoding = false;
- EnableSubtitleExtraction = true;
- AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = new[] { "mkv" };
- HardwareDecodingCodecs = new string[] { "h264", "vc1" };
- }
-
- public int EncodingThreadCount { get; set; }
-
- public string TranscodingTempPath { get; set; }
-
- public string FallbackFontPath { get; set; }
-
- public bool EnableFallbackFont { get; set; }
-
- public double DownMixAudioBoost { get; set; }
-
- public int MaxMuxingQueueSize { get; set; }
-
- public bool EnableThrottling { get; set; }
-
- public bool EnableSegmentDeletion { get; set; }
-
- public int ThrottleDelaySeconds { get; set; }
-
- public int SegmentKeepSeconds { get; set; }
-
- public string HardwareAccelerationType { get; set; }
-
- /// <summary>
- /// Gets or sets the FFmpeg path as set by the user via the UI.
- /// </summary>
- public string EncoderAppPath { get; set; }
-
- /// <summary>
- /// Gets or sets the current FFmpeg path being used by the system and displayed on the transcode page.
- /// </summary>
- public string EncoderAppPathDisplay { get; set; }
-
- public string VaapiDevice { get; set; }
-
- public bool EnableTonemapping { get; set; }
-
- public bool EnableVppTonemapping { get; set; }
-
- public string TonemappingAlgorithm { get; set; }
-
- public string TonemappingRange { get; set; }
-
- public double TonemappingDesat { get; set; }
-
- public double TonemappingThreshold { get; set; }
-
- public double TonemappingPeak { get; set; }
-
- public double TonemappingParam { get; set; }
-
- public double VppTonemappingBrightness { get; set; }
-
- public double VppTonemappingContrast { get; set; }
-
- public int H264Crf { get; set; }
-
- public int H265Crf { get; set; }
-
- public string EncoderPreset { get; set; }
-
- public bool DeinterlaceDoubleRate { get; set; }
-
- public string DeinterlaceMethod { get; set; }
-
- public bool EnableDecodingColorDepth10Hevc { get; set; }
-
- public bool EnableDecodingColorDepth10Vp9 { get; set; }
-
- public bool EnableEnhancedNvdecDecoder { get; set; }
-
- public bool PreferSystemNativeHwDecoder { get; set; }
-
- public bool EnableIntelLowPowerH264HwEncoder { get; set; }
-
- public bool EnableIntelLowPowerHevcHwEncoder { get; set; }
-
- public bool EnableHardwareEncoding { get; set; }
-
- public bool AllowHevcEncoding { get; set; }
-
- public bool EnableSubtitleExtraction { get; set; }
-
- public string[] HardwareDecodingCodecs { get; set; }
-
- public string[] AllowOnDemandMetadataBasedKeyframeExtractionForExtensions { get; set; }
+ EnableFallbackFont = false;
+ EnableAudioVbr = false;
+ DownMixAudioBoost = 2;
+ DownMixStereoAlgorithm = DownMixStereoAlgorithms.None;
+ MaxMuxingQueueSize = 2048;
+ EnableThrottling = false;
+ ThrottleDelaySeconds = 180;
+ EnableSegmentDeletion = false;
+ SegmentKeepSeconds = 360;
+ 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";
+ EnableTonemapping = false;
+ EnableVppTonemapping = false;
+ TonemappingAlgorithm = "bt2390";
+ TonemappingMode = "auto";
+ TonemappingRange = "auto";
+ TonemappingDesat = 0;
+ TonemappingPeak = 100;
+ TonemappingParam = 0;
+ VppTonemappingBrightness = 16;
+ VppTonemappingContrast = 1;
+ H264Crf = 23;
+ H265Crf = 28;
+ DeinterlaceDoubleRate = false;
+ DeinterlaceMethod = "yadif";
+ EnableDecodingColorDepth10Hevc = true;
+ EnableDecodingColorDepth10Vp9 = true;
+ // Enhanced Nvdec or system native decoder is required for DoVi to SDR tone-mapping.
+ EnableEnhancedNvdecDecoder = true;
+ PreferSystemNativeHwDecoder = true;
+ EnableIntelLowPowerH264HwEncoder = false;
+ EnableIntelLowPowerHevcHwEncoder = false;
+ EnableHardwareEncoding = true;
+ AllowHevcEncoding = false;
+ EnableSubtitleExtraction = true;
+ AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = new[] { "mkv" };
+ HardwareDecodingCodecs = new string[] { "h264", "vc1" };
}
+
+ /// <summary>
+ /// Gets or sets the thread count used for encoding.
+ /// </summary>
+ public int EncodingThreadCount { get; set; }
+
+ /// <summary>
+ /// Gets or sets the temporary transcoding path.
+ /// </summary>
+ public string TranscodingTempPath { get; set; }
+
+ /// <summary>
+ /// Gets or sets the path to the fallback font.
+ /// </summary>
+ public string FallbackFontPath { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to use the fallback font.
+ /// </summary>
+ public bool EnableFallbackFont { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether audio VBR is enabled.
+ /// </summary>
+ public bool EnableAudioVbr { get; set; }
+
+ /// <summary>
+ /// Gets or sets the audio boost applied when downmixing audio.
+ /// </summary>
+ public double DownMixAudioBoost { get; set; }
+
+ /// <summary>
+ /// Gets or sets the algorithm used for downmixing audio to stereo.
+ /// </summary>
+ public DownMixStereoAlgorithms DownMixStereoAlgorithm { get; set; }
+
+ /// <summary>
+ /// Gets or sets the maximum size of the muxing queue.
+ /// </summary>
+ public int MaxMuxingQueueSize { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether throttling is enabled.
+ /// </summary>
+ public bool EnableThrottling { get; set; }
+
+ /// <summary>
+ /// Gets or sets the delay after which throttling happens.
+ /// </summary>
+ public int ThrottleDelaySeconds { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether segment deletion is enabled.
+ /// </summary>
+ public bool EnableSegmentDeletion { get; set; }
+
+ /// <summary>
+ /// Gets or sets seconds for which segments should be kept before being deleted.
+ /// </summary>
+ public int SegmentKeepSeconds { get; set; }
+
+ /// <summary>
+ /// Gets or sets the hardware acceleration type.
+ /// </summary>
+ public string HardwareAccelerationType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the FFmpeg path as set by the user via the UI.
+ /// </summary>
+ public string EncoderAppPath { get; set; }
+
+ /// <summary>
+ /// Gets or sets the current FFmpeg path being used by the system and displayed on the transcode page.
+ /// </summary>
+ public string EncoderAppPathDisplay { get; set; }
+
+ /// <summary>
+ /// Gets or sets the VA-API device.
+ /// </summary>
+ public string VaapiDevice { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether tonemapping is enabled.
+ /// </summary>
+ public bool EnableTonemapping { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether VPP tonemapping is enabled.
+ /// </summary>
+ public bool EnableVppTonemapping { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tone-mapping algorithm.
+ /// </summary>
+ public string TonemappingAlgorithm { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tone-mapping mode.
+ /// </summary>
+ public string TonemappingMode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tone-mapping range.
+ /// </summary>
+ public string TonemappingRange { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tone-mapping desaturation.
+ /// </summary>
+ public double TonemappingDesat { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tone-mapping peak.
+ /// </summary>
+ public double TonemappingPeak { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tone-mapping parameters.
+ /// </summary>
+ public double TonemappingParam { get; set; }
+
+ /// <summary>
+ /// Gets or sets the VPP tone-mapping brightness.
+ /// </summary>
+ public double VppTonemappingBrightness { get; set; }
+
+ /// <summary>
+ /// Gets or sets the VPP tone-mapping contrast.
+ /// </summary>
+ public double VppTonemappingContrast { get; set; }
+
+ /// <summary>
+ /// Gets or sets the H264 CRF.
+ /// </summary>
+ public int H264Crf { get; set; }
+
+ /// <summary>
+ /// Gets or sets the H265 CRF.
+ /// </summary>
+ public int H265Crf { get; set; }
+
+ /// <summary>
+ /// Gets or sets the encoder preset.
+ /// </summary>
+ public string EncoderPreset { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the framerate is doubled when deinterlacing.
+ /// </summary>
+ public bool DeinterlaceDoubleRate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the deinterlace method.
+ /// </summary>
+ public string DeinterlaceMethod { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether 10bit HEVC decoding is enabled.
+ /// </summary>
+ public bool EnableDecodingColorDepth10Hevc { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether 10bit VP9 decoding is enabled.
+ /// </summary>
+ public bool EnableDecodingColorDepth10Vp9 { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the enhanced NVDEC is enabled.
+ /// </summary>
+ public bool EnableEnhancedNvdecDecoder { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the system native hardware decoder should be used.
+ /// </summary>
+ public bool PreferSystemNativeHwDecoder { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the Intel H264 low-power hardware encoder should be used.
+ /// </summary>
+ public bool EnableIntelLowPowerH264HwEncoder { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the Intel HEVC low-power hardware encoder should be used.
+ /// </summary>
+ public bool EnableIntelLowPowerHevcHwEncoder { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether hardware encoding is enabled.
+ /// </summary>
+ public bool EnableHardwareEncoding { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether HEVC encoding is enabled.
+ /// </summary>
+ public bool AllowHevcEncoding { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether subtitle extraction is enabled.
+ /// </summary>
+ public bool EnableSubtitleExtraction { get; set; }
+
+ /// <summary>
+ /// Gets or sets the codecs hardware encoding is used for.
+ /// </summary>
+ public string[] HardwareDecodingCodecs { get; set; }
+
+ /// <summary>
+ /// Gets or sets the file extensions on-demand metadata based keyframe extraction is enabled for.
+ /// </summary>
+ public string[] AllowOnDemandMetadataBasedKeyframeExtractionForExtensions { get; set; }
}
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
index c4d313bdb..df6829946 100644
--- a/MediaBrowser.Model/Configuration/LibraryOptions.cs
+++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs
@@ -30,6 +30,8 @@ namespace MediaBrowser.Model.Configuration
public bool EnableRealtimeMonitor { get; set; }
+ public bool EnableLUFSScan { get; set; }
+
public bool EnableChapterImageExtraction { get; set; }
public bool ExtractChapterImagesDuringLibraryScan { get; set; }
@@ -45,6 +47,8 @@ namespace MediaBrowser.Model.Configuration
public bool EnableEmbeddedTitles { get; set; }
+ public bool EnableEmbeddedExtrasTitles { get; set; }
+
public bool EnableEmbeddedEpisodeInfos { get; set; }
public int AutomaticRefreshIntervalDays { get; set; }
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index e61b896b9..07f02d187 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -2,7 +2,7 @@
#pragma warning disable CA1819
using System;
-using System.Collections.Generic;
+using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Updates;
@@ -193,7 +193,7 @@ namespace MediaBrowser.Model.Configuration
public string[] CodecsUsed { get; set; } = Array.Empty<string>();
- public List<RepositoryInfo> PluginRepositories { get; set; } = new List<RepositoryInfo>();
+ public RepositoryInfo[] PluginRepositories { get; set; } = Array.Empty<RepositoryInfo>();
public bool EnableExternalContentInSuggestions { get; set; } = true;
@@ -240,5 +240,23 @@ namespace MediaBrowser.Model.Configuration
/// Gets or sets a value indicating whether clients should be allowed to upload logs.
/// </summary>
public bool AllowClientLogUpload { get; set; } = true;
+
+ /// <summary>
+ /// Gets or sets the dummy chapter duration in seconds, use 0 (zero) or less to disable generation alltogether.
+ /// </summary>
+ /// <value>The dummy chapters duration.</value>
+ public int DummyChapterDuration { get; set; } = 0;
+
+ /// <summary>
+ /// Gets or sets the chapter image resolution.
+ /// </summary>
+ /// <value>The chapter image resolution.</value>
+ public ImageResolution ChapterImageResolution { get; set; } = ImageResolution.MatchSource;
+
+ /// <summary>
+ /// Gets or sets the limit for parallel image encoding.
+ /// </summary>
+ /// <value>The limit for parallel image encoding.</value>
+ public int ParallelImageEncodingLimit { get; set; } = 0;
}
}
diff --git a/MediaBrowser.Model/Cryptography/PasswordHash.cs b/MediaBrowser.Model/Cryptography/PasswordHash.cs
index 32a34d23c..ccb361c13 100644
--- a/MediaBrowser.Model/Cryptography/PasswordHash.cs
+++ b/MediaBrowser.Model/Cryptography/PasswordHash.cs
@@ -29,12 +29,7 @@ namespace MediaBrowser.Model.Cryptography
public PasswordHash(string id, byte[] hash, byte[] salt, Dictionary<string, string> parameters)
{
- ArgumentNullException.ThrowIfNull(id);
-
- if (id.Length == 0)
- {
- throw new ArgumentException("String can't be empty", nameof(id));
- }
+ ArgumentException.ThrowIfNullOrEmpty(id);
Id = id;
_hash = hash;
@@ -85,7 +80,8 @@ namespace MediaBrowser.Model.Cryptography
{
throw new FormatException("Hash string must contain a valid id");
}
- else if (nextSegment == -1)
+
+ if (nextSegment == -1)
{
return new PasswordHash(hashString.ToString(), Array.Empty<byte>());
}
diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
index 573422416..f5e1a3c49 100644
--- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs
+++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
@@ -136,12 +136,26 @@ namespace MediaBrowser.Model.Dlna
return !condition.IsRequired;
}
- if (int.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var expected))
+ var conditionType = condition.Condition;
+ if (condition.Condition == ProfileConditionType.EqualsAny)
{
- switch (condition.Condition)
+ foreach (var singleConditionString in condition.Value.AsSpan().Split('|'))
+ {
+ if (int.TryParse(singleConditionString, NumberStyles.Integer, CultureInfo.InvariantCulture, out int conditionValue)
+ && conditionValue.Equals(currentValue))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (int.TryParse(condition.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var expected))
+ {
+ switch (conditionType)
{
case ProfileConditionType.Equals:
- case ProfileConditionType.EqualsAny:
return currentValue.Value.Equals(expected);
case ProfileConditionType.GreaterThanEqual:
return currentValue.Value >= expected;
@@ -212,9 +226,24 @@ namespace MediaBrowser.Model.Dlna
return !condition.IsRequired;
}
- if (double.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var expected))
+ var conditionType = condition.Condition;
+ if (condition.Condition == ProfileConditionType.EqualsAny)
{
- switch (condition.Condition)
+ foreach (var singleConditionString in condition.Value.AsSpan().Split('|'))
+ {
+ if (double.TryParse(singleConditionString, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out double conditionValue)
+ && conditionValue.Equals(currentValue))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (double.TryParse(condition.Value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var expected))
+ {
+ switch (conditionType)
{
case ProfileConditionType.Equals:
return currentValue.Value.Equals(expected);
diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs
index c6befdd85..978004268 100644
--- a/MediaBrowser.Model/Dlna/ContainerProfile.cs
+++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Model.Dlna
[XmlAttribute("type")]
public DlnaProfileType Type { get; set; }
- public ProfileCondition[]? Conditions { get; set; } = Array.Empty<ProfileCondition>();
+ public ProfileCondition[] Conditions { get; set; } = Array.Empty<ProfileCondition>();
[XmlAttribute("container")]
public string Container { get; set; } = string.Empty;
@@ -36,7 +36,7 @@ namespace MediaBrowser.Model.Dlna
public static bool ContainsContainer(string? profileContainers, string? inputContainer)
{
var isNegativeList = false;
- if (profileContainers != null && profileContainers.StartsWith('-'))
+ if (profileContainers is not null && profileContainers.StartsWith('-'))
{
isNegativeList = true;
profileContainers = profileContainers.Substring(1);
@@ -52,7 +52,7 @@ namespace MediaBrowser.Model.Dlna
public static bool ContainsContainer(string[]? profileContainers, bool isNegativeList, string? inputContainer)
{
- if (profileContainers == null || profileContainers.Length == 0)
+ if (profileContainers is null || profileContainers.Length == 0)
{
// Empty profiles always support all containers/codecs
return true;
diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
index 1a9576361..1d5d0b1de 100644
--- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
+++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
@@ -192,7 +192,7 @@ namespace MediaBrowser.Model.Dlna
var orgPnValues = new List<string>();
- if (mediaProfile != null && !string.IsNullOrEmpty(mediaProfile.OrgPn))
+ if (mediaProfile is not null && !string.IsNullOrEmpty(mediaProfile.OrgPn))
{
orgPnValues.AddRange(mediaProfile.OrgPn.Split(',', StringSplitOptions.RemoveEmptyEntries));
}
diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
index 03c3a7265..f68235d86 100644
--- a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
+++ b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
@@ -18,17 +18,17 @@ namespace MediaBrowser.Model.Dlna
[XmlAttribute("type")]
public DlnaProfileType Type { get; set; }
- public bool SupportsContainer(string container)
+ public bool SupportsContainer(string? container)
{
return ContainerProfile.ContainsContainer(Container, container);
}
- public bool SupportsVideoCodec(string codec)
+ public bool SupportsVideoCodec(string? codec)
{
return Type == DlnaProfileType.Video && ContainerProfile.ContainsContainer(VideoCodec, codec);
}
- public bool SupportsAudioCodec(string codec)
+ public bool SupportsAudioCodec(string? codec)
{
return (Type == DlnaProfileType.Audio || Type == DlnaProfileType.Video) && ContainerProfile.ContainsContainer(AudioCodec, codec);
}
diff --git a/MediaBrowser.Model/Dlna/ITranscoderSupport.cs b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs
index a70ce44cc..d7397399d 100644
--- a/MediaBrowser.Model/Dlna/ITranscoderSupport.cs
+++ b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs
@@ -10,22 +10,4 @@ namespace MediaBrowser.Model.Dlna
bool CanExtractSubtitles(string codec);
}
-
- public class FullTranscoderSupport : ITranscoderSupport
- {
- public bool CanEncodeToAudioCodec(string codec)
- {
- return true;
- }
-
- public bool CanEncodeToSubtitleCodec(string codec)
- {
- return true;
- }
-
- public bool CanExtractSubtitles(string codec)
- {
- return true;
- }
- }
}
diff --git a/MediaBrowser.Model/Dlna/AudioOptions.cs b/MediaBrowser.Model/Dlna/MediaOptions.cs
index 33755e746..7ec0dd473 100644
--- a/MediaBrowser.Model/Dlna/AudioOptions.cs
+++ b/MediaBrowser.Model/Dlna/MediaOptions.cs
@@ -1,17 +1,17 @@
-#nullable disable
-#pragma warning disable CS1591
-
using System;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Model.Dlna
{
/// <summary>
- /// Class AudioOptions.
+ /// Class MediaOptions.
/// </summary>
- public class AudioOptions
+ public class MediaOptions
{
- public AudioOptions()
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MediaOptions"/> class.
+ /// </summary>
+ public MediaOptions()
{
Context = EncodingContext.Streaming;
@@ -19,28 +19,60 @@ namespace MediaBrowser.Model.Dlna
EnableDirectStream = true;
}
+ /// <summary>
+ /// Gets or sets a value indicating whether direct playback is allowed.
+ /// </summary>
public bool EnableDirectPlay { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether direct streaming is allowed.
+ /// </summary>
public bool EnableDirectStream { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether direct playback is forced.
+ /// </summary>
public bool ForceDirectPlay { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether direct streaming is forced.
+ /// </summary>
public bool ForceDirectStream { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether audio stream copy is allowed.
+ /// </summary>
public bool AllowAudioStreamCopy { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether video stream copy is allowed.
+ /// </summary>
+ public bool AllowVideoStreamCopy { get; set; }
+
+ /// <summary>
+ /// Gets or sets the item id.
+ /// </summary>
public Guid ItemId { get; set; }
- public MediaSourceInfo[] MediaSources { get; set; }
+ /// <summary>
+ /// Gets or sets the media sources.
+ /// </summary>
+ public MediaSourceInfo[] MediaSources { get; set; } = Array.Empty<MediaSourceInfo>();
- public DeviceProfile Profile { get; set; }
+ /// <summary>
+ /// Gets or sets the device profile.
+ /// </summary>
+ required public DeviceProfile Profile { get; set; }
/// <summary>
/// Gets or sets a media source id. Optional. Only needed if a specific AudioStreamIndex or SubtitleStreamIndex are requested.
/// </summary>
- public string MediaSourceId { get; set; }
+ public string? MediaSourceId { get; set; }
- public string DeviceId { get; set; }
+ /// <summary>
+ /// Gets or sets the device id.
+ /// </summary>
+ public string? DeviceId { get; set; }
/// <summary>
/// Gets or sets an override of supported number of audio channels
@@ -49,7 +81,7 @@ namespace MediaBrowser.Model.Dlna
public int? MaxAudioChannels { get; set; }
/// <summary>
- /// Gets or sets the application's configured quality setting.
+ /// Gets or sets the application's configured maximum bitrate.
/// </summary>
public int? MaxBitrate { get; set; }
@@ -66,6 +98,16 @@ namespace MediaBrowser.Model.Dlna
public int? AudioTranscodingBitrate { get; set; }
/// <summary>
+ /// Gets or sets an override for the audio stream index.
+ /// </summary>
+ public int? AudioStreamIndex { get; set; }
+
+ /// <summary>
+ /// Gets or sets an override for the subtitle stream index.
+ /// </summary>
+ public int? SubtitleStreamIndex { get; set; }
+
+ /// <summary>
/// Gets the maximum bitrate.
/// </summary>
/// <param name="isAudio">Whether or not this is audio.</param>
@@ -77,7 +119,7 @@ namespace MediaBrowser.Model.Dlna
return MaxBitrate;
}
- if (Profile == null)
+ if (Profile is null)
{
return null;
}
diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
index 94071b419..ce422a228 100644
--- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
+++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
@@ -39,7 +39,7 @@ namespace MediaBrowser.Model.Dlna
}
var resolutionConfig = GetResolutionConfiguration(outputBitrate);
- if (resolutionConfig != null)
+ if (resolutionConfig is not null)
{
var originvalValue = maxWidth;
diff --git a/MediaBrowser.Model/Dlna/SearchCriteria.cs b/MediaBrowser.Model/Dlna/SearchCriteria.cs
index b1fc48c08..77d6a55ea 100644
--- a/MediaBrowser.Model/Dlna/SearchCriteria.cs
+++ b/MediaBrowser.Model/Dlna/SearchCriteria.cs
@@ -9,10 +9,7 @@ namespace MediaBrowser.Model.Dlna
{
public SearchCriteria(string search)
{
- if (search.Length == 0)
- {
- throw new ArgumentException("String can't be empty.", nameof(search));
- }
+ ArgumentException.ThrowIfNullOrEmpty(search);
SearchType = SearchType.Unknown;
diff --git a/MediaBrowser.Model/Dlna/SortCriteria.cs b/MediaBrowser.Model/Dlna/SortCriteria.cs
index 7fef16e53..7df53c6d1 100644
--- a/MediaBrowser.Model/Dlna/SortCriteria.cs
+++ b/MediaBrowser.Model/Dlna/SortCriteria.cs
@@ -9,7 +9,7 @@ namespace MediaBrowser.Model.Dlna
{
public SortCriteria(string sortOrder)
{
- if (!string.IsNullOrEmpty(sortOrder) && Enum.TryParse<SortOrder>(sortOrder, true, out var sortOrderValue))
+ if (Enum.TryParse<SortOrder>(sortOrder, true, out var sortOrderValue))
{
SortOrder = sortOrderValue;
}
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 6e9b943f7..0a955e917 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -1,6 +1,3 @@
-#nullable disable
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -13,6 +10,9 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Model.Dlna
{
+ /// <summary>
+ /// Class StreamBuilder.
+ /// </summary>
public class StreamBuilder
{
// Aliases
@@ -23,76 +23,182 @@ namespace MediaBrowser.Model.Dlna
private readonly ILogger _logger;
private readonly ITranscoderSupport _transcoderSupport;
-
+ private static readonly string[] _supportedHlsVideoCodecs = new string[] { "h264", "hevc" };
+ private static readonly string[] _supportedHlsAudioCodecsTs = new string[] { "aac", "ac3", "eac3", "mp3" };
+ private static readonly string[] _supportedHlsAudioCodecsMp4 = new string[] { "aac", "ac3", "eac3", "mp3", "alac", "flac", "opus", "dca", "truehd" };
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StreamBuilder"/> class.
+ /// </summary>
+ /// <param name="transcoderSupport">The <see cref="ITranscoderSupport"/> object.</param>
+ /// <param name="logger">The <see cref="ILogger"/> object.</param>
public StreamBuilder(ITranscoderSupport transcoderSupport, ILogger logger)
{
_transcoderSupport = transcoderSupport;
_logger = logger;
}
- public StreamBuilder(ILogger<StreamBuilder> logger)
- : this(new FullTranscoderSupport(), logger)
+ /// <summary>
+ /// Gets the optimal audio stream.
+ /// </summary>
+ /// <param name="options">The <see cref="MediaOptions"/> object to get the audio stream from.</param>
+ /// <returns>The <see cref="StreamInfo"/> of the optimal audio stream.</returns>
+ public StreamInfo? GetOptimalAudioStream(MediaOptions options)
{
+ ValidateMediaOptions(options, false);
+
+ var streams = new List<StreamInfo>();
+ foreach (var mediaSource in options.MediaSources)
+ {
+ if (!(string.IsNullOrEmpty(options.MediaSourceId)
+ || string.Equals(mediaSource.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase)))
+ {
+ continue;
+ }
+
+ StreamInfo? streamInfo = GetOptimalAudioStream(mediaSource, options);
+ if (streamInfo is not null)
+ {
+ streamInfo.DeviceId = options.DeviceId;
+ streamInfo.DeviceProfileId = options.Profile.Id;
+ streams.Add(streamInfo);
+ }
+ }
+
+ return GetOptimalStream(streams, options.GetMaxBitrate(true) ?? 0);
}
- public StreamInfo BuildAudioItem(AudioOptions options)
+ private StreamInfo? GetOptimalAudioStream(MediaSourceInfo item, MediaOptions options)
{
- ValidateAudioInput(options);
+ var playlistItem = new StreamInfo
+ {
+ ItemId = options.ItemId,
+ MediaType = DlnaProfileType.Audio,
+ MediaSource = item,
+ RunTimeTicks = item.RunTimeTicks,
+ Context = options.Context,
+ DeviceProfile = options.Profile
+ };
- var mediaSources = new List<MediaSourceInfo>();
- foreach (MediaSourceInfo i in options.MediaSources)
+ if (options.ForceDirectPlay)
+ {
+ playlistItem.PlayMethod = PlayMethod.DirectPlay;
+ playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
+ return playlistItem;
+ }
+
+ if (options.ForceDirectStream)
{
- if (string.IsNullOrEmpty(options.MediaSourceId) ||
- string.Equals(i.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))
+ playlistItem.PlayMethod = PlayMethod.DirectStream;
+ playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
+ return playlistItem;
+ }
+
+ MediaStream audioStream = item.GetDefaultAudioStream(null);
+
+ var directPlayInfo = GetAudioDirectPlayProfile(item, audioStream, options);
+
+ var directPlayMethod = directPlayInfo.PlayMethod;
+ var transcodeReasons = directPlayInfo.TranscodeReasons;
+
+ var inputAudioChannels = audioStream?.Channels;
+ var inputAudioBitrate = audioStream?.BitRate;
+ var inputAudioSampleRate = audioStream?.SampleRate;
+ var inputAudioBitDepth = audioStream?.BitDepth;
+
+ if (directPlayMethod.HasValue)
+ {
+ var profile = options.Profile;
+ var audioFailureConditions = GetProfileConditionsForAudio(profile.CodecProfiles, item.Container, audioStream?.Codec, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, true);
+ var audioFailureReasons = AggregateFailureConditions(item, profile, "AudioCodecProfile", audioFailureConditions);
+ transcodeReasons |= audioFailureReasons;
+
+ if (audioFailureReasons == 0)
{
- mediaSources.Add(i);
+ playlistItem.PlayMethod = directPlayMethod.Value;
+ playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio, directPlayInfo.Profile);
+
+ return playlistItem;
}
}
- var streams = new List<StreamInfo>();
- foreach (MediaSourceInfo i in mediaSources)
+ TranscodingProfile? transcodingProfile = null;
+ foreach (var tcProfile in options.Profile.TranscodingProfiles)
{
- StreamInfo streamInfo = BuildAudioItem(i, options);
- if (streamInfo != null)
+ if (tcProfile.Type == playlistItem.MediaType
+ && tcProfile.Context == options.Context
+ && _transcoderSupport.CanEncodeToAudioCodec(tcProfile.AudioCodec ?? tcProfile.Container))
{
- streams.Add(streamInfo);
+ transcodingProfile = tcProfile;
+ break;
}
}
- foreach (StreamInfo stream in streams)
+ if (transcodingProfile != null)
{
- stream.DeviceId = options.DeviceId;
- stream.DeviceProfileId = options.Profile.Id;
+ if (!item.SupportsTranscoding)
+ {
+ return null;
+ }
+
+ SetStreamInfoOptionsFromTranscodingProfile(item, playlistItem, transcodingProfile);
+
+ var audioTranscodingConditions = GetProfileConditionsForAudio(options.Profile.CodecProfiles, transcodingProfile.Container, transcodingProfile.AudioCodec, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, false).ToArray();
+ ApplyTranscodingConditions(playlistItem, audioTranscodingConditions, null, true, true);
+
+ // Honor requested max channels
+ playlistItem.GlobalMaxAudioChannels = options.MaxAudioChannels;
+
+ var configuredBitrate = options.GetMaxBitrate(true);
+
+ long transcodingBitrate = options.AudioTranscodingBitrate
+ ?? (options.Context == EncodingContext.Streaming ? options.Profile.MusicStreamingTranscodingBitrate : null)
+ ?? configuredBitrate
+ ?? 128000;
+
+ if (configuredBitrate.HasValue)
+ {
+ transcodingBitrate = Math.Min(configuredBitrate.Value, transcodingBitrate);
+ }
+
+ var longBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate);
+ playlistItem.AudioBitrate = longBitrate > int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate);
}
- return GetOptimalStream(streams, options.GetMaxBitrate(true) ?? 0);
+ playlistItem.TranscodeReasons = transcodeReasons;
+ return playlistItem;
}
- public StreamInfo BuildVideoItem(VideoOptions options)
+ /// <summary>
+ /// Gets the optimal video stream.
+ /// </summary>
+ /// <param name="options">The <see cref="MediaOptions"/> object to get the video stream from.</param>
+ /// <returns>The <see cref="StreamInfo"/> of the optimal video stream.</returns>
+ public StreamInfo? GetOptimalVideoStream(MediaOptions options)
{
- ValidateInput(options);
+ ValidateMediaOptions(options, true);
var mediaSources = new List<MediaSourceInfo>();
- foreach (MediaSourceInfo i in options.MediaSources)
+ foreach (var mediaSourceInfo in options.MediaSources)
{
- if (string.IsNullOrEmpty(options.MediaSourceId) ||
- string.Equals(i.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))
+ if (string.IsNullOrEmpty(options.MediaSourceId)
+ || string.Equals(mediaSourceInfo.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))
{
- mediaSources.Add(i);
+ mediaSources.Add(mediaSourceInfo);
}
}
var streams = new List<StreamInfo>();
- foreach (MediaSourceInfo i in mediaSources)
+ foreach (var mediaSourceInfo in mediaSources)
{
- var streamInfo = BuildVideoItem(i, options);
- if (streamInfo != null)
+ var streamInfo = BuildVideoItem(mediaSourceInfo, options);
+ if (streamInfo is not null)
{
streams.Add(streamInfo);
}
}
- foreach (StreamInfo stream in streams)
+ foreach (var stream in streams)
{
stream.DeviceId = options.DeviceId;
stream.DeviceProfileId = options.Profile.Id;
@@ -101,7 +207,7 @@ namespace MediaBrowser.Model.Dlna
return GetOptimalStream(streams, options.GetMaxBitrate(false) ?? 0);
}
- private static StreamInfo GetOptimalStream(List<StreamInfo> streams, long maxBitrate)
+ private static StreamInfo? GetOptimalStream(List<StreamInfo> streams, long maxBitrate)
=> SortMediaSources(streams, maxBitrate).FirstOrDefault();
private static IOrderedEnumerable<StreamInfo> SortMediaSources(List<StreamInfo> streams, long maxBitrate)
@@ -236,7 +342,15 @@ namespace MediaBrowser.Model.Dlna
}
}
- public static string NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, DeviceProfile profile, DlnaProfileType type, DirectPlayProfile playProfile = null)
+ /// <summary>
+ /// Normalizes input container.
+ /// </summary>
+ /// <param name="inputContainer">The input container.</param>
+ /// <param name="profile">The <see cref="DeviceProfile"/>.</param>
+ /// <param name="type">The <see cref="DlnaProfileType"/>.</param>
+ /// <param name="playProfile">The <see cref="DirectPlayProfile"/> object to get the video stream from.</param>
+ /// <returns>The the normalized input container.</returns>
+ public static string? NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, DeviceProfile? profile, DlnaProfileType type, DirectPlayProfile? playProfile = null)
{
if (string.IsNullOrEmpty(inputContainer))
{
@@ -245,9 +359,9 @@ namespace MediaBrowser.Model.Dlna
var formats = ContainerProfile.SplitValue(inputContainer);
- if (profile != null)
+ if (profile is not null)
{
- var playProfiles = playProfile == null ? profile.DirectPlayProfiles : new[] { playProfile };
+ var playProfiles = playProfile is null ? profile.DirectPlayProfiles : new[] { playProfile };
foreach (var format in formats)
{
foreach (var directPlayProfile in playProfiles)
@@ -264,113 +378,12 @@ namespace MediaBrowser.Model.Dlna
return formats[0];
}
- private StreamInfo BuildAudioItem(MediaSourceInfo item, AudioOptions options)
- {
- StreamInfo playlistItem = new StreamInfo
- {
- ItemId = options.ItemId,
- MediaType = DlnaProfileType.Audio,
- MediaSource = item,
- RunTimeTicks = item.RunTimeTicks,
- Context = options.Context,
- DeviceProfile = options.Profile
- };
-
- if (options.ForceDirectPlay)
- {
- playlistItem.PlayMethod = PlayMethod.DirectPlay;
- playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
- return playlistItem;
- }
-
- if (options.ForceDirectStream)
- {
- playlistItem.PlayMethod = PlayMethod.DirectStream;
- playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
- return playlistItem;
- }
-
- var audioStream = item.GetDefaultAudioStream(null);
-
- var directPlayInfo = GetAudioDirectPlayProfile(item, audioStream, options);
-
- var directPlayMethod = directPlayInfo.PlayMethod;
- var transcodeReasons = directPlayInfo.TranscodeReasons;
-
- int? inputAudioChannels = audioStream?.Channels;
- int? inputAudioBitrate = audioStream?.BitDepth;
- int? inputAudioSampleRate = audioStream?.SampleRate;
- int? inputAudioBitDepth = audioStream?.BitDepth;
-
- if (directPlayMethod.HasValue)
- {
- var profile = options.Profile;
- var audioFailureConditions = GetProfileConditionsForAudio(profile.CodecProfiles, item.Container, audioStream?.Codec, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, true);
- var audioFailureReasons = AggregateFailureConditions(item, profile, "AudioCodecProfile", audioFailureConditions);
- transcodeReasons |= audioFailureReasons;
-
- if (audioFailureReasons == 0)
- {
- playlistItem.PlayMethod = directPlayMethod.Value;
- playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio, directPlayInfo.Profile);
-
- return playlistItem;
- }
- }
-
- TranscodingProfile transcodingProfile = null;
- foreach (var i in options.Profile.TranscodingProfiles)
- {
- if (i.Type == playlistItem.MediaType
- && i.Context == options.Context
- && _transcoderSupport.CanEncodeToAudioCodec(i.AudioCodec ?? i.Container))
- {
- transcodingProfile = i;
- break;
- }
- }
-
- if (transcodingProfile != null)
- {
- if (!item.SupportsTranscoding)
- {
- return null;
- }
-
- SetStreamInfoOptionsFromTranscodingProfile(item, playlistItem, transcodingProfile);
-
- var audioTranscodingConditions = GetProfileConditionsForAudio(options.Profile.CodecProfiles, transcodingProfile.Container, transcodingProfile.AudioCodec, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, false).ToArray();
- ApplyTranscodingConditions(playlistItem, audioTranscodingConditions, null, true, true);
-
- // Honor requested max channels
- playlistItem.GlobalMaxAudioChannels = options.MaxAudioChannels;
-
- var configuredBitrate = options.GetMaxBitrate(true);
-
- long transcodingBitrate = options.AudioTranscodingBitrate ??
- (options.Context == EncodingContext.Streaming ? options.Profile.MusicStreamingTranscodingBitrate : null) ??
- configuredBitrate ??
- 128000;
-
- if (configuredBitrate.HasValue)
- {
- transcodingBitrate = Math.Min(configuredBitrate.Value, transcodingBitrate);
- }
-
- var longBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate);
- playlistItem.AudioBitrate = longBitrate > int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate);
- }
-
- playlistItem.TranscodeReasons = transcodeReasons;
- return playlistItem;
- }
-
- private (DirectPlayProfile Profile, PlayMethod? PlayMethod, TranscodeReason TranscodeReasons) GetAudioDirectPlayProfile(MediaSourceInfo item, MediaStream audioStream, AudioOptions options)
+ private (DirectPlayProfile? Profile, PlayMethod? PlayMethod, TranscodeReason TranscodeReasons) GetAudioDirectPlayProfile(MediaSourceInfo item, MediaStream audioStream, MediaOptions options)
{
var directPlayProfile = options.Profile.DirectPlayProfiles
.FirstOrDefault(x => x.Type == DlnaProfileType.Audio && IsAudioDirectPlaySupported(x, item, audioStream));
- if (directPlayProfile == null)
+ if (directPlayProfile is null)
{
_logger.LogDebug(
"Profile: {0}, No audio direct play profiles found for {1} with codec {2}",
@@ -381,14 +394,13 @@ namespace MediaBrowser.Model.Dlna
return (null, null, GetTranscodeReasonsFromDirectPlayProfile(item, null, audioStream, options.Profile.DirectPlayProfiles));
}
- var playMethods = new List<PlayMethod>();
TranscodeReason transcodeReasons = 0;
// The profile describes what the device supports
// If device requirements are satisfied then allow both direct stream and direct play
if (item.SupportsDirectPlay)
{
- if (IsItemBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectPlay))
+ if (!IsBitrateLimitExceeded(item, options.GetMaxBitrate(true) ?? 0))
{
if (options.EnableDirectPlay)
{
@@ -404,7 +416,7 @@ namespace MediaBrowser.Model.Dlna
// While options takes the network and other factors into account. Only applies to direct stream
if (item.SupportsDirectStream)
{
- if (IsItemBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream))
+ if (!IsBitrateLimitExceeded(item, options.GetMaxBitrate(true) ?? 0))
{
if (options.EnableDirectStream)
{
@@ -420,14 +432,13 @@ namespace MediaBrowser.Model.Dlna
return (directPlayProfile, null, transcodeReasons);
}
- private static TranscodeReason GetTranscodeReasonsFromDirectPlayProfile(MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable<DirectPlayProfile> directPlayProfiles)
+ private static TranscodeReason GetTranscodeReasonsFromDirectPlayProfile(MediaSourceInfo item, MediaStream? videoStream, MediaStream audioStream, IEnumerable<DirectPlayProfile> directPlayProfiles)
{
- var mediaType = videoStream == null ? DlnaProfileType.Audio : DlnaProfileType.Video;
+ var mediaType = videoStream is null ? DlnaProfileType.Audio : DlnaProfileType.Video;
var containerSupported = false;
var audioSupported = false;
var videoSupported = false;
- TranscodeReason reasons = 0;
foreach (var profile in directPlayProfiles)
{
@@ -436,9 +447,9 @@ namespace MediaBrowser.Model.Dlna
{
containerSupported = true;
- videoSupported = videoStream == null || profile.SupportsVideoCodec(videoStream.Codec);
+ videoSupported = videoStream is null || profile.SupportsVideoCodec(videoStream.Codec);
- audioSupported = audioStream == null || profile.SupportsAudioCodec(audioStream.Codec);
+ audioSupported = audioStream is null || profile.SupportsAudioCodec(audioStream.Codec);
if (videoSupported && audioSupported)
{
@@ -447,6 +458,7 @@ namespace MediaBrowser.Model.Dlna
}
}
+ TranscodeReason reasons = 0;
if (!containerSupported)
{
reasons |= TranscodeReason.ContainerNotSupported;
@@ -522,8 +534,7 @@ namespace MediaBrowser.Model.Dlna
}
playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
- if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels)
- && int.TryParse(transcodingProfile.MaxAudioChannels, NumberStyles.Any, CultureInfo.InvariantCulture, out int transcodingMaxAudioChannels))
+ if (int.TryParse(transcodingProfile.MaxAudioChannels, CultureInfo.InvariantCulture, out int transcodingMaxAudioChannels))
{
playlistItem.TranscodingMaxAudioChannels = transcodingMaxAudioChannels;
}
@@ -547,7 +558,7 @@ namespace MediaBrowser.Model.Dlna
}
}
- private static void SetStreamInfoOptionsFromDirectPlayProfile(VideoOptions options, MediaSourceInfo item, StreamInfo playlistItem, DirectPlayProfile directPlayProfile)
+ private static void SetStreamInfoOptionsFromDirectPlayProfile(MediaOptions options, MediaSourceInfo item, StreamInfo playlistItem, DirectPlayProfile? directPlayProfile)
{
var container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Video, directPlayProfile);
var protocol = "http";
@@ -559,10 +570,10 @@ namespace MediaBrowser.Model.Dlna
playlistItem.SubProtocol = protocol;
playlistItem.VideoCodecs = new[] { item.VideoStream.Codec };
- playlistItem.AudioCodecs = ContainerProfile.SplitValue(directPlayProfile.AudioCodec);
+ playlistItem.AudioCodecs = ContainerProfile.SplitValue(directPlayProfile?.AudioCodec);
}
- private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options)
+ private StreamInfo BuildVideoItem(MediaSourceInfo item, MediaOptions options)
{
ArgumentNullException.ThrowIfNull(item);
@@ -580,13 +591,13 @@ namespace MediaBrowser.Model.Dlna
var subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null;
var audioStream = item.GetDefaultAudioStream(options.AudioStreamIndex ?? item.DefaultAudioStreamIndex);
- if (audioStream != null)
+ if (audioStream is not null)
{
playlistItem.AudioStreamIndex = audioStream.Index;
}
// Collect candidate audio streams
- ICollection<MediaStream> candidateAudioStreams = audioStream == null ? Array.Empty<MediaStream>() : new[] { audioStream };
+ ICollection<MediaStream> candidateAudioStreams = audioStream is null ? Array.Empty<MediaStream>() : new[] { audioStream };
if (!options.AudioStreamIndex.HasValue || options.AudioStreamIndex < 0)
{
if (audioStream?.IsDefault == true)
@@ -601,11 +612,21 @@ namespace MediaBrowser.Model.Dlna
var videoStream = item.VideoStream;
- var directPlayBitrateEligibility = IsBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectPlay);
- var directStreamBitrateEligibility = IsBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectStream);
- bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayBitrateEligibility == 0);
- bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directStreamBitrateEligibility == 0);
- var transcodeReasons = directPlayBitrateEligibility | directStreamBitrateEligibility;
+ var bitrateLimitExceeded = IsBitrateLimitExceeded(item, options.GetMaxBitrate(false) ?? 0);
+ var isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || !bitrateLimitExceeded);
+ var isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || !bitrateLimitExceeded);
+ TranscodeReason transcodeReasons = 0;
+
+ // Force transcode or remux for BD/DVD folders
+ if (item.VideoType == VideoType.Dvd || item.VideoType == VideoType.BluRay)
+ {
+ isEligibleForDirectPlay = false;
+ }
+
+ if (bitrateLimitExceeded)
+ {
+ transcodeReasons = TranscodeReason.ContainerBitrateExceedsLimit;
+ }
_logger.LogDebug(
"Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
@@ -614,7 +635,7 @@ namespace MediaBrowser.Model.Dlna
isEligibleForDirectPlay,
isEligibleForDirectStream);
- DirectPlayProfile directPlayProfile = null;
+ DirectPlayProfile? directPlayProfile = null;
if (isEligibleForDirectPlay || isEligibleForDirectStream)
{
// See if it can be direct played
@@ -643,18 +664,18 @@ namespace MediaBrowser.Model.Dlna
else if (directPlay == PlayMethod.DirectStream)
{
playlistItem.AudioStreamIndex = audioStream?.Index;
- if (audioStream != null)
+ if (audioStream is not null)
{
- playlistItem.AudioCodecs = ContainerProfile.SplitValue(directPlayProfile.AudioCodec);
+ playlistItem.AudioCodecs = ContainerProfile.SplitValue(directPlayProfile?.AudioCodec);
}
SetStreamInfoOptionsFromDirectPlayProfile(options, item, playlistItem, directPlayProfile);
- BuildStreamVideoItem(playlistItem, options, item, videoStream, audioStream, candidateAudioStreams, directPlayProfile.Container, directPlayProfile.VideoCodec, directPlayProfile.AudioCodec);
+ BuildStreamVideoItem(playlistItem, options, item, videoStream, audioStream, candidateAudioStreams, directPlayProfile?.Container, directPlayProfile?.VideoCodec, directPlayProfile?.AudioCodec);
}
- if (subtitleStream != null)
+ if (subtitleStream is not null)
{
- var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, directPlay.Value, _transcoderSupport, directPlayProfile.Container, null);
+ var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, directPlay.Value, _transcoderSupport, directPlayProfile?.Container, null);
playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method;
playlistItem.SubtitleFormat = subtitleProfile.Format;
@@ -678,7 +699,7 @@ namespace MediaBrowser.Model.Dlna
// Can't direct play, find the transcoding profile
// If we do this for direct-stream we will overwrite the info
var transcodingProfile = GetVideoTranscodeProfile(item, options, videoStream, audioStream, candidateAudioStreams, subtitleStream, playlistItem);
- if (transcodingProfile != null)
+ if (transcodingProfile is not null)
{
SetStreamInfoOptionsFromTranscodingProfile(item, playlistItem, transcodingProfile);
@@ -686,7 +707,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem.PlayMethod = PlayMethod.Transcode;
- if (subtitleStream != null)
+ if (subtitleStream is not null)
{
var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Container, transcodingProfile.Protocol);
@@ -702,7 +723,7 @@ namespace MediaBrowser.Model.Dlna
}
}
- _logger.LogInformation(
+ _logger.LogDebug(
"StreamBuilder.BuildVideoItem( Profile={0}, Path={1}, AudioStreamIndex={2}, SubtitleStreamIndex={3} ) => ( PlayMethod={4}, TranscodeReason={5} ) {6}",
options.Profile.Name ?? "Anonymous Profile",
item.Path ?? "Unknown path",
@@ -716,7 +737,14 @@ namespace MediaBrowser.Model.Dlna
return playlistItem;
}
- private TranscodingProfile GetVideoTranscodeProfile(MediaSourceInfo item, VideoOptions options, MediaStream videoStream, MediaStream audioStream, IEnumerable<MediaStream> candidateAudioStreams, MediaStream subtitleStream, StreamInfo playlistItem)
+ private TranscodingProfile? GetVideoTranscodeProfile(
+ MediaSourceInfo item,
+ MediaOptions options,
+ MediaStream? videoStream,
+ MediaStream? audioStream,
+ IEnumerable<MediaStream> candidateAudioStreams,
+ MediaStream? subtitleStream,
+ StreamInfo playlistItem)
{
if (!(item.SupportsTranscoding || item.SupportsDirectStream))
{
@@ -729,7 +757,7 @@ namespace MediaBrowser.Model.Dlna
if (options.AllowVideoStreamCopy)
{
// prefer direct copy profile
- float videoFramerate = videoStream == null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
+ float videoFramerate = videoStream?.AverageFrameRate ?? videoStream?.RealFrameRate ?? 0;
TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : item.Timestamp;
int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio);
int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video);
@@ -740,7 +768,7 @@ namespace MediaBrowser.Model.Dlna
if (ContainerProfile.ContainsContainer(videoCodecs, item.VideoStream?.Codec))
{
- var videoCodec = transcodingProfile.VideoCodec;
+ var videoCodec = videoStream?.Codec;
var container = transcodingProfile.Container;
var appliedVideoConditions = options.Profile.CodecProfiles
.Where(i => i.Type == CodecType.Video &&
@@ -763,12 +791,28 @@ namespace MediaBrowser.Model.Dlna
return transcodingProfiles.FirstOrDefault();
}
- private void BuildStreamVideoItem(StreamInfo playlistItem, VideoOptions options, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable<MediaStream> candidateAudioStreams, string container, string videoCodec, string audioCodec)
+ private void BuildStreamVideoItem(
+ StreamInfo playlistItem,
+ MediaOptions options,
+ MediaSourceInfo item,
+ MediaStream? videoStream,
+ MediaStream? audioStream,
+ IEnumerable<MediaStream> candidateAudioStreams,
+ string? container,
+ string? videoCodec,
+ string? audioCodec)
{
// Prefer matching video codecs
var videoCodecs = ContainerProfile.SplitValue(videoCodec);
+
+ // Enforce HLS video codec restrictions
+ if (string.Equals(playlistItem.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
+ {
+ videoCodecs = videoCodecs.Where(codec => _supportedHlsVideoCodecs.Contains(codec)).ToArray();
+ }
+
var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null;
- if (directVideoCodec != null)
+ if (directVideoCodec is not null)
{
// merge directVideoCodec to videoCodecs
Array.Resize(ref videoCodecs, videoCodecs.Length + 1);
@@ -780,12 +824,12 @@ namespace MediaBrowser.Model.Dlna
// Copy video codec options as a starting point, this applies to transcode and direct-stream
playlistItem.MaxFramerate = videoStream?.AverageFrameRate;
var qualifier = videoStream?.Codec;
- if (videoStream?.Level != null)
+ if (videoStream?.Level is not null)
{
playlistItem.SetOption(qualifier, "level", videoStream.Level.Value.ToString(CultureInfo.InvariantCulture));
}
- if (videoStream?.BitDepth != null)
+ if (videoStream?.BitDepth is not null)
{
playlistItem.SetOption(qualifier, "videobitdepth", videoStream.BitDepth.Value.ToString(CultureInfo.InvariantCulture));
}
@@ -795,16 +839,30 @@ namespace MediaBrowser.Model.Dlna
playlistItem.SetOption(qualifier, "profile", videoStream.Profile.ToLowerInvariant());
}
- if (videoStream != null && videoStream.Level != 0)
+ if (videoStream is not null && videoStream.Level != 0)
{
playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString());
}
// Prefer matching audio codecs, could do better here
var audioCodecs = ContainerProfile.SplitValue(audioCodec);
+
+ // Enforce HLS audio codec restrictions
+ if (string.Equals(playlistItem.SubProtocol, "hls", StringComparison.OrdinalIgnoreCase))
+ {
+ if (string.Equals(playlistItem.Container, "mp4", StringComparison.OrdinalIgnoreCase))
+ {
+ audioCodecs = audioCodecs.Where(codec => _supportedHlsAudioCodecsMp4.Contains(codec)).ToArray();
+ }
+ else
+ {
+ audioCodecs = audioCodecs.Where(codec => _supportedHlsAudioCodecsTs.Contains(codec)).ToArray();
+ }
+ }
+
var directAudioStream = candidateAudioStreams.FirstOrDefault(stream => ContainerProfile.ContainsContainer(audioCodecs, stream.Codec));
playlistItem.AudioCodecs = audioCodecs;
- if (directAudioStream != null)
+ if (directAudioStream is not null)
{
audioStream = directAudioStream;
playlistItem.AudioStreamIndex = audioStream.Index;
@@ -830,15 +888,15 @@ namespace MediaBrowser.Model.Dlna
int? bitDepth = videoStream?.BitDepth;
int? videoBitrate = videoStream?.BitRate;
double? videoLevel = videoStream?.Level;
- string videoProfile = videoStream?.Profile;
- string videoRangeType = videoStream?.VideoRangeType;
- float videoFramerate = videoStream == null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
+ string? videoProfile = videoStream?.Profile;
+ string? videoRangeType = videoStream?.VideoRangeType;
+ float videoFramerate = videoStream is null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
bool? isAnamorphic = videoStream?.IsAnamorphic;
bool? isInterlaced = videoStream?.IsInterlaced;
- string videoCodecTag = videoStream?.CodecTag;
+ string? videoCodecTag = videoStream?.CodecTag;
bool? isAvc = videoStream?.IsAVC;
- TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : item.Timestamp;
+ TransportStreamTimestamp? timestamp = videoStream is null ? TransportStreamTimestamp.None : item.Timestamp;
int? packetLength = videoStream?.PacketLength;
int? refFrames = videoStream?.RefFrames;
@@ -847,7 +905,7 @@ namespace MediaBrowser.Model.Dlna
var appliedVideoConditions = options.Profile.CodecProfiles
.Where(i => i.Type == CodecType.Video &&
- i.ContainsAnyCodec(videoCodec, container) &&
+ i.ContainsAnyCodec(videoStream?.Codec, container) &&
i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)));
var isFirstAppliedCodecProfile = true;
foreach (var i in appliedVideoConditions)
@@ -867,29 +925,29 @@ namespace MediaBrowser.Model.Dlna
// Honor requested max channels
playlistItem.GlobalMaxAudioChannels = options.MaxAudioChannels;
- int audioBitrate = GetAudioBitrate(options.GetMaxBitrate(false) ?? 0, playlistItem.TargetAudioCodec, audioStream, playlistItem);
+ int audioBitrate = GetAudioBitrate(options.GetMaxBitrate(true) ?? 0, playlistItem.TargetAudioCodec, audioStream, playlistItem);
playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
- bool? isSecondaryAudio = audioStream == null ? null : item.IsSecondaryAudio(audioStream);
- int? inputAudioBitrate = audioStream == null ? null : audioStream.BitRate;
- int? audioChannels = audioStream == null ? null : audioStream.Channels;
- string audioProfile = audioStream == null ? null : audioStream.Profile;
- int? inputAudioSampleRate = audioStream == null ? null : audioStream.SampleRate;
- int? inputAudioBitDepth = audioStream == null ? null : audioStream.BitDepth;
+ bool? isSecondaryAudio = audioStream is null ? null : item.IsSecondaryAudio(audioStream);
+ int? inputAudioBitrate = audioStream?.BitRate;
+ int? audioChannels = audioStream?.Channels;
+ string? audioProfile = audioStream?.Profile;
+ int? inputAudioSampleRate = audioStream?.SampleRate;
+ int? inputAudioBitDepth = audioStream?.BitDepth;
var appliedAudioConditions = options.Profile.CodecProfiles
.Where(i => i.Type == CodecType.VideoAudio &&
- i.ContainsAnyCodec(audioCodec, container) &&
+ i.ContainsAnyCodec(audioStream?.Codec, container) &&
i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio)));
isFirstAppliedCodecProfile = true;
- foreach (var i in appliedAudioConditions)
+ foreach (var codecProfile in appliedAudioConditions)
{
var transcodingAudioCodecs = ContainerProfile.SplitValue(audioCodec);
foreach (var transcodingAudioCodec in transcodingAudioCodecs)
{
- if (i.ContainsAnyCodec(transcodingAudioCodec, container))
+ if (codecProfile.ContainsAnyCodec(transcodingAudioCodec, container))
{
- ApplyTranscodingConditions(playlistItem, i.Conditions, transcodingAudioCodec, true, isFirstAppliedCodecProfile);
+ ApplyTranscodingConditions(playlistItem, codecProfile.Conditions, transcodingAudioCodec, true, isFirstAppliedCodecProfile);
isFirstAppliedCodecProfile = false;
break;
}
@@ -923,7 +981,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem?.TranscodeReasons);
}
- private static int GetDefaultAudioBitrate(string audioCodec, int? audioChannels)
+ private static int GetDefaultAudioBitrate(string? audioCodec, int? audioChannels)
{
if (!string.IsNullOrEmpty(audioCodec))
{
@@ -956,16 +1014,16 @@ namespace MediaBrowser.Model.Dlna
return 192000;
}
- private static int GetAudioBitrate(long maxTotalBitrate, string[] targetAudioCodecs, MediaStream audioStream, StreamInfo item)
+ private static int GetAudioBitrate(long maxTotalBitrate, string[] targetAudioCodecs, MediaStream? audioStream, StreamInfo item)
{
- string targetAudioCodec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0];
+ string? targetAudioCodec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0];
int? targetAudioChannels = item.GetTargetAudioChannels(targetAudioCodec);
int defaultBitrate;
int encoderAudioBitrateLimit = int.MaxValue;
- if (audioStream == null)
+ if (audioStream is null)
{
defaultBitrate = 192000;
}
@@ -982,7 +1040,7 @@ namespace MediaBrowser.Model.Dlna
&& audioStream.Channels.HasValue
&& audioStream.Channels.Value <= targetAudioChannels.Value
&& !string.IsNullOrEmpty(audioStream.Codec)
- && targetAudioCodecs != null
+ && targetAudioCodecs is not null
&& targetAudioCodecs.Length > 0
&& !Array.Exists(targetAudioCodecs, elem => string.Equals(audioStream.Codec, elem, StringComparison.OrdinalIgnoreCase)))
{
@@ -1017,31 +1075,38 @@ namespace MediaBrowser.Model.Dlna
{
return 128000;
}
- else if (totalBitrate <= 2000000)
+
+ if (totalBitrate <= 2000000)
{
return 384000;
}
- else if (totalBitrate <= 3000000)
+
+ if (totalBitrate <= 3000000)
{
return 448000;
}
- else if (totalBitrate <= 4000000)
+
+ if (totalBitrate <= 4000000)
{
return 640000;
}
- else if (totalBitrate <= 5000000)
+
+ if (totalBitrate <= 5000000)
{
return 768000;
}
- else if (totalBitrate <= 10000000)
+
+ if (totalBitrate <= 10000000)
{
return 1536000;
}
- else if (totalBitrate <= 15000000)
+
+ if (totalBitrate <= 15000000)
{
return 2304000;
}
- else if (totalBitrate <= 20000000)
+
+ if (totalBitrate <= 20000000)
{
return 3584000;
}
@@ -1049,13 +1114,13 @@ namespace MediaBrowser.Model.Dlna
return 7168000;
}
- private (DirectPlayProfile Profile, PlayMethod? PlayMethod, int? AudioStreamIndex, TranscodeReason TranscodeReasons) GetVideoDirectPlayProfile(
- VideoOptions options,
+ private (DirectPlayProfile? Profile, PlayMethod? PlayMethod, int? AudioStreamIndex, TranscodeReason TranscodeReasons) GetVideoDirectPlayProfile(
+ MediaOptions options,
MediaSourceInfo mediaSource,
- MediaStream videoStream,
- MediaStream audioStream,
+ MediaStream? videoStream,
+ MediaStream? audioStream,
ICollection<MediaStream> candidateAudioStreams,
- MediaStream subtitleStream,
+ MediaStream? subtitleStream,
bool isEligibleForDirectPlay,
bool isEligibleForDirectStream)
{
@@ -1078,15 +1143,15 @@ namespace MediaBrowser.Model.Dlna
int? bitDepth = videoStream?.BitDepth;
int? videoBitrate = videoStream?.BitRate;
double? videoLevel = videoStream?.Level;
- string videoProfile = videoStream?.Profile;
- string videoRangeType = videoStream?.VideoRangeType;
- float videoFramerate = videoStream == null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
+ string? videoProfile = videoStream?.Profile;
+ string? videoRangeType = videoStream?.VideoRangeType;
+ float videoFramerate = videoStream is null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
bool? isAnamorphic = videoStream?.IsAnamorphic;
bool? isInterlaced = videoStream?.IsInterlaced;
- string videoCodecTag = videoStream?.CodecTag;
+ string? videoCodecTag = videoStream?.CodecTag;
bool? isAvc = videoStream?.IsAVC;
- TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : mediaSource.Timestamp;
+ TransportStreamTimestamp? timestamp = videoStream is null ? TransportStreamTimestamp.None : mediaSource.Timestamp;
int? packetLength = videoStream?.PacketLength;
int? refFrames = videoStream?.RefFrames;
@@ -1111,7 +1176,8 @@ namespace MediaBrowser.Model.Dlna
profile,
"VideoCodecProfile",
profile.CodecProfiles
- .Where(codecProfile => codecProfile.Type == CodecType.Video && codecProfile.ContainsAnyCodec(videoStream?.Codec, container) &&
+ .Where(codecProfile => codecProfile.Type == CodecType.Video &&
+ codecProfile.ContainsAnyCodec(videoStream?.Codec, container) &&
!checkVideoConditions(codecProfile.ApplyConditions).Any())
.SelectMany(codecProfile => checkVideoConditions(codecProfile.Conditions)));
@@ -1119,7 +1185,7 @@ namespace MediaBrowser.Model.Dlna
var audioStreamMatches = candidateAudioStreams.ToDictionary(s => s, audioStream => CheckVideoAudioStreamDirectPlay(options, mediaSource, container, audioStream));
TranscodeReason subtitleProfileReasons = 0;
- if (subtitleStream != null)
+ if (subtitleStream is not null)
{
var subtitleProfile = GetSubtitleProfile(mediaSource, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.DirectPlay, _transcoderSupport, container, null);
@@ -1141,7 +1207,6 @@ namespace MediaBrowser.Model.Dlna
var reason = a & flag;
if (reason != 0)
{
- a = reason;
return index;
}
@@ -1151,6 +1216,8 @@ namespace MediaBrowser.Model.Dlna
return index;
};
+ var containerSupported = false;
+
// Check DirectPlay profiles to see if it can be direct played
var analyzedProfiles = profile.DirectPlayProfiles
.Where(directPlayProfile => directPlayProfile.Type == DlnaProfileType.Video)
@@ -1164,20 +1231,24 @@ namespace MediaBrowser.Model.Dlna
{
directPlayProfileReasons |= TranscodeReason.ContainerNotSupported;
}
+ else
+ {
+ containerSupported = true;
+ }
// Check video codec
- string videoCodec = videoStream?.Codec;
+ string? videoCodec = videoStream?.Codec;
if (!directPlayProfile.SupportsVideoCodec(videoCodec))
{
directPlayProfileReasons |= TranscodeReason.VideoCodecNotSupported;
}
// Check audio codec
- MediaStream selectedAudioStream = null;
+ MediaStream? selectedAudioStream = null;
if (candidateAudioStreams.Any())
{
selectedAudioStream = candidateAudioStreams.FirstOrDefault(audioStream => directPlayProfile.SupportsAudioCodec(audioStream.Codec));
- if (selectedAudioStream == null)
+ if (selectedAudioStream is null)
{
directPlayProfileReasons |= TranscodeReason.AudioCodecNotSupported;
}
@@ -1206,7 +1277,7 @@ namespace MediaBrowser.Model.Dlna
{
playMethod = PlayMethod.DirectPlay;
}
- else if (directStreamFailureReasons == 0 && isEligibleForDirectStream && mediaSource.SupportsDirectStream && directPlayProfile != null)
+ else if (directStreamFailureReasons == 0 && isEligibleForDirectStream && mediaSource.SupportsDirectStream)
{
playMethod = PlayMethod.DirectStream;
}
@@ -1218,17 +1289,20 @@ namespace MediaBrowser.Model.Dlna
.ThenByDescending(analysis => analysis.Rank)
.ThenBy(analysis => analysis.Order)
.ToArray()
- .ToLookup(analysis => analysis.Result.PlayMethod != null);
+ .ToLookup(analysis => analysis.Result.PlayMethod is not null);
var profileMatch = analyzedProfiles[true]
.Select(analysis => analysis.Result)
.FirstOrDefault();
- if (profileMatch.Profile != null)
+ if (profileMatch.Profile is not null)
{
return profileMatch;
}
- var failureReasons = analyzedProfiles[false].Select(analysis => analysis.Result).FirstOrDefault().TranscodeReason;
+ var failureReasons = analyzedProfiles[false]
+ .Select(analysis => analysis.Result)
+ .Where(result => !containerSupported || (result.TranscodeReason & TranscodeReason.ContainerNotSupported) == 0)
+ .FirstOrDefault().TranscodeReason;
if (failureReasons == 0)
{
failureReasons = TranscodeReason.DirectPlayError;
@@ -1237,7 +1311,7 @@ namespace MediaBrowser.Model.Dlna
return (Profile: null, PlayMethod: null, AudioStreamIndex: null, TranscodeReasons: failureReasons);
}
- private TranscodeReason CheckVideoAudioStreamDirectPlay(VideoOptions options, MediaSourceInfo mediaSource, string container, MediaStream audioStream)
+ private TranscodeReason CheckVideoAudioStreamDirectPlay(MediaOptions options, MediaSourceInfo mediaSource, string container, MediaStream audioStream)
{
var profile = options.Profile;
var audioFailureConditions = GetProfileConditionsForVideoAudio(profile.CodecProfiles, container, audioStream.Codec, audioStream.Channels, audioStream.BitRate, audioStream.SampleRate, audioStream.BitDepth, audioStream.Profile, mediaSource.IsSecondaryAudio(audioStream));
@@ -1274,31 +1348,25 @@ namespace MediaBrowser.Model.Dlna
mediaSource.Path ?? "Unknown path");
}
- private TranscodeReason IsBitrateEligibleForDirectPlayback(
- MediaSourceInfo item,
- long maxBitrate,
- VideoOptions options,
- PlayMethod playMethod)
- {
- bool result = IsItemBitrateEligibleForDirectPlayback(item, maxBitrate, playMethod);
- if (!result)
- {
- return TranscodeReason.ContainerBitrateExceedsLimit;
- }
- else
- {
- return 0;
- }
- }
-
+ /// <summary>
+ /// Normalizes input container.
+ /// </summary>
+ /// <param name="mediaSource">The <see cref="MediaSourceInfo"/>.</param>
+ /// <param name="subtitleStream">The <see cref="MediaStream"/> of the subtitle stream.</param>
+ /// <param name="subtitleProfiles">The list of supported <see cref="SubtitleProfile"/>s.</param>
+ /// <param name="playMethod">The <see cref="PlayMethod"/>.</param>
+ /// <param name="transcoderSupport">The <see cref="ITranscoderSupport"/>.</param>
+ /// <param name="outputContainer">The output container.</param>
+ /// <param name="transcodingSubProtocol">The subtitle transoding protocol.</param>
+ /// <returns>The the normalized input container.</returns>
public static SubtitleProfile GetSubtitleProfile(
MediaSourceInfo mediaSource,
MediaStream subtitleStream,
SubtitleProfile[] subtitleProfiles,
PlayMethod playMethod,
ITranscoderSupport transcoderSupport,
- string outputContainer,
- string transcodingSubProtocol)
+ string? outputContainer,
+ string? transcodingSubProtocol)
{
if (!subtitleStream.IsExternal && (playMethod != PlayMethod.Transcode || !string.Equals(transcodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase)))
{
@@ -1371,7 +1439,7 @@ namespace MediaBrowser.Model.Dlna
};
}
- private static bool IsSubtitleEmbedSupported(string transcodingContainer)
+ private static bool IsSubtitleEmbedSupported(string? transcodingContainer)
{
if (!string.IsNullOrEmpty(transcodingContainer))
{
@@ -1383,7 +1451,8 @@ namespace MediaBrowser.Model.Dlna
{
return false;
}
- else if (ContainerProfile.ContainsContainer(normalizedContainers, "mkv")
+
+ if (ContainerProfile.ContainsContainer(normalizedContainers, "mkv")
|| ContainerProfile.ContainsContainer(normalizedContainers, "matroska"))
{
return true;
@@ -1393,7 +1462,7 @@ namespace MediaBrowser.Model.Dlna
return false;
}
- private static SubtitleProfile GetExternalSubtitleProfile(MediaSourceInfo mediaSource, MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, bool allowConversion)
+ private static SubtitleProfile? GetExternalSubtitleProfile(MediaSourceInfo mediaSource, MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, bool allowConversion)
{
foreach (var profile in subtitleProfiles)
{
@@ -1448,67 +1517,60 @@ namespace MediaBrowser.Model.Dlna
return null;
}
- private bool IsItemBitrateEligibleForDirectPlayback(MediaSourceInfo item, long maxBitrate, PlayMethod playMethod)
+ private bool IsBitrateLimitExceeded(MediaSourceInfo item, long maxBitrate)
{
- // Don't restrict by bitrate if coming from an external domain
+ // Don't restrict bitrate if item is remote.
if (item.IsRemote)
{
- return true;
+ return false;
}
- long requestedMaxBitrate = maxBitrate > 0 ? maxBitrate : 1000000;
+ // If no maximum bitrate is set, default to no maximum bitrate.
+ long requestedMaxBitrate = maxBitrate > 0 ? maxBitrate : int.MaxValue;
- // If we don't know the bitrate, then force a transcode if requested max bitrate is under 40 mbps
+ // If we don't know the item bitrate, then force a transcode if requested max bitrate is under 40 mbps
int itemBitrate = item.Bitrate ?? 40000000;
if (itemBitrate > requestedMaxBitrate)
{
_logger.LogDebug(
- "Bitrate exceeds {PlayBackMethod} limit: media bitrate: {MediaBitrate}, max bitrate: {MaxBitrate}",
- playMethod,
+ "Bitrate exceeds limit: media bitrate: {MediaBitrate}, max bitrate: {MaxBitrate}",
itemBitrate,
requestedMaxBitrate);
- return false;
- }
-
- return true;
- }
-
- private static void ValidateInput(VideoOptions options)
- {
- ValidateAudioInput(options);
-
- if (options.AudioStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId))
- {
- throw new ArgumentException("MediaSourceId is required when a specific audio stream is requested");
+ return true;
}
- if (options.SubtitleStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId))
- {
- throw new ArgumentException("MediaSourceId is required when a specific subtitle stream is requested");
- }
+ return false;
}
- private static void ValidateAudioInput(AudioOptions options)
+ private static void ValidateMediaOptions(MediaOptions options, bool isMediaSource)
{
if (options.ItemId.Equals(default))
{
- throw new ArgumentException("ItemId is required");
+ ArgumentException.ThrowIfNullOrEmpty(options.DeviceId);
}
- if (string.IsNullOrEmpty(options.DeviceId))
+ if (options.Profile is null)
{
- throw new ArgumentException("DeviceId is required");
+ throw new ArgumentException("Profile is required");
}
- if (options.Profile == null)
+ if (options.MediaSources is null)
{
- throw new ArgumentException("Profile is required");
+ throw new ArgumentException("MediaSources is required");
}
- if (options.MediaSources == null)
+ if (isMediaSource)
{
- throw new ArgumentException("MediaSources is required");
+ if (options.AudioStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId))
+ {
+ throw new ArgumentException("MediaSourceId is required when a specific audio stream is requested");
+ }
+
+ if (options.SubtitleStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId))
+ {
+ throw new ArgumentException("MediaSourceId is required when a specific subtitle stream is requested");
+ }
}
}
@@ -1524,7 +1586,8 @@ namespace MediaBrowser.Model.Dlna
bool? isSecondaryAudio)
{
return codecProfiles
- .Where(profile => profile.Type == CodecType.VideoAudio && profile.ContainsAnyCodec(codec, container) &&
+ .Where(profile => profile.Type == CodecType.VideoAudio &&
+ profile.ContainsAnyCodec(codec, container) &&
profile.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio)))
.SelectMany(profile => profile.Conditions)
.Where(condition => !ConditionProcessor.IsVideoAudioConditionSatisfied(condition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio));
@@ -1533,7 +1596,7 @@ namespace MediaBrowser.Model.Dlna
private static IEnumerable<ProfileCondition> GetProfileConditionsForAudio(
IEnumerable<CodecProfile> codecProfiles,
string container,
- string codec,
+ string? codec,
int? audioChannels,
int? audioBitrate,
int? audioSampleRate,
@@ -1541,7 +1604,8 @@ namespace MediaBrowser.Model.Dlna
bool checkConditions)
{
var conditions = codecProfiles
- .Where(profile => profile.Type == CodecType.Audio && profile.ContainsAnyCodec(codec, container) &&
+ .Where(profile => profile.Type == CodecType.Audio &&
+ profile.ContainsAnyCodec(codec, container) &&
profile.ApplyConditions.All(applyCondition => ConditionProcessor.IsAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth)))
.SelectMany(profile => profile.Conditions);
@@ -1553,7 +1617,7 @@ namespace MediaBrowser.Model.Dlna
return conditions.Where(condition => !ConditionProcessor.IsAudioConditionSatisfied(condition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth));
}
- private void ApplyTranscodingConditions(StreamInfo item, IEnumerable<ProfileCondition> conditions, string qualifier, bool enableQualifiedConditions, bool enableNonQualifiedConditions)
+ private void ApplyTranscodingConditions(StreamInfo item, IEnumerable<ProfileCondition> conditions, string? qualifier, bool enableQualifiedConditions, bool enableNonQualifiedConditions)
{
foreach (ProfileCondition condition in conditions)
{
@@ -1579,7 +1643,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num))
+ if (int.TryParse(value, CultureInfo.InvariantCulture, out var num))
{
if (condition.Condition == ProfileConditionType.Equals)
{
@@ -1605,7 +1669,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num))
+ if (int.TryParse(value, CultureInfo.InvariantCulture, out var num))
{
if (condition.Condition == ProfileConditionType.Equals)
{
@@ -1641,7 +1705,7 @@ namespace MediaBrowser.Model.Dlna
}
}
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num))
+ if (int.TryParse(value, CultureInfo.InvariantCulture, out var num))
{
if (condition.Condition == ProfileConditionType.Equals)
{
@@ -1765,7 +1829,7 @@ namespace MediaBrowser.Model.Dlna
}
}
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num))
+ if (int.TryParse(value, CultureInfo.InvariantCulture, out var num))
{
if (condition.Condition == ProfileConditionType.Equals)
{
@@ -1801,7 +1865,7 @@ namespace MediaBrowser.Model.Dlna
}
}
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num))
+ if (int.TryParse(value, CultureInfo.InvariantCulture, out var num))
{
if (condition.Condition == ProfileConditionType.Equals)
{
@@ -1827,8 +1891,8 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- // change from split by | to comma
- // strip spaces to avoid having to encode
+ // Change from split by | to comma
+ // Strip spaces to avoid having to encode
var values = value
.Split('|', StringSplitOptions.RemoveEmptyEntries);
@@ -1891,7 +1955,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num))
+ if (int.TryParse(value, CultureInfo.InvariantCulture, out var num))
{
if (condition.Condition == ProfileConditionType.Equals)
{
@@ -1917,7 +1981,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num))
+ if (int.TryParse(value, CultureInfo.InvariantCulture, out var num))
{
if (condition.Condition == ProfileConditionType.Equals)
{
@@ -1943,7 +2007,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num))
+ if (float.TryParse(value, CultureInfo.InvariantCulture, out var num))
{
if (condition.Condition == ProfileConditionType.Equals)
{
@@ -1969,7 +2033,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num))
+ if (int.TryParse(value, CultureInfo.InvariantCulture, out var num))
{
if (condition.Condition == ProfileConditionType.Equals)
{
@@ -1995,7 +2059,7 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num))
+ if (int.TryParse(value, CultureInfo.InvariantCulture, out var num))
{
if (condition.Condition == ProfileConditionType.Equals)
{
@@ -2029,7 +2093,7 @@ namespace MediaBrowser.Model.Dlna
}
// Check audio codec
- string audioCodec = audioStream?.Codec;
+ string? audioCodec = audioStream?.Codec;
if (!profile.SupportsAudioCodec(audioCodec))
{
return false;
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 5cfa2e7e3..a78a28e13 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -107,9 +107,8 @@ namespace MediaBrowser.Model.Dlna
public string MediaSourceId => MediaSource?.Id;
- public bool IsDirectStream =>
- PlayMethod == PlayMethod.DirectStream ||
- PlayMethod == PlayMethod.DirectPlay;
+ public bool IsDirectStream => MediaSource?.VideoType is not (VideoType.Dvd or VideoType.BluRay)
+ && PlayMethod is PlayMethod.DirectStream or PlayMethod.DirectPlay;
/// <summary>
/// Gets the audio stream that will be used.
@@ -215,7 +214,7 @@ namespace MediaBrowser.Model.Dlna
var stream = TargetVideoStream;
return MaxFramerate.HasValue && !IsDirectStream
? MaxFramerate
- : stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate;
+ : stream is null ? null : stream.AverageFrameRate ?? stream.RealFrameRate;
}
}
@@ -431,7 +430,7 @@ namespace MediaBrowser.Model.Dlna
return totalBitrate.HasValue ?
Convert.ToInt64(totalBitrate.Value * totalSeconds) :
- (long?)null;
+ null;
}
return null;
@@ -460,7 +459,7 @@ namespace MediaBrowser.Model.Dlna
return !IsDirectStream
? defaultValue
- : MediaSource == null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None;
+ : MediaSource is null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None;
}
}
@@ -521,7 +520,7 @@ namespace MediaBrowser.Model.Dlna
{
var videoStream = TargetVideoStream;
- if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
+ if (videoStream is not null && videoStream.Width.HasValue && videoStream.Height.HasValue)
{
ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value);
@@ -540,7 +539,7 @@ namespace MediaBrowser.Model.Dlna
{
var videoStream = TargetVideoStream;
- if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue)
+ if (videoStream is not null && videoStream.Width.HasValue && videoStream.Height.HasValue)
{
ImageDimensions size = new ImageDimensions(videoStream.Width.Value, videoStream.Height.Value);
@@ -620,10 +619,7 @@ namespace MediaBrowser.Model.Dlna
public string ToUrl(string baseUrl, string accessToken)
{
- if (string.IsNullOrEmpty(baseUrl))
- {
- throw new ArgumentNullException(nameof(baseUrl));
- }
+ ArgumentException.ThrowIfNullOrEmpty(baseUrl);
var list = new List<string>();
foreach (NameValuePair pair in BuildParams(this, accessToken))
@@ -664,10 +660,7 @@ namespace MediaBrowser.Model.Dlna
private string GetUrl(string baseUrl, string queryString)
{
- if (string.IsNullOrEmpty(baseUrl))
- {
- throw new ArgumentNullException(nameof(baseUrl));
- }
+ ArgumentException.ThrowIfNullOrEmpty(baseUrl);
string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container;
@@ -928,12 +921,8 @@ namespace MediaBrowser.Model.Dlna
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))
+ if (int.TryParse(value, CultureInfo.InvariantCulture, out var result))
{
return result;
}
@@ -944,12 +933,8 @@ namespace MediaBrowser.Model.Dlna
public int? GetTargetAudioBitDepth(string codec)
{
var value = GetOption(codec, "audiobitdepth");
- if (string.IsNullOrEmpty(value))
- {
- return null;
- }
- if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
+ if (int.TryParse(value, CultureInfo.InvariantCulture, out var result))
{
return result;
}
@@ -960,12 +945,8 @@ namespace MediaBrowser.Model.Dlna
public double? GetTargetVideoLevel(string codec)
{
var value = GetOption(codec, "level");
- if (string.IsNullOrEmpty(value))
- {
- return null;
- }
- if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+ if (double.TryParse(value, CultureInfo.InvariantCulture, out var result))
{
return result;
}
@@ -976,12 +957,8 @@ namespace MediaBrowser.Model.Dlna
public int? GetTargetRefFrames(string codec)
{
var value = GetOption(codec, "maxrefframes");
- if (string.IsNullOrEmpty(value))
- {
- return null;
- }
- if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
+ if (int.TryParse(value, CultureInfo.InvariantCulture, out var result))
{
return result;
}
diff --git a/MediaBrowser.Model/Dlna/VideoOptions.cs b/MediaBrowser.Model/Dlna/VideoOptions.cs
deleted file mode 100644
index 0cb80af54..000000000
--- a/MediaBrowser.Model/Dlna/VideoOptions.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.Dlna
-{
- /// <summary>
- /// Class VideoOptions.
- /// </summary>
- public class VideoOptions : AudioOptions
- {
- public int? AudioStreamIndex { get; set; }
-
- public int? SubtitleStreamIndex { get; set; }
-
- public bool AllowVideoStreamCopy { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Drawing/DrawingUtils.cs b/MediaBrowser.Model/Drawing/DrawingUtils.cs
index 556792768..2040d26bb 100644
--- a/MediaBrowser.Model/Drawing/DrawingUtils.cs
+++ b/MediaBrowser.Model/Drawing/DrawingUtils.cs
@@ -71,18 +71,18 @@ namespace MediaBrowser.Model.Drawing
int? fillHeight)
{
// Return original size if input is invalid.
- if ((fillWidth == null || fillWidth == 0)
- && (fillHeight == null || fillHeight == 0))
+ if ((fillWidth is null || fillWidth == 0)
+ && (fillHeight is null || fillHeight == 0))
{
return size;
}
- if (fillWidth == null || fillWidth == 0)
+ if (fillWidth is null || fillWidth == 0)
{
fillWidth = 1;
}
- if (fillHeight == null || fillHeight == 0)
+ if (fillHeight is null || fillHeight == 0)
{
fillHeight = 1;
}
diff --git a/MediaBrowser.Model/Drawing/ImageResolution.cs b/MediaBrowser.Model/Drawing/ImageResolution.cs
new file mode 100644
index 000000000..34738b799
--- /dev/null
+++ b/MediaBrowser.Model/Drawing/ImageResolution.cs
@@ -0,0 +1,52 @@
+namespace MediaBrowser.Model.Drawing;
+
+/// <summary>
+/// Enum ImageResolution.
+/// </summary>
+public enum ImageResolution
+{
+ /// <summary>
+ /// MatchSource.
+ /// </summary>
+ MatchSource = 0,
+
+ /// <summary>
+ /// 144p.
+ /// </summary>
+ P144 = 1,
+
+ /// <summary>
+ /// 240p.
+ /// </summary>
+ P240 = 2,
+
+ /// <summary>
+ /// 360p.
+ /// </summary>
+ P360 = 3,
+
+ /// <summary>
+ /// 480p.
+ /// </summary>
+ P480 = 4,
+
+ /// <summary>
+ /// 720p.
+ /// </summary>
+ P720 = 5,
+
+ /// <summary>
+ /// 1080p.
+ /// </summary>
+ P1080 = 6,
+
+ /// <summary>
+ /// 1440p.
+ /// </summary>
+ P1440 = 7,
+
+ /// <summary>
+ /// 2160p.
+ /// </summary>
+ P2160 = 8
+}
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 2a86fded2..8fab1ca6d 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -780,6 +780,12 @@ namespace MediaBrowser.Model.Dto
public string TimerId { get; set; }
/// <summary>
+ /// Gets or sets the LUFS value.
+ /// </summary>
+ /// <value>The LUFS Value.</value>
+ public float? LUFS { get; set; }
+
+ /// <summary>
/// Gets or sets the current program.
/// </summary>
/// <value>The current program.</value>
diff --git a/MediaBrowser.Model/Dto/BaseItemPerson.cs b/MediaBrowser.Model/Dto/BaseItemPerson.cs
index 6b920b0ef..d3bcf492d 100644
--- a/MediaBrowser.Model/Dto/BaseItemPerson.cs
+++ b/MediaBrowser.Model/Dto/BaseItemPerson.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.Dto
@@ -33,7 +34,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
- public string Type { get; set; }
+ public PersonKind Type { get; set; }
/// <summary>
/// Gets or sets the primary image tag.
@@ -52,6 +53,6 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value><c>true</c> if this instance has primary image; otherwise, <c>false</c>.</value>
[JsonIgnore]
- public bool HasPrimaryImage => PrimaryImageTag != null;
+ public bool HasPrimaryImage => PrimaryImageTag is not null;
}
}
diff --git a/MediaBrowser.Model/Dto/ImageOptions.cs b/MediaBrowser.Model/Dto/ImageOptions.cs
deleted file mode 100644
index 3f4405f1e..000000000
--- a/MediaBrowser.Model/Dto/ImageOptions.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-#nullable disable
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Entities;
-
-namespace MediaBrowser.Model.Dto
-{
- /// <summary>
- /// Class ImageOptions.
- /// </summary>
- public class ImageOptions
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="ImageOptions" /> class.
- /// </summary>
- public ImageOptions()
- {
- EnableImageEnhancers = true;
- }
-
- /// <summary>
- /// Gets or sets the type of the image.
- /// </summary>
- /// <value>The type of the image.</value>
- public ImageType ImageType { get; set; }
-
- /// <summary>
- /// Gets or sets the index of the image.
- /// </summary>
- /// <value>The index of the image.</value>
- public int? ImageIndex { get; set; }
-
- /// <summary>
- /// Gets or sets the width.
- /// </summary>
- /// <value>The width.</value>
- public int? Width { get; set; }
-
- /// <summary>
- /// Gets or sets the height.
- /// </summary>
- /// <value>The height.</value>
- public int? Height { get; set; }
-
- /// <summary>
- /// Gets or sets the width of the max.
- /// </summary>
- /// <value>The width of the max.</value>
- public int? MaxWidth { get; set; }
-
- /// <summary>
- /// Gets or sets the height of the max.
- /// </summary>
- /// <value>The height of the max.</value>
- public int? MaxHeight { get; set; }
-
- /// <summary>
- /// Gets or sets the quality.
- /// </summary>
- /// <value>The quality.</value>
- public int? Quality { get; set; }
-
- /// <summary>
- /// Gets or sets the image tag.
- /// If set this will result in strong, unconditional response caching.
- /// </summary>
- /// <value>The hash.</value>
- public string Tag { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [crop whitespace].
- /// </summary>
- /// <value><c>null</c> if [crop whitespace] contains no value, <c>true</c> if [crop whitespace]; otherwise, <c>false</c>.</value>
- public bool? CropWhitespace { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable image enhancers].
- /// </summary>
- /// <value><c>true</c> if [enable image enhancers]; otherwise, <c>false</c>.</value>
- public bool EnableImageEnhancers { get; set; }
-
- /// <summary>
- /// Gets or sets the format.
- /// </summary>
- /// <value>The format.</value>
- public ImageFormat? Format { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether [add played indicator].
- /// </summary>
- /// <value><c>true</c> if [add played indicator]; otherwise, <c>false</c>.</value>
- public bool AddPlayedIndicator { get; set; }
-
- /// <summary>
- /// Gets or sets the percent played.
- /// </summary>
- /// <value>The percent played.</value>
- public int? PercentPlayed { get; set; }
-
- /// <summary>
- /// Gets or sets the un played count.
- /// </summary>
- /// <value>The un played count.</value>
- public int? UnPlayedCount { get; set; }
-
- /// <summary>
- /// Gets or sets the color of the background.
- /// </summary>
- /// <value>The color of the background.</value>
- public string BackgroundColor { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
index c348e83ae..520832aee 100644
--- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs
+++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
@@ -134,7 +134,7 @@ namespace MediaBrowser.Model.Dto
public void InferTotalBitrate(bool force = false)
{
- if (MediaStreams == null)
+ if (MediaStreams is null)
{
return;
}
diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs
index 256d7b10f..05019741e 100644
--- a/MediaBrowser.Model/Dto/UserDto.cs
+++ b/MediaBrowser.Model/Dto/UserDto.cs
@@ -66,6 +66,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets a value indicating whether this instance has configured easy password.
/// </summary>
/// <value><c>true</c> if this instance has configured easy password; otherwise, <c>false</c>.</value>
+ [Obsolete("Easy Password has been replaced with Quick Connect")]
public bool HasConfiguredEasyPassword { get; set; }
/// <summary>
diff --git a/MediaBrowser.Model/Entities/DownMixStereoAlgorithms.cs b/MediaBrowser.Model/Entities/DownMixStereoAlgorithms.cs
new file mode 100644
index 000000000..385cd6a34
--- /dev/null
+++ b/MediaBrowser.Model/Entities/DownMixStereoAlgorithms.cs
@@ -0,0 +1,23 @@
+namespace MediaBrowser.Model.Entities;
+
+/// <summary>
+/// An enum representing an algorithm to downmix 6ch+ to stereo.
+/// Algorithms sourced from https://superuser.com/questions/852400/properly-downmix-5-1-to-stereo-using-ffmpeg/1410620#1410620.
+/// </summary>
+public enum DownMixStereoAlgorithms
+{
+ /// <summary>
+ /// No special algorithm.
+ /// </summary>
+ None = 0,
+
+ /// <summary>
+ /// Algorithm by Dave_750.
+ /// </summary>
+ Dave750 = 1,
+
+ /// <summary>
+ /// Nightmode Dialogue algorithm.
+ /// </summary>
+ NightmodeDialogue = 2
+}
diff --git a/MediaBrowser.Model/Entities/IHasShares.cs b/MediaBrowser.Model/Entities/IHasShares.cs
new file mode 100644
index 000000000..b34d1a037
--- /dev/null
+++ b/MediaBrowser.Model/Entities/IHasShares.cs
@@ -0,0 +1,12 @@
+namespace MediaBrowser.Model.Entities;
+
+/// <summary>
+/// Interface for access to shares.
+/// </summary>
+public interface IHasShares
+{
+ /// <summary>
+ /// Gets or sets the shares.
+ /// </summary>
+ Share[] Shares { get; set; }
+}
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index 344ebaf80..47341f4e1 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -635,11 +635,12 @@ namespace MediaBrowser.Model.Entities
// sub = external .sub file
- return !codec.Contains("pgs", StringComparison.OrdinalIgnoreCase) &&
- !codec.Contains("dvd", StringComparison.OrdinalIgnoreCase) &&
- !codec.Contains("dvbsub", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase) &&
- !string.Equals(codec, "dvb_subtitle", StringComparison.OrdinalIgnoreCase);
+ return !codec.Contains("pgs", StringComparison.OrdinalIgnoreCase)
+ && !codec.Contains("dvd", StringComparison.OrdinalIgnoreCase)
+ && !codec.Contains("dvbsub", StringComparison.OrdinalIgnoreCase)
+ && !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase)
+ && !string.Equals(codec, "sup", StringComparison.OrdinalIgnoreCase)
+ && !string.Equals(codec, "dvb_subtitle", StringComparison.OrdinalIgnoreCase);
}
public bool SupportsSubtitleConversionTo(string toCodec)
diff --git a/MediaBrowser.Model/Entities/MetadataProvider.cs b/MediaBrowser.Model/Entities/MetadataProvider.cs
index 37e3d8864..bd8db9941 100644
--- a/MediaBrowser.Model/Entities/MetadataProvider.cs
+++ b/MediaBrowser.Model/Entities/MetadataProvider.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Entities
{
/// <summary>
@@ -14,38 +12,78 @@ namespace MediaBrowser.Model.Entities
Custom = 0,
/// <summary>
- /// The imdb.
+ /// The IMDb provider.
/// </summary>
Imdb = 2,
/// <summary>
- /// The TMDB.
+ /// The TMDb provider.
/// </summary>
Tmdb = 3,
/// <summary>
- /// The TVDB.
+ /// The TVDb provider.
/// </summary>
Tvdb = 4,
/// <summary>
- /// The tvcom.
+ /// The tvcom providerd.
/// </summary>
Tvcom = 5,
/// <summary>
- /// Tmdb Collection Id.
+ /// TMDb collection provider.
/// </summary>
TmdbCollection = 7,
+
+ /// <summary>
+ /// The MusicBrainz album provider.
+ /// </summary>
MusicBrainzAlbum = 8,
+
+ /// <summary>
+ /// The MusicBrainz album artist provider.
+ /// </summary>
MusicBrainzAlbumArtist = 9,
+
+ /// <summary>
+ /// The MusicBrainz artist provider.
+ /// </summary>
MusicBrainzArtist = 10,
+
+ /// <summary>
+ /// The MusicBrainz release group provider.
+ /// </summary>
MusicBrainzReleaseGroup = 11,
+
+ /// <summary>
+ /// The Zap2It provider.
+ /// </summary>
Zap2It = 12,
+
+ /// <summary>
+ /// The TvRage provider.
+ /// </summary>
TvRage = 15,
+
+ /// <summary>
+ /// The AudioDb artist provider.
+ /// </summary>
AudioDbArtist = 16,
+
+ /// <summary>
+ /// The AudioDb collection provider.
+ /// </summary>
AudioDbAlbum = 17,
+
+ /// <summary>
+ /// The MusicBrainz track provider.
+ /// </summary>
MusicBrainzTrack = 18,
+
+ /// <summary>
+ /// The TvMaze provider.
+ /// </summary>
TvMaze = 19
}
}
diff --git a/MediaBrowser.Model/Entities/ParentalRating.cs b/MediaBrowser.Model/Entities/ParentalRating.cs
index 17b2868a3..c92640818 100644
--- a/MediaBrowser.Model/Entities/ParentalRating.cs
+++ b/MediaBrowser.Model/Entities/ParentalRating.cs
@@ -12,7 +12,7 @@ namespace MediaBrowser.Model.Entities
{
}
- public ParentalRating(string name, int value)
+ public ParentalRating(string name, int? value)
{
Name = name;
Value = value;
@@ -28,6 +28,6 @@ namespace MediaBrowser.Model.Entities
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
- public int Value { get; set; }
+ public int? Value { get; set; }
}
}
diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
index d3b8400f3..cf453d62c 100644
--- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
+++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
@@ -55,7 +55,7 @@ namespace MediaBrowser.Model.Entities
{
ArgumentNullException.ThrowIfNull(instance);
- if (instance.ProviderIds == null)
+ if (instance.ProviderIds is null)
{
id = null;
return false;
diff --git a/MediaBrowser.Model/Entities/Share.cs b/MediaBrowser.Model/Entities/Share.cs
new file mode 100644
index 000000000..186aad189
--- /dev/null
+++ b/MediaBrowser.Model/Entities/Share.cs
@@ -0,0 +1,17 @@
+namespace MediaBrowser.Model.Entities;
+
+/// <summary>
+/// Class to hold data on sharing permissions.
+/// </summary>
+public class Share
+{
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ public string? UserId { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the user has edit permissions.
+ /// </summary>
+ public bool CanEdit { get; set; }
+}
diff --git a/MediaBrowser.Model/Extensions/EnumerableExtensions.cs b/MediaBrowser.Model/Extensions/EnumerableExtensions.cs
index a5a6b18aa..c6d1f3900 100644
--- a/MediaBrowser.Model/Extensions/EnumerableExtensions.cs
+++ b/MediaBrowser.Model/Extensions/EnumerableExtensions.cs
@@ -24,24 +24,27 @@ namespace MediaBrowser.Model.Extensions
requestedLanguage = "en";
}
- var isRequestedLanguageEn = string.Equals(requestedLanguage, "en", StringComparison.OrdinalIgnoreCase);
-
return remoteImageInfos.OrderByDescending(i =>
{
+ // Image priority ordering:
+ // - Images that match the requested language
+ // - Images with no language
+ // - TODO: Images that match the original language
+ // - Images in English
+ // - Images that don't match the requested language
+
if (string.Equals(requestedLanguage, i.Language, StringComparison.OrdinalIgnoreCase))
{
- return 3;
+ return 4;
}
if (string.IsNullOrEmpty(i.Language))
{
- // Assume empty image language is likely to be English.
- return isRequestedLanguageEn ? 3 : 2;
+ return 3;
}
- if (!isRequestedLanguageEn && string.Equals(i.Language, "en", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(i.Language, "en", StringComparison.OrdinalIgnoreCase))
{
- // Prioritize English over non-requested languages.
return 2;
}
diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
index e00157dce..02a29e7fa 100644
--- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs
+++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
@@ -30,8 +30,9 @@ namespace MediaBrowser.Model.Globalization
/// Gets the rating level.
/// </summary>
/// <param name="rating">The rating.</param>
+ /// <param name="countryCode">The optional two letter ISO language string.</param>
/// <returns><see cref="int" /> or <c>null</c>.</returns>
- int? GetRatingLevel(string rating);
+ int? GetRatingLevel(string rating, string? countryCode = null);
/// <summary>
/// Gets the localized string.
diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
index 4cece941c..25e5c7796 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
@@ -40,5 +40,9 @@ namespace MediaBrowser.Model.LiveTv
public string RecordingPostProcessor { get; set; }
public string RecordingPostProcessorArguments { get; set; }
+
+ public bool SaveRecordingNFO { get; set; } = true;
+
+ public bool SaveRecordingImages { get; set; } = true;
}
}
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 4172e9825..9a5804485 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -14,7 +14,7 @@
</PropertyGroup>
<PropertyGroup>
- <TargetFramework>net6.0</TargetFramework>
+ <TargetFramework>net7.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
@@ -24,7 +24,7 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
- <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
+ <CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Stability)'=='Unstable'">
@@ -33,14 +33,14 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
- <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.3" />
- <PackageReference Include="MimeTypes" Version="2.4.0">
+ <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
+ <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
+ <PackageReference Include="MimeTypes">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- <PackageReference Include="System.Globalization" Version="4.3.0" />
- <PackageReference Include="System.Text.Json" Version="6.0.7" />
+ <PackageReference Include="System.Globalization" />
+ <PackageReference Include="System.Text.Json" />
</ItemGroup>
<ItemGroup>
@@ -49,13 +49,13 @@
<!-- Code Analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
- <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3">
+ <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
- <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
- <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" />
- <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+ <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
+ <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Jellyfin.Data/Jellyfin.Data.csproj" />
diff --git a/MediaBrowser.Model/MediaInfo/AudioCodec.cs b/MediaBrowser.Model/MediaInfo/AudioCodec.cs
index 7b83b1b9d..4c22af449 100644
--- a/MediaBrowser.Model/MediaInfo/AudioCodec.cs
+++ b/MediaBrowser.Model/MediaInfo/AudioCodec.cs
@@ -17,11 +17,13 @@ namespace MediaBrowser.Model.MediaInfo
{
return "Dolby Digital";
}
- else if (string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase))
{
return "Dolby Digital+";
}
- else if (string.Equals(codec, "dca", StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(codec, "dca", StringComparison.OrdinalIgnoreCase))
{
return "DTS";
}
diff --git a/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
index 83f982a5c..d546ffccd 100644
--- a/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
+++ b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
@@ -1,39 +1,41 @@
#nullable disable
-#pragma warning disable CS1591
using MediaBrowser.Model.Entities;
-namespace MediaBrowser.Model.MediaInfo
+namespace MediaBrowser.Model.MediaInfo;
+
+/// <summary>
+/// Represents the result of BDInfo output.
+/// </summary>
+public class BlurayDiscInfo
{
/// <summary>
- /// Represents the result of BDInfo output.
+ /// Gets or sets the media streams.
/// </summary>
- public class BlurayDiscInfo
- {
- /// <summary>
- /// Gets or sets the media streams.
- /// </summary>
- /// <value>The media streams.</value>
- public MediaStream[] MediaStreams { get; set; }
+ /// <value>The media streams.</value>
+ public MediaStream[] MediaStreams { get; set; }
- /// <summary>
- /// Gets or sets the run time ticks.
- /// </summary>
- /// <value>The run time ticks.</value>
- public long? RunTimeTicks { get; set; }
+ /// <summary>
+ /// Gets or sets the run time ticks.
+ /// </summary>
+ /// <value>The run time ticks.</value>
+ public long? RunTimeTicks { get; set; }
- /// <summary>
- /// Gets or sets the files.
- /// </summary>
- /// <value>The files.</value>
- public string[] Files { get; set; }
+ /// <summary>
+ /// Gets or sets the files.
+ /// </summary>
+ /// <value>The files.</value>
+ public string[] Files { get; set; }
- public string PlaylistName { get; set; }
+ /// <summary>
+ /// Gets or sets the playlist name.
+ /// </summary>
+ /// <value>The playlist name.</value>
+ public string PlaylistName { get; set; }
- /// <summary>
- /// Gets or sets the chapters.
- /// </summary>
- /// <value>The chapters.</value>
- public double[] Chapters { get; set; }
- }
+ /// <summary>
+ /// Gets or sets the chapters.
+ /// </summary>
+ /// <value>The chapters.</value>
+ public double[] Chapters { get; set; }
}
diff --git a/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs b/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs
index 5b7d1d03c..d39725301 100644
--- a/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs
+++ b/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs
@@ -1,15 +1,14 @@
-namespace MediaBrowser.Model.MediaInfo
+namespace MediaBrowser.Model.MediaInfo;
+
+/// <summary>
+/// Interface IBlurayExaminer.
+/// </summary>
+public interface IBlurayExaminer
{
/// <summary>
- /// Interface IBlurayExaminer.
+ /// Gets the disc info.
/// </summary>
- public interface IBlurayExaminer
- {
- /// <summary>
- /// Gets the disc info.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>BlurayDiscInfo.</returns>
- BlurayDiscInfo GetDiscInfo(string path);
- }
+ /// <param name="path">The path.</param>
+ /// <returns>BlurayDiscInfo.</returns>
+ BlurayDiscInfo GetDiscInfo(string path);
}
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
index 3b03466e9..5a1871070 100644
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ b/MediaBrowser.Model/Net/MimeTypes.cs
@@ -117,7 +117,9 @@ namespace MediaBrowser.Model.Net
// Type image
{ "image/jpeg", ".jpg" },
+ { "image/tiff", ".tiff" },
{ "image/x-png", ".png" },
+ { "image/x-icon", ".ico" },
// Type text
{ "text/plain", ".txt" },
@@ -140,10 +142,7 @@ namespace MediaBrowser.Model.Net
[return: NotNullIfNotNull("defaultValue")]
public static string? GetMimeType(string filename, string? defaultValue = null)
{
- if (filename.Length == 0)
- {
- throw new ArgumentException("String can't be empty.", nameof(filename));
- }
+ ArgumentException.ThrowIfNullOrEmpty(filename);
var ext = Path.GetExtension(filename);
@@ -168,10 +167,7 @@ namespace MediaBrowser.Model.Net
public static string? ToExtension(string mimeType)
{
- if (mimeType.Length == 0)
- {
- throw new ArgumentException("String can't be empty.", nameof(mimeType));
- }
+ ArgumentException.ThrowIfNullOrEmpty(mimeType);
// handle text/html; charset=UTF-8
mimeType = mimeType.AsSpan().LeftPart(';').ToString();
@@ -184,5 +180,8 @@ namespace MediaBrowser.Model.Net
var extension = Model.MimeTypes.GetMimeTypeExtensions(mimeType).FirstOrDefault();
return string.IsNullOrEmpty(extension) ? null : "." + extension;
}
+
+ public static bool IsImage(ReadOnlySpan<char> mimeType)
+ => mimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase);
}
}
diff --git a/MediaBrowser.Model/Net/WebSocketMessage.cs b/MediaBrowser.Model/Net/WebSocketMessage.cs
deleted file mode 100644
index b00158cb3..000000000
--- a/MediaBrowser.Model/Net/WebSocketMessage.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Model.Session;
-
-namespace MediaBrowser.Model.Net
-{
- /// <summary>
- /// Class WebSocketMessage.
- /// </summary>
- /// <typeparam name="T">The type of the data.</typeparam>
- public class WebSocketMessage<T>
- {
- /// <summary>
- /// Gets or sets the type of the message.
- /// </summary>
- /// <value>The type of the message.</value>
- public SessionMessageType MessageType { get; set; }
-
- public Guid MessageId { get; set; }
-
- public string ServerId { get; set; }
-
- /// <summary>
- /// Gets or sets the data.
- /// </summary>
- /// <value>The data.</value>
- public T Data { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Notifications/NotificationLevel.cs b/MediaBrowser.Model/Notifications/NotificationLevel.cs
deleted file mode 100644
index 14fead3f0..000000000
--- a/MediaBrowser.Model/Notifications/NotificationLevel.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.Notifications
-{
- public enum NotificationLevel
- {
- Normal = 0,
- Warning = 1,
- Error = 2
- }
-}
diff --git a/MediaBrowser.Model/Notifications/NotificationOption.cs b/MediaBrowser.Model/Notifications/NotificationOption.cs
deleted file mode 100644
index 58aecb3d3..000000000
--- a/MediaBrowser.Model/Notifications/NotificationOption.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-#pragma warning disable CA1819 // Properties should not return arrays
-#pragma warning disable CS1591
-
-using System;
-
-namespace MediaBrowser.Model.Notifications
-{
- public class NotificationOption
- {
- public NotificationOption(string type)
- {
- Type = type;
- DisabledServices = Array.Empty<string>();
- DisabledMonitorUsers = Array.Empty<string>();
- SendToUsers = Array.Empty<string>();
- }
-
- public NotificationOption()
- {
- DisabledServices = Array.Empty<string>();
- DisabledMonitorUsers = Array.Empty<string>();
- SendToUsers = Array.Empty<string>();
- }
-
- public string? Type { get; set; }
-
- /// <summary>
- /// Gets or sets user Ids to not monitor (it's opt out).
- /// </summary>
- public string[] DisabledMonitorUsers { get; set; }
-
- /// <summary>
- /// Gets or sets user Ids to send to (if SendToUserMode == Custom).
- /// </summary>
- public string[] SendToUsers { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this <see cref="NotificationOption"/> is enabled.
- /// </summary>
- /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
- public bool Enabled { get; set; }
-
- /// <summary>
- /// Gets or sets the disabled services.
- /// </summary>
- /// <value>The disabled services.</value>
- public string[] DisabledServices { get; set; }
-
- /// <summary>
- /// Gets or sets the send to user mode.
- /// </summary>
- /// <value>The send to user mode.</value>
- public SendToUserType SendToUserMode { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs
deleted file mode 100644
index d1b5491bd..000000000
--- a/MediaBrowser.Model/Notifications/NotificationOptions.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System;
-using Jellyfin.Data.Entities;
-using Jellyfin.Data.Enums;
-using Jellyfin.Extensions;
-
-namespace MediaBrowser.Model.Notifications
-{
- public class NotificationOptions
- {
- public NotificationOptions()
- {
- Options = new[]
- {
- new NotificationOption(NotificationType.TaskFailed.ToString())
- {
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption(NotificationType.ServerRestartRequired.ToString())
- {
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption(NotificationType.ApplicationUpdateAvailable.ToString())
- {
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption(NotificationType.ApplicationUpdateInstalled.ToString())
- {
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption(NotificationType.PluginUpdateInstalled.ToString())
- {
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption(NotificationType.PluginUninstalled.ToString())
- {
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption(NotificationType.InstallationFailed.ToString())
- {
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption(NotificationType.PluginInstalled.ToString())
- {
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption(NotificationType.PluginError.ToString())
- {
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- },
- new NotificationOption(NotificationType.UserLockedOut.ToString())
- {
- Enabled = true,
- SendToUserMode = SendToUserType.Admins
- }
- };
- }
-
- public NotificationOption[] Options { get; set; }
-
- public NotificationOption GetOptions(string type)
- {
- foreach (NotificationOption i in Options)
- {
- if (string.Equals(type, i.Type, StringComparison.OrdinalIgnoreCase))
- {
- return i;
- }
- }
-
- return null;
- }
-
- public bool IsEnabled(string type)
- {
- NotificationOption opt = GetOptions(type);
-
- return opt != null && opt.Enabled;
- }
-
- public bool IsServiceEnabled(string service, string notificationType)
- {
- NotificationOption opt = GetOptions(notificationType);
-
- return opt == null
- || !opt.DisabledServices.Contains(service, StringComparison.OrdinalIgnoreCase);
- }
-
- public bool IsEnabledToMonitorUser(string type, Guid userId)
- {
- NotificationOption opt = GetOptions(type);
-
- return opt != null
- && opt.Enabled
- && !opt.DisabledMonitorUsers.Contains(userId.ToString("N"), StringComparison.OrdinalIgnoreCase);
- }
-
- public bool IsEnabledToSendToUser(string type, string userId, User user)
- {
- NotificationOption opt = GetOptions(type);
-
- if (opt != null && opt.Enabled)
- {
- if (opt.SendToUserMode == SendToUserType.All)
- {
- return true;
- }
-
- if (opt.SendToUserMode == SendToUserType.Admins && user.HasPermission(PermissionKind.IsAdministrator))
- {
- return true;
- }
-
- return opt.SendToUsers.Contains(userId, StringComparison.OrdinalIgnoreCase);
- }
-
- return false;
- }
- }
-}
diff --git a/MediaBrowser.Model/Notifications/NotificationRequest.cs b/MediaBrowser.Model/Notifications/NotificationRequest.cs
deleted file mode 100644
index 622c50cd8..000000000
--- a/MediaBrowser.Model/Notifications/NotificationRequest.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System;
-
-namespace MediaBrowser.Model.Notifications
-{
- public class NotificationRequest
- {
- public NotificationRequest()
- {
- UserIds = Array.Empty<Guid>();
- Date = DateTime.UtcNow;
- }
-
- public string Name { get; set; }
-
- public string Description { get; set; }
-
- public string Url { get; set; }
-
- public NotificationLevel Level { get; set; }
-
- public Guid[] UserIds { get; set; }
-
- public DateTime Date { get; set; }
-
- /// <summary>
- /// Gets or sets the corresponding type name used in configuration. Not for display.
- /// </summary>
- public string NotificationType { get; set; }
-
- public SendToUserType? SendToUserMode { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs b/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs
deleted file mode 100644
index 402fbe81a..000000000
--- a/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.Notifications
-{
- public class NotificationTypeInfo
- {
- public string Type { get; set; }
-
- public string Name { get; set; }
-
- public bool Enabled { get; set; }
-
- public string Category { get; set; }
-
- public bool IsBasedOnUserEvent { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Notifications/SendToUserType.cs b/MediaBrowser.Model/Notifications/SendToUserType.cs
deleted file mode 100644
index 65fc4e1ab..000000000
--- a/MediaBrowser.Model/Notifications/SendToUserType.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.Notifications
-{
- public enum SendToUserType
- {
- All = 0,
- Admins = 1,
- Custom = 2
- }
-}
diff --git a/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs b/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs
index e8ee49403..847269716 100644
--- a/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs
+++ b/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs
@@ -1,19 +1,36 @@
-#nullable disable
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Model.Playlists;
-namespace MediaBrowser.Model.Playlists
+/// <summary>
+/// A playlist creation request.
+/// </summary>
+public class PlaylistCreationRequest
{
- public class PlaylistCreationRequest
- {
- public string Name { get; set; }
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ public string? Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of items.
+ /// </summary>
+ public IReadOnlyList<Guid> ItemIdList { get; set; } = Array.Empty<Guid>();
- public IReadOnlyList<Guid> ItemIdList { get; set; } = Array.Empty<Guid>();
+ /// <summary>
+ /// Gets or sets the media type.
+ /// </summary>
+ public string? MediaType { get; set; }
- public string MediaType { get; set; }
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ public Guid UserId { get; set; }
- public Guid UserId { get; set; }
- }
+ /// <summary>
+ /// Gets or sets the shares.
+ /// </summary>
+ public Share[]? Shares { get; set; }
}
diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs
index e6c3a6c26..6fa1d778a 100644
--- a/MediaBrowser.Model/Querying/ItemFields.cs
+++ b/MediaBrowser.Model/Querying/ItemFields.cs
@@ -126,7 +126,7 @@ namespace MediaBrowser.Model.Querying
ProductionLocations,
/// <summary>
- /// Imdb, tmdb, etc.
+ /// The ids from IMDb, TMDb, etc.
/// </summary>
ProviderIds,
diff --git a/MediaBrowser.Model/Querying/ItemSortBy.cs b/MediaBrowser.Model/Querying/ItemSortBy.cs
index 470507c53..1a7c9a63b 100644
--- a/MediaBrowser.Model/Querying/ItemSortBy.cs
+++ b/MediaBrowser.Model/Querying/ItemSortBy.cs
@@ -154,5 +154,10 @@ namespace MediaBrowser.Model.Querying
/// The similarity score.
/// </summary>
public const string SimilarityScore = "SimilarityScore";
+
+ /// <summary>
+ /// The search score.
+ /// </summary>
+ public const string SearchScore = "SearchScore";
}
}
diff --git a/MediaBrowser.Model/SyncPlay/GroupUpdate.cs b/MediaBrowser.Model/SyncPlay/GroupUpdate.cs
index 6f159d653..ec67d7ea8 100644
--- a/MediaBrowser.Model/SyncPlay/GroupUpdate.cs
+++ b/MediaBrowser.Model/SyncPlay/GroupUpdate.cs
@@ -1,42 +1,30 @@
using System;
-namespace MediaBrowser.Model.SyncPlay
+namespace MediaBrowser.Model.SyncPlay;
+
+/// <summary>
+/// Group update without data.
+/// </summary>
+public abstract class GroupUpdate
{
/// <summary>
- /// Class GroupUpdate.
+ /// Initializes a new instance of the <see cref="GroupUpdate"/> class.
/// </summary>
- /// <typeparam name="T">The type of the data of the message.</typeparam>
- public class GroupUpdate<T>
+ /// <param name="groupId">The group identifier.</param>
+ protected GroupUpdate(Guid groupId)
{
- /// <summary>
- /// Initializes a new instance of the <see cref="GroupUpdate{T}"/> class.
- /// </summary>
- /// <param name="groupId">The group identifier.</param>
- /// <param name="type">The update type.</param>
- /// <param name="data">The update data.</param>
- public GroupUpdate(Guid groupId, GroupUpdateType type, T data)
- {
- GroupId = groupId;
- Type = type;
- Data = data;
- }
-
- /// <summary>
- /// Gets the group identifier.
- /// </summary>
- /// <value>The group identifier.</value>
- public Guid GroupId { get; }
+ GroupId = groupId;
+ }
- /// <summary>
- /// Gets the update type.
- /// </summary>
- /// <value>The update type.</value>
- public GroupUpdateType Type { get; }
+ /// <summary>
+ /// Gets the group identifier.
+ /// </summary>
+ /// <value>The group identifier.</value>
+ public Guid GroupId { get; }
- /// <summary>
- /// Gets the update data.
- /// </summary>
- /// <value>The update data.</value>
- public T Data { get; }
- }
+ /// <summary>
+ /// Gets the update type.
+ /// </summary>
+ /// <value>The update type.</value>
+ public GroupUpdateType Type { get; init; }
}
diff --git a/MediaBrowser.Model/SyncPlay/GroupUpdateOfT.cs b/MediaBrowser.Model/SyncPlay/GroupUpdateOfT.cs
new file mode 100644
index 000000000..25cd44461
--- /dev/null
+++ b/MediaBrowser.Model/SyncPlay/GroupUpdateOfT.cs
@@ -0,0 +1,31 @@
+#pragma warning disable SA1649
+
+using System;
+
+namespace MediaBrowser.Model.SyncPlay;
+
+/// <summary>
+/// Class GroupUpdate.
+/// </summary>
+/// <typeparam name="T">The type of the data of the message.</typeparam>
+public class GroupUpdate<T> : GroupUpdate
+{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GroupUpdate{T}"/> class.
+ /// </summary>
+ /// <param name="groupId">The group identifier.</param>
+ /// <param name="type">The update type.</param>
+ /// <param name="data">The update data.</param>
+ public GroupUpdate(Guid groupId, GroupUpdateType type, T data)
+ : base(groupId)
+ {
+ Data = data;
+ Type = type;
+ }
+
+ /// <summary>
+ /// Gets the update data.
+ /// </summary>
+ /// <value>The update data.</value>
+ public T Data { get; }
+}
diff --git a/MediaBrowser.Model/SyncPlay/PlayQueueUpdate.cs b/MediaBrowser.Model/SyncPlay/PlayQueueUpdate.cs
index cce99c77d..376d926c9 100644
--- a/MediaBrowser.Model/SyncPlay/PlayQueueUpdate.cs
+++ b/MediaBrowser.Model/SyncPlay/PlayQueueUpdate.cs
@@ -19,7 +19,7 @@ namespace MediaBrowser.Model.SyncPlay
/// <param name="isPlaying">The playing item status.</param>
/// <param name="shuffleMode">The shuffle mode.</param>
/// <param name="repeatMode">The repeat mode.</param>
- public PlayQueueUpdate(PlayQueueUpdateReason reason, DateTime lastUpdate, IReadOnlyList<QueueItem> playlist, int playingItemIndex, long startPositionTicks, bool isPlaying, GroupShuffleMode shuffleMode, GroupRepeatMode repeatMode)
+ public PlayQueueUpdate(PlayQueueUpdateReason reason, DateTime lastUpdate, IReadOnlyList<SyncPlayQueueItem> playlist, int playingItemIndex, long startPositionTicks, bool isPlaying, GroupShuffleMode shuffleMode, GroupRepeatMode repeatMode)
{
Reason = reason;
LastUpdate = lastUpdate;
@@ -47,7 +47,7 @@ namespace MediaBrowser.Model.SyncPlay
/// Gets the playlist.
/// </summary>
/// <value>The playlist.</value>
- public IReadOnlyList<QueueItem> Playlist { get; }
+ public IReadOnlyList<SyncPlayQueueItem> Playlist { get; }
/// <summary>
/// Gets the playing item index in the playlist.
diff --git a/MediaBrowser.Model/SyncPlay/QueueItem.cs b/MediaBrowser.Model/SyncPlay/SyncPlayQueueItem.cs
index a6dcc109e..da81fecbd 100644
--- a/MediaBrowser.Model/SyncPlay/QueueItem.cs
+++ b/MediaBrowser.Model/SyncPlay/SyncPlayQueueItem.cs
@@ -5,13 +5,13 @@ namespace MediaBrowser.Model.SyncPlay
/// <summary>
/// Class QueueItem.
/// </summary>
- public class QueueItem
+ public class SyncPlayQueueItem
{
/// <summary>
- /// Initializes a new instance of the <see cref="QueueItem"/> class.
+ /// Initializes a new instance of the <see cref="SyncPlayQueueItem"/> class.
/// </summary>
/// <param name="itemId">The item identifier.</param>
- public QueueItem(Guid itemId)
+ public SyncPlayQueueItem(Guid itemId)
{
ItemId = itemId;
}
diff --git a/MediaBrowser.Model/System/OperatingSystemId.cs b/MediaBrowser.Model/System/OperatingSystemId.cs
deleted file mode 100644
index 2e417f6b5..000000000
--- a/MediaBrowser.Model/System/OperatingSystemId.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Model.System
-{
- public enum OperatingSystemId
- {
- Windows,
- Linux,
- Darwin,
- BSD
- }
-}
diff --git a/MediaBrowser.Model/System/PublicSystemInfo.cs b/MediaBrowser.Model/System/PublicSystemInfo.cs
index 53030843a..31a895642 100644
--- a/MediaBrowser.Model/System/PublicSystemInfo.cs
+++ b/MediaBrowser.Model/System/PublicSystemInfo.cs
@@ -1,6 +1,8 @@
#nullable disable
#pragma warning disable CS1591
+using System;
+
namespace MediaBrowser.Model.System
{
public class PublicSystemInfo
@@ -32,7 +34,8 @@ namespace MediaBrowser.Model.System
/// Gets or sets the operating system.
/// </summary>
/// <value>The operating system.</value>
- public string OperatingSystem { get; set; }
+ [Obsolete("This is no longer set")]
+ public string OperatingSystem { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the id.
diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs
index a82c1c8c0..bd0099af7 100644
--- a/MediaBrowser.Model/System/SystemInfo.cs
+++ b/MediaBrowser.Model/System/SystemInfo.cs
@@ -42,7 +42,8 @@ namespace MediaBrowser.Model.System
/// Gets or sets the display name of the operating system.
/// </summary>
/// <value>The display name of the operating system.</value>
- public string OperatingSystemDisplayName { get; set; }
+ [Obsolete("This is no longer set")]
+ public string OperatingSystemDisplayName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the package name.
@@ -79,8 +80,9 @@ namespace MediaBrowser.Model.System
/// <summary>
/// Gets or sets a value indicating whether this instance can self restart.
/// </summary>
- /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value>
- public bool CanSelfRestart { get; set; }
+ /// <value><c>true</c>.</value>
+ [Obsolete("This is always true")]
+ public bool CanSelfRestart { get; set; } = true;
public bool CanLaunchWebBrowser { get; set; }
@@ -136,6 +138,7 @@ namespace MediaBrowser.Model.System
[Obsolete("This isn't set correctly anymore")]
public FFmpegLocation EncoderLocation { get; set; }
- public Architecture SystemArchitecture { get; set; }
+ [Obsolete("This is no longer set")]
+ public Architecture SystemArchitecture { get; set; } = Architecture.X64;
}
}
diff --git a/MediaBrowser.Model/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs
index 13bebc479..5b55667e8 100644
--- a/MediaBrowser.Model/Tasks/ITaskManager.cs
+++ b/MediaBrowser.Model/Tasks/ITaskManager.cs
@@ -9,9 +9,9 @@ namespace MediaBrowser.Model.Tasks
{
public interface ITaskManager : IDisposable
{
- event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting;
+ event EventHandler<GenericEventArgs<IScheduledTaskWorker>>? TaskExecuting;
- event EventHandler<TaskCompletionEventArgs> TaskCompleted;
+ event EventHandler<TaskCompletionEventArgs>? TaskCompleted;
/// <summary>
/// Gets the list of Scheduled Tasks.
diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs
index 03a540dde..320199f98 100644
--- a/MediaBrowser.Model/Updates/VersionInfo.cs
+++ b/MediaBrowser.Model/Updates/VersionInfo.cs
@@ -17,7 +17,7 @@ namespace MediaBrowser.Model.Updates
[JsonPropertyName("version")]
public string Version
{
- get => _version == null ? string.Empty : _version.ToString();
+ get => _version is null ? string.Empty : _version.ToString();
set => _version = SysVersion.Parse(value);
}
diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
index 3634d0705..80f5e2c37 100644
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ b/MediaBrowser.Model/Users/UserPolicy.cs
@@ -13,6 +13,7 @@ namespace MediaBrowser.Model.Users
public UserPolicy()
{
IsHidden = true;
+ EnableCollectionManagement = false;
EnableContentDeletion = false;
EnableContentDeletionFromFolders = Array.Empty<string>();
@@ -35,6 +36,7 @@ namespace MediaBrowser.Model.Users
EnableSharedDeviceControl = true;
BlockedTags = Array.Empty<string>();
+ AllowedTags = Array.Empty<string>();
BlockUnratedItems = Array.Empty<UnratedItem>();
EnableUserPreferenceAccess = true;
@@ -44,6 +46,7 @@ namespace MediaBrowser.Model.Users
LoginAttemptsBeforeLockout = -1;
MaxActiveSessions = 0;
+ MaxParentalRating = null;
EnableAllChannels = true;
EnabledChannels = Array.Empty<Guid>();
@@ -73,6 +76,12 @@ namespace MediaBrowser.Model.Users
public bool IsHidden { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether this instance can manage collections.
+ /// </summary>
+ /// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
+ public bool EnableCollectionManagement { get; set; }
+
+ /// <summary>
/// Gets or sets a value indicating whether this instance is disabled.
/// </summary>
/// <value><c>true</c> if this instance is disabled; otherwise, <c>false</c>.</value>
@@ -86,6 +95,8 @@ namespace MediaBrowser.Model.Users
public string[] BlockedTags { get; set; }
+ public string[] AllowedTags { get; set; }
+
public bool EnableUserPreferenceAccess { get; set; }
public AccessSchedule[] AccessSchedules { get; set; }