diff options
| author | MBR-0001 <55142207+MBR-0001@users.noreply.github.com> | 2026-04-18 17:14:31 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-18 17:14:31 +0200 |
| commit | a2eff41d6617bed7a7c5518d00f227a2406a8421 (patch) | |
| tree | 933386669537d5145ca6b3706728454aff27d703 /MediaBrowser.Model | |
| parent | d4a46bc6291560b35a9b64fb88b68019f903d0ef (diff) | |
| parent | 5aa093d2997d8120ce19037642556070d27ce0bb (diff) | |
Merge branch 'master' into removeGlobalSubtitleConfiguration
Diffstat (limited to 'MediaBrowser.Model')
18 files changed, 149 insertions, 140 deletions
diff --git a/MediaBrowser.Model/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs index 95aa567ada..96958e9a73 100644 --- a/MediaBrowser.Model/Activity/IActivityManager.cs +++ b/MediaBrowser.Model/Activity/IActivityManager.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Threading.Tasks; using Jellyfin.Data.Events; @@ -7,21 +5,36 @@ using Jellyfin.Data.Queries; using Jellyfin.Database.Implementations.Entities; using MediaBrowser.Model.Querying; -namespace MediaBrowser.Model.Activity +namespace MediaBrowser.Model.Activity; + +/// <summary> +/// Interface for the activity manager. +/// </summary> +public interface IActivityManager { - public interface IActivityManager - { - event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated; + /// <summary> + /// The event that is triggered when an entity is created. + /// </summary> + event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated; - Task CreateAsync(ActivityLog entry); + /// <summary> + /// Create a new activity log entry. + /// </summary> + /// <param name="entry">The entry to create.</param> + /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> + Task CreateAsync(ActivityLog entry); - Task<QueryResult<ActivityLogEntry>> GetPagedResultAsync(ActivityLogQuery query); + /// <summary> + /// Get a paged list of activity log entries. + /// </summary> + /// <param name="query">The activity log query.</param> + /// <returns>The page of entries.</returns> + Task<QueryResult<ActivityLogEntry>> GetPagedResultAsync(ActivityLogQuery query); - /// <summary> - /// Remove all activity logs before the specified date. - /// </summary> - /// <param name="startDate">Activity log start date.</param> - /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> - Task CleanAsync(DateTime startDate); - } + /// <summary> + /// Remove all activity logs before the specified date. + /// </summary> + /// <param name="startDate">Activity log start date.</param> + /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> + Task CleanAsync(DateTime startDate); } diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 2720c0bdf6..98fc2e632f 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -1,6 +1,7 @@ #pragma warning disable CA1819 // XML serialization handles collections improperly, so we need to use arrays #nullable disable +using System.ComponentModel; using MediaBrowser.Model.Entities; namespace MediaBrowser.Model.Configuration; @@ -57,8 +58,10 @@ public class EncodingOptions AllowHevcEncoding = false; AllowAv1Encoding = false; EnableSubtitleExtraction = true; + SubtitleExtractionTimeoutMinutes = 30; AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = ["mkv"]; HardwareDecodingCodecs = ["h264", "vc1"]; + HlsAudioSeekStrategy = HlsAudioSeekStrategy.DisableAccurateSeek; } /// <summary> @@ -287,6 +290,11 @@ public class EncodingOptions public bool EnableSubtitleExtraction { get; set; } /// <summary> + /// Gets or sets the timeout for subtitle extraction in minutes. + /// </summary> + public int SubtitleExtractionTimeoutMinutes { get; set; } + + /// <summary> /// Gets or sets the codecs hardware encoding is used for. /// </summary> public string[] HardwareDecodingCodecs { get; set; } @@ -295,4 +303,10 @@ public class EncodingOptions /// Gets or sets the file extensions on-demand metadata based keyframe extraction is enabled for. /// </summary> public string[] AllowOnDemandMetadataBasedKeyframeExtractionForExtensions { get; set; } + + /// <summary> + /// Gets or sets the method used for audio seeking in HLS. + /// </summary> + [DefaultValue(HlsAudioSeekStrategy.DisableAccurateSeek)] + public HlsAudioSeekStrategy HlsAudioSeekStrategy { get; set; } } diff --git a/MediaBrowser.Model/Configuration/HlsAudioSeekStrategy.cs b/MediaBrowser.Model/Configuration/HlsAudioSeekStrategy.cs new file mode 100644 index 0000000000..49feeb435f --- /dev/null +++ b/MediaBrowser.Model/Configuration/HlsAudioSeekStrategy.cs @@ -0,0 +1,23 @@ +namespace MediaBrowser.Model.Configuration +{ + /// <summary> + /// An enum representing the options to seek the input audio stream when + /// transcoding HLS segments. + /// </summary> + 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. + /// </summary> + DisableAccurateSeek = 0, + + /// <summary> + /// Prevent audio streams from being copied if the video stream is transcoded. + /// The resulting timestamps will be accurate, but additional audio transcoding + /// overhead will be incurred. + /// </summary> + TranscodeAudio = 1, + } +} diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index a58c01c960..ac5c12304e 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -287,5 +287,5 @@ public class ServerConfiguration : BaseApplicationConfiguration /// <summary> /// Gets or sets a value indicating whether old authorization methods are allowed. /// </summary> - public bool EnableLegacyAuthorization { get; set; } = true; + public bool EnableLegacyAuthorization { get; set; } } diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs index 1b61bfe155..79ee683a2d 100644 --- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs +++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs @@ -324,7 +324,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/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 61e04a8134..c9697c685c 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -610,7 +610,6 @@ namespace MediaBrowser.Model.Dlna playlistItem.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest; playlistItem.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; - playlistItem.BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames; playlistItem.EnableAudioVbrEncoding = transcodingProfile.EnableAudioVbrEncoding; if (transcodingProfile.MinSegments > 0) @@ -1556,7 +1555,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (!subtitleStream.IsExternal && !transcoderSupport.CanExtractSubtitles(subtitleStream.Codec)) + if (!subtitleStream.IsExternal && playMethod == PlayMethod.Transcode && !transcoderSupport.CanExtractSubtitles(subtitleStream.Codec)) { continue; } @@ -2010,7 +2009,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) { diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 13acd15a3f..7aad97ce01 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -87,11 +87,6 @@ public class StreamInfo public int? MinSegments { get; set; } /// <summary> - /// Gets or sets a value indicating whether the stream can be broken on non-keyframes. - /// </summary> - public bool BreakOnNonKeyFrames { get; set; } - - /// <summary> /// Gets or sets a value indicating whether the stream requires AVC. /// </summary> public bool RequireAvc { get; set; } @@ -900,7 +895,7 @@ public class StreamInfo if (SubProtocol == MediaStreamProtocol.hls) { - sb.Append("/master.m3u8?"); + sb.Append("/master.m3u8"); } else { @@ -911,10 +906,10 @@ public class StreamInfo sb.Append('.'); sb.Append(Container); } - - sb.Append('?'); } + var queryStart = sb.Length; + if (!string.IsNullOrEmpty(DeviceProfileId)) { sb.Append("&DeviceProfileId="); @@ -1018,9 +1013,6 @@ public class StreamInfo sb.Append("&MinSegments="); sb.Append(MinSegments.Value.ToString(CultureInfo.InvariantCulture)); } - - sb.Append("&BreakOnNonKeyFrames="); - sb.Append(BreakOnNonKeyFrames.ToString(CultureInfo.InvariantCulture)); } else { @@ -1141,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(); } @@ -1250,30 +1248,37 @@ public class StreamInfo if (info.DeliveryMethod == SubtitleDeliveryMethod.External) { - if (MediaSource.Protocol == MediaProtocol.File || !string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) || !stream.IsExternal) - { - info.Url = string.Format( - CultureInfo.InvariantCulture, - "{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", - baseUrl, - ItemId, - MediaSourceId, - stream.Index.ToString(CultureInfo.InvariantCulture), - startPositionTicks.ToString(CultureInfo.InvariantCulture), - subtitleProfile.Format); - - if (!string.IsNullOrEmpty(accessToken)) - { - info.Url += "?ApiKey=" + accessToken; - } - - info.IsExternalUrl = false; - } - else + // Default to using the API URL + info.Url = string.Format( + CultureInfo.InvariantCulture, + "{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", + baseUrl, + ItemId, + MediaSourceId, + stream.Index.ToString(CultureInfo.InvariantCulture), + startPositionTicks.ToString(CultureInfo.InvariantCulture), + subtitleProfile.Format); + info.IsExternalUrl = false; + + // Check conditions for potentially using the direct path + if (stream.IsExternal // Must be external + && stream.SupportsExternalStream + && string.Equals(stream.Codec, subtitleProfile.Format, StringComparison.OrdinalIgnoreCase) // Format must match (no conversion needed) + && !string.IsNullOrEmpty(stream.Path) // Path must exist + && Uri.TryCreate(stream.Path, UriKind.Absolute, out Uri? uriResult) // Path must be an absolute URI + && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps)) // Scheme must be HTTP or HTTPS { + // All conditions met, override with the direct path info.Url = stream.Path; info.IsExternalUrl = true; } + + // Append ApiKey only if we are using the API URL + if (!info.IsExternalUrl && !string.IsNullOrEmpty(accessToken)) + { + // Use "?ApiKey=" as seen in HEAD and other parts of the code + info.Url += "?ApiKey=" + accessToken; + } } return info; diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs index 5797d42506..f49b24976a 100644 --- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs @@ -41,7 +41,6 @@ public class TranscodingProfile MaxAudioChannels = other.MaxAudioChannels; MinSegments = other.MinSegments; SegmentLength = other.SegmentLength; - BreakOnNonKeyFrames = other.BreakOnNonKeyFrames; Conditions = other.Conditions; EnableAudioVbrEncoding = other.EnableAudioVbrEncoding; } @@ -143,7 +142,8 @@ public class TranscodingProfile /// </summary> [DefaultValue(false)] [XmlAttribute("breakOnNonKeyFrames")] - public bool BreakOnNonKeyFrames { get; set; } + [Obsolete("This is always false")] + public bool? BreakOnNonKeyFrames { get; set; } /// <summary> /// Gets or sets the profile conditions. diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index 8f223c12a5..e96bba0464 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -790,6 +790,12 @@ 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> diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs index 05019741e0..c6b4a4d141 100644 --- a/MediaBrowser.Model/Dto/UserDto.cs +++ b/MediaBrowser.Model/Dto/UserDto.cs @@ -1,5 +1,6 @@ #nullable disable using System; +using System.ComponentModel; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Users; @@ -54,20 +55,22 @@ namespace MediaBrowser.Model.Dto /// Gets or sets a value indicating whether this instance has password. /// </summary> /// <value><c>true</c> if this instance has password; otherwise, <c>false</c>.</value> - public bool HasPassword { get; set; } + [Obsolete("This information is no longer provided")] + public bool? HasPassword { get; set; } = true; /// <summary> /// Gets or sets a value indicating whether this instance has configured password. /// </summary> /// <value><c>true</c> if this instance has configured password; otherwise, <c>false</c>.</value> - public bool HasConfiguredPassword { get; set; } + [Obsolete("This is always true")] + public bool? HasConfiguredPassword { get; set; } = true; /// <summary> /// Gets or sets a value indicating whether this instance has configured easy password. /// </summary> /// <value><c>true</c> if this instance has configured easy password; otherwise, <c>false</c>.</value> [Obsolete("Easy Password has been replaced with Quick Connect")] - public bool HasConfiguredEasyPassword { get; set; } + public bool? HasConfiguredEasyPassword { get; set; } = false; /// <summary> /// Gets or sets whether async login is enabled or not. diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index b1626e2c98..4491fb5ace 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; @@ -260,6 +258,8 @@ namespace MediaBrowser.Model.Entities public string LocalizedHearingImpaired { get; set; } + public string LocalizedLanguage { get; set; } + public string DisplayTitle { get @@ -273,29 +273,8 @@ namespace MediaBrowser.Model.Entities // 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)) { - // Get full language string i.e. eng -> English, zh-Hans -> Chinese (Simplified). - var cultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures); - CultureInfo match = null; - if (Language.Contains('-', StringComparison.OrdinalIgnoreCase)) - { - match = cultures.FirstOrDefault(r => - r.Name.Equals(Language, StringComparison.OrdinalIgnoreCase)); - - if (match is null) - { - string baseLang = Language.AsSpan().LeftPart('-').ToString(); - match = cultures.FirstOrDefault(r => - r.TwoLetterISOLanguageName.Equals(baseLang, StringComparison.OrdinalIgnoreCase)); - } - } - else - { - match = cultures.FirstOrDefault(r => - r.ThreeLetterISOLanguageName.Equals(Language, StringComparison.OrdinalIgnoreCase)); - } - - string fullLanguage = match?.DisplayName; - attributes.Add(StringHelper.FirstToUpper(fullLanguage ?? Language)); + // Use pre-resolved localized language name, falling back to raw language code. + attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language)); } if (!string.IsNullOrEmpty(Profile) && !string.Equals(Profile, "lc", StringComparison.OrdinalIgnoreCase)) @@ -393,29 +372,8 @@ namespace MediaBrowser.Model.Entities if (!string.IsNullOrEmpty(Language)) { - // Get full language string i.e. eng -> English, zh-Hans -> Chinese (Simplified). - var cultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures); - CultureInfo match = null; - if (Language.Contains('-', StringComparison.OrdinalIgnoreCase)) - { - match = cultures.FirstOrDefault(r => - r.Name.Equals(Language, StringComparison.OrdinalIgnoreCase)); - - if (match is null) - { - string baseLang = Language.AsSpan().LeftPart('-').ToString(); - match = cultures.FirstOrDefault(r => - r.TwoLetterISOLanguageName.Equals(baseLang, StringComparison.OrdinalIgnoreCase)); - } - } - else - { - match = cultures.FirstOrDefault(r => - r.ThreeLetterISOLanguageName.Equals(Language, StringComparison.OrdinalIgnoreCase)); - } - - string fullLanguage = match?.DisplayName; - attributes.Add(StringHelper.FirstToUpper(fullLanguage ?? Language)); + // Use pre-resolved localized language name, falling back to raw language code. + attributes.Add(StringHelper.FirstToUpper(LocalizedLanguage ?? Language)); } else { diff --git a/MediaBrowser.Model/Extensions/EnumerableExtensions.cs b/MediaBrowser.Model/Extensions/EnumerableExtensions.cs index 94f4252295..7c9ee18ca4 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,12 +38,12 @@ 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; } diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs index 77cbef00f5..58cde8620c 100644 --- a/MediaBrowser.Model/Extensions/StringHelper.cs +++ b/MediaBrowser.Model/Extensions/StringHelper.cs @@ -1,3 +1,5 @@ +using System; + namespace MediaBrowser.Model.Extensions { /// <summary> @@ -25,14 +27,11 @@ namespace MediaBrowser.Model.Extensions return string.Create( str.Length, - str, + str.AsSpan(), (chars, buf) => { chars[0] = char.ToUpperInvariant(buf[0]); - for (int i = 1; i < chars.Length; i++) - { - chars[i] = buf[i]; - } + buf.Slice(1).CopyTo(chars.Slice(1)); }); } } diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index ef025d02dc..c655c4ccb3 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -14,7 +14,7 @@ </PropertyGroup> <PropertyGroup> - <TargetFramework>net9.0</TargetFramework> + <TargetFramework>net10.0</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateDocumentationFile>true</GenerateDocumentationFile> <PublishRepositoryUrl>true</PublishRepositoryUrl> @@ -37,13 +37,10 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" /> <PackageReference Include="MimeTypes"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="System.Globalization" /> - <PackageReference Include="System.Text.Json" /> </ItemGroup> <ItemGroup> diff --git a/MediaBrowser.Model/Net/IPData.cs b/MediaBrowser.Model/Net/IPData.cs index c116d883ed..e016ffea10 100644 --- a/MediaBrowser.Model/Net/IPData.cs +++ b/MediaBrowser.Model/Net/IPData.cs @@ -1,6 +1,5 @@ using System.Net; using System.Net.Sockets; -using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork; namespace MediaBrowser.Model.Net; @@ -66,9 +65,9 @@ public class IPData { if (Address.Equals(IPAddress.None)) { - return Subnet.Prefix.AddressFamily.Equals(IPAddress.None) + return Subnet.BaseAddress.AddressFamily.Equals(IPAddress.None) ? AddressFamily.Unspecified - : Subnet.Prefix.AddressFamily; + : Subnet.BaseAddress.AddressFamily; } else { diff --git a/MediaBrowser.Model/Providers/RemoteSearchResult.cs b/MediaBrowser.Model/Providers/RemoteSearchResult.cs index a29e7ad1c5..7d3b5e4ab8 100644 --- a/MediaBrowser.Model/Providers/RemoteSearchResult.cs +++ b/MediaBrowser.Model/Providers/RemoteSearchResult.cs @@ -1,4 +1,3 @@ -#nullable disable #pragma warning disable CS1591 using System; @@ -19,7 +18,7 @@ namespace MediaBrowser.Model.Providers /// Gets or sets the name. /// </summary> /// <value>The name.</value> - public string Name { get; set; } + public string? Name { get; set; } /// <summary> /// Gets or sets the provider ids. @@ -41,13 +40,13 @@ namespace MediaBrowser.Model.Providers public DateTime? PremiereDate { get; set; } - public string ImageUrl { get; set; } + public string? ImageUrl { get; set; } - public string SearchProviderName { get; set; } + public string? SearchProviderName { get; set; } - public string Overview { get; set; } + public string? Overview { get; set; } - public RemoteSearchResult AlbumArtist { get; set; } + public RemoteSearchResult? AlbumArtist { get; set; } public RemoteSearchResult[] Artists { get; set; } } diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index fc1f24ae16..597845fc17 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using Jellyfin.Data.Enums; using MediaBrowser.Model.Dlna; @@ -31,15 +30,5 @@ namespace MediaBrowser.Model.Session public string AppStoreUrl { get; set; } public string IconUrl { get; set; } - - // TODO: Remove after 10.9 - [Obsolete("Unused")] - [DefaultValue(false)] - public bool? SupportsContentUploading { get; set; } = false; - - // TODO: Remove after 10.9 - [Obsolete("Unused")] - [DefaultValue(false)] - public bool? SupportsSync { get; set; } = false; } } 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; } |
