aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Model
diff options
context:
space:
mode:
authorClaus Vium <cvium@users.noreply.github.com>2022-10-07 09:57:16 +0200
committerGitHub <noreply@github.com>2022-10-07 09:57:16 +0200
commit81b04ddbb54201d2e07310e3c700cca8a94d8955 (patch)
treea4e9aeb70e35b3e47a7d8af17b328e88a1c77ed0 /MediaBrowser.Model
parent6bf71c0fd355e9c95a1e142019d9bc5cce34200d (diff)
parent14027f962ce074623fd89967ca9565bbeb785066 (diff)
Merge branch 'master' into providermanager-cleanup
Diffstat (limited to 'MediaBrowser.Model')
-rw-r--r--MediaBrowser.Model/Branding/BrandingOptions.cs6
-rw-r--r--MediaBrowser.Model/Configuration/EmbeddedSubtitleOptions.cs2
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs10
-rw-r--r--MediaBrowser.Model/Configuration/LibraryOptions.cs2
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs2
-rw-r--r--MediaBrowser.Model/Configuration/UserConfiguration.cs16
-rw-r--r--MediaBrowser.Model/Cryptography/PasswordHash.cs5
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs3
-rw-r--r--MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs6
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfile.cs4
-rw-r--r--MediaBrowser.Model/Dlna/ProfileConditionValue.cs3
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs162
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs24
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs6
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs182
-rw-r--r--MediaBrowser.Model/Entities/MediaStreamType.cs7
-rw-r--r--MediaBrowser.Model/Entities/MetadataProvider.cs6
-rw-r--r--MediaBrowser.Model/Entities/ProviderIdsExtensions.cs38
-rw-r--r--MediaBrowser.Model/LiveTv/TunerHostInfo.cs3
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj10
-rw-r--r--MediaBrowser.Model/MediaInfo/SubtitleFormat.cs1
-rw-r--r--MediaBrowser.Model/Querying/ItemSortBy.cs52
-rw-r--r--MediaBrowser.Model/Querying/NextUpQuery.cs2
-rw-r--r--MediaBrowser.Model/Search/SearchHint.cs66
-rw-r--r--MediaBrowser.Model/Session/GeneralCommand.cs4
-rw-r--r--MediaBrowser.Model/Session/PlayerStateInfo.cs6
-rw-r--r--MediaBrowser.Model/Session/TranscodeReason.cs1
-rw-r--r--MediaBrowser.Model/SyncPlay/GroupStateType.cs2
-rw-r--r--MediaBrowser.Model/Tasks/ITaskManager.cs10
-rw-r--r--MediaBrowser.Model/Tasks/ITaskTrigger.cs4
30 files changed, 483 insertions, 162 deletions
diff --git a/MediaBrowser.Model/Branding/BrandingOptions.cs b/MediaBrowser.Model/Branding/BrandingOptions.cs
index cc42c1718a..695267d46d 100644
--- a/MediaBrowser.Model/Branding/BrandingOptions.cs
+++ b/MediaBrowser.Model/Branding/BrandingOptions.cs
@@ -1,5 +1,4 @@
using System.Text.Json.Serialization;
-using System.Xml.Serialization;
namespace MediaBrowser.Model.Branding;
@@ -21,6 +20,11 @@ public class BrandingOptions
public string? CustomCss { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether to enable the splashscreen.
+ /// </summary>
+ public bool SplashscreenEnabled { get; set; } = true;
+
+ /// <summary>
/// Gets or sets the splashscreen location on disk.
/// </summary>
/// <remarks>
diff --git a/MediaBrowser.Model/Configuration/EmbeddedSubtitleOptions.cs b/MediaBrowser.Model/Configuration/EmbeddedSubtitleOptions.cs
index 42f07dbff5..98e0f84e3f 100644
--- a/MediaBrowser.Model/Configuration/EmbeddedSubtitleOptions.cs
+++ b/MediaBrowser.Model/Configuration/EmbeddedSubtitleOptions.cs
@@ -5,7 +5,6 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
public enum EmbeddedSubtitleOptions
{
-
/// <summary>
/// Allow all embedded subs.
/// </summary>
@@ -26,5 +25,4 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
AllowNone = 3,
}
-
}
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index 06931ac3b5..f4cd2f0065 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -1,5 +1,3 @@
-using System;
-
#nullable disable
#pragma warning disable CS1591
@@ -26,6 +24,8 @@ namespace MediaBrowser.Model.Configuration
TonemappingThreshold = 0.8;
TonemappingPeak = 100;
TonemappingParam = 0;
+ VppTonemappingBrightness = 0;
+ VppTonemappingContrast = 1.2;
H264Crf = 23;
H265Crf = 28;
DeinterlaceDoubleRate = false;
@@ -39,7 +39,7 @@ namespace MediaBrowser.Model.Configuration
EnableHardwareEncoding = true;
AllowHevcEncoding = false;
EnableSubtitleExtraction = true;
- AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = Array.Empty<string>();
+ AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = new[] { "mkv" };
HardwareDecodingCodecs = new string[] { "h264", "vc1" };
}
@@ -89,6 +89,10 @@ namespace MediaBrowser.Model.Configuration
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; }
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
index ad3bce86ea..c4d313bdba 100644
--- a/MediaBrowser.Model/Configuration/LibraryOptions.cs
+++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs
@@ -17,7 +17,7 @@ namespace MediaBrowser.Model.Configuration
RequirePerfectSubtitleMatch = true;
AllowEmbeddedSubtitles = EmbeddedSubtitleOptions.AllowAll;
- AutomaticallyAddToCollection = true;
+ AutomaticallyAddToCollection = false;
EnablePhotos = true;
SaveSubtitlesWithMedia = true;
EnableRealtimeMonitor = true;
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 46e61ee1a2..e61b896b9d 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -79,7 +79,7 @@ namespace MediaBrowser.Model.Configuration
/// <summary>
/// Gets or sets a value indicating whether quick connect is available for use on this server.
/// </summary>
- public bool QuickConnectAvailable { get; set; } = false;
+ public bool QuickConnectAvailable { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether [enable case sensitive item ids].
diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs
index 81359462c3..94f3546608 100644
--- a/MediaBrowser.Model/Configuration/UserConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs
@@ -22,10 +22,10 @@ namespace MediaBrowser.Model.Configuration
HidePlayedInLatest = true;
PlayDefaultAudioTrack = true;
- LatestItemsExcludes = Array.Empty<string>();
- OrderedViews = Array.Empty<string>();
- MyMediaExcludes = Array.Empty<string>();
- GroupedFolders = Array.Empty<string>();
+ LatestItemsExcludes = Array.Empty<Guid>();
+ OrderedViews = Array.Empty<Guid>();
+ MyMediaExcludes = Array.Empty<Guid>();
+ GroupedFolders = Array.Empty<Guid>();
}
/// <summary>
@@ -48,7 +48,7 @@ namespace MediaBrowser.Model.Configuration
public bool DisplayMissingEpisodes { get; set; }
- public string[] GroupedFolders { get; set; }
+ public Guid[] GroupedFolders { get; set; }
public SubtitlePlaybackMode SubtitleMode { get; set; }
@@ -56,11 +56,11 @@ namespace MediaBrowser.Model.Configuration
public bool EnableLocalPassword { get; set; }
- public string[] OrderedViews { get; set; }
+ public Guid[] OrderedViews { get; set; }
- public string[] LatestItemsExcludes { get; set; }
+ public Guid[] LatestItemsExcludes { get; set; }
- public string[] MyMediaExcludes { get; set; }
+ public Guid[] MyMediaExcludes { get; set; }
public bool HidePlayedInLatest { get; set; }
diff --git a/MediaBrowser.Model/Cryptography/PasswordHash.cs b/MediaBrowser.Model/Cryptography/PasswordHash.cs
index eec5410411..32a34d23c1 100644
--- a/MediaBrowser.Model/Cryptography/PasswordHash.cs
+++ b/MediaBrowser.Model/Cryptography/PasswordHash.cs
@@ -29,10 +29,7 @@ namespace MediaBrowser.Model.Cryptography
public PasswordHash(string id, byte[] hash, byte[] salt, Dictionary<string, string> parameters)
{
- if (id == null)
- {
- throw new ArgumentNullException(nameof(id));
- }
+ ArgumentNullException.ThrowIfNull(id);
if (id.Length == 0)
{
diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
index 8d03b4c0b3..5734224167 100644
--- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs
+++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
@@ -16,6 +16,7 @@ namespace MediaBrowser.Model.Dlna
int? videoBitDepth,
int? videoBitrate,
string? videoProfile,
+ string? videoRangeType,
double? videoLevel,
float? videoFramerate,
int? packetLength,
@@ -42,6 +43,8 @@ namespace MediaBrowser.Model.Dlna
return IsConditionSatisfied(condition, videoLevel);
case ProfileConditionValue.VideoProfile:
return IsConditionSatisfied(condition, videoProfile);
+ case ProfileConditionValue.VideoRangeType:
+ return IsConditionSatisfied(condition, videoRangeType);
case ProfileConditionValue.VideoCodecTag:
return IsConditionSatisfied(condition, videoCodecTag);
case ProfileConditionValue.PacketLength:
diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
index 6e129246b0..1a95763610 100644
--- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
+++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
@@ -8,7 +8,7 @@ using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.Model.Dlna
{
- public class ContentFeatureBuilder
+ public static class ContentFeatureBuilder
{
public static string BuildImageHeader(
DeviceProfile profile,
@@ -128,6 +128,7 @@ namespace MediaBrowser.Model.Dlna
bool isDirectStream,
long? runtimeTicks,
string videoProfile,
+ string videoRangeType,
double? videoLevel,
float? videoFramerate,
int? packetLength,
@@ -156,7 +157,7 @@ namespace MediaBrowser.Model.Dlna
flagValue |= DlnaFlags.ByteBasedSeek;
}
- // Time based seek is curently disabled when streaming. On LG CX3 adding DlnaFlags.TimeBasedSeek and orgPn causes the DLNA playback to fail (format not supported). Further investigations are needed before enabling the remaining code paths.
+ // Time based seek is currently disabled when streaming. On LG CX3 adding DlnaFlags.TimeBasedSeek and orgPn causes the DLNA playback to fail (format not supported). Further investigations are needed before enabling the remaining code paths.
// else if (runtimeTicks.HasValue)
// {
// flagValue = flagValue | DlnaFlags.TimeBasedSeek;
@@ -176,6 +177,7 @@ namespace MediaBrowser.Model.Dlna
bitDepth,
videoBitrate,
videoProfile,
+ videoRangeType,
videoLevel,
videoFramerate,
packetLength,
diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs
index 6170ff5bd6..79ae951708 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs
@@ -423,6 +423,7 @@ namespace MediaBrowser.Model.Dlna
/// <param name="bitDepth">The bit depth.</param>
/// <param name="videoBitrate">The video bitrate.</param>
/// <param name="videoProfile">The video profile.</param>
+ /// <param name="videoRangeType">The video range type.</param>
/// <param name="videoLevel">The video level.</param>
/// <param name="videoFramerate">The video framerate.</param>
/// <param name="packetLength">The packet length.</param>
@@ -444,6 +445,7 @@ namespace MediaBrowser.Model.Dlna
int? bitDepth,
int? videoBitrate,
string videoProfile,
+ string videoRangeType,
double? videoLevel,
float? videoFramerate,
int? packetLength,
@@ -483,7 +485,7 @@ namespace MediaBrowser.Model.Dlna
var anyOff = false;
foreach (ProfileCondition c in i.Conditions)
{
- if (!ConditionProcessor.IsVideoConditionSatisfied(GetModelProfileCondition(c), width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
+ if (!ConditionProcessor.IsVideoConditionSatisfied(GetModelProfileCondition(c), width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
{
anyOff = true;
break;
diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
index eb81fde751..a32433e185 100644
--- a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
+++ b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
@@ -26,6 +26,7 @@ namespace MediaBrowser.Model.Dlna
IsAvc = 20,
IsInterlaced = 21,
AudioSampleRate = 22,
- AudioBitDepth = 23
+ AudioBitDepth = 23,
+ VideoRangeType = 24
}
}
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 8671e5cbbf..b121a29058 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -221,6 +221,9 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.VideoProfile:
return TranscodeReason.VideoProfileNotSupported;
+ case ProfileConditionValue.VideoRangeType:
+ return TranscodeReason.VideoRangeTypeNotSupported;
+
case ProfileConditionValue.VideoTimestamp:
// TODO
return 0;
@@ -385,7 +388,7 @@ namespace MediaBrowser.Model.Dlna
// If device requirements are satisfied then allow both direct stream and direct play
if (item.SupportsDirectPlay)
{
- if (IsItemBitrateEligibleForDirectPlay(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectPlay))
+ if (IsItemBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectPlay))
{
if (options.EnableDirectPlay)
{
@@ -401,7 +404,7 @@ namespace MediaBrowser.Model.Dlna
// While options takes the network and other factors into account. Only applies to direct stream
if (item.SupportsDirectStream)
{
- if (IsItemBitrateEligibleForDirectPlay(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream))
+ if (IsItemBitrateEligibleForDirectPlayback(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream))
{
if (options.EnableDirectStream)
{
@@ -562,10 +565,7 @@ namespace MediaBrowser.Model.Dlna
private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options)
{
- if (item == null)
- {
- throw new ArgumentNullException(nameof(item));
- }
+ ArgumentNullException.ThrowIfNull(item);
StreamInfo playlistItem = new StreamInfo
{
@@ -604,11 +604,11 @@ namespace MediaBrowser.Model.Dlna
var videoStream = item.VideoStream;
- var directPlayEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectPlay);
- var directStreamEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false) ?? 0, options, PlayMethod.DirectStream);
- bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayEligibilityResult == 0);
- bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directPlayEligibilityResult == 0);
- var transcodeReasons = directPlayEligibilityResult | directStreamEligibilityResult;
+ 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;
_logger.LogDebug(
"Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}",
@@ -625,7 +625,7 @@ namespace MediaBrowser.Model.Dlna
var directPlay = directPlayInfo.PlayMethod;
transcodeReasons |= directPlayInfo.TranscodeReasons;
- if (directPlay != null)
+ if (directPlay.HasValue)
{
directPlayProfile = directPlayInfo.Profile;
playlistItem.PlayMethod = directPlay.Value;
@@ -676,7 +676,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem.TranscodeReasons = transcodeReasons;
- if (playlistItem.PlayMethod != PlayMethod.DirectStream || !options.EnableDirectStream)
+ if (playlistItem.PlayMethod != PlayMethod.DirectStream && playlistItem.PlayMethod != PlayMethod.DirectPlay)
{
// Can't direct play, find the transcoding profile
// If we do this for direct-stream we will overwrite the info
@@ -687,6 +687,8 @@ namespace MediaBrowser.Model.Dlna
BuildStreamVideoItem(playlistItem, options, item, videoStream, audioStream, candidateAudioStreams, transcodingProfile.Container, transcodingProfile.VideoCodec, transcodingProfile.AudioCodec);
+ playlistItem.PlayMethod = PlayMethod.Transcode;
+
if (subtitleStream != null)
{
var subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Container, transcodingProfile.Protocol);
@@ -696,14 +698,9 @@ namespace MediaBrowser.Model.Dlna
playlistItem.SubtitleCodecs = new[] { subtitleProfile.Format };
}
- if (playlistItem.PlayMethod != PlayMethod.DirectPlay)
+ if ((playlistItem.TranscodeReasons & (VideoReasons | TranscodeReason.ContainerBitrateExceedsLimit)) != 0)
{
- playlistItem.PlayMethod = PlayMethod.Transcode;
-
- if ((playlistItem.TranscodeReasons & (VideoReasons | TranscodeReason.ContainerBitrateExceedsLimit)) != 0)
- {
- ApplyTranscodingConditions(playlistItem, transcodingProfile.Conditions, null, true, true);
- }
+ ApplyTranscodingConditions(playlistItem, transcodingProfile.Conditions, null, true, true);
}
}
}
@@ -744,16 +741,19 @@ namespace MediaBrowser.Model.Dlna
{
var videoCodecs = ContainerProfile.SplitValue(transcodingProfile.VideoCodec);
- if (ContainerProfile.ContainsContainer(videoCodecs, item.VideoStream.Codec))
+ if (ContainerProfile.ContainsContainer(videoCodecs, item.VideoStream?.Codec))
{
var videoCodec = transcodingProfile.VideoCodec;
var container = transcodingProfile.Container;
var appliedVideoConditions = options.Profile.CodecProfiles
.Where(i => i.Type == CodecType.Video &&
- i.ContainsAnyCodec(videoCodec, container))
+ i.ContainsAnyCodec(videoCodec, container) &&
+ i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.VideoRangeType, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC)))
.Select(i =>
- i.ApplyConditions.Any(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC)));
- var conditionsSatisfied = !appliedVideoConditions.Any() || !appliedVideoConditions.Any(satisfied => !satisfied);
+ i.Conditions.All(condition => ConditionProcessor.IsVideoConditionSatisfied(condition, videoStream?.Width, videoStream?.Height, videoStream?.BitDepth, videoStream?.BitRate, videoStream?.Profile, videoStream?.VideoRangeType, videoStream?.Level, videoFramerate, videoStream?.PacketLength, timestamp, videoStream?.IsAnamorphic, videoStream?.IsInterlaced, videoStream?.RefFrames, numVideoStreams, numAudioStreams, videoStream?.CodecTag, videoStream?.IsAVC)));
+
+ // An empty appliedVideoConditions means that the codec has no conditions for the current video stream
+ var conditionsSatisfied = appliedVideoConditions.All(satisfied => satisfied);
return conditionsSatisfied ? 1 : 2;
}
@@ -768,35 +768,42 @@ namespace MediaBrowser.Model.Dlna
private void BuildStreamVideoItem(StreamInfo playlistItem, VideoOptions options, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable<MediaStream> candidateAudioStreams, string container, string videoCodec, string audioCodec)
{
- // prefer matching video codecs
+ // Prefer matching video codecs
var videoCodecs = ContainerProfile.SplitValue(videoCodec);
- var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream.Codec) ? videoStream.Codec : null;
- playlistItem.VideoCodecs = directVideoCodec != null ? new[] { directVideoCodec } : videoCodecs;
+ var directVideoCodec = ContainerProfile.ContainsContainer(videoCodecs, videoStream?.Codec) ? videoStream?.Codec : null;
+ if (directVideoCodec != null)
+ {
+ // merge directVideoCodec to videoCodecs
+ Array.Resize(ref videoCodecs, videoCodecs.Length + 1);
+ videoCodecs[^1] = directVideoCodec;
+ }
+
+ playlistItem.VideoCodecs = videoCodecs;
- // 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.HasValue)
+ // 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)
{
playlistItem.SetOption(qualifier, "level", videoStream.Level.Value.ToString(CultureInfo.InvariantCulture));
}
- if (videoStream.BitDepth.HasValue)
+ if (videoStream?.BitDepth != null)
{
playlistItem.SetOption(qualifier, "videobitdepth", videoStream.BitDepth.Value.ToString(CultureInfo.InvariantCulture));
}
- if (!string.IsNullOrEmpty(videoStream.Profile))
+ if (!string.IsNullOrEmpty(videoStream?.Profile))
{
playlistItem.SetOption(qualifier, "profile", videoStream.Profile.ToLowerInvariant());
}
- if (videoStream.Level != 0)
+ if (videoStream != null && videoStream.Level != 0)
{
playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString());
}
- // prefer matching audio codecs, could do better here
+ // Prefer matching audio codecs, could do better here
var audioCodecs = ContainerProfile.SplitValue(audioCodec);
var directAudioStream = candidateAudioStreams.FirstOrDefault(stream => ContainerProfile.ContainsContainer(audioCodecs, stream.Codec));
playlistItem.AudioCodecs = audioCodecs;
@@ -806,7 +813,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem.AudioStreamIndex = audioStream.Index;
playlistItem.AudioCodecs = new[] { audioStream.Codec };
- // copy matching audio codec options
+ // Copy matching audio codec options
playlistItem.AudioSampleRate = audioStream.SampleRate;
playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels.ToString());
@@ -827,6 +834,7 @@ namespace MediaBrowser.Model.Dlna
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;
bool? isAnamorphic = videoStream?.IsAnamorphic;
bool? isInterlaced = videoStream?.IsInterlaced;
@@ -843,7 +851,7 @@ namespace MediaBrowser.Model.Dlna
var appliedVideoConditions = options.Profile.CodecProfiles
.Where(i => i.Type == CodecType.Video &&
i.ContainsAnyCodec(videoCodec, container) &&
- i.ApplyConditions.Any(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)));
+ 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)
{
@@ -873,9 +881,9 @@ namespace MediaBrowser.Model.Dlna
int? inputAudioBitDepth = audioStream == null ? null : audioStream.BitDepth;
var appliedAudioConditions = options.Profile.CodecProfiles
- .Where(i => i.Type == CodecType.Video &&
+ .Where(i => i.Type == CodecType.VideoAudio &&
i.ContainsAnyCodec(audioCodec, container) &&
- i.ApplyConditions.Any(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio)));
+ i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio)));
isFirstAppliedCodecProfile = true;
foreach (var i in appliedAudioConditions)
{
@@ -1067,19 +1075,20 @@ namespace MediaBrowser.Model.Dlna
DeviceProfile profile = options.Profile;
string container = mediaSource.Container;
- // video
+ // Video
int? width = videoStream?.Width;
int? height = videoStream?.Height;
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;
bool? isAnamorphic = videoStream?.IsAnamorphic;
bool? isInterlaced = videoStream?.IsInterlaced;
string videoCodecTag = videoStream?.CodecTag;
bool? isAvc = videoStream?.IsAVC;
- // audio
+ // Audio
var defaultLanguage = audioStream?.Language ?? string.Empty;
var defaultMarked = audioStream?.IsDefault ?? false;
@@ -1091,7 +1100,7 @@ namespace MediaBrowser.Model.Dlna
int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video);
var checkVideoConditions = (ProfileCondition[] conditions) =>
- conditions.Where(applyCondition => !ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc));
+ conditions.Where(applyCondition => !ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc));
// Check container conditions
var containerProfileReasons = AggregateFailureConditions(
@@ -1108,18 +1117,9 @@ namespace MediaBrowser.Model.Dlna
profile,
"VideoCodecProfile",
profile.CodecProfiles
- .Where(codecProfile => codecProfile.Type == CodecType.Video && codecProfile.ContainsAnyCodec(videoStream?.Codec, container))
- .SelectMany(codecProfile =>
- {
- var failedApplyConditions = checkVideoConditions(codecProfile.ApplyConditions);
- if (!failedApplyConditions.Any())
- {
- return Array.Empty<ProfileCondition>();
- }
-
- var failedConditions = checkVideoConditions(codecProfile.Conditions);
- return failedApplyConditions.Concat(failedConditions);
- }));
+ .Where(codecProfile => codecProfile.Type == CodecType.Video && codecProfile.ContainsAnyCodec(videoStream?.Codec, container) &&
+ !checkVideoConditions(codecProfile.ApplyConditions).Any())
+ .SelectMany(codecProfile => checkVideoConditions(codecProfile.Conditions)));
// Check audiocandidates profile conditions
var audioStreamMatches = candidateAudioStreams.ToDictionary(s => s, audioStream => CheckVideoAudioStreamDirectPlay(options, mediaSource, container, audioStream, defaultLanguage, defaultMarked));
@@ -1189,7 +1189,18 @@ namespace MediaBrowser.Model.Dlna
audioCodecProfileReasons = audioStreamMatches.GetValueOrDefault(selectedAudioStream);
}
- var failureReasons = directPlayProfileReasons | containerProfileReasons | videoCodecProfileReasons | audioCodecProfileReasons | subtitleProfileReasons;
+ var failureReasons = directPlayProfileReasons | containerProfileReasons | subtitleProfileReasons;
+
+ if ((failureReasons & TranscodeReason.VideoCodecNotSupported) == 0)
+ {
+ failureReasons |= videoCodecProfileReasons;
+ }
+
+ if ((failureReasons & TranscodeReason.AudioCodecNotSupported) == 0)
+ {
+ failureReasons |= audioCodecProfileReasons;
+ }
+
var directStreamFailureReasons = failureReasons & (~DirectStreamReasons);
PlayMethod? playMethod = null;
@@ -1206,6 +1217,7 @@ namespace MediaBrowser.Model.Dlna
return (Result: (Profile: directPlayProfile, PlayMethod: playMethod, AudioStreamIndex: selectedAudioStream?.Index, TranscodeReason: failureReasons), Order: order, Rank: ranked);
})
.OrderByDescending(analysis => analysis.Result.PlayMethod)
+ .ThenByDescending(analysis => analysis.Rank)
.ThenBy(analysis => analysis.Order)
.ToArray()
.ToLookup(analysis => analysis.Result.PlayMethod != null);
@@ -1218,7 +1230,7 @@ namespace MediaBrowser.Model.Dlna
return profileMatch;
}
- var failureReasons = analyzedProfiles[false].OrderBy(a => a.Result.TranscodeReason).ThenBy(analysis => analysis.Order).FirstOrDefault().Result.TranscodeReason;
+ var failureReasons = analyzedProfiles[false].Select(analysis => analysis.Result).FirstOrDefault().TranscodeReason;
if (failureReasons == 0)
{
failureReasons = TranscodeReason.DirectPlayError;
@@ -1264,13 +1276,13 @@ namespace MediaBrowser.Model.Dlna
mediaSource.Path ?? "Unknown path");
}
- private TranscodeReason IsEligibleForDirectPlay(
+ private TranscodeReason IsBitrateEligibleForDirectPlayback(
MediaSourceInfo item,
long maxBitrate,
VideoOptions options,
PlayMethod playMethod)
{
- bool result = IsItemBitrateEligibleForDirectPlay(item, maxBitrate, playMethod);
+ bool result = IsItemBitrateEligibleForDirectPlayback(item, maxBitrate, playMethod);
if (!result)
{
return TranscodeReason.ContainerBitrateExceedsLimit;
@@ -1438,7 +1450,7 @@ namespace MediaBrowser.Model.Dlna
return null;
}
- private bool IsItemBitrateEligibleForDirectPlay(MediaSourceInfo item, long maxBitrate, PlayMethod playMethod)
+ private bool IsItemBitrateEligibleForDirectPlayback(MediaSourceInfo item, long maxBitrate, PlayMethod playMethod)
{
// Don't restrict by bitrate if coming from an external domain
if (item.IsRemote)
@@ -1842,6 +1854,38 @@ namespace MediaBrowser.Model.Dlna
break;
}
+ case ProfileConditionValue.VideoRangeType:
+ {
+ if (string.IsNullOrEmpty(qualifier))
+ {
+ continue;
+ }
+
+ // change from split by | to comma
+ // strip spaces to avoid having to encode
+ var values = value
+ .Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
+
+ if (condition.Condition == ProfileConditionType.Equals)
+ {
+ item.SetOption(qualifier, "rangetype", string.Join(',', values));
+ }
+ else if (condition.Condition == ProfileConditionType.EqualsAny)
+ {
+ var currentValue = item.GetOption(qualifier, "rangetype");
+ if (!string.IsNullOrEmpty(currentValue) && values.Any(v => string.Equals(v, currentValue, StringComparison.OrdinalIgnoreCase)))
+ {
+ item.SetOption(qualifier, "rangetype", currentValue);
+ }
+ else
+ {
+ item.SetOption(qualifier, "rangetype", string.Join(',', values));
+ }
+ }
+
+ break;
+ }
+
case ProfileConditionValue.Height:
{
if (!enableNonQualifiedConditions)
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index da089602f1..5cfa2e7e3c 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.Linq;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -281,6 +280,29 @@ namespace MediaBrowser.Model.Dlna
}
/// <summary>
+ /// Gets the target video range type that will be in the output stream.
+ /// </summary>
+ public string TargetVideoRangeType
+ {
+ get
+ {
+ if (IsDirectStream)
+ {
+ return TargetVideoStream?.VideoRangeType;
+ }
+
+ var targetVideoCodecs = TargetVideoCodec;
+ var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
+ if (!string.IsNullOrEmpty(videoCodec))
+ {
+ return GetOption(videoCodec, "rangetype");
+ }
+
+ return TargetVideoStream?.VideoRangeType;
+ }
+ }
+
+ /// <summary>
/// Gets the target video codec tag.
/// </summary>
/// <value>The target video codec tag.</value>
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 094dc73b27..fdb84fa320 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -294,13 +294,13 @@ namespace MediaBrowser.Model.Dto
public NameGuidPair[] GenreItems { get; set; }
/// <summary>
- /// Gets or sets wether the item has a logo, this will hold the Id of the Parent that has one.
+ /// Gets or sets whether the item has a logo, this will hold the Id of the Parent that has one.
/// </summary>
/// <value>The parent logo item id.</value>
public Guid? ParentLogoItemId { get; set; }
/// <summary>
- /// Gets or sets wether the item has any backdrops, this will hold the Id of the Parent that has one.
+ /// Gets or sets whether the item has any backdrops, this will hold the Id of the Parent that has one.
/// </summary>
/// <value>The parent backdrop item id.</value>
public Guid? ParentBackdropItemId { get; set; }
@@ -506,7 +506,7 @@ namespace MediaBrowser.Model.Dto
public string ParentLogoImageTag { get; set; }
/// <summary>
- /// Gets or sets wether the item has fan art, this will hold the Id of the Parent that has one.
+ /// Gets or sets whether the item has fan art, this will hold the Id of the Parent that has one.
/// </summary>
/// <value>The parent art item id.</value>
public Guid? ParentArtItemId { get; set; }
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index 4742d21e9e..90a60cf470 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -73,6 +73,54 @@ namespace MediaBrowser.Model.Entities
public string ColorPrimaries { get; set; }
/// <summary>
+ /// Gets or sets the Dolby Vision version major.
+ /// </summary>
+ /// <value>The Dolby Vision version major.</value>
+ public int? DvVersionMajor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Dolby Vision version minor.
+ /// </summary>
+ /// <value>The Dolby Vision version minor.</value>
+ public int? DvVersionMinor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Dolby Vision profile.
+ /// </summary>
+ /// <value>The Dolby Vision profile.</value>
+ public int? DvProfile { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Dolby Vision level.
+ /// </summary>
+ /// <value>The Dolby Vision level.</value>
+ public int? DvLevel { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Dolby Vision rpu present flag.
+ /// </summary>
+ /// <value>The Dolby Vision rpu present flag.</value>
+ public int? RpuPresentFlag { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Dolby Vision el present flag.
+ /// </summary>
+ /// <value>The Dolby Vision el present flag.</value>
+ public int? ElPresentFlag { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Dolby Vision bl present flag.
+ /// </summary>
+ /// <value>The Dolby Vision bl present flag.</value>
+ public int? BlPresentFlag { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Dolby Vision bl signal compatibility id.
+ /// </summary>
+ /// <value>The Dolby Vision bl signal compatibility id.</value>
+ public int? DvBlSignalCompatibilityId { get; set; }
+
+ /// <summary>
/// Gets or sets the comment.
/// </summary>
/// <value>The comment.</value>
@@ -104,33 +152,64 @@ namespace MediaBrowser.Model.Entities
{
get
{
- if (Type != MediaStreamType.Video)
- {
- return null;
- }
+ var (videoRange, _) = GetVideoColorRange();
- var colorTransfer = ColorTransfer;
-
- if (string.Equals(colorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)
- || string.Equals(colorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
- {
- return "HDR";
- }
+ return videoRange;
+ }
+ }
- // For some Dolby Vision files, no color transfer is provided, so check the codec
+ /// <summary>
+ /// Gets the video range type.
+ /// </summary>
+ /// <value>The video range type.</value>
+ public string VideoRangeType
+ {
+ get
+ {
+ var (_, videoRangeType) = GetVideoColorRange();
- var codecTag = CodecTag;
+ return videoRangeType;
+ }
+ }
- if (string.Equals(codecTag, "dva1", StringComparison.OrdinalIgnoreCase)
- || string.Equals(codecTag, "dvav", StringComparison.OrdinalIgnoreCase)
- || string.Equals(codecTag, "dvh1", StringComparison.OrdinalIgnoreCase)
- || string.Equals(codecTag, "dvhe", StringComparison.OrdinalIgnoreCase)
- || string.Equals(codecTag, "dav1", StringComparison.OrdinalIgnoreCase))
+ /// <summary>
+ /// Gets the video dovi title.
+ /// </summary>
+ /// <value>The video dovi title.</value>
+ public string VideoDoViTitle
+ {
+ get
+ {
+ var dvProfile = DvProfile;
+ var rpuPresentFlag = RpuPresentFlag == 1;
+ var blPresentFlag = BlPresentFlag == 1;
+ var dvBlCompatId = DvBlSignalCompatibilityId;
+
+ if (rpuPresentFlag
+ && blPresentFlag
+ && (dvProfile == 4
+ || dvProfile == 5
+ || dvProfile == 7
+ || dvProfile == 8
+ || dvProfile == 9))
{
- return "HDR";
+ var title = "DV Profile " + dvProfile;
+
+ if (dvBlCompatId > 0)
+ {
+ title += "." + dvBlCompatId;
+ }
+
+ return dvBlCompatId switch
+ {
+ 1 => title + " (HDR10)",
+ 2 => title + " (SDR)",
+ 4 => title + " (HLG)",
+ _ => title
+ };
}
- return "SDR";
+ return null;
}
}
@@ -509,15 +588,26 @@ namespace MediaBrowser.Model.Entities
return Width switch
{
- <= 720 when Height <= 480 => IsInterlaced ? "480i" : "480p",
- // 720x576 (PAL) (768 when rescaled for square pixels)
- <= 768 when Height <= 576 => IsInterlaced ? "576i" : "576p",
- // 960x540 (sometimes 544 which is multiple of 16)
+ // 256x144 (16:9 square pixel format)
+ <= 256 when Height <= 144 => IsInterlaced ? "144i" : "144p",
+ // 426x240 (16:9 square pixel format)
+ <= 426 when Height <= 240 => IsInterlaced ? "240i" : "240p",
+ // 640x360 (16:9 square pixel format)
+ <= 640 when Height <= 360 => IsInterlaced ? "360i" : "360p",
+ // 682x384 (16:9 square pixel format)
+ <= 682 when Height <= 384 => IsInterlaced ? "384i" : "384p",
+ // 720x404 (16:9 square pixel format)
+ <= 720 when Height <= 404 => IsInterlaced ? "404i" : "404p",
+ // 854x480 (16:9 square pixel format)
+ <= 854 when Height <= 480 => IsInterlaced ? "480i" : "480p",
+ // 960x544 (16:9 square pixel format)
<= 960 when Height <= 544 => IsInterlaced ? "540i" : "540p",
+ // 1024x576 (16:9 square pixel format)
+ <= 1024 when Height <= 576 => IsInterlaced ? "576i" : "576p",
// 1280x720
<= 1280 when Height <= 962 => IsInterlaced ? "720i" : "720p",
- // 1920x1080
- <= 1920 when Height <= 1440 => IsInterlaced ? "1080i" : "1080p",
+ // 2560x1080 (FHD ultra wide 21:9) using 1440px width to accommodate WQHD
+ <= 2560 when Height <= 1440 => IsInterlaced ? "1080i" : "1080p",
// 4K
<= 4096 when Height <= 3072 => "4K",
// 8K
@@ -572,5 +662,45 @@ namespace MediaBrowser.Model.Entities
return true;
}
+
+ public (string VideoRange, string VideoRangeType) GetVideoColorRange()
+ {
+ if (Type != MediaStreamType.Video)
+ {
+ return (null, null);
+ }
+
+ var colorTransfer = ColorTransfer;
+
+ if (string.Equals(colorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase))
+ {
+ return ("HDR", "HDR10");
+ }
+
+ if (string.Equals(colorTransfer, "arib-std-b67", StringComparison.OrdinalIgnoreCase))
+ {
+ return ("HDR", "HLG");
+ }
+
+ var codecTag = CodecTag;
+ var dvProfile = DvProfile;
+ var rpuPresentFlag = RpuPresentFlag == 1;
+ var blPresentFlag = BlPresentFlag == 1;
+ var dvBlCompatId = DvBlSignalCompatibilityId;
+
+ var isDoViHDRProfile = dvProfile == 5 || dvProfile == 7 || dvProfile == 8;
+ var isDoViHDRFlag = rpuPresentFlag && blPresentFlag && (dvBlCompatId == 0 || dvBlCompatId == 1 || dvBlCompatId == 4);
+
+ if ((isDoViHDRProfile && isDoViHDRFlag)
+ || string.Equals(codecTag, "dovi", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(codecTag, "dvh1", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(codecTag, "dvhe", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(codecTag, "dav1", StringComparison.OrdinalIgnoreCase))
+ {
+ return ("HDR", "DOVI");
+ }
+
+ return ("SDR", "SDR");
+ }
}
}
diff --git a/MediaBrowser.Model/Entities/MediaStreamType.cs b/MediaBrowser.Model/Entities/MediaStreamType.cs
index e09aaf6d05..83751a6a7d 100644
--- a/MediaBrowser.Model/Entities/MediaStreamType.cs
+++ b/MediaBrowser.Model/Entities/MediaStreamType.cs
@@ -23,6 +23,11 @@ namespace MediaBrowser.Model.Entities
/// <summary>
/// The embedded image.
/// </summary>
- EmbeddedImage
+ EmbeddedImage,
+
+ /// <summary>
+ /// The data.
+ /// </summary>
+ Data
}
}
diff --git a/MediaBrowser.Model/Entities/MetadataProvider.cs b/MediaBrowser.Model/Entities/MetadataProvider.cs
index e9c098021d..37e3d88645 100644
--- a/MediaBrowser.Model/Entities/MetadataProvider.cs
+++ b/MediaBrowser.Model/Entities/MetadataProvider.cs
@@ -8,6 +8,12 @@ namespace MediaBrowser.Model.Entities
public enum MetadataProvider
{
/// <summary>
+ /// This metadata provider is for users and/or plugins to override the
+ /// default merging behaviour.
+ /// </summary>
+ Custom = 0,
+
+ /// <summary>
/// The imdb.
/// </summary>
Imdb = 2,
diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
index ce4b0ec92e..d3b8400f34 100644
--- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
+++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Linq;
namespace MediaBrowser.Model.Entities
{
@@ -10,6 +11,16 @@ namespace MediaBrowser.Model.Entities
public static class ProviderIdsExtensions
{
/// <summary>
+ /// Case insensitive dictionary of <see cref="MetadataProvider"/> string representation.
+ /// </summary>
+ private static readonly Dictionary<string, string> _metadataProviderEnumDictionary =
+ Enum.GetValues<MetadataProvider>()
+ .ToDictionary(
+ enumValue => enumValue.ToString(),
+ enumValue => enumValue.ToString(),
+ StringComparer.OrdinalIgnoreCase);
+
+ /// <summary>
/// Checks if this instance has an id for the given provider.
/// </summary>
/// <param name="instance">The instance.</param>
@@ -17,10 +28,7 @@ namespace MediaBrowser.Model.Entities
/// <returns><c>true</c> if a provider id with the given name was found; otherwise <c>false</c>.</returns>
public static bool HasProviderId(this IHasProviderIds instance, string name)
{
- if (instance == null)
- {
- throw new ArgumentNullException(nameof(instance));
- }
+ ArgumentNullException.ThrowIfNull(instance);
return instance.TryGetProviderId(name, out _);
}
@@ -45,10 +53,7 @@ namespace MediaBrowser.Model.Entities
/// <returns><c>true</c> if a provider id with the given name was found; otherwise <c>false</c>.</returns>
public static bool TryGetProviderId(this IHasProviderIds instance, string name, [NotNullWhen(true)] out string? id)
{
- if (instance == null)
- {
- throw new ArgumentNullException(nameof(instance));
- }
+ ArgumentNullException.ThrowIfNull(instance);
if (instance.ProviderIds == null)
{
@@ -108,12 +113,9 @@ namespace MediaBrowser.Model.Entities
/// <param name="instance">The instance.</param>
/// <param name="name">The name.</param>
/// <param name="value">The value.</param>
- public static void SetProviderId(this IHasProviderIds instance, string name, string value)
+ public static void SetProviderId(this IHasProviderIds instance, string name, string? value)
{
- if (instance == null)
- {
- throw new ArgumentNullException(nameof(instance));
- }
+ ArgumentNullException.ThrowIfNull(instance);
// If it's null remove the key from the dictionary
if (string.IsNullOrEmpty(value))
@@ -125,7 +127,15 @@ namespace MediaBrowser.Model.Entities
// Ensure it exists
instance.ProviderIds ??= new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- instance.ProviderIds[name] = value;
+ // Match on internal MetadataProvider enum string values before adding arbitrary providers
+ if (_metadataProviderEnumDictionary.TryGetValue(name, out var enumValue))
+ {
+ instance.ProviderIds[enumValue] = value;
+ }
+ else
+ {
+ instance.ProviderIds[name] = value;
+ }
}
}
diff --git a/MediaBrowser.Model/LiveTv/TunerHostInfo.cs b/MediaBrowser.Model/LiveTv/TunerHostInfo.cs
index 05576a0f8d..a832169c2a 100644
--- a/MediaBrowser.Model/LiveTv/TunerHostInfo.cs
+++ b/MediaBrowser.Model/LiveTv/TunerHostInfo.cs
@@ -8,6 +8,7 @@ namespace MediaBrowser.Model.LiveTv
public TunerHostInfo()
{
AllowHWTranscoding = true;
+ IgnoreDts = true;
}
public string Id { get; set; }
@@ -31,5 +32,7 @@ namespace MediaBrowser.Model.LiveTv
public int TunerCount { get; set; }
public string UserAgent { get; set; }
+
+ public bool IgnoreDts { get; set; }
}
}
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 43b465b29a..ad2ff1ba29 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -8,7 +8,7 @@
<PropertyGroup>
<Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Model</PackageId>
- <VersionPrefix>10.8.0</VersionPrefix>
+ <VersionPrefix>10.9.0</VersionPrefix>
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
</PropertyGroup>
@@ -34,13 +34,13 @@
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
- <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
- <PackageReference Include="MimeTypes" Version="2.3.0">
+ <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.2" />
+ <PackageReference Include="MimeTypes" Version="2.4.0">
<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.2" />
+ <PackageReference Include="System.Text.Json" Version="6.0.6" />
</ItemGroup>
<ItemGroup>
@@ -54,7 +54,7 @@
<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.406" PrivateAssets="All" />
+ <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
index 9bc5c31f62..85de916940 100644
--- a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
+++ b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
@@ -5,6 +5,7 @@ namespace MediaBrowser.Model.MediaInfo
public static class SubtitleFormat
{
public const string SRT = "srt";
+ public const string SUBRIP = "subrip";
public const string SSA = "ssa";
public const string ASS = "ass";
public const string VTT = "vtt";
diff --git a/MediaBrowser.Model/Querying/ItemSortBy.cs b/MediaBrowser.Model/Querying/ItemSortBy.cs
index 0a28acf37b..470507c530 100644
--- a/MediaBrowser.Model/Querying/ItemSortBy.cs
+++ b/MediaBrowser.Model/Querying/ItemSortBy.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Querying
{
/// <summary>
@@ -7,6 +5,9 @@ namespace MediaBrowser.Model.Querying
/// </summary>
public static class ItemSortBy
{
+ /// <summary>
+ /// The aired episode order.
+ /// </summary>
public const string AiredEpisodeOrder = "AiredEpisodeOrder";
/// <summary>
@@ -44,6 +45,9 @@ namespace MediaBrowser.Model.Querying
/// </summary>
public const string PremiereDate = "PremiereDate";
+ /// <summary>
+ /// The start date.
+ /// </summary>
public const string StartDate = "StartDate";
/// <summary>
@@ -51,6 +55,9 @@ namespace MediaBrowser.Model.Querying
/// </summary>
public const string SortName = "SortName";
+ /// <summary>
+ /// The name.
+ /// </summary>
public const string Name = "Name";
/// <summary>
@@ -83,28 +90,69 @@ namespace MediaBrowser.Model.Querying
/// </summary>
public const string CriticRating = "CriticRating";
+ /// <summary>
+ /// The IsFolder boolean.
+ /// </summary>
public const string IsFolder = "IsFolder";
+ /// <summary>
+ /// The IsUnplayed boolean.
+ /// </summary>
public const string IsUnplayed = "IsUnplayed";
+ /// <summary>
+ /// The IsPlayed boolean.
+ /// </summary>
public const string IsPlayed = "IsPlayed";
+ /// <summary>
+ /// The series sort.
+ /// </summary>
public const string SeriesSortName = "SeriesSortName";
+ /// <summary>
+ /// The video bitrate.
+ /// </summary>
public const string VideoBitRate = "VideoBitRate";
+ /// <summary>
+ /// The air time.
+ /// </summary>
public const string AirTime = "AirTime";
+ /// <summary>
+ /// The studio.
+ /// </summary>
public const string Studio = "Studio";
+ /// <summary>
+ /// The IsFavouriteOrLiked boolean.
+ /// </summary>
public const string IsFavoriteOrLiked = "IsFavoriteOrLiked";
+ /// <summary>
+ /// The last content added date.
+ /// </summary>
public const string DateLastContentAdded = "DateLastContentAdded";
+ /// <summary>
+ /// The series last played date.
+ /// </summary>
public const string SeriesDatePlayed = "SeriesDatePlayed";
+ /// <summary>
+ /// The parent index number.
+ /// </summary>
public const string ParentIndexNumber = "ParentIndexNumber";
+ /// <summary>
+ /// The index number.
+ /// </summary>
public const string IndexNumber = "IndexNumber";
+
+ /// <summary>
+ /// The similarity score.
+ /// </summary>
+ public const string SimilarityScore = "SimilarityScore";
}
}
diff --git a/MediaBrowser.Model/Querying/NextUpQuery.cs b/MediaBrowser.Model/Querying/NextUpQuery.cs
index 133d6a9162..0fb996df97 100644
--- a/MediaBrowser.Model/Querying/NextUpQuery.cs
+++ b/MediaBrowser.Model/Querying/NextUpQuery.cs
@@ -33,7 +33,7 @@ namespace MediaBrowser.Model.Querying
/// Gets or sets the series id.
/// </summary>
/// <value>The series id.</value>
- public string SeriesId { get; set; }
+ public Guid? SeriesId { get; set; }
/// <summary>
/// Gets or sets the start index. Use for paging.
diff --git a/MediaBrowser.Model/Search/SearchHint.cs b/MediaBrowser.Model/Search/SearchHint.cs
index 983dbd2bc0..3fa7f3d565 100644
--- a/MediaBrowser.Model/Search/SearchHint.cs
+++ b/MediaBrowser.Model/Search/SearchHint.cs
@@ -1,8 +1,6 @@
-#nullable disable
-#pragma warning disable CS1591
-
using System;
using System.Collections.Generic;
+using Jellyfin.Data.Enums;
namespace MediaBrowser.Model.Search
{
@@ -12,11 +10,27 @@ namespace MediaBrowser.Model.Search
public class SearchHint
{
/// <summary>
+ /// Initializes a new instance of the <see cref="SearchHint" /> class.
+ /// </summary>
+ public SearchHint()
+ {
+ Name = string.Empty;
+ MatchedTerm = string.Empty;
+ MediaType = string.Empty;
+ Artists = Array.Empty<string>();
+ }
+
+ /// <summary>
/// Gets or sets the item id.
/// </summary>
/// <value>The item id.</value>
+ [Obsolete("Use Id instead")]
public Guid ItemId { get; set; }
+ /// <summary>
+ /// Gets or sets the item id.
+ /// </summary>
+ /// <value>The item id.</value>
public Guid Id { get; set; }
/// <summary>
@@ -53,38 +67,42 @@ namespace MediaBrowser.Model.Search
/// Gets or sets the image tag.
/// </summary>
/// <value>The image tag.</value>
- public string PrimaryImageTag { get; set; }
+ public string? PrimaryImageTag { get; set; }
/// <summary>
/// Gets or sets the thumb image tag.
/// </summary>
/// <value>The thumb image tag.</value>
- public string ThumbImageTag { get; set; }
+ public string? ThumbImageTag { get; set; }
/// <summary>
/// Gets or sets the thumb image item identifier.
/// </summary>
/// <value>The thumb image item identifier.</value>
- public string ThumbImageItemId { get; set; }
+ public string? ThumbImageItemId { get; set; }
/// <summary>
/// Gets or sets the backdrop image tag.
/// </summary>
/// <value>The backdrop image tag.</value>
- public string BackdropImageTag { get; set; }
+ public string? BackdropImageTag { get; set; }
/// <summary>
/// Gets or sets the backdrop image item identifier.
/// </summary>
/// <value>The backdrop image item identifier.</value>
- public string BackdropImageItemId { get; set; }
+ public string? BackdropImageItemId { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
- public string Type { get; set; }
+ public BaseItemKind Type { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether this instance is folder.
+ /// </summary>
+ /// <value><c>true</c> if this instance is folder; otherwise, <c>false</c>.</value>
public bool? IsFolder { get; set; }
/// <summary>
@@ -99,31 +117,47 @@ namespace MediaBrowser.Model.Search
/// <value>The type of the media.</value>
public string MediaType { get; set; }
+ /// <summary>
+ /// Gets or sets the start date.
+ /// </summary>
+ /// <value>The start date.</value>
public DateTime? StartDate { get; set; }
+ /// <summary>
+ /// Gets or sets the end date.
+ /// </summary>
+ /// <value>The end date.</value>
public DateTime? EndDate { get; set; }
/// <summary>
/// Gets or sets the series.
/// </summary>
/// <value>The series.</value>
- public string Series { get; set; }
+ public string? Series { get; set; }
- public string Status { get; set; }
+ /// <summary>
+ /// Gets or sets the status.
+ /// </summary>
+ /// <value>The status.</value>
+ public string? Status { get; set; }
/// <summary>
/// Gets or sets the album.
/// </summary>
/// <value>The album.</value>
- public string Album { get; set; }
+ public string? Album { get; set; }
- public Guid AlbumId { get; set; }
+ /// <summary>
+ /// Gets or sets the album id.
+ /// </summary>
+ /// <value>The album id.</value>
+ public Guid? AlbumId { get; set; }
/// <summary>
/// Gets or sets the album artist.
/// </summary>
/// <value>The album artist.</value>
- public string AlbumArtist { get; set; }
+ public string? AlbumArtist { get; set; }
/// <summary>
/// Gets or sets the artists.
@@ -147,13 +181,13 @@ namespace MediaBrowser.Model.Search
/// Gets or sets the channel identifier.
/// </summary>
/// <value>The channel identifier.</value>
- public Guid ChannelId { get; set; }
+ public Guid? ChannelId { get; set; }
/// <summary>
/// Gets or sets the name of the channel.
/// </summary>
/// <value>The name of the channel.</value>
- public string ChannelName { get; set; }
+ public string? ChannelName { get; set; }
/// <summary>
/// Gets or sets the primary image aspect ratio.
diff --git a/MediaBrowser.Model/Session/GeneralCommand.cs b/MediaBrowser.Model/Session/GeneralCommand.cs
index 757b19b317..dfbb616aa8 100644
--- a/MediaBrowser.Model/Session/GeneralCommand.cs
+++ b/MediaBrowser.Model/Session/GeneralCommand.cs
@@ -14,9 +14,9 @@ public class GeneralCommand
}
[JsonConstructor]
- public GeneralCommand(Dictionary<string, string> arguments)
+ public GeneralCommand(Dictionary<string, string>? arguments)
{
- Arguments = arguments;
+ Arguments = arguments ?? new Dictionary<string, string>();
}
public GeneralCommandType Name { get; set; }
diff --git a/MediaBrowser.Model/Session/PlayerStateInfo.cs b/MediaBrowser.Model/Session/PlayerStateInfo.cs
index 0f10605ea1..80e6d4e0b0 100644
--- a/MediaBrowser.Model/Session/PlayerStateInfo.cs
+++ b/MediaBrowser.Model/Session/PlayerStateInfo.cs
@@ -64,5 +64,11 @@ namespace MediaBrowser.Model.Session
/// </summary>
/// <value>The repeat mode.</value>
public RepeatMode RepeatMode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the now playing live stream identifier.
+ /// </summary>
+ /// <value>The live stream identifier.</value>
+ public string LiveStreamId { get; set; }
}
}
diff --git a/MediaBrowser.Model/Session/TranscodeReason.cs b/MediaBrowser.Model/Session/TranscodeReason.cs
index 9da9f3323b..bbdf4536b7 100644
--- a/MediaBrowser.Model/Session/TranscodeReason.cs
+++ b/MediaBrowser.Model/Session/TranscodeReason.cs
@@ -17,6 +17,7 @@ namespace MediaBrowser.Model.Session
// Video Constraints
VideoProfileNotSupported = 1 << 6,
+ VideoRangeTypeNotSupported = 1 << 24,
VideoLevelNotSupported = 1 << 7,
VideoResolutionNotSupported = 1 << 8,
VideoBitDepthNotSupported = 1 << 9,
diff --git a/MediaBrowser.Model/SyncPlay/GroupStateType.cs b/MediaBrowser.Model/SyncPlay/GroupStateType.cs
index 7aa454f928..96364cacc7 100644
--- a/MediaBrowser.Model/SyncPlay/GroupStateType.cs
+++ b/MediaBrowser.Model/SyncPlay/GroupStateType.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Model.SyncPlay
Idle = 0,
/// <summary>
- /// The group is in wating state. Playback is paused. Will start playing when users are ready.
+ /// The group is in waiting state. Playback is paused. Will start playing when users are ready.
/// </summary>
Waiting = 1,
diff --git a/MediaBrowser.Model/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs
index a86bf2a1c8..13bebc479e 100644
--- a/MediaBrowser.Model/Tasks/ITaskManager.cs
+++ b/MediaBrowser.Model/Tasks/ITaskManager.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Model.Tasks
/// <summary>
/// Cancels if running and queue.
/// </summary>
- /// <typeparam name="T">An implementatin of <see cref="IScheduledTask" />.</typeparam>
+ /// <typeparam name="T">An implementation of <see cref="IScheduledTask" />.</typeparam>
/// <param name="options">Task options.</param>
void CancelIfRunningAndQueue<T>(TaskOptions options)
where T : IScheduledTask;
@@ -30,21 +30,21 @@ namespace MediaBrowser.Model.Tasks
/// <summary>
/// Cancels if running and queue.
/// </summary>
- /// <typeparam name="T">An implementatin of <see cref="IScheduledTask" />.</typeparam>
+ /// <typeparam name="T">An implementation of <see cref="IScheduledTask" />.</typeparam>
void CancelIfRunningAndQueue<T>()
where T : IScheduledTask;
/// <summary>
/// Cancels if running.
/// </summary>
- /// <typeparam name="T">An implementatin of <see cref="IScheduledTask" />.</typeparam>
+ /// <typeparam name="T">An implementation of <see cref="IScheduledTask" />.</typeparam>
void CancelIfRunning<T>()
where T : IScheduledTask;
/// <summary>
/// Queues the scheduled task.
/// </summary>
- /// <typeparam name="T">An implementatin of <see cref="IScheduledTask" />.</typeparam>
+ /// <typeparam name="T">An implementation of <see cref="IScheduledTask" />.</typeparam>
/// <param name="options">Task options.</param>
void QueueScheduledTask<T>(TaskOptions options)
where T : IScheduledTask;
@@ -52,7 +52,7 @@ namespace MediaBrowser.Model.Tasks
/// <summary>
/// Queues the scheduled task.
/// </summary>
- /// <typeparam name="T">An implementatin of <see cref="IScheduledTask" />.</typeparam>
+ /// <typeparam name="T">An implementation of <see cref="IScheduledTask" />.</typeparam>
void QueueScheduledTask<T>()
where T : IScheduledTask;
diff --git a/MediaBrowser.Model/Tasks/ITaskTrigger.cs b/MediaBrowser.Model/Tasks/ITaskTrigger.cs
index 8c3ec6626c..0536f4ef77 100644
--- a/MediaBrowser.Model/Tasks/ITaskTrigger.cs
+++ b/MediaBrowser.Model/Tasks/ITaskTrigger.cs
@@ -21,10 +21,10 @@ namespace MediaBrowser.Model.Tasks
/// <summary>
/// Stars waiting for the trigger action.
/// </summary>
- /// <param name="lastResult">Result of the last run triggerd task.</param>
+ /// <param name="lastResult">Result of the last run triggered task.</param>
/// <param name="logger">The <see cref="ILogger"/>.</param>
/// <param name="taskName">The name of the task.</param>
- /// <param name="isApplicationStartup">Wheter or not this is is fired during startup.</param>
+ /// <param name="isApplicationStartup">Whether or not this is is fired during startup.</param>
void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup);
/// <summary>