aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Model
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Model')
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs43
-rw-r--r--MediaBrowser.Model/Configuration/LibraryOptions.cs2
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs23
-rw-r--r--MediaBrowser.Model/Cryptography/PasswordHash.cs3
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs132
-rw-r--r--MediaBrowser.Model/Dlna/ContainerProfile.cs2
-rw-r--r--MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs3
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfile.cs3
-rw-r--r--MediaBrowser.Model/Dlna/DirectPlayProfile.cs6
-rw-r--r--MediaBrowser.Model/Dlna/ITranscoderSupport.cs18
-rw-r--r--MediaBrowser.Model/Dlna/MediaOptions.cs10
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionNormalizer.cs22
-rw-r--r--MediaBrowser.Model/Dlna/SortCriteria.cs2
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs245
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs43
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs6
-rw-r--r--MediaBrowser.Model/Dto/BaseItemPerson.cs3
-rw-r--r--MediaBrowser.Model/Dto/UserDto.cs1
-rw-r--r--MediaBrowser.Model/Entities/IHasShares.cs12
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs21
-rw-r--r--MediaBrowser.Model/Entities/ParentalRating.cs4
-rw-r--r--MediaBrowser.Model/Entities/Share.cs17
-rw-r--r--MediaBrowser.Model/Globalization/ILocalizationManager.cs3
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj18
-rw-r--r--MediaBrowser.Model/MediaInfo/AudioCodec.cs6
-rw-r--r--MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs41
-rw-r--r--MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs14
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs5
-rw-r--r--MediaBrowser.Model/Net/WebSocketMessage.cs31
-rw-r--r--MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs39
-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/Tasks/ITaskManager.cs4
-rw-r--r--MediaBrowser.Model/Users/UserPolicy.cs13
36 files changed, 569 insertions, 321 deletions
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index 0ff95a2e1f..3f0e98ec8e 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -14,11 +14,14 @@ public class EncodingOptions
public EncodingOptions()
{
EnableFallbackFont = false;
+ EnableAudioVbr = false;
DownMixAudioBoost = 2;
DownMixStereoAlgorithm = DownMixStereoAlgorithms.None;
MaxMuxingQueueSize = 2048;
EnableThrottling = false;
ThrottleDelaySeconds = 180;
+ EnableSegmentDeletion = false;
+ SegmentKeepSeconds = 720;
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
@@ -26,25 +29,27 @@ public class EncodingOptions
EnableTonemapping = false;
EnableVppTonemapping = false;
TonemappingAlgorithm = "bt2390";
+ TonemappingMode = "auto";
TonemappingRange = "auto";
TonemappingDesat = 0;
- TonemappingThreshold = 0.8;
TonemappingPeak = 100;
TonemappingParam = 0;
- VppTonemappingBrightness = 0;
- VppTonemappingContrast = 1.2;
+ VppTonemappingBrightness = 16;
+ VppTonemappingContrast = 1;
H264Crf = 23;
H265Crf = 28;
DeinterlaceDoubleRate = false;
DeinterlaceMethod = "yadif";
EnableDecodingColorDepth10Hevc = true;
EnableDecodingColorDepth10Vp9 = true;
- EnableEnhancedNvdecDecoder = false;
+ // 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;
+ AllowAv1Encoding = false;
EnableSubtitleExtraction = true;
AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = new[] { "mkv" };
HardwareDecodingCodecs = new string[] { "h264", "vc1" };
@@ -71,6 +76,11 @@ public class EncodingOptions
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; }
@@ -96,6 +106,16 @@ public class EncodingOptions
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; }
@@ -131,6 +151,11 @@ public class EncodingOptions
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; }
@@ -141,11 +166,6 @@ public class EncodingOptions
public double TonemappingDesat { get; set; }
/// <summary>
- /// Gets or sets the tone-mapping threshold.
- /// </summary>
- public double TonemappingThreshold { get; set; }
-
- /// <summary>
/// Gets or sets the tone-mapping peak.
/// </summary>
public double TonemappingPeak { get; set; }
@@ -231,6 +251,11 @@ public class EncodingOptions
public bool AllowHevcEncoding { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether AV1 encoding is enabled.
+ /// </summary>
+ public bool AllowAv1Encoding { get; set; }
+
+ /// <summary>
/// Gets or sets a value indicating whether subtitle extraction is enabled.
/// </summary>
public bool EnableSubtitleExtraction { get; set; }
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
index 81f2f02bc5..df68299465 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; }
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index c39162250a..78a310f0b1 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -2,7 +2,6 @@
#pragma warning disable CA1819
using System;
-using System.Collections.Generic;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Updates;
@@ -167,6 +166,12 @@ namespace MediaBrowser.Model.Configuration
public int LibraryMonitorDelay { get; set; } = 60;
/// <summary>
+ /// Gets or sets the duration in seconds that we will wait after a library updated event before executing the library changed notification.
+ /// </summary>
+ /// <value>The library update duration.</value>
+ public int LibraryUpdateDuration { get; set; } = 30;
+
+ /// <summary>
/// Gets or sets the image saving convention.
/// </summary>
/// <value>The image saving convention.</value>
@@ -184,7 +189,7 @@ namespace MediaBrowser.Model.Configuration
public NameValuePair[] ContentTypes { get; set; } = Array.Empty<NameValuePair>();
- public int RemoteClientBitrateLimit { get; set; } = 0;
+ public int RemoteClientBitrateLimit { get; set; }
public bool EnableFolderView { get; set; } = false;
@@ -198,7 +203,7 @@ namespace MediaBrowser.Model.Configuration
public bool EnableExternalContentInSuggestions { get; set; } = true;
- public int ImageExtractionTimeoutMs { get; set; } = 0;
+ public int ImageExtractionTimeoutMs { get; set; }
public PathSubstitution[] PathSubstitutions { get; set; } = Array.Empty<PathSubstitution>();
@@ -243,16 +248,10 @@ namespace MediaBrowser.Model.Configuration
public bool AllowClientLogUpload { get; set; } = true;
/// <summary>
- /// Gets or sets the dummy chapters duration in seconds.
+ /// 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; } = 300;
-
- /// <summary>
- /// Gets or sets the dummy chapter count.
- /// </summary>
- /// <value>The dummy chapter count.</value>
- public int DummyChapterCount { get; set; } = 100;
+ public int DummyChapterDuration { get; set; }
/// <summary>
/// Gets or sets the chapter image resolution.
@@ -264,6 +263,6 @@ namespace MediaBrowser.Model.Configuration
/// Gets or sets the limit for parallel image encoding.
/// </summary>
/// <value>The limit for parallel image encoding.</value>
- public int ParallelImageEncodingLimit { get; set; } = 0;
+ public int ParallelImageEncodingLimit { get; set; }
}
}
diff --git a/MediaBrowser.Model/Cryptography/PasswordHash.cs b/MediaBrowser.Model/Cryptography/PasswordHash.cs
index 80a30684ab..ccb361c132 100644
--- a/MediaBrowser.Model/Cryptography/PasswordHash.cs
+++ b/MediaBrowser.Model/Cryptography/PasswordHash.cs
@@ -80,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 5734224167..af0787990d 100644
--- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs
+++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
@@ -1,14 +1,38 @@
-#pragma warning disable CS1591
-
using System;
using System.Globalization;
+using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Model.Dlna
{
+ /// <summary>
+ /// The condition processor.
+ /// </summary>
public static class ConditionProcessor
{
+ /// <summary>
+ /// Checks if a video condition is satisfied.
+ /// </summary>
+ /// <param name="condition">The <see cref="ProfileCondition"/>.</param>
+ /// <param name="width">The width.</param>
+ /// <param name="height">The height.</param>
+ /// <param name="videoBitDepth">The bit depth.</param>
+ /// <param name="videoBitrate">The bitrate.</param>
+ /// <param name="videoProfile">The video profile.</param>
+ /// <param name="videoRangeType">The <see cref="VideoRangeType"/>.</param>
+ /// <param name="videoLevel">The video level.</param>
+ /// <param name="videoFramerate">The framerate.</param>
+ /// <param name="packetLength">The packet length.</param>
+ /// <param name="timestamp">The <see cref="TransportStreamTimestamp"/>.</param>
+ /// <param name="isAnamorphic">A value indicating whether tthe video is anamorphic.</param>
+ /// <param name="isInterlaced">A value indicating whether tthe video is interlaced.</param>
+ /// <param name="refFrames">The reference frames.</param>
+ /// <param name="numVideoStreams">The number of video streams.</param>
+ /// <param name="numAudioStreams">The number of audio streams.</param>
+ /// <param name="videoCodecTag">The video codec tag.</param>
+ /// <param name="isAvc">A value indicating whether the video is AVC.</param>
+ /// <returns><b>True</b> if the condition is satisfied.</returns>
public static bool IsVideoConditionSatisfied(
ProfileCondition condition,
int? width,
@@ -16,7 +40,7 @@ namespace MediaBrowser.Model.Dlna
int? videoBitDepth,
int? videoBitrate,
string? videoProfile,
- string? videoRangeType,
+ VideoRangeType? videoRangeType,
double? videoLevel,
float? videoFramerate,
int? packetLength,
@@ -70,6 +94,13 @@ namespace MediaBrowser.Model.Dlna
}
}
+ /// <summary>
+ /// Checks if a image condition is satisfied.
+ /// </summary>
+ /// <param name="condition">The <see cref="ProfileCondition"/>.</param>
+ /// <param name="width">The width.</param>
+ /// <param name="height">The height.</param>
+ /// <returns><b>True</b> if the condition is satisfied.</returns>
public static bool IsImageConditionSatisfied(ProfileCondition condition, int? width, int? height)
{
switch (condition.Property)
@@ -83,6 +114,15 @@ namespace MediaBrowser.Model.Dlna
}
}
+ /// <summary>
+ /// Checks if an audio condition is satisfied.
+ /// </summary>
+ /// <param name="condition">The <see cref="ProfileCondition"/>.</param>
+ /// <param name="audioChannels">The channel count.</param>
+ /// <param name="audioBitrate">The bitrate.</param>
+ /// <param name="audioSampleRate">The sample rate.</param>
+ /// <param name="audioBitDepth">The bit depth.</param>
+ /// <returns><b>True</b> if the condition is satisfied.</returns>
public static bool IsAudioConditionSatisfied(ProfileCondition condition, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth)
{
switch (condition.Property)
@@ -100,6 +140,17 @@ namespace MediaBrowser.Model.Dlna
}
}
+ /// <summary>
+ /// Checks if an audio condition is satisfied for a video.
+ /// </summary>
+ /// <param name="condition">The <see cref="ProfileCondition"/>.</param>
+ /// <param name="audioChannels">The channel count.</param>
+ /// <param name="audioBitrate">The bitrate.</param>
+ /// <param name="audioSampleRate">The sample rate.</param>
+ /// <param name="audioBitDepth">The bit depth.</param>
+ /// <param name="audioProfile">The profile.</param>
+ /// <param name="isSecondaryTrack">A value indicating whether the audio is a secondary track.</param>
+ /// <returns><b>True</b> if the condition is satisfied.</returns>
public static bool IsVideoAudioConditionSatisfied(
ProfileCondition condition,
int? audioChannels,
@@ -136,12 +187,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 +277,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);
@@ -252,5 +332,41 @@ namespace MediaBrowser.Model.Dlna
throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition);
}
}
+
+ private static bool IsConditionSatisfied(ProfileCondition condition, VideoRangeType? currentValue)
+ {
+ if (!currentValue.HasValue || currentValue.Equals(VideoRangeType.Unknown))
+ {
+ // If the value is unknown, it satisfies if not marked as required
+ return !condition.IsRequired;
+ }
+
+ var conditionType = condition.Condition;
+ if (conditionType == ProfileConditionType.EqualsAny)
+ {
+ foreach (var singleConditionString in condition.Value.AsSpan().Split('|'))
+ {
+ if (Enum.TryParse(singleConditionString, true, out VideoRangeType conditionValue)
+ && conditionValue.Equals(currentValue))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (Enum.TryParse(condition.Value, true, out VideoRangeType expected))
+ {
+ return conditionType switch
+ {
+ ProfileConditionType.Equals => currentValue.Value == expected,
+ ProfileConditionType.NotEquals => currentValue.Value != expected,
+ _ => throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition)
+ };
+ }
+
+ return false;
+ }
}
}
diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs
index 927df8e4ef..9780042684 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;
diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
index 1d5d0b1de3..f29022b54e 100644
--- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
+++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Model.Dlna
@@ -128,7 +129,7 @@ namespace MediaBrowser.Model.Dlna
bool isDirectStream,
long? runtimeTicks,
string videoProfile,
- string videoRangeType,
+ VideoRangeType videoRangeType,
double? videoLevel,
float? videoFramerate,
int? packetLength,
diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs
index 79ae951708..b7c23669df 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs
@@ -2,6 +2,7 @@
using System;
using System.ComponentModel;
using System.Xml.Serialization;
+using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Model.MediaInfo;
@@ -445,7 +446,7 @@ namespace MediaBrowser.Model.Dlna
int? bitDepth,
int? videoBitrate,
string videoProfile,
- string videoRangeType,
+ VideoRangeType videoRangeType,
double? videoLevel,
float? videoFramerate,
int? packetLength,
diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs
index 03c3a72657..f68235d869 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 a70ce44cc6..d7397399dd 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/MediaOptions.cs b/MediaBrowser.Model/Dlna/MediaOptions.cs
index 29aecf97fc..eca971e95e 100644
--- a/MediaBrowser.Model/Dlna/MediaOptions.cs
+++ b/MediaBrowser.Model/Dlna/MediaOptions.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using MediaBrowser.Model.Dto;
@@ -59,22 +57,22 @@ namespace MediaBrowser.Model.Dlna
/// <summary>
/// Gets or sets the media sources.
/// </summary>
- public MediaSourceInfo[] MediaSources { get; set; }
+ public MediaSourceInfo[] MediaSources { get; set; } = Array.Empty<MediaSourceInfo>();
/// <summary>
/// Gets or sets the device profile.
/// </summary>
- public DeviceProfile Profile { get; set; }
+ public required 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; }
/// <summary>
/// Gets or sets the device id.
/// </summary>
- public string DeviceId { get; set; }
+ public string? DeviceId { get; set; }
/// <summary>
/// Gets or sets an override of supported number of audio channels
diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
index ce422a2288..5d7daa81aa 100644
--- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
+++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
@@ -73,27 +73,5 @@ namespace MediaBrowser.Model.Dlna
return null;
}
-
- private static double GetVideoBitrateScaleFactor(string codec)
- {
- if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
- || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)
- || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase))
- {
- return .6;
- }
-
- return 1;
- }
-
- public static int ScaleBitrate(int bitrate, string inputVideoCodec, string outputVideoCodec)
- {
- var inputScaleFactor = GetVideoBitrateScaleFactor(inputVideoCodec);
- var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec);
- var scaleFactor = outputScaleFactor / inputScaleFactor;
- var newBitrate = scaleFactor * bitrate;
-
- return Convert.ToInt32(newBitrate);
- }
}
}
diff --git a/MediaBrowser.Model/Dlna/SortCriteria.cs b/MediaBrowser.Model/Dlna/SortCriteria.cs
index 7fef16e535..7df53c6d19 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 62d67c0257..f6b882c3e6 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -1,9 +1,8 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Jellyfin.Data.Enums;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
@@ -25,6 +24,9 @@ namespace MediaBrowser.Model.Dlna
private readonly ILogger _logger;
private readonly ITranscoderSupport _transcoderSupport;
+ private static readonly string[] _supportedHlsVideoCodecs = new string[] { "h264", "hevc", "av1" };
+ 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.
@@ -38,53 +40,36 @@ namespace MediaBrowser.Model.Dlna
}
/// <summary>
- /// Initializes a new instance of the <see cref="StreamBuilder"/> class.
- /// </summary>
- /// <param name="logger">The <see cref="ILogger"/> object.</param>
- 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)
+ public StreamInfo? GetOptimalAudioStream(MediaOptions options)
{
ValidateMediaOptions(options, false);
- var mediaSources = new List<MediaSourceInfo>();
+ var streams = new List<StreamInfo>();
foreach (var mediaSource in options.MediaSources)
{
- if (string.IsNullOrEmpty(options.MediaSourceId) ||
- string.Equals(mediaSource.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))
+ if (!(string.IsNullOrEmpty(options.MediaSourceId)
+ || string.Equals(mediaSource.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase)))
{
- mediaSources.Add(mediaSource);
+ continue;
}
- }
- var streams = new List<StreamInfo>();
- foreach (var mediaSourceInfo in mediaSources)
- {
- StreamInfo streamInfo = GetOptimalAudioStream(mediaSourceInfo, options);
+ StreamInfo? streamInfo = GetOptimalAudioStream(mediaSource, options);
if (streamInfo is not null)
{
+ streamInfo.DeviceId = options.DeviceId;
+ streamInfo.DeviceProfileId = options.Profile.Id;
streams.Add(streamInfo);
}
}
- foreach (var stream in streams)
- {
- stream.DeviceId = options.DeviceId;
- stream.DeviceProfileId = options.Profile.Id;
- }
-
return GetOptimalStream(streams, options.GetMaxBitrate(true) ?? 0);
}
- private StreamInfo GetOptimalAudioStream(MediaSourceInfo item, MediaOptions options)
+ private StreamInfo? GetOptimalAudioStream(MediaSourceInfo item, MediaOptions options)
{
var playlistItem = new StreamInfo
{
@@ -118,7 +103,7 @@ namespace MediaBrowser.Model.Dlna
var transcodeReasons = directPlayInfo.TranscodeReasons;
var inputAudioChannels = audioStream?.Channels;
- var inputAudioBitrate = audioStream?.BitDepth;
+ var inputAudioBitrate = audioStream?.BitRate;
var inputAudioSampleRate = audioStream?.SampleRate;
var inputAudioBitDepth = audioStream?.BitDepth;
@@ -138,12 +123,12 @@ namespace MediaBrowser.Model.Dlna
}
}
- TranscodingProfile transcodingProfile = null;
+ TranscodingProfile? transcodingProfile = null;
foreach (var tcProfile in options.Profile.TranscodingProfiles)
{
if (tcProfile.Type == playlistItem.MediaType
&& tcProfile.Context == options.Context
- && _transcoderSupport.CanEncodeToAudioCodec(transcodingProfile.AudioCodec ?? tcProfile.Container))
+ && _transcoderSupport.CanEncodeToAudioCodec(tcProfile.AudioCodec ?? tcProfile.Container))
{
transcodingProfile = tcProfile;
break;
@@ -190,15 +175,15 @@ namespace MediaBrowser.Model.Dlna
/// </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)
+ public StreamInfo? GetOptimalVideoStream(MediaOptions options)
{
ValidateMediaOptions(options, true);
var mediaSources = new List<MediaSourceInfo>();
foreach (var mediaSourceInfo in options.MediaSources)
{
- if (string.IsNullOrEmpty(options.MediaSourceId) ||
- string.Equals(mediaSourceInfo.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))
+ if (string.IsNullOrEmpty(options.MediaSourceId)
+ || string.Equals(mediaSourceInfo.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))
{
mediaSources.Add(mediaSourceInfo);
}
@@ -223,7 +208,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)
@@ -366,7 +351,7 @@ namespace MediaBrowser.Model.Dlna
/// <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)
+ public static string? NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, DeviceProfile? profile, DlnaProfileType type, DirectPlayProfile? playProfile = null)
{
if (string.IsNullOrEmpty(inputContainer))
{
@@ -394,7 +379,7 @@ namespace MediaBrowser.Model.Dlna
return formats[0];
}
- private (DirectPlayProfile Profile, PlayMethod? PlayMethod, TranscodeReason TranscodeReasons) GetAudioDirectPlayProfile(MediaSourceInfo item, MediaStream audioStream, MediaOptions 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));
@@ -410,7 +395,6 @@ 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
@@ -449,7 +433,7 @@ 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 is null ? DlnaProfileType.Audio : DlnaProfileType.Video;
@@ -551,8 +535,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;
}
@@ -576,7 +559,7 @@ namespace MediaBrowser.Model.Dlna
}
}
- private static void SetStreamInfoOptionsFromDirectPlayProfile(MediaOptions 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";
@@ -588,7 +571,7 @@ 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, MediaOptions options)
@@ -635,6 +618,12 @@ namespace MediaBrowser.Model.Dlna
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;
@@ -647,7 +636,7 @@ namespace MediaBrowser.Model.Dlna
isEligibleForDirectPlay,
isEligibleForDirectStream);
- DirectPlayProfile directPlayProfile = null;
+ DirectPlayProfile? directPlayProfile = null;
if (isEligibleForDirectPlay || isEligibleForDirectStream)
{
// See if it can be direct played
@@ -678,16 +667,16 @@ namespace MediaBrowser.Model.Dlna
playlistItem.AudioStreamIndex = audioStream?.Index;
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 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;
@@ -749,7 +738,14 @@ namespace MediaBrowser.Model.Dlna
return playlistItem;
}
- private TranscodingProfile GetVideoTranscodeProfile(MediaSourceInfo item, MediaOptions 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))
{
@@ -762,8 +758,8 @@ namespace MediaBrowser.Model.Dlna
if (options.AllowVideoStreamCopy)
{
// prefer direct copy profile
- float videoFramerate = videoStream is null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0;
- TransportStreamTimestamp? timestamp = videoStream is null ? TransportStreamTimestamp.None : item.Timestamp;
+ 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);
@@ -773,7 +769,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 &&
@@ -796,10 +792,26 @@ namespace MediaBrowser.Model.Dlna
return transcodingProfiles.FirstOrDefault();
}
- private void BuildStreamVideoItem(StreamInfo playlistItem, MediaOptions 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 is not null)
{
@@ -835,6 +847,20 @@ namespace MediaBrowser.Model.Dlna
// 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 is not null)
@@ -863,12 +889,12 @@ namespace MediaBrowser.Model.Dlna
int? bitDepth = videoStream?.BitDepth;
int? videoBitrate = videoStream?.BitRate;
double? videoLevel = videoStream?.Level;
- string videoProfile = videoStream?.Profile;
- string videoRangeType = videoStream?.VideoRangeType;
+ string? videoProfile = videoStream?.Profile;
+ VideoRangeType? 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 is null ? TransportStreamTimestamp.None : item.Timestamp;
@@ -880,7 +906,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)
@@ -904,15 +930,15 @@ namespace MediaBrowser.Model.Dlna
playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
bool? isSecondaryAudio = audioStream is null ? null : item.IsSecondaryAudio(audioStream);
- int? inputAudioBitrate = audioStream is null ? null : audioStream.BitRate;
- int? audioChannels = audioStream is null ? null : audioStream.Channels;
- string audioProfile = audioStream is null ? null : audioStream.Profile;
- int? inputAudioSampleRate = audioStream is null ? null : audioStream.SampleRate;
- int? inputAudioBitDepth = audioStream is null ? null : audioStream.BitDepth;
+ 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 codecProfile in appliedAudioConditions)
@@ -956,7 +982,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))
{
@@ -989,9 +1015,9 @@ 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);
@@ -1050,31 +1076,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;
}
@@ -1082,13 +1115,13 @@ namespace MediaBrowser.Model.Dlna
return 7168000;
}
- private (DirectPlayProfile Profile, PlayMethod? PlayMethod, int? AudioStreamIndex, TranscodeReason TranscodeReasons) GetVideoDirectPlayProfile(
+ 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)
{
@@ -1111,12 +1144,12 @@ namespace MediaBrowser.Model.Dlna
int? bitDepth = videoStream?.BitDepth;
int? videoBitrate = videoStream?.BitRate;
double? videoLevel = videoStream?.Level;
- string videoProfile = videoStream?.Profile;
- string videoRangeType = videoStream?.VideoRangeType;
+ string? videoProfile = videoStream?.Profile;
+ VideoRangeType? 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 is null ? TransportStreamTimestamp.None : mediaSource.Timestamp;
@@ -1144,7 +1177,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)));
@@ -1204,14 +1238,14 @@ namespace MediaBrowser.Model.Dlna
}
// 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));
@@ -1332,8 +1366,8 @@ namespace MediaBrowser.Model.Dlna
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)))
{
@@ -1406,7 +1440,7 @@ namespace MediaBrowser.Model.Dlna
};
}
- private static bool IsSubtitleEmbedSupported(string transcodingContainer)
+ private static bool IsSubtitleEmbedSupported(string? transcodingContainer)
{
if (!string.IsNullOrEmpty(transcodingContainer))
{
@@ -1418,7 +1452,8 @@ namespace MediaBrowser.Model.Dlna
{
return false;
}
- else if (ContainerProfile.ContainsContainer(normalizedContainers, "mkv")
+
+ if (ContainerProfile.ContainsContainer(normalizedContainers, "mkv")
|| ContainerProfile.ContainsContainer(normalizedContainers, "matroska"))
{
return true;
@@ -1428,7 +1463,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)
{
@@ -1552,7 +1587,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));
@@ -1561,7 +1597,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,
@@ -1569,7 +1605,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);
@@ -1581,7 +1618,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)
{
@@ -1607,7 +1644,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)
{
@@ -1633,7 +1670,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)
{
@@ -1669,7 +1706,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)
{
@@ -1793,7 +1830,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)
{
@@ -1829,7 +1866,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)
{
@@ -1896,6 +1933,10 @@ namespace MediaBrowser.Model.Dlna
{
item.SetOption(qualifier, "rangetype", string.Join(',', values));
}
+ else if (condition.Condition == ProfileConditionType.NotEquals)
+ {
+ item.SetOption(qualifier, "rangetype", string.Join(',', Enum.GetNames(typeof(VideoRangeType)).Except(values)));
+ }
else if (condition.Condition == ProfileConditionType.EqualsAny)
{
var currentValue = item.GetOption(qualifier, "rangetype");
@@ -1919,7 +1960,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)
{
@@ -1945,7 +1986,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)
{
@@ -1971,7 +2012,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)
{
@@ -1997,7 +2038,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)
{
@@ -2023,7 +2064,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)
{
@@ -2057,7 +2098,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 3b55099079..00543616d1 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
+using Jellyfin.Data.Enums;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -107,9 +108,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.
@@ -282,23 +282,24 @@ namespace MediaBrowser.Model.Dlna
/// <summary>
/// Gets the target video range type that will be in the output stream.
/// </summary>
- public string TargetVideoRangeType
+ public VideoRangeType TargetVideoRangeType
{
get
{
if (IsDirectStream)
{
- return TargetVideoStream?.VideoRangeType;
+ return TargetVideoStream?.VideoRangeType ?? VideoRangeType.Unknown;
}
var targetVideoCodecs = TargetVideoCodec;
var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
- if (!string.IsNullOrEmpty(videoCodec))
+ if (!string.IsNullOrEmpty(videoCodec)
+ && Enum.TryParse(GetOption(videoCodec, "rangetype"), true, out VideoRangeType videoRangeType))
{
- return GetOption(videoCodec, "rangetype");
+ return videoRangeType;
}
- return TargetVideoStream?.VideoRangeType;
+ return TargetVideoStream?.VideoRangeType ?? VideoRangeType.Unknown;
}
}
@@ -431,7 +432,7 @@ namespace MediaBrowser.Model.Dlna
return totalBitrate.HasValue ?
Convert.ToInt64(totalBitrate.Value * totalSeconds) :
- (long?)null;
+ null;
}
return null;
@@ -922,12 +923,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;
}
@@ -938,12 +935,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;
}
@@ -954,12 +947,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;
}
@@ -970,12 +959,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/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 2a86fded22..8fab1ca6d6 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 9c65a2308d..d3bcf492d8 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.
diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs
index 256d7b10f1..05019741e0 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/IHasShares.cs b/MediaBrowser.Model/Entities/IHasShares.cs
new file mode 100644
index 0000000000..b34d1a0376
--- /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 47341f4e17..34642b83aa 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
+using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Extensions;
@@ -148,7 +149,7 @@ namespace MediaBrowser.Model.Entities
/// Gets the video range.
/// </summary>
/// <value>The video range.</value>
- public string VideoRange
+ public VideoRange VideoRange
{
get
{
@@ -162,7 +163,7 @@ namespace MediaBrowser.Model.Entities
/// Gets the video range type.
/// </summary>
/// <value>The video range type.</value>
- public string VideoRangeType
+ public VideoRangeType VideoRangeType
{
get
{
@@ -306,9 +307,9 @@ namespace MediaBrowser.Model.Entities
attributes.Add(Codec.ToUpperInvariant());
}
- if (!string.IsNullOrEmpty(VideoRange))
+ if (VideoRange != VideoRange.Unknown)
{
- attributes.Add(VideoRange.ToUpperInvariant());
+ attributes.Add(VideoRange.ToString());
}
if (!string.IsNullOrEmpty(Title))
@@ -677,23 +678,23 @@ namespace MediaBrowser.Model.Entities
return true;
}
- public (string VideoRange, string VideoRangeType) GetVideoColorRange()
+ public (VideoRange VideoRange, VideoRangeType VideoRangeType) GetVideoColorRange()
{
if (Type != MediaStreamType.Video)
{
- return (null, null);
+ return (VideoRange.Unknown, VideoRangeType.Unknown);
}
var colorTransfer = ColorTransfer;
if (string.Equals(colorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase))
{
- return ("HDR", "HDR10");
+ return (VideoRange.HDR, VideoRangeType.HDR10);
}
if (string.Equals(colorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
{
- return ("HDR", "HLG");
+ return (VideoRange.HDR, VideoRangeType.HLG);
}
var codecTag = CodecTag;
@@ -711,10 +712,10 @@ namespace MediaBrowser.Model.Entities
|| string.Equals(codecTag, "dvhe", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codecTag, "dav1", StringComparison.OrdinalIgnoreCase))
{
- return ("HDR", "DOVI");
+ return (VideoRange.HDR, VideoRangeType.DOVI);
}
- return ("SDR", "SDR");
+ return (VideoRange.SDR, VideoRangeType.SDR);
}
}
}
diff --git a/MediaBrowser.Model/Entities/ParentalRating.cs b/MediaBrowser.Model/Entities/ParentalRating.cs
index 17b2868a31..c92640818c 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/Share.cs b/MediaBrowser.Model/Entities/Share.cs
new file mode 100644
index 0000000000..186aad1892
--- /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/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
index e00157dce9..02a29e7faf 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/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 521ba0f107..9a58044853 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -33,14 +33,14 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
- <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
- <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="7.0.1" />
+ <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.4">
+ <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 7b83b1b9df..4c22af4498 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
new file mode 100644
index 0000000000..d546ffccdc
--- /dev/null
+++ b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs
@@ -0,0 +1,41 @@
+#nullable disable
+
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Model.MediaInfo;
+
+/// <summary>
+/// Represents the result of BDInfo output.
+/// </summary>
+public class BlurayDiscInfo
+{
+ /// <summary>
+ /// Gets or sets the media streams.
+ /// </summary>
+ /// <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 files.
+ /// </summary>
+ /// <value>The files.</value>
+ public string[] Files { 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; }
+}
diff --git a/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs b/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs
new file mode 100644
index 0000000000..d397253010
--- /dev/null
+++ b/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs
@@ -0,0 +1,14 @@
+namespace MediaBrowser.Model.MediaInfo;
+
+/// <summary>
+/// Interface IBlurayExaminer.
+/// </summary>
+public interface IBlurayExaminer
+{
+ /// <summary>
+ /// Gets the disc info.
+ /// </summary>
+ /// <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 8157dc0c24..5a1871070d 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" },
@@ -178,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 b00158cb33..0000000000
--- 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/Playlists/PlaylistCreationRequest.cs b/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs
index e8ee494034..8472697164 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/SyncPlay/GroupUpdate.cs b/MediaBrowser.Model/SyncPlay/GroupUpdate.cs
index 6f159d653c..ec67d7ea87 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 0000000000..25cd444611
--- /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 cce99c77d5..376d926c9a 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 a6dcc109ed..da81fecbdc 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/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs
index 13bebc479e..5b55667e82 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/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
index 3634d07058..8354c60efb 100644
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ b/MediaBrowser.Model/Users/UserPolicy.cs
@@ -2,6 +2,7 @@
#pragma warning disable CS1591, CA1819
using System;
+using System.ComponentModel;
using System.Xml.Serialization;
using Jellyfin.Data.Enums;
using AccessSchedule = Jellyfin.Data.Entities.AccessSchedule;
@@ -13,6 +14,7 @@ namespace MediaBrowser.Model.Users
public UserPolicy()
{
IsHidden = true;
+ EnableCollectionManagement = false;
EnableContentDeletion = false;
EnableContentDeletionFromFolders = Array.Empty<string>();
@@ -35,6 +37,7 @@ namespace MediaBrowser.Model.Users
EnableSharedDeviceControl = true;
BlockedTags = Array.Empty<string>();
+ AllowedTags = Array.Empty<string>();
BlockUnratedItems = Array.Empty<UnratedItem>();
EnableUserPreferenceAccess = true;
@@ -44,6 +47,7 @@ namespace MediaBrowser.Model.Users
LoginAttemptsBeforeLockout = -1;
MaxActiveSessions = 0;
+ MaxParentalRating = null;
EnableAllChannels = true;
EnabledChannels = Array.Empty<Guid>();
@@ -73,6 +77,13 @@ 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>
+ [DefaultValue(false)]
+ 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 +97,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; }