diff options
Diffstat (limited to 'MediaBrowser.Model')
| -rw-r--r-- | MediaBrowser.Model/Configuration/EncodingOptions.cs | 8 | ||||
| -rw-r--r-- | MediaBrowser.Model/Configuration/HlsAudioSeekStrategy.cs | 23 | ||||
| -rw-r--r-- | MediaBrowser.Model/Dlna/ConditionProcessor.cs | 2 | ||||
| -rw-r--r-- | MediaBrowser.Model/Dlna/StreamBuilder.cs | 5 | ||||
| -rw-r--r-- | MediaBrowser.Model/Dlna/StreamInfo.cs | 24 | ||||
| -rw-r--r-- | MediaBrowser.Model/Dlna/TranscodingProfile.cs | 4 | ||||
| -rw-r--r-- | MediaBrowser.Model/Dto/BaseItemDto.cs | 6 | ||||
| -rw-r--r-- | MediaBrowser.Model/Entities/MediaStream.cs | 54 | ||||
| -rw-r--r-- | MediaBrowser.Model/Extensions/EnumerableExtensions.cs | 8 | ||||
| -rw-r--r-- | MediaBrowser.Model/Net/MimeTypes.cs | 1 | ||||
| -rw-r--r-- | MediaBrowser.Model/Providers/RemoteSearchResult.cs | 11 | ||||
| -rw-r--r-- | MediaBrowser.Model/Providers/SubtitleOptions.cs | 36 | ||||
| -rw-r--r-- | MediaBrowser.Model/Session/ClientCapabilities.cs | 11 | ||||
| -rw-r--r-- | MediaBrowser.Model/System/FolderStorageInfo.cs | 11 |
14 files changed, 77 insertions, 127 deletions
diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index f7f386d289..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; @@ -60,6 +61,7 @@ public class EncodingOptions SubtitleExtractionTimeoutMinutes = 30; AllowOnDemandMetadataBasedKeyframeExtractionForExtensions = ["mkv"]; HardwareDecodingCodecs = ["h264", "vc1"]; + HlsAudioSeekStrategy = HlsAudioSeekStrategy.DisableAccurateSeek; } /// <summary> @@ -301,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/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 92404de508..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(); } @@ -1260,11 +1258,11 @@ public class StreamInfo stream.Index.ToString(CultureInfo.InvariantCulture), startPositionTicks.ToString(CultureInfo.InvariantCulture), subtitleProfile.Format); - info.IsExternalUrl = false; // Default to API URL + info.IsExternalUrl = false; // Check conditions for potentially using the direct path if (stream.IsExternal // Must be external - && MediaSource?.Protocol != MediaProtocol.File // Main media must not be a local file + && 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 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/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/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/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/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/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; } |
