diff options
Diffstat (limited to 'MediaBrowser.Controller')
| -rw-r--r-- | MediaBrowser.Controller/Channels/IChannelManager.cs | 4 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/BaseItem.cs | 30 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/PeopleHelper.cs | 23 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/PersonInfo.cs | 9 | ||||
| -rw-r--r-- | MediaBrowser.Controller/LiveTv/ILiveTvManager.cs | 4 | ||||
| -rw-r--r-- | MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 182 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Providers/ImageRefreshOptions.cs | 3 |
7 files changed, 171 insertions, 84 deletions
diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index e392a3493..8eb27888a 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -46,14 +46,14 @@ namespace MediaBrowser.Controller.Channels /// </summary> /// <param name="query">The query.</param> /// <returns>The channels.</returns> - QueryResult<Channel> GetChannelsInternal(ChannelQuery query); + Task<QueryResult<Channel>> GetChannelsInternalAsync(ChannelQuery query); /// <summary> /// Gets the channels. /// </summary> /// <param name="query">The query.</param> /// <returns>The channels.</returns> - QueryResult<BaseItemDto> GetChannels(ChannelQuery query); + Task<QueryResult<BaseItemDto>> GetChannelsAsync(ChannelQuery query); /// <summary> /// Gets the latest channel items. diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 8fe9cfa7f..a04f02bf9 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -893,16 +893,6 @@ namespace MediaBrowser.Controller.Entities var sortable = Name.Trim().ToLowerInvariant(); - foreach (var removeChar in ConfigurationManager.Configuration.SortRemoveCharacters) - { - sortable = sortable.Replace(removeChar, string.Empty, StringComparison.Ordinal); - } - - foreach (var replaceChar in ConfigurationManager.Configuration.SortReplaceCharacters) - { - sortable = sortable.Replace(replaceChar, " ", StringComparison.Ordinal); - } - foreach (var search in ConfigurationManager.Configuration.SortRemoveWords) { // Remove from beginning if a space follows @@ -921,12 +911,22 @@ namespace MediaBrowser.Controller.Entities } } + foreach (var removeChar in ConfigurationManager.Configuration.SortRemoveCharacters) + { + sortable = sortable.Replace(removeChar, string.Empty, StringComparison.Ordinal); + } + + foreach (var replaceChar in ConfigurationManager.Configuration.SortReplaceCharacters) + { + sortable = sortable.Replace(replaceChar, " ", StringComparison.Ordinal); + } + return ModifySortChunks(sortable); } - internal static string ModifySortChunks(string name) + internal static string ModifySortChunks(ReadOnlySpan<char> name) { - void AppendChunk(StringBuilder builder, bool isDigitChunk, ReadOnlySpan<char> chunk) + static void AppendChunk(StringBuilder builder, bool isDigitChunk, ReadOnlySpan<char> chunk) { if (isDigitChunk && chunk.Length < 10) { @@ -936,7 +936,7 @@ namespace MediaBrowser.Controller.Entities builder.Append(chunk); } - if (name.Length == 0) + if (name.IsEmpty) { return string.Empty; } @@ -950,13 +950,13 @@ namespace MediaBrowser.Controller.Entities var isDigit = char.IsDigit(name[i]); if (isDigit != isDigitChunk) { - AppendChunk(builder, isDigitChunk, name.AsSpan(chunkStart, i - chunkStart)); + AppendChunk(builder, isDigitChunk, name.Slice(chunkStart, i - chunkStart)); chunkStart = i; isDigitChunk = isDigit; } } - AppendChunk(builder, isDigitChunk, name.AsSpan(chunkStart)); + AppendChunk(builder, isDigitChunk, name.Slice(chunkStart)); // logger.LogDebug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString()); return builder.ToString().RemoveDiacritics(); diff --git a/MediaBrowser.Controller/Entities/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs index 7f8dc069c..5292bd772 100644 --- a/MediaBrowser.Controller/Entities/PeopleHelper.cs +++ b/MediaBrowser.Controller/Entities/PeopleHelper.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Jellyfin.Data.Enums; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities @@ -17,38 +18,38 @@ namespace MediaBrowser.Controller.Entities // Normalize if (string.Equals(person.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) { - person.Type = PersonType.GuestStar; + person.Type = PersonKind.GuestStar; } else if (string.Equals(person.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase)) { - person.Type = PersonType.Director; + person.Type = PersonKind.Director; } else if (string.Equals(person.Role, PersonType.Producer, StringComparison.OrdinalIgnoreCase)) { - person.Type = PersonType.Producer; + person.Type = PersonKind.Producer; } else if (string.Equals(person.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase)) { - person.Type = PersonType.Writer; + person.Type = PersonKind.Writer; } // If the type is GuestStar and there's already an Actor entry, then update it to avoid dupes - if (string.Equals(person.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) + if (person.Type == PersonKind.GuestStar) { - var existing = people.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && p.Type.Equals(PersonType.Actor, StringComparison.OrdinalIgnoreCase)); + var existing = people.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && p.Type == PersonKind.Actor); if (existing is not null) { - existing.Type = PersonType.GuestStar; + existing.Type = PersonKind.GuestStar; MergeExisting(existing, person); return; } } - if (string.Equals(person.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase)) + if (person.Type == PersonKind.Actor) { // If the actor already exists without a role and we have one, fill it in - var existing = people.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && (p.Type.Equals(PersonType.Actor, StringComparison.OrdinalIgnoreCase) || p.Type.Equals(PersonType.GuestStar, StringComparison.OrdinalIgnoreCase))); + var existing = people.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && (p.Type == PersonKind.Actor || p.Type == PersonKind.GuestStar)); if (existing is null) { // Wasn't there - add it @@ -68,8 +69,8 @@ namespace MediaBrowser.Controller.Entities else { var existing = people.FirstOrDefault(p => - string.Equals(p.Name, person.Name, StringComparison.OrdinalIgnoreCase) && - string.Equals(p.Type, person.Type, StringComparison.OrdinalIgnoreCase)); + string.Equals(p.Name, person.Name, StringComparison.OrdinalIgnoreCase) + && p.Type == person.Type); // Check for dupes based on the combination of Name and Type if (existing is null) diff --git a/MediaBrowser.Controller/Entities/PersonInfo.cs b/MediaBrowser.Controller/Entities/PersonInfo.cs index 2b689ae7e..3df0b0b78 100644 --- a/MediaBrowser.Controller/Entities/PersonInfo.cs +++ b/MediaBrowser.Controller/Entities/PersonInfo.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Jellyfin.Data.Enums; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities @@ -36,7 +37,7 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the type. /// </summary> /// <value>The type.</value> - public string Type { get; set; } + public PersonKind Type { get; set; } /// <summary> /// Gets or sets the ascending sort order. @@ -57,10 +58,6 @@ namespace MediaBrowser.Controller.Entities return Name; } - public bool IsType(string type) - { - return string.Equals(Type, type, StringComparison.OrdinalIgnoreCase) - || string.Equals(Role, type, StringComparison.OrdinalIgnoreCase); - } + public bool IsType(PersonKind type) => Type == type || string.Equals(type.ToString(), Role, StringComparison.OrdinalIgnoreCase); } } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 46bdca302..3b6a16dee 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -97,7 +97,7 @@ namespace MediaBrowser.Controller.LiveTv /// <param name="query">The query.</param> /// <param name="options">The options.</param> /// <returns>A recording.</returns> - QueryResult<BaseItemDto> GetRecordings(RecordingQuery query, DtoOptions options); + Task<QueryResult<BaseItemDto>> GetRecordingsAsync(RecordingQuery query, DtoOptions options); /// <summary> /// Gets the timers. @@ -308,6 +308,6 @@ namespace MediaBrowser.Controller.LiveTv void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, User user = null); - List<BaseItem> GetRecordingFolders(User user); + Task<BaseItem[]> GetRecordingFoldersAsync(User user); } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 3e338e871..f7ee3c173 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -89,6 +89,16 @@ namespace MediaBrowser.Controller.MediaEncoding { "truehd", 6 }, }; + public static readonly string[] LosslessAudioCodecs = new string[] + { + "alac", + "ape", + "flac", + "mlp", + "truehd", + "wavpack" + }; + public EncodingHelper( IApplicationPaths appPaths, IMediaEncoder mediaEncoder, @@ -620,6 +630,11 @@ namespace MediaBrowser.Controller.MediaEncoding return "flac"; } + if (string.Equals(codec, "dts", StringComparison.OrdinalIgnoreCase)) + { + return "dca"; + } + return codec.ToLowerInvariant(); } @@ -2050,9 +2065,9 @@ namespace MediaBrowser.Controller.MediaEncoding } } - // Video bitrate must fall within requested value + // Audio bitrate must fall within requested value if (request.AudioBitRate.HasValue - && audioStream.BitDepth.HasValue + && audioStream.BitRate.HasValue && audioStream.BitRate.Value > request.AudioBitRate.Value) { return false; @@ -2116,14 +2131,20 @@ namespace MediaBrowser.Controller.MediaEncoding private static double GetVideoBitrateScaleFactor(string codec) { + // hevc & vp9 - 40% more efficient than h.264 if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase) - || string.Equals(codec, "av1", StringComparison.OrdinalIgnoreCase)) + || string.Equals(codec, "vp9", StringComparison.OrdinalIgnoreCase)) { return .6; } + // av1 - 50% more efficient than h.264 + if (string.Equals(codec, "av1", StringComparison.OrdinalIgnoreCase)) + { + return .5; + } + return 1; } @@ -2131,7 +2152,9 @@ namespace MediaBrowser.Controller.MediaEncoding { var inputScaleFactor = GetVideoBitrateScaleFactor(inputVideoCodec); var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec); - var scaleFactor = outputScaleFactor / inputScaleFactor; + + // Don't scale the real bitrate lower than the requested bitrate + var scaleFactor = Math.Min(outputScaleFactor / inputScaleFactor, 1); if (bitrate <= 500000) { @@ -2153,56 +2176,96 @@ namespace MediaBrowser.Controller.MediaEncoding return Convert.ToInt32(scaleFactor * bitrate); } - public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream) + public int? GetAudioBitrateParam(BaseEncodingJobOptions request, MediaStream audioStream, int? outputAudioChannels) { - return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream); + return GetAudioBitrateParam(request.AudioBitRate, request.AudioCodec, audioStream, outputAudioChannels); } - public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream) + public int? GetAudioBitrateParam(int? audioBitRate, string audioCodec, MediaStream audioStream, int? outputAudioChannels) { if (audioStream is null) { return null; } - if (audioBitRate.HasValue && string.IsNullOrEmpty(audioCodec)) + var inputChannels = audioStream.Channels ?? 0; + var outputChannels = outputAudioChannels ?? 0; + var bitrate = audioBitRate ?? int.MaxValue; + + if (string.IsNullOrEmpty(audioCodec) + || string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "opus", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "vorbis", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase)) { - return Math.Min(384000, audioBitRate.Value); + return (inputChannels, outputChannels) switch + { + (>= 6, >= 6 or 0) => Math.Min(640000, bitrate), + (> 0, > 0) => Math.Min(outputChannels * 128000, bitrate), + (> 0, _) => Math.Min(inputChannels * 128000, bitrate), + (_, _) => Math.Min(384000, bitrate) + }; } - if (audioBitRate.HasValue && !string.IsNullOrEmpty(audioCodec)) + if (string.Equals(audioCodec, "dts", StringComparison.OrdinalIgnoreCase) + || string.Equals(audioCodec, "dca", StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(audioCodec, "aac", StringComparison.OrdinalIgnoreCase) - || string.Equals(audioCodec, "mp3", StringComparison.OrdinalIgnoreCase) - || string.Equals(audioCodec, "opus", StringComparison.OrdinalIgnoreCase) - || string.Equals(audioCodec, "vorbis", StringComparison.OrdinalIgnoreCase) - || string.Equals(audioCodec, "ac3", StringComparison.OrdinalIgnoreCase) - || string.Equals(audioCodec, "eac3", StringComparison.OrdinalIgnoreCase)) + return (inputChannels, outputChannels) switch { - if ((audioStream.Channels ?? 0) >= 6) - { - return Math.Min(640000, audioBitRate.Value); - } + (>= 6, >= 6 or 0) => Math.Min(768000, bitrate), + (> 0, > 0) => Math.Min(outputChannels * 136000, bitrate), + (> 0, _) => Math.Min(inputChannels * 136000, bitrate), + (_, _) => Math.Min(672000, bitrate) + }; + } - return Math.Min(384000, audioBitRate.Value); - } + // Empty bitrate area is not allow on iOS + // Default audio bitrate to 128K per channel if we don't have codec specific defaults + // https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options + return 128000 * (outputAudioChannels ?? audioStream.Channels ?? 2); + } - if (string.Equals(audioCodec, "flac", StringComparison.OrdinalIgnoreCase) - || string.Equals(audioCodec, "alac", StringComparison.OrdinalIgnoreCase)) + public string GetAudioVbrModeParam(string encoder, int bitratePerChannel) + { + if (string.Equals(encoder, "libfdk_aac", StringComparison.OrdinalIgnoreCase)) + { + return " -vbr:a " + bitratePerChannel switch { - if ((audioStream.Channels ?? 0) >= 6) - { - return Math.Min(3584000, audioBitRate.Value); - } + < 32000 => "1", + < 48000 => "2", + < 64000 => "3", + < 96000 => "4", + _ => "5" + }; + } - return Math.Min(1536000, audioBitRate.Value); - } + if (string.Equals(encoder, "libmp3lame", StringComparison.OrdinalIgnoreCase)) + { + return " -qscale:a " + bitratePerChannel switch + { + < 48000 => "8", + < 64000 => "6", + < 88000 => "4", + < 112000 => "2", + _ => "0" + }; } - // Empty bitrate area is not allow on iOS - // Default audio bitrate to 128K if it is not being requested - // https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options - return 128000; + if (string.Equals(encoder, "libvorbis", StringComparison.OrdinalIgnoreCase)) + { + return " -qscale:a " + bitratePerChannel switch + { + < 40000 => "0", + < 56000 => "2", + < 80000 => "4", + < 112000 => "6", + _ => "8" + }; + } + + return null; } public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions) @@ -5662,14 +5725,22 @@ namespace MediaBrowser.Controller.MediaEncoding } var inputChannels = audioStream is null ? 6 : audioStream.Channels ?? 6; + var shiftAudioCodecs = new List<string>(); if (inputChannels >= 6) { - return; + // DTS and TrueHD are not supported by HLS + // Keep them in the supported codecs list, but shift them to the end of the list so that if transcoding happens, another codec is used + shiftAudioCodecs.Add("dca"); + shiftAudioCodecs.Add("truehd"); + } + else + { + // Transcoding to 2ch ac3 or eac3 almost always causes a playback failure + // Keep them in the supported codecs list, but shift them to the end of the list so that if transcoding happens, another codec is used + shiftAudioCodecs.Add("ac3"); + shiftAudioCodecs.Add("eac3"); } - // Transcoding to 2ch ac3 almost always causes a playback failure - // Keep it in the supported codecs list, but shift it to the end of the list so that if transcoding happens, another codec is used - var shiftAudioCodecs = new[] { "ac3", "eac3" }; if (audioCodecs.All(i => shiftAudioCodecs.Contains(i, StringComparison.OrdinalIgnoreCase))) { return; @@ -5911,10 +5982,17 @@ namespace MediaBrowser.Controller.MediaEncoding } var bitrate = state.OutputAudioBitrate; - - if (bitrate.HasValue) + if (bitrate.HasValue && !LosslessAudioCodecs.Contains(codec, StringComparison.OrdinalIgnoreCase)) { - args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture); + var vbrParam = GetAudioVbrModeParam(codec, bitrate.Value / (channels ?? 2)); + if (encodingOptions.EnableAudioVbr && vbrParam is not null) + { + args += vbrParam; + } + else + { + args += " -ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture); + } } if (state.OutputAudioSampleRate.HasValue) @@ -5932,23 +6010,33 @@ namespace MediaBrowser.Controller.MediaEncoding var audioTranscodeParams = new List<string>(); var bitrate = state.OutputAudioBitrate; + var channels = state.OutputAudioChannels; + var outputCodec = state.OutputAudioCodec; - if (bitrate.HasValue) + if (bitrate.HasValue && !LosslessAudioCodecs.Contains(outputCodec, StringComparison.OrdinalIgnoreCase)) { - audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture)); + var vbrParam = GetAudioVbrModeParam(GetAudioEncoder(state), bitrate.Value / (channels ?? 2)); + if (encodingOptions.EnableAudioVbr && vbrParam is not null) + { + audioTranscodeParams.Add(vbrParam); + } + else + { + audioTranscodeParams.Add("-ab " + bitrate.Value.ToString(CultureInfo.InvariantCulture)); + } } - if (state.OutputAudioChannels.HasValue) + if (channels.HasValue) { audioTranscodeParams.Add("-ac " + state.OutputAudioChannels.Value.ToString(CultureInfo.InvariantCulture)); } - if (!string.IsNullOrEmpty(state.OutputAudioCodec)) + if (!string.IsNullOrEmpty(outputCodec)) { audioTranscodeParams.Add("-acodec " + GetAudioEncoder(state)); } - if (!string.Equals(state.OutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(outputCodec, "opus", StringComparison.OrdinalIgnoreCase)) { // opus only supports specific sampling rates var sampleRate = state.OutputAudioSampleRate; diff --git a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs index fd73ed5f8..05b4d43a5 100644 --- a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs +++ b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs @@ -1,6 +1,7 @@ #pragma warning disable CA1819, CS1591 using System; +using System.Collections.Generic; using System.Linq; using MediaBrowser.Model.Entities; @@ -23,7 +24,7 @@ namespace MediaBrowser.Controller.Providers public bool ReplaceAllImages { get; set; } - public ImageType[] ReplaceImages { get; set; } + public IReadOnlyList<ImageType> ReplaceImages { get; set; } public bool IsAutomated { get; set; } |
