aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller
diff options
context:
space:
mode:
authorShadowghost <Ghost_of_Stone@web.de>2023-05-09 15:25:41 +0200
committerShadowghost <Ghost_of_Stone@web.de>2023-05-09 15:25:41 +0200
commit6cc1203c1b423ee2765be7e33aad56be374c8314 (patch)
tree57ce21874de41124f2626c5f06328bcb2e9734d5 /MediaBrowser.Controller
parent520c07e8cad3e4372f6a5214160d1600106a7bfd (diff)
parent92a0d26f31743ca0015fcc3e0a4fe094792ac63c (diff)
Merge branch 'master' into network-rewrite
Diffstat (limited to 'MediaBrowser.Controller')
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs44
-rw-r--r--MediaBrowser.Controller/Entities/IHasShares.cs11
-rw-r--r--MediaBrowser.Controller/Entities/PeopleHelper.cs23
-rw-r--r--MediaBrowser.Controller/Entities/PersonInfo.cs9
-rw-r--r--MediaBrowser.Controller/Entities/Share.cs13
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvProgram.cs6
-rw-r--r--MediaBrowser.Controller/Lyrics/LyricMetadata.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs373
-rw-r--r--MediaBrowser.Controller/Playlists/IPlaylistManager.cs15
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs7
-rw-r--r--MediaBrowser.Controller/Providers/ImageRefreshOptions.cs3
-rw-r--r--MediaBrowser.Controller/Session/ISessionManager.cs1
-rw-r--r--MediaBrowser.Controller/Subtitles/ISubtitleManager.cs1
-rw-r--r--MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs8
-rw-r--r--MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs29
15 files changed, 299 insertions, 246 deletions
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 8fe9cfa7f9..adc7b2f953 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -801,16 +801,14 @@ namespace MediaBrowser.Controller.Entities
{
return allowed.Contains(ChannelId);
}
- else
- {
- var collectionFolders = LibraryManager.GetCollectionFolders(this, allCollectionFolders);
- foreach (var folder in collectionFolders)
+ var collectionFolders = LibraryManager.GetCollectionFolders(this, allCollectionFolders);
+
+ foreach (var folder in collectionFolders)
+ {
+ if (allowed.Contains(folder.Id))
{
- if (allowed.Contains(folder.Id))
- {
- return true;
- }
+ return true;
}
}
@@ -893,16 +891,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 +909,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 +934,7 @@ namespace MediaBrowser.Controller.Entities
builder.Append(chunk);
}
- if (name.Length == 0)
+ if (name.IsEmpty)
{
return string.Empty;
}
@@ -950,13 +948,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/IHasShares.cs b/MediaBrowser.Controller/Entities/IHasShares.cs
deleted file mode 100644
index e6fa27703b..0000000000
--- a/MediaBrowser.Controller/Entities/IHasShares.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-#nullable disable
-
-#pragma warning disable CA1819, CS1591
-
-namespace MediaBrowser.Controller.Entities
-{
- public interface IHasShares
- {
- Share[] Shares { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/Entities/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs
index 7f8dc069cf..5292bd7727 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 2b689ae7e2..3df0b0b785 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/Entities/Share.cs b/MediaBrowser.Controller/Entities/Share.cs
deleted file mode 100644
index 64f446eef2..0000000000
--- a/MediaBrowser.Controller/Entities/Share.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-namespace MediaBrowser.Controller.Entities
-{
- public class Share
- {
- public string UserId { get; set; }
-
- public bool CanEdit { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index 514323238e..c721fb7785 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -197,10 +197,8 @@ namespace MediaBrowser.Controller.LiveTv
{
return 2.0 / 3;
}
- else
- {
- return 16.0 / 9;
- }
+
+ return 16.0 / 9;
}
public override string GetClientTypeName()
diff --git a/MediaBrowser.Controller/Lyrics/LyricMetadata.cs b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs
index 6091ede52a..c4f0334892 100644
--- a/MediaBrowser.Controller/Lyrics/LyricMetadata.cs
+++ b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs
@@ -1,5 +1,3 @@
-using System;
-
namespace MediaBrowser.Controller.Lyrics;
/// <summary>
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 5de57917e7..920925fc68 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,
@@ -128,14 +138,10 @@ namespace MediaBrowser.Controller.MediaEncoding
if (!string.IsNullOrEmpty(hwType)
&& encodingOptions.EnableHardwareEncoding
- && codecMap.ContainsKey(hwType))
+ && codecMap.TryGetValue(hwType, out var preferredEncoder)
+ && _mediaEncoder.SupportsEncoder(preferredEncoder))
{
- var preferredEncoder = codecMap[hwType];
-
- if (_mediaEncoder.SupportsEncoder(preferredEncoder))
- {
- return preferredEncoder;
- }
+ return preferredEncoder;
}
}
@@ -551,7 +557,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return Array.FindIndex(_videoProfilesH264, x => string.Equals(x, profile, StringComparison.OrdinalIgnoreCase));
}
- else if (string.Equals("hevc", videoCodec, StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals("hevc", videoCodec, StringComparison.OrdinalIgnoreCase))
{
return Array.FindIndex(_videoProfilesH265, x => string.Equals(x, profile, StringComparison.OrdinalIgnoreCase));
}
@@ -620,6 +627,11 @@ namespace MediaBrowser.Controller.MediaEncoding
return "flac";
}
+ if (string.Equals(codec, "dts", StringComparison.OrdinalIgnoreCase))
+ {
+ return "dca";
+ }
+
return codec.ToLowerInvariant();
}
@@ -827,39 +839,38 @@ namespace MediaBrowser.Controller.MediaEncoding
}
var filterDevArgs = GetFilterHwDeviceArgs(VaapiAlias);
+ var doOclTonemap = isHwTonemapAvailable && IsOpenclFullSupported();
- if (isHwTonemapAvailable && IsOpenclFullSupported())
+ if (_mediaEncoder.IsVaapiDeviceInteliHD || _mediaEncoder.IsVaapiDeviceInteli965)
{
- if (_mediaEncoder.IsVaapiDeviceInteliHD || _mediaEncoder.IsVaapiDeviceInteli965)
+ if (doOclTonemap && !isVaapiDecoder)
{
- if (!isVaapiDecoder)
- {
- args.Append(GetOpenclDeviceArgs(0, null, VaapiAlias, OpenclAlias));
- filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
- }
+ args.Append(GetOpenclDeviceArgs(0, null, VaapiAlias, OpenclAlias));
+ filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
}
- else if (_mediaEncoder.IsVaapiDeviceAmd)
+ }
+ else if (_mediaEncoder.IsVaapiDeviceAmd)
+ {
+ if (IsVulkanFullSupported()
+ && _mediaEncoder.IsVaapiDeviceSupportVulkanFmtModifier
+ && Environment.OSVersion.Version >= _minKernelVersionAmdVkFmtModifier)
{
- if (!IsVulkanFullSupported()
- || !_mediaEncoder.IsVaapiDeviceSupportVulkanFmtModifier
- || Environment.OSVersion.Version < _minKernelVersionAmdVkFmtModifier)
- {
- args.Append(GetOpenclDeviceArgs(0, "Advanced Micro Devices", null, OpenclAlias));
- filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
- }
- else
- {
- // libplacebo wants an explicitly set vulkan filter device.
- args.Append(GetVulkanDeviceArgs(0, null, VaapiAlias, VulkanAlias));
- filterDevArgs = GetFilterHwDeviceArgs(VulkanAlias);
- }
+ // libplacebo wants an explicitly set vulkan filter device.
+ args.Append(GetVulkanDeviceArgs(0, null, VaapiAlias, VulkanAlias));
+ filterDevArgs = GetFilterHwDeviceArgs(VulkanAlias);
}
- else
+ else if (doOclTonemap)
{
- args.Append(GetOpenclDeviceArgs(0, null, null, OpenclAlias));
+ // ROCm/ROCr OpenCL runtime
+ args.Append(GetOpenclDeviceArgs(0, "Advanced Micro Devices", null, OpenclAlias));
filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
}
}
+ else if (doOclTonemap)
+ {
+ args.Append(GetOpenclDeviceArgs(0, null, null, OpenclAlias));
+ filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
+ }
args.Append(filterDevArgs);
}
@@ -1099,19 +1110,19 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return "-bsf:v h264_mp4toannexb";
}
- else if (IsH265(stream))
+
+ if (IsH265(stream))
{
return "-bsf:v hevc_mp4toannexb";
}
- else if (IsAAC(stream))
+
+ if (IsAAC(stream))
{
// Convert adts header(mpegts) to asc header(mp4).
return "-bsf:a aac_adtstoasc";
}
- else
- {
- return null;
- }
+
+ return null;
}
public static string GetAudioBitStreamArguments(EncodingJobInfo state, string segmentContainer, string mediaSourceContainer)
@@ -1189,10 +1200,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return FormattableString.Invariant($" -rc_mode CBR -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
}
- else
- {
- return FormattableString.Invariant($" -rc_mode VBR -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
- }
+
+ return FormattableString.Invariant($" -rc_mode VBR -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
}
return FormattableString.Invariant($" -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}");
@@ -1406,7 +1415,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var param = string.Empty;
// Tutorials: Enable Intel GuC / HuC firmware loading for Low Power Encoding.
- // https://01.org/linuxgraphics/downloads/firmware
+ // https://01.org/group/43/downloads/firmware
// https://wiki.archlinux.org/title/intel_graphics#Enable_GuC_/_HuC_firmware_loading
// Intel Low Power Encoding can save unnecessary CPU-GPU synchronization,
// which will reduce overhead in performance intensive tasks such as 4k transcoding and tonemapping.
@@ -2050,9 +2059,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;
@@ -2139,7 +2148,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec);
// Don't scale the real bitrate lower than the requested bitrate
- var scaleFactor = Math.Min(outputScaleFactor / inputScaleFactor, 1);
+ var scaleFactor = Math.Max(outputScaleFactor / inputScaleFactor, 1);
if (bitrate <= 500000)
{
@@ -2161,56 +2170,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)
@@ -2564,8 +2613,8 @@ namespace MediaBrowser.Controller.MediaEncoding
if (outputWidth > maximumWidth || outputHeight > maximumHeight)
{
- var scaleW = (double)maximumWidth / (double)outputWidth;
- var scaleH = (double)maximumHeight / (double)outputHeight;
+ var scaleW = (double)maximumWidth / outputWidth;
+ var scaleH = (double)maximumHeight / outputHeight;
var scale = Math.Min(scaleW, scaleH);
outputWidth = Math.Min(maximumWidth, (int)(outputWidth * scale));
outputHeight = Math.Min(maximumHeight, (int)(outputHeight * scale));
@@ -2712,79 +2761,76 @@ namespace MediaBrowser.Controller.MediaEncoding
widthParam,
heightParam);
}
- else
- {
- return GetFixedSwScaleFilter(threedFormat, requestedWidth.Value, requestedHeight.Value);
- }
+
+ return GetFixedSwScaleFilter(threedFormat, requestedWidth.Value, requestedHeight.Value);
}
// If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size
- else if (requestedMaxWidth.HasValue && requestedMaxHeight.HasValue)
+
+ if (requestedMaxWidth.HasValue && requestedMaxHeight.HasValue)
{
var maxWidthParam = requestedMaxWidth.Value.ToString(CultureInfo.InvariantCulture);
var maxHeightParam = requestedMaxHeight.Value.ToString(CultureInfo.InvariantCulture);
return string.Format(
- CultureInfo.InvariantCulture,
- "scale=trunc(min(max(iw\\,ih*a)\\,min({0}\\,{1}*a))/{2})*{2}:trunc(min(max(iw/a\\,ih)\\,min({0}/a\\,{1}))/2)*2",
- maxWidthParam,
- maxHeightParam,
- scaleVal);
+ CultureInfo.InvariantCulture,
+ "scale=trunc(min(max(iw\\,ih*a)\\,min({0}\\,{1}*a))/{2})*{2}:trunc(min(max(iw/a\\,ih)\\,min({0}/a\\,{1}))/2)*2",
+ maxWidthParam,
+ maxHeightParam,
+ scaleVal);
}
// If a fixed width was requested
- else if (requestedWidth.HasValue)
+ if (requestedWidth.HasValue)
{
if (threedFormat.HasValue)
{
// This method can handle 0 being passed in for the requested height
return GetFixedSwScaleFilter(threedFormat, requestedWidth.Value, 0);
}
- else
- {
- var widthParam = requestedWidth.Value.ToString(CultureInfo.InvariantCulture);
- return string.Format(
- CultureInfo.InvariantCulture,
- "scale={0}:trunc(ow/a/2)*2",
- widthParam);
- }
+ var widthParam = requestedWidth.Value.ToString(CultureInfo.InvariantCulture);
+
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "scale={0}:trunc(ow/a/2)*2",
+ widthParam);
}
// If a fixed height was requested
- else if (requestedHeight.HasValue)
+ if (requestedHeight.HasValue)
{
var heightParam = requestedHeight.Value.ToString(CultureInfo.InvariantCulture);
return string.Format(
- CultureInfo.InvariantCulture,
- "scale=trunc(oh*a/{1})*{1}:{0}",
- heightParam,
- scaleVal);
+ CultureInfo.InvariantCulture,
+ "scale=trunc(oh*a/{1})*{1}:{0}",
+ heightParam,
+ scaleVal);
}
// If a max width was requested
- else if (requestedMaxWidth.HasValue)
+ if (requestedMaxWidth.HasValue)
{
var maxWidthParam = requestedMaxWidth.Value.ToString(CultureInfo.InvariantCulture);
return string.Format(
- CultureInfo.InvariantCulture,
- "scale=trunc(min(max(iw\\,ih*a)\\,{0})/{1})*{1}:trunc(ow/a/2)*2",
- maxWidthParam,
- scaleVal);
+ CultureInfo.InvariantCulture,
+ "scale=trunc(min(max(iw\\,ih*a)\\,{0})/{1})*{1}:trunc(ow/a/2)*2",
+ maxWidthParam,
+ scaleVal);
}
// If a max height was requested
- else if (requestedMaxHeight.HasValue)
+ if (requestedMaxHeight.HasValue)
{
var maxHeightParam = requestedMaxHeight.Value.ToString(CultureInfo.InvariantCulture);
return string.Format(
- CultureInfo.InvariantCulture,
- "scale=trunc(oh*a/{1})*{1}:min(max(iw/a\\,ih)\\,{0})",
- maxHeightParam,
- scaleVal);
+ CultureInfo.InvariantCulture,
+ "scale=trunc(oh*a/{1})*{1}:min(max(iw/a\\,ih)\\,{0})",
+ maxHeightParam,
+ scaleVal);
}
return string.Empty;
@@ -2858,18 +2904,21 @@ namespace MediaBrowser.Controller.MediaEncoding
"yadif_cuda={0}:-1:0",
doubleRateDeint ? "1" : "0");
}
- else if (hwDeintSuffix.Contains("vaapi", StringComparison.OrdinalIgnoreCase))
+
+ if (hwDeintSuffix.Contains("vaapi", StringComparison.OrdinalIgnoreCase))
{
return string.Format(
CultureInfo.InvariantCulture,
"deinterlace_vaapi=rate={0}",
doubleRateDeint ? "field" : "frame");
}
- else if (hwDeintSuffix.Contains("qsv", StringComparison.OrdinalIgnoreCase))
+
+ if (hwDeintSuffix.Contains("qsv", StringComparison.OrdinalIgnoreCase))
{
return "deinterlace_qsv=mode=2";
}
- else if (hwDeintSuffix.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase))
+
+ if (hwDeintSuffix.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase))
{
return string.Format(
CultureInfo.InvariantCulture,
@@ -2900,7 +2949,8 @@ namespace MediaBrowser.Controller.MediaEncoding
options.VppTonemappingBrightness,
options.VppTonemappingContrast);
}
- else if (string.Equals(hwTonemapSuffix, "vulkan", StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(hwTonemapSuffix, "vulkan", StringComparison.OrdinalIgnoreCase))
{
args = "libplacebo=format={1}:tonemapping={2}:color_primaries=bt709:color_trc=bt709:colorspace=bt709:peak_detect=0:upscaler=none:downscaler=none";
@@ -4214,7 +4264,8 @@ namespace MediaBrowser.Controller.MediaEncoding
// sw => hw
if (doVkTonemap)
{
- mainFilters.Add("hwupload_vaapi");
+ mainFilters.Add("hwupload=derive_device=vaapi");
+ mainFilters.Add("format=vaapi");
mainFilters.Add("hwmap=derive_device=vulkan");
mainFilters.Add("format=vulkan");
}
@@ -4325,12 +4376,15 @@ namespace MediaBrowser.Controller.MediaEncoding
// prefer vaapi hwupload to vulkan hwupload,
// Mesa RADV does not support a dedicated transfer queue.
- subFilters.Add("hwupload_vaapi");
+ subFilters.Add("hwupload=derive_device=vaapi");
+ subFilters.Add("format=vaapi");
subFilters.Add("hwmap=derive_device=vulkan");
subFilters.Add("format=vulkan");
overlayFilters.Add("overlay_vulkan=eof_action=endall:shortest=1:repeatlast=0");
- overlayFilters.Add("scale_vulkan=format=nv12");
+
+ // TODO: figure out why libplacebo can sync without vaSyncSurface VPP support in radeonsi.
+ overlayFilters.Add("libplacebo=format=nv12:apply_filmgrain=0:apply_dolbyvision=0:upscaler=none:downscaler=none:dithering=none");
// OUTPUT vaapi(nv12/bgra) surface(vram)
// reverse-mapping via vaapi-vulkan interop.
@@ -4772,26 +4826,27 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return videoStream.BitDepth.Value;
}
- else if (string.Equals(videoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase)
- || string.Equals(videoStream.PixelFormat, "yuvj420p", StringComparison.OrdinalIgnoreCase)
- || string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(videoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuvj420p", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase))
{
return 8;
}
- else if (string.Equals(videoStream.PixelFormat, "yuv420p10le", StringComparison.OrdinalIgnoreCase)
- || string.Equals(videoStream.PixelFormat, "yuv444p10le", StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(videoStream.PixelFormat, "yuv420p10le", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv444p10le", StringComparison.OrdinalIgnoreCase))
{
return 10;
}
- else if (string.Equals(videoStream.PixelFormat, "yuv420p12le", StringComparison.OrdinalIgnoreCase)
- || string.Equals(videoStream.PixelFormat, "yuv444p12le", StringComparison.OrdinalIgnoreCase))
+
+ if (string.Equals(videoStream.PixelFormat, "yuv420p12le", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(videoStream.PixelFormat, "yuv444p12le", StringComparison.OrdinalIgnoreCase))
{
return 12;
}
- else
- {
- return 8;
- }
+
+ return 8;
}
return 0;
@@ -5023,11 +5078,9 @@ namespace MediaBrowser.Controller.MediaEncoding
return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty)
+ (nvdecNoInternalCopy ? " -hwaccel_flags +unsafe_output" : string.Empty) + " -threads 1" + (isAv1 ? " -c:v av1" : string.Empty);
}
- else
- {
- // cuvid decoder doesn't have threading issue.
- return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty);
- }
+
+ // cuvid decoder doesn't have threading issue.
+ return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty);
}
}
@@ -5385,7 +5438,8 @@ namespace MediaBrowser.Controller.MediaEncoding
// Automatically set thread count
return mustSetThreadCount ? Math.Max(Environment.ProcessorCount - 1, 1) : 0;
}
- else if (threads >= Environment.ProcessorCount)
+
+ if (threads >= Environment.ProcessorCount)
{
return Environment.ProcessorCount;
}
@@ -5670,14 +5724,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;
@@ -5919,10 +5981,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)
@@ -5940,23 +6009,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/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
index f6c5920709..d889436629 100644
--- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
+++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
@@ -56,5 +56,20 @@ namespace MediaBrowser.Controller.Playlists
/// <param name="newIndex">The new index.</param>
/// <returns>Task.</returns>
Task MoveItemAsync(string playlistId, string entryId, int newIndex);
+
+ /// <summary>
+ /// Removed all playlists of a user.
+ /// If the playlist is shared, ownership is transferred.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <returns>Task.</returns>
+ Task RemovePlaylistsAsync(Guid userId);
+
+ /// <summary>
+ /// Updates a playlist.
+ /// </summary>
+ /// <param name="playlist">The updated playlist.</param>
+ /// <returns>Task.</returns>
+ Task UpdatePlaylistAsync(Playlist playlist);
}
}
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index e6bcc9ea85..344e996ea8 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -15,6 +15,7 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
namespace MediaBrowser.Controller.Playlists
@@ -232,7 +233,8 @@ namespace MediaBrowser.Controller.Playlists
return base.IsVisible(user);
}
- if (user.Id.Equals(OwnerUserId))
+ var userId = user.Id;
+ if (userId.Equals(OwnerUserId))
{
return true;
}
@@ -240,10 +242,9 @@ namespace MediaBrowser.Controller.Playlists
var shares = Shares;
if (shares.Length == 0)
{
- return base.IsVisible(user);
+ return false;
}
- var userId = user.Id;
return shares.Any(share => Guid.TryParse(share.UserId, out var id) && id.Equals(userId));
}
diff --git a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs
index fd73ed5f80..05b4d43a5a 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; }
diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs
index eefc5d222e..0c4719a0e5 100644
--- a/MediaBrowser.Controller/Session/ISessionManager.cs
+++ b/MediaBrowser.Controller/Session/ISessionManager.cs
@@ -7,7 +7,6 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities.Security;
-using Jellyfin.Data.Events;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Session;
diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
index b86e482435..fcfc18a644 100644
--- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
+++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs
@@ -1,7 +1,6 @@
#pragma warning disable CS1591
using System;
-using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
diff --git a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs
index 2164945560..dcc06db1ed 100644
--- a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs
+++ b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs
@@ -533,11 +533,9 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates
_logger.LogWarning("Session {SessionId} is seeking to wrong position, correcting.", session.Id);
return;
}
- else
- {
- // Session is ready.
- context.SetBuffering(session, false);
- }
+
+ // Session is ready.
+ context.SetBuffering(session, false);
if (!context.IsBuffering())
{
diff --git a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs
index ddbfeb8de8..bdebbbfd49 100644
--- a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs
+++ b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs
@@ -313,17 +313,13 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
return true;
}
- else
- {
- // Restoring playing item.
- SetPlayingItemByPlaylistId(playingItem.PlaylistItemId);
- return false;
- }
- }
- else
- {
+
+ // Restoring playing item.
+ SetPlayingItemByPlaylistId(playingItem.PlaylistItemId);
return false;
}
+
+ return false;
}
/// <summary>
@@ -528,10 +524,8 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
{
return _shuffledPlaylist;
}
- else
- {
- return _sortedPlaylist;
- }
+
+ return _sortedPlaylist;
}
/// <summary>
@@ -544,14 +538,13 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
{
return null;
}
- else if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
+
+ if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
{
return _shuffledPlaylist[PlayingItemIndex];
}
- else
- {
- return _sortedPlaylist[PlayingItemIndex];
- }
+
+ return _sortedPlaylist[PlayingItemIndex];
}
}
}