aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Model
diff options
context:
space:
mode:
authorMBR-0001 <55142207+MBR-0001@users.noreply.github.com>2026-04-18 17:14:31 +0200
committerGitHub <noreply@github.com>2026-04-18 17:14:31 +0200
commita2eff41d6617bed7a7c5518d00f227a2406a8421 (patch)
tree933386669537d5145ca6b3706728454aff27d703 /MediaBrowser.Model
parentd4a46bc6291560b35a9b64fb88b68019f903d0ef (diff)
parent5aa093d2997d8120ce19037642556070d27ce0bb (diff)
Merge branch 'master' into removeGlobalSubtitleConfiguration
Diffstat (limited to 'MediaBrowser.Model')
-rw-r--r--MediaBrowser.Model/Activity/IActivityManager.cs43
-rw-r--r--MediaBrowser.Model/Configuration/EncodingOptions.cs14
-rw-r--r--MediaBrowser.Model/Configuration/HlsAudioSeekStrategy.cs23
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs2
-rw-r--r--MediaBrowser.Model/Dlna/ConditionProcessor.cs2
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs5
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs67
-rw-r--r--MediaBrowser.Model/Dlna/TranscodingProfile.cs4
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs6
-rw-r--r--MediaBrowser.Model/Dto/UserDto.cs9
-rw-r--r--MediaBrowser.Model/Entities/MediaStream.cs54
-rw-r--r--MediaBrowser.Model/Extensions/EnumerableExtensions.cs8
-rw-r--r--MediaBrowser.Model/Extensions/StringHelper.cs9
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj5
-rw-r--r--MediaBrowser.Model/Net/IPData.cs5
-rw-r--r--MediaBrowser.Model/Providers/RemoteSearchResult.cs11
-rw-r--r--MediaBrowser.Model/Session/ClientCapabilities.cs11
-rw-r--r--MediaBrowser.Model/System/FolderStorageInfo.cs11
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; }