aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Model
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Model')
-rw-r--r--MediaBrowser.Model/Channels/ChannelFeatures.cs13
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs7
-rw-r--r--MediaBrowser.Model/Configuration/HlsAudioSeekStrategy.cs9
-rw-r--r--MediaBrowser.Model/Configuration/ImageSavingConvention.cs12
-rw-r--r--MediaBrowser.Model/Configuration/MetadataPluginType.cs5
-rw-r--r--MediaBrowser.Model/Configuration/TypeOptions.cs16
-rw-r--r--MediaBrowser.Model/Dlna/CodecType.cs16
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs8
-rw-r--r--MediaBrowser.Model/Dlna/EncodingContext.cs12
-rw-r--r--MediaBrowser.Model/Dlna/PlaybackErrorCode.cs16
-rw-r--r--MediaBrowser.Model/Dlna/ProfileConditionValue.cs3
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionOptions.cs11
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs88
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs12
-rw-r--r--MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs12
-rw-r--r--MediaBrowser.Model/Drawing/ImageDimensions.cs1
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs8
-rw-r--r--MediaBrowser.Model/Dto/IHasServerId.cs7
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceType.cs16
-rw-r--r--MediaBrowser.Model/Dto/RatingType.cs12
-rw-r--r--MediaBrowser.Model/Dto/SessionInfoDto.cs8
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs275
-rw-r--r--MediaBrowser.Model/Extensions/EnumerableExtensions.cs10
-rw-r--r--MediaBrowser.Model/Globalization/ILocalizationManager.cs9
-rw-r--r--MediaBrowser.Model/Library/PlayAccess.cs12
-rw-r--r--MediaBrowser.Model/LiveTv/DayPattern.cs16
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs12
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj2
-rw-r--r--MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs16
-rw-r--r--MediaBrowser.Model/MediaSegments/MediaSegmentGenerationRequest.cs2
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs1
-rw-r--r--MediaBrowser.Model/Providers/SubtitleOptions.cs36
-rw-r--r--MediaBrowser.Model/Querying/QueryFilters.cs11
-rw-r--r--MediaBrowser.Model/Session/MessageCommand.cs13
-rw-r--r--MediaBrowser.Model/Session/PlayMethod.cs16
-rw-r--r--MediaBrowser.Model/Session/PlaystateRequest.cs11
-rw-r--r--MediaBrowser.Model/Session/QueueItem.cs10
-rw-r--r--MediaBrowser.Model/Session/RepeatMode.cs16
-rw-r--r--MediaBrowser.Model/Session/TranscodeReason.cs1
-rw-r--r--MediaBrowser.Model/SyncPlay/PlaybackRequestType.cs2
-rw-r--r--MediaBrowser.Model/SyncPlay/SyncPlayGroupDoesNotExistUpdate.cs2
-rw-r--r--MediaBrowser.Model/SyncPlay/SyncPlayGroupJoinedUpdate.cs2
-rw-r--r--MediaBrowser.Model/SyncPlay/SyncPlayGroupLeftUpdate.cs2
-rw-r--r--MediaBrowser.Model/SyncPlay/SyncPlayLibraryAccessDeniedUpdate.cs2
-rw-r--r--MediaBrowser.Model/SyncPlay/SyncPlayNotInGroupUpdate.cs2
-rw-r--r--MediaBrowser.Model/SyncPlay/SyncPlayPlayQueueUpdate.cs2
-rw-r--r--MediaBrowser.Model/SyncPlay/SyncPlayStateUpdate.cs2
-rw-r--r--MediaBrowser.Model/SyncPlay/SyncPlayUserJoinedUpdate.cs2
-rw-r--r--MediaBrowser.Model/SyncPlay/SyncPlayUserLeftUpdate.cs2
-rw-r--r--MediaBrowser.Model/System/FolderStorageInfo.cs11
-rw-r--r--MediaBrowser.Model/Users/UserPolicy.cs2
51 files changed, 543 insertions, 251 deletions
diff --git a/MediaBrowser.Model/Channels/ChannelFeatures.cs b/MediaBrowser.Model/Channels/ChannelFeatures.cs
index 1ca8e80a6f..57803c9765 100644
--- a/MediaBrowser.Model/Channels/ChannelFeatures.cs
+++ b/MediaBrowser.Model/Channels/ChannelFeatures.cs
@@ -1,6 +1,7 @@
#pragma warning disable CS1591
using System;
+using System.Collections.Generic;
namespace MediaBrowser.Model.Channels
{
@@ -8,9 +9,9 @@ namespace MediaBrowser.Model.Channels
{
public ChannelFeatures(string name, Guid id)
{
- MediaTypes = Array.Empty<ChannelMediaType>();
- ContentTypes = Array.Empty<ChannelMediaContentType>();
- DefaultSortFields = Array.Empty<ChannelItemSortField>();
+ MediaTypes = [];
+ ContentTypes = [];
+ DefaultSortFields = [];
Name = name;
Id = id;
@@ -38,13 +39,13 @@ namespace MediaBrowser.Model.Channels
/// Gets or sets the media types.
/// </summary>
/// <value>The media types.</value>
- public ChannelMediaType[] MediaTypes { get; set; }
+ public IReadOnlyList<ChannelMediaType> MediaTypes { get; set; }
/// <summary>
/// Gets or sets the content types.
/// </summary>
/// <value>The content types.</value>
- public ChannelMediaContentType[] ContentTypes { get; set; }
+ public IReadOnlyList<ChannelMediaContentType> ContentTypes { get; set; }
/// <summary>
/// Gets or sets the maximum number of records the channel allows retrieving at a time.
@@ -61,7 +62,7 @@ namespace MediaBrowser.Model.Channels
/// Gets or sets the default sort orders.
/// </summary>
/// <value>The default sort orders.</value>
- public ChannelItemSortField[] DefaultSortFields { get; set; }
+ public IReadOnlyList<ChannelItemSortField> DefaultSortFields { get; set; }
/// <summary>
/// Gets or sets a value indicating whether a sort ascending/descending toggle is supported.
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs
index 98fc2e632f..4d052d8012 100644
--- a/MediaBrowser.Model/Configuration/EncodingOptions.cs
+++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs
@@ -43,6 +43,7 @@ public class EncodingOptions
VppTonemappingContrast = 1;
H264Crf = 23;
H265Crf = 28;
+ EncoderPreset = EncoderPreset.auto;
DeinterlaceDoubleRate = false;
DeinterlaceMethod = DeinterlaceMethod.yadif;
EnableDecodingColorDepth10Hevc = true;
@@ -61,7 +62,7 @@ public class EncodingOptions
SubtitleExtractionTimeoutMinutes = 30;
AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = ["mkv"];
HardwareDecodingCodecs = ["h264", "vc1"];
- HlsAudioSeekStrategy = HlsAudioSeekStrategy.DisableAccurateSeek;
+ HlsAudioSeekStrategy = HlsAudioSeekStrategy.TrimCopiedAudio;
}
/// <summary>
@@ -217,7 +218,7 @@ public class EncodingOptions
/// <summary>
/// Gets or sets the encoder preset.
/// </summary>
- public EncoderPreset? EncoderPreset { get; set; }
+ public EncoderPreset EncoderPreset { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the framerate is doubled when deinterlacing.
@@ -307,6 +308,6 @@ public class EncodingOptions
/// <summary>
/// Gets or sets the method used for audio seeking in HLS.
/// </summary>
- [DefaultValue(HlsAudioSeekStrategy.DisableAccurateSeek)]
+ [DefaultValue(HlsAudioSeekStrategy.TrimCopiedAudio)]
public HlsAudioSeekStrategy HlsAudioSeekStrategy { get; set; }
}
diff --git a/MediaBrowser.Model/Configuration/HlsAudioSeekStrategy.cs b/MediaBrowser.Model/Configuration/HlsAudioSeekStrategy.cs
index 49feeb435f..c9155faeb1 100644
--- a/MediaBrowser.Model/Configuration/HlsAudioSeekStrategy.cs
+++ b/MediaBrowser.Model/Configuration/HlsAudioSeekStrategy.cs
@@ -7,11 +7,12 @@ namespace MediaBrowser.Model.Configuration
public enum HlsAudioSeekStrategy
{
/// <summary>
- /// If the video stream is transcoded and the audio stream is copied,
- /// seek the video stream to the same keyframe as the audio stream. The
- /// resulting timestamps in the output streams may be inaccurate.
+ /// When video is transcoded and audio is copied, use a bitstream filter
+ /// to drop copied audio packets before the seek point, aligning them
+ /// with the accurately-seeked video. Timestamps are accurate and audio
+ /// remains stream-copied (no re-encoding overhead).
/// </summary>
- DisableAccurateSeek = 0,
+ TrimCopiedAudio = 0,
/// <summary>
/// Prevent audio streams from being copied if the video stream is transcoded.
diff --git a/MediaBrowser.Model/Configuration/ImageSavingConvention.cs b/MediaBrowser.Model/Configuration/ImageSavingConvention.cs
index 485a4d2f80..c67f379fde 100644
--- a/MediaBrowser.Model/Configuration/ImageSavingConvention.cs
+++ b/MediaBrowser.Model/Configuration/ImageSavingConvention.cs
@@ -1,10 +1,18 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Configuration
{
+ /// <summary>
+ /// The convention used for naming saved images.
+ /// </summary>
public enum ImageSavingConvention
{
+ /// <summary>
+ /// The legacy naming convention.
+ /// </summary>
Legacy,
+
+ /// <summary>
+ /// The naming convention compatible with other media servers and metadata managers.
+ /// </summary>
Compatible
}
}
diff --git a/MediaBrowser.Model/Configuration/MetadataPluginType.cs b/MediaBrowser.Model/Configuration/MetadataPluginType.cs
index 670d6e3837..dd9a599a29 100644
--- a/MediaBrowser.Model/Configuration/MetadataPluginType.cs
+++ b/MediaBrowser.Model/Configuration/MetadataPluginType.cs
@@ -15,6 +15,9 @@ namespace MediaBrowser.Model.Configuration
MetadataSaver,
SubtitleFetcher,
LyricFetcher,
- MediaSegmentProvider
+ MediaSegmentProvider,
+ LocalSimilarityProvider,
+ SimilarityProvider,
+ SearchProvider
}
}
diff --git a/MediaBrowser.Model/Configuration/TypeOptions.cs b/MediaBrowser.Model/Configuration/TypeOptions.cs
index d0179e5aab..3aa85034e5 100644
--- a/MediaBrowser.Model/Configuration/TypeOptions.cs
+++ b/MediaBrowser.Model/Configuration/TypeOptions.cs
@@ -304,11 +304,13 @@ namespace MediaBrowser.Model.Configuration
public TypeOptions()
{
- MetadataFetchers = Array.Empty<string>();
- MetadataFetcherOrder = Array.Empty<string>();
- ImageFetchers = Array.Empty<string>();
- ImageFetcherOrder = Array.Empty<string>();
- ImageOptions = Array.Empty<ImageOption>();
+ MetadataFetchers = [];
+ MetadataFetcherOrder = [];
+ ImageFetchers = [];
+ ImageFetcherOrder = [];
+ ImageOptions = [];
+ SimilarItemProviders = [];
+ SimilarItemProviderOrder = [];
}
public string Type { get; set; }
@@ -323,6 +325,10 @@ namespace MediaBrowser.Model.Configuration
public ImageOption[] ImageOptions { get; set; }
+ public string[] SimilarItemProviders { get; set; }
+
+ public string[] SimilarItemProviderOrder { get; set; }
+
public ImageOption GetImageOptions(ImageType type)
{
foreach (var i in ImageOptions)
diff --git a/MediaBrowser.Model/Dlna/CodecType.cs b/MediaBrowser.Model/Dlna/CodecType.cs
index c9f090e4cc..12730a76fa 100644
--- a/MediaBrowser.Model/Dlna/CodecType.cs
+++ b/MediaBrowser.Model/Dlna/CodecType.cs
@@ -1,11 +1,23 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Dlna
{
+ /// <summary>
+ /// The codec type of a codec profile.
+ /// </summary>
public enum CodecType
{
+ /// <summary>
+ /// The profile applies to a video codec.
+ /// </summary>
Video = 0,
+
+ /// <summary>
+ /// The profile applies to the audio codec of a video stream.
+ /// </summary>
VideoAudio = 1,
+
+ /// <summary>
+ /// The profile applies to an audio codec.
+ /// </summary>
Audio = 2
}
}
diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
index 1b61bfe155..a6018f369d 100644
--- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs
+++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs
@@ -33,6 +33,7 @@ namespace MediaBrowser.Model.Dlna
/// <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>
+ /// <param name="videoRotation">The video rotation angle, usually 0 or +-90/180.</param>
/// <returns><b>True</b> if the condition is satisfied.</returns>
public static bool IsVideoConditionSatisfied(
ProfileCondition condition,
@@ -53,7 +54,8 @@ namespace MediaBrowser.Model.Dlna
int? numVideoStreams,
int? numAudioStreams,
string? videoCodecTag,
- bool? isAvc)
+ bool? isAvc,
+ int? videoRotation)
{
switch (condition.Property)
{
@@ -93,6 +95,8 @@ namespace MediaBrowser.Model.Dlna
return IsConditionSatisfied(condition, numVideoStreams);
case ProfileConditionValue.VideoTimestamp:
return IsConditionSatisfied(condition, timestamp);
+ case ProfileConditionValue.VideoRotation:
+ return IsConditionSatisfied(condition, videoRotation);
default:
return true;
}
@@ -324,7 +328,7 @@ namespace MediaBrowser.Model.Dlna
return !condition.IsRequired;
}
- var expected = (TransportStreamTimestamp)Enum.Parse(typeof(TransportStreamTimestamp), condition.Value, true);
+ var expected = Enum.Parse<TransportStreamTimestamp>(condition.Value, true);
switch (condition.Condition)
{
diff --git a/MediaBrowser.Model/Dlna/EncodingContext.cs b/MediaBrowser.Model/Dlna/EncodingContext.cs
index 79ca6366d7..1408333d2e 100644
--- a/MediaBrowser.Model/Dlna/EncodingContext.cs
+++ b/MediaBrowser.Model/Dlna/EncodingContext.cs
@@ -1,10 +1,18 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Dlna
{
+ /// <summary>
+ /// The encoding context.
+ /// </summary>
public enum EncodingContext
{
+ /// <summary>
+ /// The media is transcoded on the fly and delivered as a stream.
+ /// </summary>
Streaming = 0,
+
+ /// <summary>
+ /// The media is transcoded to a static file.
+ /// </summary>
Static = 1
}
}
diff --git a/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs b/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs
index 300fab5c50..a28f422a2b 100644
--- a/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs
+++ b/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs
@@ -1,11 +1,23 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Dlna
{
+ /// <summary>
+ /// The playback error code.
+ /// </summary>
public enum PlaybackErrorCode
{
+ /// <summary>
+ /// Playback of the item is not allowed.
+ /// </summary>
NotAllowed = 0,
+
+ /// <summary>
+ /// No stream compatible with the device profile was found.
+ /// </summary>
NoCompatibleStream = 1,
+
+ /// <summary>
+ /// The rate limit has been exceeded.
+ /// </summary>
RateLimitExceeded = 2
}
}
diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
index b66a15840b..c6171c7ab2 100644
--- a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
+++ b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
@@ -28,6 +28,7 @@ namespace MediaBrowser.Model.Dlna
AudioSampleRate = 22,
AudioBitDepth = 23,
VideoRangeType = 24,
- NumStreams = 25
+ NumStreams = 25,
+ VideoRotation = 26
}
}
diff --git a/MediaBrowser.Model/Dlna/ResolutionOptions.cs b/MediaBrowser.Model/Dlna/ResolutionOptions.cs
index 774592abc7..b161b4a1e4 100644
--- a/MediaBrowser.Model/Dlna/ResolutionOptions.cs
+++ b/MediaBrowser.Model/Dlna/ResolutionOptions.cs
@@ -1,11 +1,18 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Dlna
{
+ /// <summary>
+ /// The resolution constraints.
+ /// </summary>
public class ResolutionOptions
{
+ /// <summary>
+ /// Gets or sets the maximum width.
+ /// </summary>
public int? MaxWidth { get; set; }
+ /// <summary>
+ /// Gets or sets the maximum height.
+ /// </summary>
public int? MaxHeight { get; set; }
}
}
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 42cb208d08..59f97d8c7c 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Model.Dlna
internal const TranscodeReason ContainerReasons = TranscodeReason.ContainerNotSupported | TranscodeReason.ContainerBitrateExceedsLimit;
internal const TranscodeReason AudioCodecReasons = TranscodeReason.AudioBitrateNotSupported | TranscodeReason.AudioChannelsNotSupported | TranscodeReason.AudioProfileNotSupported | TranscodeReason.AudioSampleRateNotSupported | TranscodeReason.SecondaryAudioNotSupported | TranscodeReason.AudioBitDepthNotSupported | TranscodeReason.AudioIsExternal;
internal const TranscodeReason AudioReasons = TranscodeReason.AudioCodecNotSupported | AudioCodecReasons;
- internal const TranscodeReason VideoCodecReasons = TranscodeReason.VideoResolutionNotSupported | TranscodeReason.AnamorphicVideoNotSupported | TranscodeReason.InterlacedVideoNotSupported | TranscodeReason.VideoBitDepthNotSupported | TranscodeReason.VideoBitrateNotSupported | TranscodeReason.VideoFramerateNotSupported | TranscodeReason.VideoLevelNotSupported | TranscodeReason.RefFramesNotSupported | TranscodeReason.VideoRangeTypeNotSupported | TranscodeReason.VideoProfileNotSupported;
+ internal const TranscodeReason VideoCodecReasons = TranscodeReason.VideoResolutionNotSupported | TranscodeReason.AnamorphicVideoNotSupported | TranscodeReason.InterlacedVideoNotSupported | TranscodeReason.VideoBitDepthNotSupported | TranscodeReason.VideoBitrateNotSupported | TranscodeReason.VideoFramerateNotSupported | TranscodeReason.VideoLevelNotSupported | TranscodeReason.RefFramesNotSupported | TranscodeReason.VideoRangeTypeNotSupported | TranscodeReason.VideoProfileNotSupported | TranscodeReason.VideoRotationNotSupported;
internal const TranscodeReason VideoReasons = TranscodeReason.VideoCodecNotSupported | VideoCodecReasons;
internal const TranscodeReason DirectStreamReasons = AudioReasons | TranscodeReason.ContainerNotSupported | TranscodeReason.VideoCodecTagNotSupported;
@@ -380,6 +380,9 @@ namespace MediaBrowser.Model.Dlna
case ProfileConditionValue.VideoRangeType:
return TranscodeReason.VideoRangeTypeNotSupported;
+ case ProfileConditionValue.VideoRotation:
+ return TranscodeReason.VideoRotationNotSupported;
+
case ProfileConditionValue.VideoTimestamp:
// TODO
return 0;
@@ -572,7 +575,12 @@ namespace MediaBrowser.Model.Dlna
{
foreach (var profile in subtitleProfiles)
{
- if (profile.Method == SubtitleDeliveryMethod.External && string.Equals(profile.Format, stream.Codec, StringComparison.OrdinalIgnoreCase))
+ if (profile.Method == SubtitleDeliveryMethod.External
+ && (string.Equals(profile.Format, stream.Codec, StringComparison.OrdinalIgnoreCase)
+ // FFmpeg cannot mux VobSub back into an .idx/.sub pair, so extracted VobSub streams are exposed as .mks.
+ || (string.Equals(profile.Format, "mks", StringComparison.OrdinalIgnoreCase)
+ && stream.IsVobSubSubtitleStream
+ && (!stream.IsExternal || stream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase)))))
{
return stream.Index;
}
@@ -943,6 +951,10 @@ namespace MediaBrowser.Model.Dlna
}
playlistItem.VideoCodecs = videoCodecs;
+ if (videoStream is not null && !ContainerHelper.ContainsContainer(videoCodecs, false, videoStream.Codec))
+ {
+ playlistItem.TranscodeReasons |= TranscodeReason.VideoCodecNotSupported;
+ }
// Copy video codec options as a starting point, this applies to transcode and direct-stream
playlistItem.MaxFramerate = videoStream?.ReferenceFrameRate;
@@ -991,6 +1003,10 @@ namespace MediaBrowser.Model.Dlna
var directAudioFailures = audioStreamWithSupportedCodec is null ? default : GetCompatibilityAudioCodec(options, item, container ?? string.Empty, audioStreamWithSupportedCodec, null, true, false);
playlistItem.TranscodeReasons |= directAudioFailures;
+ if (audioStream is not null && audioStreamWithSupportedCodec is null)
+ {
+ playlistItem.TranscodeReasons |= TranscodeReason.AudioCodecNotSupported;
+ }
var directAudioStreamSatisfied = audioStreamWithSupportedCodec is not null && !channelsExceedsLimit
&& directAudioFailures == 0;
@@ -1040,6 +1056,7 @@ namespace MediaBrowser.Model.Dlna
bool? isInterlaced = videoStream?.IsInterlaced;
string? videoCodecTag = videoStream?.CodecTag;
bool? isAvc = videoStream?.IsAVC;
+ int? videoRotation = videoStream?.Rotation;
TransportStreamTimestamp? timestamp = videoStream is null ? TransportStreamTimestamp.None : item.Timestamp;
int? packetLength = videoStream?.PacketLength;
@@ -1054,7 +1071,7 @@ namespace MediaBrowser.Model.Dlna
var appliedVideoConditions = options.Profile.CodecProfiles
.Where(i => i.Type == CodecType.Video &&
i.ContainsAnyCodec(playlistItem.VideoCodecs, container, useSubContainer) &&
- i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numStreams, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)))
+ i.ApplyConditions.All(applyCondition => ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numStreams, numVideoStreams, numAudioStreams, videoCodecTag, isAvc, videoRotation)))
// Reverse codec profiles for backward compatibility - first codec profile has higher priority
.Reverse();
foreach (var condition in appliedVideoConditions)
@@ -1447,7 +1464,7 @@ namespace MediaBrowser.Model.Dlna
string? outputContainer,
MediaStreamProtocol? transcodingSubProtocol)
{
- if (!subtitleStream.IsExternal && (playMethod != PlayMethod.Transcode || transcodingSubProtocol != MediaStreamProtocol.hls))
+ if (CanConsiderEmbedSubtitle(subtitleStream, playMethod, transcodingSubProtocol, outputContainer))
{
// Look for supported embedded subs of the same format
foreach (var profile in subtitleProfiles)
@@ -1536,6 +1553,19 @@ namespace MediaBrowser.Model.Dlna
return false;
}
+ private static bool CanConsiderEmbedSubtitle(MediaStream subtitleStream, PlayMethod playMethod, MediaStreamProtocol? transcodingSubProtocol, string? outputContainer)
+ {
+ if (subtitleStream.IsExternal)
+ {
+ return playMethod == PlayMethod.Transcode
+ && transcodingSubProtocol != MediaStreamProtocol.hls
+ && IsSubtitleEmbedSupported(outputContainer);
+ }
+
+ return playMethod != PlayMethod.Transcode
+ || transcodingSubProtocol != MediaStreamProtocol.hls;
+ }
+
private static SubtitleProfile? GetExternalSubtitleProfile(MediaSourceInfo mediaSource, MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, bool allowConversion)
{
foreach (var profile in subtitleProfiles)
@@ -1555,15 +1585,22 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if (!subtitleStream.IsExternal && !transcoderSupport.CanExtractSubtitles(subtitleStream.Codec))
+ if (!subtitleStream.IsExternal && playMethod == PlayMethod.Transcode && !transcoderSupport.CanExtractSubtitles(subtitleStream.Codec))
{
continue;
}
- if ((profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) ||
+ // FFmpeg cannot mux VobSub back into an .idx/.sub pair, so extracted VobSub streams are matched against external .mks delivery profiles.
+ bool isVobSubMksProfile = string.Equals(profile.Format, "mks", StringComparison.OrdinalIgnoreCase)
+ && subtitleStream.IsVobSubSubtitleStream
+ && (!subtitleStream.IsExternal || subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase));
+
+ if ((profile.Method == SubtitleDeliveryMethod.External
+ && (isVobSubMksProfile || subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))) ||
(profile.Method == SubtitleDeliveryMethod.Hls && subtitleStream.IsTextSubtitleStream))
{
- bool requiresConversion = !string.Equals(subtitleStream.Codec, profile.Format, StringComparison.OrdinalIgnoreCase);
+ bool requiresConversion = !isVobSubMksProfile
+ && !string.Equals(subtitleStream.Codec, profile.Format, StringComparison.OrdinalIgnoreCase);
if (!requiresConversion)
{
@@ -2009,7 +2046,7 @@ namespace MediaBrowser.Model.Dlna
}
else if (condition.Condition == ProfileConditionType.NotEquals)
{
- item.SetOption(qualifier, "rangetype", string.Join(',', Enum.GetNames(typeof(VideoRangeType)).Except(values)));
+ item.SetOption(qualifier, "rangetype", string.Join(',', Enum.GetNames<VideoRangeType>().Except(values)));
}
else if (condition.Condition == ProfileConditionType.EqualsAny)
{
@@ -2059,6 +2096,38 @@ namespace MediaBrowser.Model.Dlna
break;
}
+ case ProfileConditionValue.VideoRotation:
+ {
+ 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, "rotation", string.Join(',', values));
+ }
+ else if (condition.Condition == ProfileConditionType.EqualsAny)
+ {
+ var currentValue = item.GetOption(qualifier, "rotation");
+ if (!string.IsNullOrEmpty(currentValue) && values.Any(v => string.Equals(v, currentValue, StringComparison.OrdinalIgnoreCase)))
+ {
+ item.SetOption(qualifier, "rotation", currentValue);
+ }
+ else
+ {
+ item.SetOption(qualifier, "rotation", string.Join(',', values));
+ }
+ }
+
+ break;
+ }
+
case ProfileConditionValue.Height:
{
if (!enableNonQualifiedConditions)
@@ -2281,6 +2350,7 @@ namespace MediaBrowser.Model.Dlna
bool? isInterlaced = videoStream?.IsInterlaced;
string? videoCodecTag = videoStream?.CodecTag;
bool? isAvc = videoStream?.IsAVC;
+ int? videoRotation = videoStream?.Rotation;
TransportStreamTimestamp? timestamp = videoStream is null ? TransportStreamTimestamp.None : mediaSource.Timestamp;
int? packetLength = videoStream?.PacketLength;
@@ -2290,7 +2360,7 @@ namespace MediaBrowser.Model.Dlna
int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio);
int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video);
- return conditions.Where(applyCondition => !ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numStreams, numVideoStreams, numAudioStreams, videoCodecTag, isAvc));
+ return conditions.Where(applyCondition => !ConditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoRangeType, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numStreams, numVideoStreams, numAudioStreams, videoCodecTag, isAvc, videoRotation));
}
/// <summary>
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 551bee89e3..7aad97ce01 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -895,7 +895,7 @@ public class StreamInfo
if (SubProtocol == MediaStreamProtocol.hls)
{
- sb.Append("/master.m3u8?");
+ sb.Append("/master.m3u8");
}
else
{
@@ -906,10 +906,10 @@ public class StreamInfo
sb.Append('.');
sb.Append(Container);
}
-
- sb.Append('?');
}
+ var queryStart = sb.Length;
+
if (!string.IsNullOrEmpty(DeviceProfileId))
{
sb.Append("&DeviceProfileId=");
@@ -1133,6 +1133,12 @@ public class StreamInfo
sb.Append(query);
}
+ // Replace the first '&' with '?' to form a valid query string.
+ if (sb.Length > queryStart)
+ {
+ sb[queryStart] = '?';
+ }
+
return sb.ToString();
}
diff --git a/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs b/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs
index cc0c6069bf..1563ffd17a 100644
--- a/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs
+++ b/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs
@@ -1,10 +1,18 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Dlna
{
+ /// <summary>
+ /// The transcode seek info.
+ /// </summary>
public enum TranscodeSeekInfo
{
+ /// <summary>
+ /// The seek method is chosen automatically.
+ /// </summary>
Auto = 0,
+
+ /// <summary>
+ /// Seeking is performed by byte position.
+ /// </summary>
Bytes = 1
}
}
diff --git a/MediaBrowser.Model/Drawing/ImageDimensions.cs b/MediaBrowser.Model/Drawing/ImageDimensions.cs
index f84fe68305..49528ef8ae 100644
--- a/MediaBrowser.Model/Drawing/ImageDimensions.cs
+++ b/MediaBrowser.Model/Drawing/ImageDimensions.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+#pragma warning disable CA1815
using System.Globalization;
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 8f223c12a5..062034327e 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -790,9 +790,17 @@ namespace MediaBrowser.Model.Dto
public float? NormalizationGain { get; set; }
/// <summary>
+ /// Gets or sets the gain required for audio normalization. This field is inherited from music album normalization gain.
+ /// </summary>
+ /// <value>The gain required for audio normalization.</value>
+ public float? AlbumNormalizationGain { get; set; }
+
+ /// <summary>
/// Gets or sets the current program.
/// </summary>
/// <value>The current program.</value>
public BaseItemDto CurrentProgram { get; set; }
+
+ public string OriginalLanguage { get; set; }
}
}
diff --git a/MediaBrowser.Model/Dto/IHasServerId.cs b/MediaBrowser.Model/Dto/IHasServerId.cs
index c754d276c5..49452d736a 100644
--- a/MediaBrowser.Model/Dto/IHasServerId.cs
+++ b/MediaBrowser.Model/Dto/IHasServerId.cs
@@ -1,10 +1,15 @@
#nullable disable
-#pragma warning disable CS1591
namespace MediaBrowser.Model.Dto
{
+ /// <summary>
+ /// Interface for DTOs that reference the id of the server they originate from.
+ /// </summary>
public interface IHasServerId
{
+ /// <summary>
+ /// Gets the server id.
+ /// </summary>
string ServerId { get; }
}
}
diff --git a/MediaBrowser.Model/Dto/MediaSourceType.cs b/MediaBrowser.Model/Dto/MediaSourceType.cs
index 42314d5198..ca6649e64b 100644
--- a/MediaBrowser.Model/Dto/MediaSourceType.cs
+++ b/MediaBrowser.Model/Dto/MediaSourceType.cs
@@ -1,11 +1,23 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Dto
{
+ /// <summary>
+ /// The type of a media source.
+ /// </summary>
public enum MediaSourceType
{
+ /// <summary>
+ /// A default media source.
+ /// </summary>
Default = 0,
+
+ /// <summary>
+ /// A grouping of media sources.
+ /// </summary>
Grouping = 1,
+
+ /// <summary>
+ /// A placeholder media source, for example a disc that has to be inserted.
+ /// </summary>
Placeholder = 2
}
}
diff --git a/MediaBrowser.Model/Dto/RatingType.cs b/MediaBrowser.Model/Dto/RatingType.cs
index 033776f9c6..2c2b7b705d 100644
--- a/MediaBrowser.Model/Dto/RatingType.cs
+++ b/MediaBrowser.Model/Dto/RatingType.cs
@@ -1,10 +1,18 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Dto
{
+ /// <summary>
+ /// The type of a community rating.
+ /// </summary>
public enum RatingType
{
+ /// <summary>
+ /// The rating is a numeric score.
+ /// </summary>
Score,
+
+ /// <summary>
+ /// The rating is based on likes.
+ /// </summary>
Likes
}
}
diff --git a/MediaBrowser.Model/Dto/SessionInfoDto.cs b/MediaBrowser.Model/Dto/SessionInfoDto.cs
index d727cd8741..16b201de9d 100644
--- a/MediaBrowser.Model/Dto/SessionInfoDto.cs
+++ b/MediaBrowser.Model/Dto/SessionInfoDto.cs
@@ -149,13 +149,7 @@ public class SessionInfoDto
public IReadOnlyList<QueueItem>? NowPlayingQueue { get; set; }
/// <summary>
- /// Gets or sets the now playing queue full items.
- /// </summary>
- /// <value>The now playing queue full items.</value>
- public IReadOnlyList<BaseItemDto>? NowPlayingQueueFullItems { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether the session has a custom device name.
+ /// Gets or sets a value indicating whether this session has a custom device name.
/// </summary>
/// <value><c>true</c> if this session has a custom device name; otherwise, <c>false</c>.</value>
public bool HasCustomDeviceName { get; set; }
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index c443af32cf..f057714bea 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -2,11 +2,9 @@
#pragma warning disable CS1591
using System;
-using System.Collections.Frozen;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
-using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using Jellyfin.Data.Enums;
@@ -262,6 +260,8 @@ namespace MediaBrowser.Model.Entities
public string LocalizedLanguage { get; set; }
+ public string LocalizedOriginal { get; set; }
+
public string DisplayTitle
{
get
@@ -269,162 +269,167 @@ namespace MediaBrowser.Model.Entities
switch (Type)
{
case MediaStreamType.Audio:
- {
- var attributes = new List<string>();
-
- // Do not display the language code in display titles if unset or set to a special code. Show it in all other cases (possibly expanded).
- if (!string.IsNullOrEmpty(Language) && !_specialCodes.Contains(Language, StringComparison.OrdinalIgnoreCase))
{
- // Use pre-resolved localized language name, falling back to raw language code.
- attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language));
- }
+ var attributes = new List<string>();
- if (!string.IsNullOrEmpty(Profile) && !string.Equals(Profile, "lc", StringComparison.OrdinalIgnoreCase))
- {
- attributes.Add(Profile);
- }
- else if (!string.IsNullOrEmpty(Codec))
- {
- attributes.Add(AudioCodec.GetFriendlyName(Codec));
- }
+ // Do not display the language code in display titles if unset or set to a special code. Show it in all other cases (possibly expanded).
+ if (!string.IsNullOrEmpty(Language) && !_specialCodes.Contains(Language, StringComparison.OrdinalIgnoreCase))
+ {
+ // Use pre-resolved localized language name, falling back to raw language code.
+ attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language));
+ }
- if (!string.IsNullOrEmpty(ChannelLayout))
- {
- attributes.Add(StringHelper.FirstToUpper(ChannelLayout));
- }
- else if (Channels.HasValue)
- {
- attributes.Add(Channels.Value.ToString(CultureInfo.InvariantCulture) + " ch");
- }
+ if (!string.IsNullOrEmpty(Profile) && !string.Equals(Profile, "lc", StringComparison.OrdinalIgnoreCase))
+ {
+ attributes.Add(Profile);
+ }
+ else if (!string.IsNullOrEmpty(Codec))
+ {
+ attributes.Add(AudioCodec.GetFriendlyName(Codec));
+ }
- if (IsDefault)
- {
- attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault);
- }
+ if (!string.IsNullOrEmpty(ChannelLayout))
+ {
+ attributes.Add(StringHelper.FirstToUpper(ChannelLayout));
+ }
+ else if (Channels.HasValue)
+ {
+ attributes.Add(Channels.Value.ToString(CultureInfo.InvariantCulture) + " ch");
+ }
- if (IsExternal)
- {
- attributes.Add(string.IsNullOrEmpty(LocalizedExternal) ? "External" : LocalizedExternal);
- }
+ if (IsDefault)
+ {
+ attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault);
+ }
- if (!string.IsNullOrEmpty(Title))
- {
- var result = new StringBuilder(Title);
- foreach (var tag in attributes)
+ if (IsExternal)
+ {
+ attributes.Add(string.IsNullOrEmpty(LocalizedExternal) ? "External" : LocalizedExternal);
+ }
+
+ if (IsOriginal)
+ {
+ attributes.Add(string.IsNullOrEmpty(LocalizedOriginal) ? "Original" : LocalizedOriginal);
+ }
+
+ if (!string.IsNullOrEmpty(Title))
{
- // Keep Tags that are not already in Title.
- if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
+ var result = new StringBuilder(Title);
+ foreach (var tag in attributes)
{
- result.Append(" - ").Append(tag);
+ // Keep Tags that are not already in Title.
+ if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
+ {
+ result.Append(" - ").Append(tag);
+ }
}
+
+ return result.ToString();
}
- return result.ToString();
+ return string.Join(" - ", attributes);
}
- return string.Join(" - ", attributes);
- }
-
case MediaStreamType.Video:
- {
- var attributes = new List<string>();
+ {
+ var attributes = new List<string>();
- var resolutionText = GetResolutionText();
+ var resolutionText = GetResolutionText();
- if (!string.IsNullOrEmpty(resolutionText))
- {
- attributes.Add(resolutionText);
- }
+ if (!string.IsNullOrEmpty(resolutionText))
+ {
+ attributes.Add(resolutionText);
+ }
- if (!string.IsNullOrEmpty(Codec))
- {
- attributes.Add(Codec.ToUpperInvariant());
- }
+ if (!string.IsNullOrEmpty(Codec))
+ {
+ attributes.Add(Codec.ToUpperInvariant());
+ }
- if (VideoDoViTitle is not null)
- {
- attributes.Add(VideoDoViTitle);
- }
- else if (VideoRange != VideoRange.Unknown)
- {
- attributes.Add(VideoRange.ToString());
- }
+ if (VideoDoViTitle is not null)
+ {
+ attributes.Add(VideoDoViTitle);
+ }
+ else if (VideoRange != VideoRange.Unknown)
+ {
+ attributes.Add(VideoRange.ToString());
+ }
- if (!string.IsNullOrEmpty(Title))
- {
- var result = new StringBuilder(Title);
- foreach (var tag in attributes)
+ if (!string.IsNullOrEmpty(Title))
{
- // Keep Tags that are not already in Title.
- if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
+ var result = new StringBuilder(Title);
+ foreach (var tag in attributes)
{
- result.Append(" - ").Append(tag);
+ // Keep Tags that are not already in Title.
+ if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
+ {
+ result.Append(" - ").Append(tag);
+ }
}
+
+ return result.ToString();
}
- return result.ToString();
+ return string.Join(' ', attributes);
}
- return string.Join(' ', attributes);
- }
-
case MediaStreamType.Subtitle:
- {
- var attributes = new List<string>();
-
- if (!string.IsNullOrEmpty(Language))
- {
- // Use pre-resolved localized language name, falling back to raw language code.
- attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language));
- }
- else
{
- attributes.Add(string.IsNullOrEmpty(LocalizedUndefined) ? "Und" : LocalizedUndefined);
- }
+ var attributes = new List<string>();
- if (IsHearingImpaired == true)
- {
- attributes.Add(string.IsNullOrEmpty(LocalizedHearingImpaired) ? "Hearing Impaired" : LocalizedHearingImpaired);
- }
+ if (!string.IsNullOrEmpty(Language))
+ {
+ // Use pre-resolved localized language name, falling back to raw language code.
+ attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language));
+ }
+ else
+ {
+ attributes.Add(string.IsNullOrEmpty(LocalizedUndefined) ? "Und" : LocalizedUndefined);
+ }
- if (IsDefault)
- {
- attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault);
- }
+ if (IsHearingImpaired == true)
+ {
+ attributes.Add(string.IsNullOrEmpty(LocalizedHearingImpaired) ? "Hearing Impaired" : LocalizedHearingImpaired);
+ }
- if (IsForced)
- {
- attributes.Add(string.IsNullOrEmpty(LocalizedForced) ? "Forced" : LocalizedForced);
- }
+ if (IsDefault)
+ {
+ attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault);
+ }
- if (!string.IsNullOrEmpty(Codec))
- {
- attributes.Add(Codec.ToUpperInvariant());
- }
+ if (IsForced)
+ {
+ attributes.Add(string.IsNullOrEmpty(LocalizedForced) ? "Forced" : LocalizedForced);
+ }
- if (IsExternal)
- {
- attributes.Add(string.IsNullOrEmpty(LocalizedExternal) ? "External" : LocalizedExternal);
- }
+ if (!string.IsNullOrEmpty(Codec))
+ {
+ attributes.Add(Codec.ToUpperInvariant());
+ }
- if (!string.IsNullOrEmpty(Title))
- {
- var result = new StringBuilder(Title);
- foreach (var tag in attributes)
+ if (IsExternal)
{
- // Keep Tags that are not already in Title.
- if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
+ attributes.Add(string.IsNullOrEmpty(LocalizedExternal) ? "External" : LocalizedExternal);
+ }
+
+ if (!string.IsNullOrEmpty(Title))
+ {
+ var result = new StringBuilder(Title);
+ foreach (var tag in attributes)
{
- result.Append(" - ").Append(tag);
+ // Keep Tags that are not already in Title.
+ if (!Title.Contains(tag, StringComparison.OrdinalIgnoreCase))
+ {
+ result.Append(" - ").Append(tag);
+ }
}
+
+ return result.ToString();
}
- return result.ToString();
+ return string.Join(" - ", attributes);
}
- return string.Join(" - ", attributes);
- }
-
default:
return null;
}
@@ -502,6 +507,12 @@ namespace MediaBrowser.Model.Entities
public bool IsHearingImpaired { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether this instance is original.
+ /// </summary>
+ /// <value><c>true</c> if this instance is original; otherwise, <c>false</c>.</value>
+ public bool IsOriginal { get; set; }
+
+ /// <summary>
/// Gets or sets the height.
/// </summary>
/// <value>The height.</value>
@@ -633,13 +644,32 @@ namespace MediaBrowser.Model.Entities
}
}
+ [JsonIgnore]
+ public bool IsVobSubSubtitleStream
+ {
+ get
+ {
+ if (Type != MediaStreamType.Subtitle)
+ {
+ return false;
+ }
+
+ if (string.IsNullOrEmpty(Codec) && !IsExternal)
+ {
+ return false;
+ }
+
+ return IsVobSubFormat(Codec);
+ }
+ }
+
/// <summary>
/// Gets a value indicating whether this is a subtitle steam that is extractable by ffmpeg.
/// All text-based and pgs subtitles can be extracted.
/// </summary>
/// <value><c>true</c> if this is a extractable subtitle steam otherwise, <c>false</c>.</value>
[JsonIgnore]
- public bool IsExtractableSubtitleStream => IsTextSubtitleStream || IsPgsSubtitleStream;
+ public bool IsExtractableSubtitleStream => IsTextSubtitleStream || IsPgsSubtitleStream || IsVobSubSubtitleStream;
/// <summary>
/// Gets or sets a value indicating whether [supports external stream].
@@ -717,6 +747,7 @@ namespace MediaBrowser.Model.Entities
return codec.Contains("microdvd", StringComparison.OrdinalIgnoreCase)
|| (!codec.Contains("pgs", StringComparison.OrdinalIgnoreCase)
&& !codec.Contains("dvdsub", StringComparison.OrdinalIgnoreCase)
+ && !codec.Contains("vobsub", StringComparison.OrdinalIgnoreCase)
&& !codec.Contains("dvbsub", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(codec, "sup", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase));
@@ -730,6 +761,14 @@ namespace MediaBrowser.Model.Entities
|| string.Equals(codec, "sup", StringComparison.OrdinalIgnoreCase);
}
+ public static bool IsVobSubFormat(string format)
+ {
+ string codec = format ?? string.Empty;
+
+ return codec.Contains("dvdsub", StringComparison.OrdinalIgnoreCase)
+ || codec.Contains("vobsub", StringComparison.OrdinalIgnoreCase);
+ }
+
public bool SupportsSubtitleConversionTo(string toCodec)
{
if (!IsTextSubtitleStream)
diff --git a/MediaBrowser.Model/Extensions/EnumerableExtensions.cs b/MediaBrowser.Model/Extensions/EnumerableExtensions.cs
index 94f4252295..28c3c66af7 100644
--- a/MediaBrowser.Model/Extensions/EnumerableExtensions.cs
+++ b/MediaBrowser.Model/Extensions/EnumerableExtensions.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Model.Extensions
public static class EnumerableExtensions
{
/// <summary>
- /// Orders <see cref="RemoteImageInfo"/> by requested language in descending order, prioritizing "en" over other non-matches.
+ /// Orders <see cref="RemoteImageInfo"/> by requested language in descending order, then "en", then no language, over other non-matches.
/// </summary>
/// <param name="remoteImageInfos">The remote image infos.</param>
/// <param name="requestedLanguage">The requested language for the images.</param>
@@ -28,9 +28,9 @@ namespace MediaBrowser.Model.Extensions
{
// Image priority ordering:
// - Images that match the requested language
- // - Images with no language
// - TODO: Images that match the original language
// - Images in English
+ // - Images with no language
// - Images that don't match the requested language
if (string.Equals(requestedLanguage, i.Language, StringComparison.OrdinalIgnoreCase))
@@ -38,19 +38,19 @@ namespace MediaBrowser.Model.Extensions
return 4;
}
- if (string.IsNullOrEmpty(i.Language))
+ if (string.Equals(i.Language, "en", StringComparison.OrdinalIgnoreCase))
{
return 3;
}
- if (string.Equals(i.Language, "en", StringComparison.OrdinalIgnoreCase))
+ if (string.IsNullOrEmpty(i.Language))
{
return 2;
}
return 0;
})
- .ThenByDescending(i => Math.Round(i.CommunityRating ?? 0, 1) )
+ .ThenByDescending(i => Math.Round(i.CommunityRating ?? 0, 1))
.ThenByDescending(i => i.VoteCount ?? 0);
}
}
diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
index f6e65028e4..7ad240abfb 100644
--- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs
+++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs
@@ -51,6 +51,15 @@ public interface ILocalizationManager
string GetLocalizedString(string phrase);
/// <summary>
+ /// Gets the localized string using the server's configured UICulture,
+ /// ignoring the current request's culture. Use this for data that is
+ /// persisted (e.g. activity log entries) rather than returned per-request.
+ /// </summary>
+ /// <param name="phrase">The phrase.</param>
+ /// <returns>System.String.</returns>
+ string GetServerLocalizedString(string phrase);
+
+ /// <summary>
/// Gets the localization options.
/// </summary>
/// <returns><see cref="IEnumerable{LocalizationOption}" />.</returns>
diff --git a/MediaBrowser.Model/Library/PlayAccess.cs b/MediaBrowser.Model/Library/PlayAccess.cs
index a2f263ce54..22daaf7254 100644
--- a/MediaBrowser.Model/Library/PlayAccess.cs
+++ b/MediaBrowser.Model/Library/PlayAccess.cs
@@ -1,10 +1,18 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Library
{
+ /// <summary>
+ /// The play access of an item.
+ /// </summary>
public enum PlayAccess
{
+ /// <summary>
+ /// The item can be played.
+ /// </summary>
Full = 0,
+
+ /// <summary>
+ /// The item cannot be played.
+ /// </summary>
None = 1
}
}
diff --git a/MediaBrowser.Model/LiveTv/DayPattern.cs b/MediaBrowser.Model/LiveTv/DayPattern.cs
index 17efe39088..dab69e8974 100644
--- a/MediaBrowser.Model/LiveTv/DayPattern.cs
+++ b/MediaBrowser.Model/LiveTv/DayPattern.cs
@@ -1,11 +1,23 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.LiveTv
{
+ /// <summary>
+ /// The day pattern of a recurring timer.
+ /// </summary>
public enum DayPattern
{
+ /// <summary>
+ /// Every day.
+ /// </summary>
Daily,
+
+ /// <summary>
+ /// Monday through Friday.
+ /// </summary>
Weekdays,
+
+ /// <summary>
+ /// Saturday and Sunday.
+ /// </summary>
Weekends
}
}
diff --git a/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs b/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs
index 72a0e2d7bf..a3df1dc411 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs
@@ -1,10 +1,18 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.LiveTv
{
+ /// <summary>
+ /// The status of a live TV service.
+ /// </summary>
public enum LiveTvServiceStatus
{
+ /// <summary>
+ /// The service is available.
+ /// </summary>
Ok = 0,
+
+ /// <summary>
+ /// The service is unavailable.
+ /// </summary>
Unavailable = 1
}
}
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index c655c4ccb3..2dddd39ef4 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.12.0</VersionPrefix>
+ <VersionPrefix>12.0.0</VersionPrefix>
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
</PropertyGroup>
diff --git a/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs b/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs
index b7ee5747ab..1988dd8078 100644
--- a/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs
+++ b/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs
@@ -1,11 +1,23 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.MediaInfo
{
+ /// <summary>
+ /// The type of timestamps used in a transport stream.
+ /// </summary>
public enum TransportStreamTimestamp
{
+ /// <summary>
+ /// The stream contains no timestamps.
+ /// </summary>
None,
+
+ /// <summary>
+ /// The stream contains zero-value timestamps.
+ /// </summary>
Zero,
+
+ /// <summary>
+ /// The stream contains valid timestamps.
+ /// </summary>
Valid
}
}
diff --git a/MediaBrowser.Model/MediaSegments/MediaSegmentGenerationRequest.cs b/MediaBrowser.Model/MediaSegments/MediaSegmentGenerationRequest.cs
index 53d0173750..9a21461d82 100644
--- a/MediaBrowser.Model/MediaSegments/MediaSegmentGenerationRequest.cs
+++ b/MediaBrowser.Model/MediaSegments/MediaSegmentGenerationRequest.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using Jellyfin.Database.Implementations.Entities;
using MediaBrowser.Model.MediaSegments;
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
index 79f8675cbf..c0d1bc86e7 100644
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ b/MediaBrowser.Model/Net/MimeTypes.cs
@@ -132,6 +132,7 @@ namespace MediaBrowser.Model.Net
// Type image
new("image/jpeg", ".jpg"),
+ new("image/jpg", ".jpg"),
new("image/tiff", ".tiff"),
new("image/x-png", ".png"),
new("image/x-icon", ".ico"),
diff --git a/MediaBrowser.Model/Providers/SubtitleOptions.cs b/MediaBrowser.Model/Providers/SubtitleOptions.cs
deleted file mode 100644
index 6ea1e14862..0000000000
--- a/MediaBrowser.Model/Providers/SubtitleOptions.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System;
-
-namespace MediaBrowser.Model.Providers
-{
- public class SubtitleOptions
- {
- public SubtitleOptions()
- {
- DownloadLanguages = Array.Empty<string>();
-
- SkipIfAudioTrackMatches = true;
- RequirePerfectMatch = true;
- }
-
- public bool SkipIfEmbeddedSubtitlesPresent { get; set; }
-
- public bool SkipIfAudioTrackMatches { get; set; }
-
- public string[] DownloadLanguages { get; set; }
-
- public bool DownloadMovieSubtitles { get; set; }
-
- public bool DownloadEpisodeSubtitles { get; set; }
-
- public string OpenSubtitlesUsername { get; set; }
-
- public string OpenSubtitlesPasswordHash { get; set; }
-
- public bool IsOpenSubtitleVipAccount { get; set; }
-
- public bool RequirePerfectMatch { get; set; }
- }
-}
diff --git a/MediaBrowser.Model/Querying/QueryFilters.cs b/MediaBrowser.Model/Querying/QueryFilters.cs
index 73b27a7b06..095f460923 100644
--- a/MediaBrowser.Model/Querying/QueryFilters.cs
+++ b/MediaBrowser.Model/Querying/QueryFilters.cs
@@ -2,6 +2,7 @@
#pragma warning disable CS1591
using System;
+using System.Collections.Generic;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Model.Querying
@@ -12,10 +13,16 @@ namespace MediaBrowser.Model.Querying
{
Tags = Array.Empty<string>();
Genres = Array.Empty<NameGuidPair>();
+ AudioLanguages = Array.Empty<NameValuePair>();
+ SubtitleLanguages = Array.Empty<NameValuePair>();
}
- public NameGuidPair[] Genres { get; set; }
+ public IReadOnlyList<NameGuidPair> Genres { get; set; }
- public string[] Tags { get; set; }
+ public IReadOnlyList<string> Tags { get; set; }
+
+ public IReadOnlyList<NameValuePair> AudioLanguages { get; set; }
+
+ public IReadOnlyList<NameValuePair> SubtitleLanguages { get; set; }
}
}
diff --git a/MediaBrowser.Model/Session/MessageCommand.cs b/MediaBrowser.Model/Session/MessageCommand.cs
index cc9db8e6c5..e041a9cccd 100644
--- a/MediaBrowser.Model/Session/MessageCommand.cs
+++ b/MediaBrowser.Model/Session/MessageCommand.cs
@@ -1,17 +1,28 @@
#nullable disable
-#pragma warning disable CS1591
using System.ComponentModel.DataAnnotations;
namespace MediaBrowser.Model.Session
{
+ /// <summary>
+ /// A command to display a message on a client.
+ /// </summary>
public class MessageCommand
{
+ /// <summary>
+ /// Gets or sets the message header.
+ /// </summary>
public string Header { get; set; }
+ /// <summary>
+ /// Gets or sets the message text.
+ /// </summary>
[Required(AllowEmptyStrings = false)]
public string Text { get; set; }
+ /// <summary>
+ /// Gets or sets the timeout in milliseconds after which the message should be dismissed.
+ /// </summary>
public long? TimeoutMs { get; set; }
}
}
diff --git a/MediaBrowser.Model/Session/PlayMethod.cs b/MediaBrowser.Model/Session/PlayMethod.cs
index 8067627843..2bd11cc91a 100644
--- a/MediaBrowser.Model/Session/PlayMethod.cs
+++ b/MediaBrowser.Model/Session/PlayMethod.cs
@@ -1,11 +1,23 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Session
{
+ /// <summary>
+ /// The play method.
+ /// </summary>
public enum PlayMethod
{
+ /// <summary>
+ /// The media is transcoded before it is sent to the client.
+ /// </summary>
Transcode = 0,
+
+ /// <summary>
+ /// The media is remuxed into a compatible container but the streams are not re-encoded.
+ /// </summary>
DirectStream = 1,
+
+ /// <summary>
+ /// The media is sent to the client as-is.
+ /// </summary>
DirectPlay = 2
}
}
diff --git a/MediaBrowser.Model/Session/PlaystateRequest.cs b/MediaBrowser.Model/Session/PlaystateRequest.cs
index ba2c024b76..040affa144 100644
--- a/MediaBrowser.Model/Session/PlaystateRequest.cs
+++ b/MediaBrowser.Model/Session/PlaystateRequest.cs
@@ -1,11 +1,18 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Session
{
+ /// <summary>
+ /// A request to change the playstate of a session.
+ /// </summary>
public class PlaystateRequest
{
+ /// <summary>
+ /// Gets or sets the playstate command.
+ /// </summary>
public PlaystateCommand Command { get; set; }
+ /// <summary>
+ /// Gets or sets the seek position in ticks.
+ /// </summary>
public long? SeekPositionTicks { get; set; }
/// <summary>
diff --git a/MediaBrowser.Model/Session/QueueItem.cs b/MediaBrowser.Model/Session/QueueItem.cs
index 43920a8464..b9f3181da0 100644
--- a/MediaBrowser.Model/Session/QueueItem.cs
+++ b/MediaBrowser.Model/Session/QueueItem.cs
@@ -1,13 +1,21 @@
#nullable disable
-#pragma warning disable CS1591
using System;
namespace MediaBrowser.Model.Session;
+/// <summary>
+/// An item in a play queue.
+/// </summary>
public record QueueItem
{
+ /// <summary>
+ /// Gets or sets the item id.
+ /// </summary>
public Guid Id { get; set; }
+ /// <summary>
+ /// Gets or sets the playlist item id.
+ /// </summary>
public string PlaylistItemId { get; set; }
}
diff --git a/MediaBrowser.Model/Session/RepeatMode.cs b/MediaBrowser.Model/Session/RepeatMode.cs
index c6e173d6b8..c6c657d220 100644
--- a/MediaBrowser.Model/Session/RepeatMode.cs
+++ b/MediaBrowser.Model/Session/RepeatMode.cs
@@ -1,11 +1,23 @@
-#pragma warning disable CS1591
-
namespace MediaBrowser.Model.Session
{
+ /// <summary>
+ /// The repeat mode of a play queue.
+ /// </summary>
public enum RepeatMode
{
+ /// <summary>
+ /// Nothing is repeated.
+ /// </summary>
RepeatNone = 0,
+
+ /// <summary>
+ /// The whole queue is repeated.
+ /// </summary>
RepeatAll = 1,
+
+ /// <summary>
+ /// The current item is repeated.
+ /// </summary>
RepeatOne = 2
}
}
diff --git a/MediaBrowser.Model/Session/TranscodeReason.cs b/MediaBrowser.Model/Session/TranscodeReason.cs
index 902bab9a6e..4ea60f115a 100644
--- a/MediaBrowser.Model/Session/TranscodeReason.cs
+++ b/MediaBrowser.Model/Session/TranscodeReason.cs
@@ -24,6 +24,7 @@ namespace MediaBrowser.Model.Session
VideoResolutionNotSupported = 1 << 8,
VideoBitDepthNotSupported = 1 << 9,
VideoFramerateNotSupported = 1 << 10,
+ VideoRotationNotSupported = 1 << 27,
RefFramesNotSupported = 1 << 11,
AnamorphicVideoNotSupported = 1 << 12,
InterlacedVideoNotSupported = 1 << 13,
diff --git a/MediaBrowser.Model/SyncPlay/PlaybackRequestType.cs b/MediaBrowser.Model/SyncPlay/PlaybackRequestType.cs
index 4429623dd9..ded66652ce 100644
--- a/MediaBrowser.Model/SyncPlay/PlaybackRequestType.cs
+++ b/MediaBrowser.Model/SyncPlay/PlaybackRequestType.cs
@@ -50,7 +50,7 @@ namespace MediaBrowser.Model.SyncPlay
/// </summary>
Seek = 8,
- /// <summary>
+ /// <summary>
/// A user is signaling that playback is buffering.
/// </summary>
Buffer = 9,
diff --git a/MediaBrowser.Model/SyncPlay/SyncPlayGroupDoesNotExistUpdate.cs b/MediaBrowser.Model/SyncPlay/SyncPlayGroupDoesNotExistUpdate.cs
index 7e2d10c8b8..ccf5fdb07e 100644
--- a/MediaBrowser.Model/SyncPlay/SyncPlayGroupDoesNotExistUpdate.cs
+++ b/MediaBrowser.Model/SyncPlay/SyncPlayGroupDoesNotExistUpdate.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
namespace MediaBrowser.Model.SyncPlay;
diff --git a/MediaBrowser.Model/SyncPlay/SyncPlayGroupJoinedUpdate.cs b/MediaBrowser.Model/SyncPlay/SyncPlayGroupJoinedUpdate.cs
index bfb49152a3..dcb039ee93 100644
--- a/MediaBrowser.Model/SyncPlay/SyncPlayGroupJoinedUpdate.cs
+++ b/MediaBrowser.Model/SyncPlay/SyncPlayGroupJoinedUpdate.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
namespace MediaBrowser.Model.SyncPlay;
diff --git a/MediaBrowser.Model/SyncPlay/SyncPlayGroupLeftUpdate.cs b/MediaBrowser.Model/SyncPlay/SyncPlayGroupLeftUpdate.cs
index 5ff60c5c27..f20e143e02 100644
--- a/MediaBrowser.Model/SyncPlay/SyncPlayGroupLeftUpdate.cs
+++ b/MediaBrowser.Model/SyncPlay/SyncPlayGroupLeftUpdate.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
namespace MediaBrowser.Model.SyncPlay;
diff --git a/MediaBrowser.Model/SyncPlay/SyncPlayLibraryAccessDeniedUpdate.cs b/MediaBrowser.Model/SyncPlay/SyncPlayLibraryAccessDeniedUpdate.cs
index 0d9a722f78..89e5706d86 100644
--- a/MediaBrowser.Model/SyncPlay/SyncPlayLibraryAccessDeniedUpdate.cs
+++ b/MediaBrowser.Model/SyncPlay/SyncPlayLibraryAccessDeniedUpdate.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
namespace MediaBrowser.Model.SyncPlay;
diff --git a/MediaBrowser.Model/SyncPlay/SyncPlayNotInGroupUpdate.cs b/MediaBrowser.Model/SyncPlay/SyncPlayNotInGroupUpdate.cs
index a3b610f619..4ba893be5b 100644
--- a/MediaBrowser.Model/SyncPlay/SyncPlayNotInGroupUpdate.cs
+++ b/MediaBrowser.Model/SyncPlay/SyncPlayNotInGroupUpdate.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
namespace MediaBrowser.Model.SyncPlay;
diff --git a/MediaBrowser.Model/SyncPlay/SyncPlayPlayQueueUpdate.cs b/MediaBrowser.Model/SyncPlay/SyncPlayPlayQueueUpdate.cs
index 83d9bd40bc..a39f20735b 100644
--- a/MediaBrowser.Model/SyncPlay/SyncPlayPlayQueueUpdate.cs
+++ b/MediaBrowser.Model/SyncPlay/SyncPlayPlayQueueUpdate.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
namespace MediaBrowser.Model.SyncPlay;
diff --git a/MediaBrowser.Model/SyncPlay/SyncPlayStateUpdate.cs b/MediaBrowser.Model/SyncPlay/SyncPlayStateUpdate.cs
index 744ca46a0b..61cb8adbaa 100644
--- a/MediaBrowser.Model/SyncPlay/SyncPlayStateUpdate.cs
+++ b/MediaBrowser.Model/SyncPlay/SyncPlayStateUpdate.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
namespace MediaBrowser.Model.SyncPlay;
diff --git a/MediaBrowser.Model/SyncPlay/SyncPlayUserJoinedUpdate.cs b/MediaBrowser.Model/SyncPlay/SyncPlayUserJoinedUpdate.cs
index e8c6b4df41..247e6a57b2 100644
--- a/MediaBrowser.Model/SyncPlay/SyncPlayUserJoinedUpdate.cs
+++ b/MediaBrowser.Model/SyncPlay/SyncPlayUserJoinedUpdate.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
namespace MediaBrowser.Model.SyncPlay;
diff --git a/MediaBrowser.Model/SyncPlay/SyncPlayUserLeftUpdate.cs b/MediaBrowser.Model/SyncPlay/SyncPlayUserLeftUpdate.cs
index 97be8e63a8..ba053747cc 100644
--- a/MediaBrowser.Model/SyncPlay/SyncPlayUserLeftUpdate.cs
+++ b/MediaBrowser.Model/SyncPlay/SyncPlayUserLeftUpdate.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
namespace MediaBrowser.Model.SyncPlay;
diff --git a/MediaBrowser.Model/System/FolderStorageInfo.cs b/MediaBrowser.Model/System/FolderStorageInfo.cs
index 7b10e4ea58..ebca39228b 100644
--- a/MediaBrowser.Model/System/FolderStorageInfo.cs
+++ b/MediaBrowser.Model/System/FolderStorageInfo.cs
@@ -11,17 +11,22 @@ public record FolderStorageInfo
public required string Path { get; init; }
/// <summary>
- /// Gets the free space of the underlying storage device of the <see cref="Path"/>.
+ /// Gets the fully resolved path of the folder in question (interpolating any symlinks if present).
+ /// </summary>
+ public required string ResolvedPath { get; init; }
+
+ /// <summary>
+ /// Gets the free space of the underlying storage device of the <see cref="ResolvedPath"/>.
/// </summary>
public long FreeSpace { get; init; }
/// <summary>
- /// Gets the used space of the underlying storage device of the <see cref="Path"/>.
+ /// Gets the used space of the underlying storage device of the <see cref="ResolvedPath"/>.
/// </summary>
public long UsedSpace { get; init; }
/// <summary>
- /// Gets the kind of storage device of the <see cref="Path"/>.
+ /// Gets the kind of storage device of the <see cref="ResolvedPath"/>.
/// </summary>
public string? StorageType { get; init; }
diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
index 2c393ca862..95e4d46c59 100644
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ b/MediaBrowser.Model/Users/UserPolicy.cs
@@ -187,7 +187,7 @@ namespace MediaBrowser.Model.Users
[Required(AllowEmptyStrings = false)]
public string AuthenticationProviderId { get; set; }
- [Required(AllowEmptyStrings= false)]
+ [Required(AllowEmptyStrings = false)]
public string PasswordResetProviderId { get; set; }
/// <summary>