aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller')
-rw-r--r--MediaBrowser.Controller/Entities/AggregateFolder.cs5
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs2
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs110
-rw-r--r--MediaBrowser.Controller/Entities/BaseItemExtensions.cs10
-rw-r--r--MediaBrowser.Controller/Entities/BasePluginFolder.cs4
-rw-r--r--MediaBrowser.Controller/Entities/Extensions.cs8
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs44
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs10
-rw-r--r--MediaBrowser.Controller/Entities/PeopleHelper.cs5
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs2
-rw-r--r--MediaBrowser.Controller/IDisplayPreferencesManager.cs6
-rw-r--r--MediaBrowser.Controller/IO/FileData.cs5
-rw-r--r--MediaBrowser.Controller/Library/ILibraryMonitor.cs7
-rw-r--r--MediaBrowser.Controller/Lyrics/ILyricManager.cs24
-rw-r--r--MediaBrowser.Controller/Lyrics/ILyricProvider.cs36
-rw-r--r--MediaBrowser.Controller/Lyrics/LyricInfo.cs49
-rw-r--r--MediaBrowser.Controller/Lyrics/LyricLine.cs28
-rw-r--r--MediaBrowser.Controller/Lyrics/LyricMetadata.cs54
-rw-r--r--MediaBrowser.Controller/Lyrics/LyricResponse.cs20
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs341
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs1
-rw-r--r--MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs7
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs12
-rw-r--r--MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs10
-rw-r--r--MediaBrowser.Controller/Net/ISessionContext.cs20
-rw-r--r--MediaBrowser.Controller/Net/IWebSocketConnection.cs1
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs1
-rw-r--r--MediaBrowser.Controller/Properties/AssemblyInfo.cs3
-rw-r--r--MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs6
-rw-r--r--MediaBrowser.Controller/Resolvers/ItemResolver.cs4
-rw-r--r--MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs2
-rw-r--r--MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs4
-rw-r--r--MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs4
33 files changed, 623 insertions, 222 deletions
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index 77a857b78..e671e5c71 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -171,10 +171,7 @@ namespace MediaBrowser.Controller.Entities
/// <exception cref="ArgumentNullException">Throws if child is null.</exception>
public void AddVirtualChild(BaseItem child)
{
- if (child == null)
- {
- throw new ArgumentNullException(nameof(child));
- }
+ ArgumentNullException.ThrowIfNull(child);
_virtualChildren.Add(child);
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index bd397bdd1..6555de855 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -54,7 +54,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public string AlbumArtist => AlbumArtists.FirstOrDefault();
[JsonIgnore]
- public override bool SupportsPeople => false;
+ public override bool SupportsPeople => true;
/// <summary>
/// Gets the tracks.
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 5cee6ce40..7f5f9f74b 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -75,7 +75,9 @@ namespace MediaBrowser.Controller.Entities
Model.Entities.ExtraType.DeletedScene,
Model.Entities.ExtraType.Interview,
Model.Entities.ExtraType.Sample,
- Model.Entities.ExtraType.Scene
+ Model.Entities.ExtraType.Scene,
+ Model.Entities.ExtraType.Featurette,
+ Model.Entities.ExtraType.Short
};
private string _sortName;
@@ -775,36 +777,6 @@ namespace MediaBrowser.Controller.Entities
return Id.ToString("N", CultureInfo.InvariantCulture);
}
- private List<Tuple<StringBuilder, bool>> GetSortChunks(string s1)
- {
- var list = new List<Tuple<StringBuilder, bool>>();
-
- int thisMarker = 0;
-
- while (thisMarker < s1.Length)
- {
- char thisCh = s1[thisMarker];
-
- var thisChunk = new StringBuilder();
- bool isNumeric = char.IsDigit(thisCh);
-
- while (thisMarker < s1.Length && char.IsDigit(thisCh) == isNumeric)
- {
- thisChunk.Append(thisCh);
- thisMarker++;
-
- if (thisMarker < s1.Length)
- {
- thisCh = s1[thisMarker];
- }
- }
-
- list.Add(new Tuple<StringBuilder, bool>(thisChunk, isNumeric));
- }
-
- return list;
- }
-
public virtual bool CanDelete()
{
if (SourceType == SourceType.Channel)
@@ -951,28 +923,40 @@ namespace MediaBrowser.Controller.Entities
return ModifySortChunks(sortable);
}
- private string ModifySortChunks(string name)
+ internal static string ModifySortChunks(string name)
{
- var chunks = GetSortChunks(name);
+ void AppendChunk(StringBuilder builder, bool isDigitChunk, ReadOnlySpan<char> chunk)
+ {
+ if (isDigitChunk && chunk.Length < 10)
+ {
+ builder.Append('0', 10 - chunk.Length);
+ }
- var builder = new StringBuilder();
+ builder.Append(chunk);
+ }
- foreach (var chunk in chunks)
+ if (name.Length == 0)
{
- var chunkBuilder = chunk.Item1;
+ return string.Empty;
+ }
+
+ var builder = new StringBuilder(name.Length);
- // This chunk is numeric
- if (chunk.Item2)
+ int chunkStart = 0;
+ bool isDigitChunk = char.IsDigit(name[0]);
+ for (int i = 0; i < name.Length; i++)
+ {
+ var isDigit = char.IsDigit(name[i]);
+ if (isDigit != isDigitChunk)
{
- while (chunkBuilder.Length < 10)
- {
- chunkBuilder.Insert(0, '0');
- }
+ AppendChunk(builder, isDigitChunk, name.AsSpan(chunkStart, i - chunkStart));
+ chunkStart = i;
+ isDigitChunk = isDigit;
}
-
- builder.Append(chunkBuilder);
}
+ AppendChunk(builder, isDigitChunk, name.AsSpan(chunkStart));
+
// logger.LogDebug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString());
return builder.ToString().RemoveDiacritics();
}
@@ -1101,10 +1085,7 @@ namespace MediaBrowser.Controller.Entities
private MediaSourceInfo GetVersionInfo(bool enablePathSubstitution, BaseItem item, MediaSourceType type)
{
- if (item == null)
- {
- throw new ArgumentNullException(nameof(item));
- }
+ ArgumentNullException.ThrowIfNull(item);
var protocol = item.PathProtocol;
@@ -1544,10 +1525,7 @@ namespace MediaBrowser.Controller.Entities
/// <exception cref="ArgumentNullException">If user is null.</exception>
public bool IsParentalAllowed(User user)
{
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
+ ArgumentNullException.ThrowIfNull(user);
if (!IsVisibleViaTags(user))
{
@@ -1667,10 +1645,7 @@ namespace MediaBrowser.Controller.Entities
/// <exception cref="ArgumentNullException"><paramref name="user" /> is <c>null</c>.</exception>
public virtual bool IsVisible(User user)
{
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
+ ArgumentNullException.ThrowIfNull(user);
return IsParentalAllowed(user);
}
@@ -1842,10 +1817,7 @@ namespace MediaBrowser.Controller.Entities
DateTime? datePlayed,
bool resetPosition)
{
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
+ ArgumentNullException.ThrowIfNull(user);
var data = UserDataManager.GetUserData(user, this);
@@ -1876,10 +1848,7 @@ namespace MediaBrowser.Controller.Entities
/// <exception cref="ArgumentNullException">Throws if user is null.</exception>
public virtual void MarkUnplayed(User user)
{
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
+ ArgumentNullException.ThrowIfNull(user);
var data = UserDataManager.GetUserData(user, this);
@@ -2110,10 +2079,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>Image index.</returns>
public int GetImageIndex(ItemImageInfo image)
{
- if (image == null)
- {
- throw new ArgumentNullException(nameof(image));
- }
+ ArgumentNullException.ThrowIfNull(image);
if (image.Type == ImageType.Chapter)
{
@@ -2320,10 +2286,7 @@ namespace MediaBrowser.Controller.Entities
public virtual bool IsUnplayed(User user)
{
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
+ ArgumentNullException.ThrowIfNull(user);
var userdata = UserDataManager.GetUserData(user, this);
@@ -2616,7 +2579,8 @@ namespace MediaBrowser.Controller.Entities
return ExtraIds
.Select(LibraryManager.GetItemById)
.Where(i => i != null)
- .Where(i => i.ExtraType.HasValue && extraTypes.Contains(i.ExtraType.Value));
+ .Where(i => i.ExtraType.HasValue && extraTypes.Contains(i.ExtraType.Value))
+ .OrderBy(i => i.SortName);
}
public virtual long GetRunTimeTicksForPlayState()
diff --git a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
index e0583e630..948eb4375 100644
--- a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
+++ b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs
@@ -71,15 +71,9 @@ namespace MediaBrowser.Controller.Entities
where T : BaseItem
where TU : BaseItem
{
- if (source == null)
- {
- throw new ArgumentNullException(nameof(source));
- }
+ ArgumentNullException.ThrowIfNull(source);
- if (dest == null)
- {
- throw new ArgumentNullException(nameof(dest));
- }
+ ArgumentNullException.ThrowIfNull(dest);
var destProps = typeof(TU).GetProperties().Where(x => x.CanWrite).ToList();
diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
index 272a37df1..afafaf1c2 100644
--- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs
+++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System.Text.Json.Serialization;
@@ -13,7 +11,7 @@ namespace MediaBrowser.Controller.Entities
public abstract class BasePluginFolder : Folder, ICollectionFolder
{
[JsonIgnore]
- public virtual string CollectionType => null;
+ public virtual string? CollectionType => null;
[JsonIgnore]
public override bool SupportsInheritedParentImages => false;
diff --git a/MediaBrowser.Controller/Entities/Extensions.cs b/MediaBrowser.Controller/Entities/Extensions.cs
index 9ce8eebe3..1acb92fdc 100644
--- a/MediaBrowser.Controller/Entities/Extensions.cs
+++ b/MediaBrowser.Controller/Entities/Extensions.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Linq;
using Jellyfin.Extensions;
@@ -19,9 +17,11 @@ namespace MediaBrowser.Controller.Entities
/// <param name="url">Trailer URL.</param>
public static void AddTrailerUrl(this BaseItem item, string url)
{
- if (string.IsNullOrEmpty(url))
+ ArgumentNullException.ThrowIfNull(url);
+
+ if (url.Length == 0)
{
- throw new ArgumentNullException(nameof(url));
+ throw new ArgumentException("String can't be empty", nameof(url));
}
var current = item.RemoteTrailers.FirstOrDefault(i => string.Equals(i.Url, url, StringComparison.OrdinalIgnoreCase));
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 1860da4c7..808f91810 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -892,29 +892,7 @@ namespace MediaBrowser.Controller.Entities
private static BaseItem[] SortItemsByRequest(InternalItemsQuery query, IReadOnlyList<BaseItem> items)
{
- var ids = query.ItemIds;
- int size = items.Count;
-
- // ids can potentially contain non-unique guids, but query result cannot,
- // so we include only first occurrence of each guid
- var positions = new Dictionary<Guid, int>(size);
- int index = 0;
- for (int i = 0; i < ids.Length; i++)
- {
- if (positions.TryAdd(ids[i], index))
- {
- index++;
- }
- }
-
- var newItems = new BaseItem[size];
- for (int i = 0; i < size; i++)
- {
- var item = items[i];
- newItems[positions[item.Id]] = item;
- }
-
- return newItems;
+ return items.OrderBy(i => Array.IndexOf(query.ItemIds, i.Id)).ToArray();
}
public QueryResult<BaseItem> GetItems(InternalItemsQuery query)
@@ -1045,10 +1023,7 @@ namespace MediaBrowser.Controller.Entities
IServerConfigurationManager configurationManager,
ICollectionManager collectionManager)
{
- if (items == null)
- {
- throw new ArgumentNullException(nameof(items));
- }
+ ArgumentNullException.ThrowIfNull(items);
if (CollapseBoxSetItems(query, queryParent, user, configurationManager))
{
@@ -1295,20 +1270,14 @@ namespace MediaBrowser.Controller.Entities
public List<BaseItem> GetChildren(User user, bool includeLinkedChildren)
{
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
+ ArgumentNullException.ThrowIfNull(user);
return GetChildren(user, includeLinkedChildren, null);
}
public virtual List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
{
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
+ ArgumentNullException.ThrowIfNull(user);
// the true root should return our users root folder children
if (IsPhysicalRoot)
@@ -1389,10 +1358,7 @@ namespace MediaBrowser.Controller.Entities
public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
{
- if (user == null)
- {
- throw new ArgumentNullException(nameof(user));
- }
+ ArgumentNullException.ThrowIfNull(user);
var result = new Dictionary<Guid, BaseItem>();
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index 13bfd07c3..1bf528538 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -205,6 +205,16 @@ namespace MediaBrowser.Controller.Entities
public int? MinIndexNumber { get; set; }
+ /// <summary>
+ /// Gets or sets the minimum ParentIndexNumber and IndexNumber.
+ /// </summary>
+ /// <remarks>
+ /// It produces this where clause:
+ /// <para>(ParentIndexNumber = X and IndexNumber >= Y) or ParentIndexNumber > X.
+ /// </para>
+ /// </remarks>
+ public (int ParentIndexNumber, int IndexNumber)? MinParentAndIndexNumber { get; set; }
+
public int? AiredDuringSeason { get; set; }
public double? MinCriticRating { get; set; }
diff --git a/MediaBrowser.Controller/Entities/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs
index 687ce1ec8..8571bfcea 100644
--- a/MediaBrowser.Controller/Entities/PeopleHelper.cs
+++ b/MediaBrowser.Controller/Entities/PeopleHelper.cs
@@ -11,10 +11,7 @@ namespace MediaBrowser.Controller.Entities
{
public static void AddPerson(List<PersonInfo> people, PersonInfo person)
{
- if (person == null)
- {
- throw new ArgumentNullException(nameof(person));
- }
+ ArgumentNullException.ThrowIfNull(person);
if (string.IsNullOrEmpty(person.Name))
{
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index bd8df2fac..599d35da6 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -244,7 +244,7 @@ namespace MediaBrowser.Controller.Entities.TV
/// <summary>
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
/// </summary>
- /// <param name="replaceAllMetadata"><c>true</c> to replace metdata, <c>false</c> to not.</param>
+ /// <param name="replaceAllMetadata"><c>true</c> to replace metadata, <c>false</c> to not.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
diff --git a/MediaBrowser.Controller/IDisplayPreferencesManager.cs b/MediaBrowser.Controller/IDisplayPreferencesManager.cs
index 1678d5067..10c0f56e0 100644
--- a/MediaBrowser.Controller/IDisplayPreferencesManager.cs
+++ b/MediaBrowser.Controller/IDisplayPreferencesManager.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
using System;
using System.Collections.Generic;
using Jellyfin.Data.Entities;
@@ -50,7 +48,7 @@ namespace MediaBrowser.Controller
/// <param name="itemId">The item id.</param>
/// <param name="client">The client string.</param>
/// <returns>The dictionary of custom item display preferences.</returns>
- Dictionary<string, string> ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client);
+ Dictionary<string, string?> ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client);
/// <summary>
/// Sets the custom item display preference for the user and client.
@@ -59,7 +57,7 @@ namespace MediaBrowser.Controller
/// <param name="itemId">The item id.</param>
/// <param name="client">The client id.</param>
/// <param name="customPreferences">A dictionary of custom item display preferences.</param>
- void SetCustomItemDisplayPreferences(Guid userId, Guid itemId, string client, Dictionary<string, string> customPreferences);
+ void SetCustomItemDisplayPreferences(Guid userId, Guid itemId, string client, Dictionary<string, string?> customPreferences);
/// <summary>
/// Saves changes made to the database.
diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs
index 2429ac42d..71feae536 100644
--- a/MediaBrowser.Controller/IO/FileData.cs
+++ b/MediaBrowser.Controller/IO/FileData.cs
@@ -40,10 +40,7 @@ namespace MediaBrowser.Controller.IO
throw new ArgumentNullException(nameof(path));
}
- if (args == null)
- {
- throw new ArgumentNullException(nameof(args));
- }
+ ArgumentNullException.ThrowIfNull(args);
var entries = directoryService.GetFileSystemEntries(path);
diff --git a/MediaBrowser.Controller/Library/ILibraryMonitor.cs b/MediaBrowser.Controller/Library/ILibraryMonitor.cs
index 455054bd1..de74aa5a1 100644
--- a/MediaBrowser.Controller/Library/ILibraryMonitor.cs
+++ b/MediaBrowser.Controller/Library/ILibraryMonitor.cs
@@ -34,12 +34,5 @@ namespace MediaBrowser.Controller.Library
/// </summary>
/// <param name="path">The path.</param>
void ReportFileSystemChanged(string path);
-
- /// <summary>
- /// Determines whether [is path locked] [the specified path].
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns><c>true</c> if [is path locked] [the specified path]; otherwise, <c>false</c>.</returns>
- bool IsPathLocked(string path);
}
}
diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs
new file mode 100644
index 000000000..bb93e1e4c
--- /dev/null
+++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs
@@ -0,0 +1,24 @@
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities;
+
+namespace MediaBrowser.Controller.Lyrics;
+
+/// <summary>
+/// Interface ILyricManager.
+/// </summary>
+public interface ILyricManager
+{
+ /// <summary>
+ /// Gets the lyrics.
+ /// </summary>
+ /// <param name="item">The media item.</param>
+ /// <returns>A task representing found lyrics the passed item.</returns>
+ Task<LyricResponse?> GetLyrics(BaseItem item);
+
+ /// <summary>
+ /// Checks if requested item has a matching local lyric file.
+ /// </summary>
+ /// <param name="item">The media item.</param>
+ /// <returns>True if item has a matching lyric file; otherwise false.</returns>
+ bool HasLyricFile(BaseItem item);
+}
diff --git a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs
new file mode 100644
index 000000000..2a04c6152
--- /dev/null
+++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs
@@ -0,0 +1,36 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Resolvers;
+
+namespace MediaBrowser.Controller.Lyrics;
+
+/// <summary>
+/// Interface ILyricsProvider.
+/// </summary>
+public interface ILyricProvider
+{
+ /// <summary>
+ /// Gets a value indicating the provider name.
+ /// </summary>
+ string Name { get; }
+
+ /// <summary>
+ /// Gets the priority.
+ /// </summary>
+ /// <value>The priority.</value>
+ ResolverPriority Priority { get; }
+
+ /// <summary>
+ /// Gets the supported media types for this provider.
+ /// </summary>
+ /// <value>The supported media types.</value>
+ IReadOnlyCollection<string> SupportedMediaTypes { get; }
+
+ /// <summary>
+ /// Gets the lyrics.
+ /// </summary>
+ /// <param name="item">The media item.</param>
+ /// <returns>A task representing found lyrics.</returns>
+ Task<LyricResponse?> GetLyrics(BaseItem item);
+}
diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs
new file mode 100644
index 000000000..6ec6df582
--- /dev/null
+++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs
@@ -0,0 +1,49 @@
+using System;
+using System.IO;
+using Jellyfin.Extensions;
+
+namespace MediaBrowser.Controller.Lyrics;
+
+/// <summary>
+/// Lyric helper methods.
+/// </summary>
+public static class LyricInfo
+{
+ /// <summary>
+ /// Gets matching lyric file for a requested item.
+ /// </summary>
+ /// <param name="lyricProvider">The lyricProvider interface to use.</param>
+ /// <param name="itemPath">Path of requested item.</param>
+ /// <returns>Lyric file path if passed lyric provider's supported media type is found; otherwise, null.</returns>
+ public static string? GetLyricFilePath(this ILyricProvider lyricProvider, string itemPath)
+ {
+ // Ensure we have a provider
+ if (lyricProvider is null)
+ {
+ return null;
+ }
+
+ // Ensure the path to the item is not null
+ string? itemDirectoryPath = Path.GetDirectoryName(itemPath);
+ if (itemDirectoryPath is null)
+ {
+ return null;
+ }
+
+ // Ensure the directory path exists
+ if (!Directory.Exists(itemDirectoryPath))
+ {
+ return null;
+ }
+
+ foreach (var lyricFilePath in Directory.GetFiles(itemDirectoryPath, $"{Path.GetFileNameWithoutExtension(itemPath)}.*"))
+ {
+ if (lyricProvider.SupportedMediaTypes.Contains(Path.GetExtension(lyricFilePath.AsSpan())[1..], StringComparison.OrdinalIgnoreCase))
+ {
+ return lyricFilePath;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/MediaBrowser.Controller/Lyrics/LyricLine.cs b/MediaBrowser.Controller/Lyrics/LyricLine.cs
new file mode 100644
index 000000000..c406f92fc
--- /dev/null
+++ b/MediaBrowser.Controller/Lyrics/LyricLine.cs
@@ -0,0 +1,28 @@
+namespace MediaBrowser.Controller.Lyrics;
+
+/// <summary>
+/// Lyric model.
+/// </summary>
+public class LyricLine
+{
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LyricLine"/> class.
+ /// </summary>
+ /// <param name="text">The lyric text.</param>
+ /// <param name="start">The lyric start time in ticks.</param>
+ public LyricLine(string text, long? start = null)
+ {
+ Text = text;
+ Start = start;
+ }
+
+ /// <summary>
+ /// Gets the text of this lyric line.
+ /// </summary>
+ public string Text { get; }
+
+ /// <summary>
+ /// Gets the start time in ticks.
+ /// </summary>
+ public long? Start { get; }
+}
diff --git a/MediaBrowser.Controller/Lyrics/LyricMetadata.cs b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs
new file mode 100644
index 000000000..6091ede52
--- /dev/null
+++ b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs
@@ -0,0 +1,54 @@
+using System;
+
+namespace MediaBrowser.Controller.Lyrics;
+
+/// <summary>
+/// LyricMetadata model.
+/// </summary>
+public class LyricMetadata
+{
+ /// <summary>
+ /// Gets or sets the song artist.
+ /// </summary>
+ public string? Artist { get; set; }
+
+ /// <summary>
+ /// Gets or sets the album this song is on.
+ /// </summary>
+ public string? Album { get; set; }
+
+ /// <summary>
+ /// Gets or sets the title of the song.
+ /// </summary>
+ public string? Title { get; set; }
+
+ /// <summary>
+ /// Gets or sets the author of the lyric data.
+ /// </summary>
+ public string? Author { get; set; }
+
+ /// <summary>
+ /// Gets or sets the length of the song in ticks.
+ /// </summary>
+ public long? Length { get; set; }
+
+ /// <summary>
+ /// Gets or sets who the LRC file was created by.
+ /// </summary>
+ public string? By { get; set; }
+
+ /// <summary>
+ /// Gets or sets the lyric offset compared to audio in ticks.
+ /// </summary>
+ public long? Offset { get; set; }
+
+ /// <summary>
+ /// Gets or sets the software used to create the LRC file.
+ /// </summary>
+ public string? Creator { get; set; }
+
+ /// <summary>
+ /// Gets or sets the version of the creator used.
+ /// </summary>
+ public string? Version { get; set; }
+}
diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs
new file mode 100644
index 000000000..0d52b5ec5
--- /dev/null
+++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Controller.Lyrics;
+
+/// <summary>
+/// LyricResponse model.
+/// </summary>
+public class LyricResponse
+{
+ /// <summary>
+ /// Gets or sets Metadata for the lyrics.
+ /// </summary>
+ public LyricMetadata Metadata { get; set; } = new();
+
+ /// <summary>
+ /// Gets or sets a collection of individual lyric lines.
+ /// </summary>
+ public IReadOnlyList<LyricLine> Lyrics { get; set; } = Array.Empty<LyricLine>();
+}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index e5aae620a..cee08eeda 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -31,10 +31,13 @@ namespace MediaBrowser.Controller.MediaEncoding
private const string VideotoolboxAlias = "vt";
private const string OpenclAlias = "ocl";
private const string CudaAlias = "cu";
+ private const string DrmAlias = "dr";
+ private const string VulkanAlias = "vk";
private readonly IApplicationPaths _appPaths;
private readonly IMediaEncoder _mediaEncoder;
private readonly ISubtitleEncoder _subtitleEncoder;
private readonly IConfiguration _config;
+ private readonly Version _minKernelVersionAmdVkFmtModifier = new Version(5, 15);
private readonly Version _minKernelVersioni915Hang = new Version(5, 18);
private static readonly string[] _videoProfilesH264 = new[]
@@ -149,6 +152,14 @@ namespace MediaBrowser.Controller.MediaEncoding
&& _mediaEncoder.SupportsFilter("hwupload_cuda");
}
+ private bool IsVulkanFullSupported()
+ {
+ return _mediaEncoder.SupportsHwaccel("vulkan")
+ && _mediaEncoder.SupportsFilter("libplacebo")
+ && _mediaEncoder.SupportsFilter("scale_vulkan")
+ && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayVulkanFrameSync);
+ }
+
private bool IsHwTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
{
if (state.VideoStream == null
@@ -176,6 +187,19 @@ namespace MediaBrowser.Controller.MediaEncoding
|| string.Equals(state.VideoStream.VideoRangeType, "HLG", StringComparison.OrdinalIgnoreCase));
}
+ private bool IsVulkanHwTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
+ {
+ if (state.VideoStream == null)
+ {
+ return false;
+ }
+
+ // libplacebo has partial Dolby Vision to SDR tonemapping support.
+ return options.EnableTonemapping
+ && string.Equals(state.VideoStream.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase)
+ && GetVideoColorBitDepth(state) == 10;
+ }
+
private bool IsVaapiVppTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
{
if (state.VideoStream == null
@@ -194,7 +218,7 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// Gets the name of the output video codec.
/// </summary>
- /// <param name="state">Encording state.</param>
+ /// <param name="state">Encoding state.</param>
/// <param name="encodingOptions">Encoding options.</param>
/// <returns>Encoder string.</returns>
public string GetVideoEncoder(EncodingJobInfo state, EncodingOptions encodingOptions)
@@ -256,6 +280,21 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Empty;
}
+ /// <summary>
+ /// Gets the referer param.
+ /// </summary>
+ /// <param name="state">The state.</param>
+ /// <returns>System.String.</returns>
+ public string GetRefererParam(EncodingJobInfo state)
+ {
+ if (state.RemoteHttpHeaders.TryGetValue("Referer", out string referer))
+ {
+ return "-referer \"" + referer + "\"";
+ }
+
+ return string.Empty;
+ }
+
public static string GetInputFormat(string container)
{
if (string.IsNullOrEmpty(container))
@@ -741,8 +780,13 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (_mediaEncoder.IsVaapiDeviceAmd)
{
- args.Append(GetOpenclDeviceArgs(0, "Advanced Micro Devices", null, OpenclAlias));
- filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
+ if (!IsVulkanFullSupported()
+ || !_mediaEncoder.IsVaapiDeviceSupportVulkanFmtModifier
+ || Environment.OSVersion.Version < _minKernelVersionAmdVkFmtModifier)
+ {
+ args.Append(GetOpenclDeviceArgs(0, "Advanced Micro Devices", null, OpenclAlias));
+ filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
+ }
}
else
{
@@ -1415,7 +1459,11 @@ namespace MediaBrowser.Controller.MediaEncoding
param += " -preset 7";
}
- param += " -look_ahead 0";
+ // Only h264_qsv has look_ahead option
+ if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase))
+ {
+ param += " -look_ahead 0";
+ }
}
else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc)
|| string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_nvenc)
@@ -1453,7 +1501,7 @@ namespace MediaBrowser.Controller.MediaEncoding
break;
default:
- param += " -preset p4";
+ param += " -preset p1";
break;
}
}
@@ -1980,7 +2028,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- // Cap the max target bitrate to intMax/2 to satisify the bufsize=bitrate*2.
+ // Cap the max target bitrate to intMax/2 to satisfy the bufsize=bitrate*2.
return Math.Min(bitrate ?? 0, int.MaxValue / 2);
}
@@ -2759,22 +2807,41 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Empty;
}
- var args = "tonemap_{0}=format={1}:p=bt709:t=bt709:m=bt709";
+ var args = string.Empty;
+ var algorithm = options.TonemappingAlgorithm;
- if (hwTonemapSuffix.Contains("vaapi", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(hwTonemapSuffix, "vaapi", StringComparison.OrdinalIgnoreCase))
{
- args += ",procamp_vaapi=b={2}:c={3}:extra_hw_frames=16";
+ args = "tonemap_vaapi=format={0}:p=bt709:t=bt709:m=bt709,procamp_vaapi=b={1}:c={2}:extra_hw_frames=16";
return string.Format(
CultureInfo.InvariantCulture,
args,
- hwTonemapSuffix,
videoFormat ?? "nv12",
options.VppTonemappingBrightness,
options.VppTonemappingContrast);
}
+ else 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";
+
+ if (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase))
+ {
+ args += ":range={6}";
+ }
+
+ if (string.Equals(options.TonemappingAlgorithm, "bt2390", StringComparison.OrdinalIgnoreCase))
+ {
+ algorithm = "bt.2390";
+ }
+
+ else if (string.Equals(options.TonemappingAlgorithm, "none", StringComparison.OrdinalIgnoreCase))
+ {
+ algorithm = "clip";
+ }
+ }
else
{
- args += ":tonemap={2}:peak={3}:desat={4}";
+ args = "tonemap_{0}=format={1}:p=bt709:t=bt709:m=bt709:tonemap={2}:peak={3}:desat={4}";
if (options.TonemappingParam != 0)
{
@@ -2792,7 +2859,7 @@ namespace MediaBrowser.Controller.MediaEncoding
args,
hwTonemapSuffix,
videoFormat ?? "nv12",
- options.TonemappingAlgorithm,
+ algorithm,
options.TonemappingPeak,
options.TonemappingDesat,
options.TonemappingParam,
@@ -3404,6 +3471,12 @@ namespace MediaBrowser.Controller.MediaEncoding
// map from d3d11va to qsv.
mainFilters.Add("hwmap=derive_device=qsv");
}
+ else
+ {
+ // Insert a qsv scaler to sync the decoder surface,
+ // msdk will passthrough this internally.
+ mainFilters.Add("hwmap=derive_device=qsv,scale_qsv");
+ }
}
// hw deint
@@ -3755,7 +3828,9 @@ namespace MediaBrowser.Controller.MediaEncoding
var vidDecoder = GetHardwareVideoDecoder(state, options) ?? string.Empty;
var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
var isSwEncoder = !vidEncoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase);
- var isVaapiOclSupported = isLinux && IsVaapiSupported(state) && IsVaapiFullSupported() && IsOpenclFullSupported();
+ var isVaapiFullSupported = isLinux && IsVaapiSupported(state) && IsVaapiFullSupported();
+ var isVaapiOclSupported = isVaapiFullSupported && IsOpenclFullSupported();
+ var isVaapiVkSupported = isVaapiFullSupported && IsVulkanFullSupported();
// legacy vaapi pipeline(copy-back)
if ((isSwDecoder && isSwEncoder)
@@ -3783,14 +3858,24 @@ namespace MediaBrowser.Controller.MediaEncoding
if (_mediaEncoder.IsVaapiDeviceInteliHD)
{
// Intel iHD path, with extra vpp tonemap and overlay support.
- return GetVaapiFullVidFiltersPrefered(state, options, vidDecoder, vidEncoder);
+ return GetIntelVaapiFullVidFiltersPrefered(state, options, vidDecoder, vidEncoder);
+ }
+
+ // prefered vaapi + vulkan filters pipeline
+ if (_mediaEncoder.IsVaapiDeviceAmd
+ && isVaapiVkSupported
+ && _mediaEncoder.IsVaapiDeviceSupportVulkanFmtModifier
+ && Environment.OSVersion.Version >= _minKernelVersionAmdVkFmtModifier)
+ {
+ // AMD radeonsi path(Vega/gfx9+, kernel>=5.15), with extra vulkan tonemap and overlay support.
+ return GetAmdVaapiFullVidFiltersPrefered(state, options, vidDecoder, vidEncoder);
}
- // Intel i965 and Amd radeonsi/r600 path, only featuring scale and deinterlace support.
+ // Intel i965 and Amd radeonsi/r600 path(Polaris/gfx8-), only featuring scale and deinterlace support.
return GetVaapiLimitedVidFiltersPrefered(state, options, vidDecoder, vidEncoder);
}
- public (List<string> MainFilters, List<string> SubFilters, List<string> OverlayFilters) GetVaapiFullVidFiltersPrefered(
+ public (List<string> MainFilters, List<string> SubFilters, List<string> OverlayFilters) GetIntelVaapiFullVidFiltersPrefered(
EncodingJobInfo state,
EncodingOptions options,
string vidDecoder,
@@ -3988,6 +4073,203 @@ namespace MediaBrowser.Controller.MediaEncoding
return (mainFilters, subFilters, overlayFilters);
}
+ public (List<string> MainFilters, List<string> SubFilters, List<string> OverlayFilters) GetAmdVaapiFullVidFiltersPrefered(
+ EncodingJobInfo state,
+ EncodingOptions options,
+ string vidDecoder,
+ string vidEncoder)
+ {
+ var inW = state.VideoStream?.Width;
+ var inH = state.VideoStream?.Height;
+ var reqW = state.BaseRequest.Width;
+ var reqH = state.BaseRequest.Height;
+ var reqMaxW = state.BaseRequest.MaxWidth;
+ var reqMaxH = state.BaseRequest.MaxHeight;
+ var threeDFormat = state.MediaSource.Video3DFormat;
+
+ var isVaapiDecoder = vidDecoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase);
+ var isVaapiEncoder = vidEncoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase);
+ var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
+ var isSwEncoder = !isVaapiEncoder;
+ var isVaInVaOut = isVaapiDecoder && isVaapiEncoder;
+
+ var doDeintH264 = state.DeInterlace("h264", true) || state.DeInterlace("avc", true);
+ var doDeintHevc = state.DeInterlace("h265", true) || state.DeInterlace("hevc", true);
+ var doVkTonemap = IsVulkanHwTonemapAvailable(state, options);
+ var doDeintH2645 = doDeintH264 || doDeintHevc;
+
+ var hasSubs = state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
+ var hasTextSubs = hasSubs && state.SubtitleStream.IsTextSubtitleStream;
+ var hasGraphicalSubs = hasSubs && !state.SubtitleStream.IsTextSubtitleStream;
+ var hasAssSubs = hasSubs
+ && (string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase));
+
+ /* Make main filters for video stream */
+ var mainFilters = new List<string>();
+
+ mainFilters.Add(GetOverwriteColorPropertiesParam(state, doVkTonemap));
+
+ if (isSwDecoder)
+ {
+ // INPUT sw surface(memory)
+ // sw deint
+ if (doDeintH2645)
+ {
+ var swDeintFilter = GetSwDeinterlaceFilter(state, options);
+ mainFilters.Add(swDeintFilter);
+ }
+
+ var outFormat = doVkTonemap ? "yuv420p10le" : "nv12";
+ var swScaleFilter = GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH);
+ // sw scale
+ mainFilters.Add(swScaleFilter);
+ mainFilters.Add("format=" + outFormat);
+
+ // keep video at memory except vk tonemap,
+ // since the overhead caused by hwupload >>> using sw filter.
+ // sw => hw
+ if (doVkTonemap)
+ {
+ mainFilters.Add("hwupload=derive_device=vulkan:extra_hw_frames=16");
+ }
+ }
+ else if (isVaapiDecoder)
+ {
+ // INPUT vaapi surface(vram)
+ // hw deint
+ if (doDeintH2645)
+ {
+ var deintFilter = GetHwDeinterlaceFilter(state, options, "vaapi");
+ mainFilters.Add(deintFilter);
+ }
+
+ var outFormat = doVkTonemap ? string.Empty : (hasSubs && isVaInVaOut ? "bgra" : "nv12");
+ var hwScaleFilter = GetHwScaleFilter("vaapi", outFormat, inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+
+ // allocate extra pool sizes for overlay_vulkan
+ if (!string.IsNullOrEmpty(hwScaleFilter) && isVaInVaOut && hasSubs)
+ {
+ hwScaleFilter += ":extra_hw_frames=32";
+ }
+
+ // hw scale
+ mainFilters.Add(hwScaleFilter);
+ }
+
+ if ((isVaapiDecoder && doVkTonemap) || (isVaInVaOut && (doVkTonemap || hasSubs)))
+ {
+ // map from vaapi to vulkan via vaapi-vulkan interop (Vega/gfx9+).
+ mainFilters.Add("hwmap=derive_device=vulkan");
+ }
+
+ // vk tonemap
+ if (doVkTonemap)
+ {
+ var outFormat = isVaInVaOut && hasSubs ? "bgra" : "nv12";
+ var tonemapFilter = GetHwTonemapFilter(options, "vulkan", outFormat);
+ mainFilters.Add(tonemapFilter);
+ }
+
+ if (doVkTonemap && isVaInVaOut && !hasSubs)
+ {
+ // OUTPUT vaapi(nv12/bgra) surface(vram)
+ // reverse-mapping via vaapi-vulkan interop.
+ mainFilters.Add("hwmap=derive_device=vaapi:reverse=1");
+ mainFilters.Add("format=vaapi");
+ }
+
+ var memoryOutput = false;
+ var isUploadForVkTonemap = isSwDecoder && doVkTonemap;
+ if ((isVaapiDecoder && isSwEncoder) || isUploadForVkTonemap)
+ {
+ memoryOutput = true;
+
+ // OUTPUT nv12 surface(memory)
+ mainFilters.Add("hwdownload");
+ mainFilters.Add("format=nv12");
+ }
+
+ // OUTPUT nv12 surface(memory)
+ if (isSwDecoder && isVaapiEncoder)
+ {
+ memoryOutput = true;
+ }
+
+ if (memoryOutput)
+ {
+ // text subtitles
+ if (hasTextSubs)
+ {
+ var textSubtitlesFilter = GetTextSubtitlesFilter(state, false, false);
+ mainFilters.Add(textSubtitlesFilter);
+ }
+ }
+
+ if (memoryOutput && isVaapiEncoder)
+ {
+ if (!hasGraphicalSubs)
+ {
+ mainFilters.Add("hwupload_vaapi");
+ }
+ }
+
+ /* Make sub and overlay filters for subtitle stream */
+ var subFilters = new List<string>();
+ var overlayFilters = new List<string>();
+ if (isVaInVaOut)
+ {
+ if (hasSubs)
+ {
+ if (hasGraphicalSubs)
+ {
+ // scale=s=1280x720,format=bgra,hwupload
+ var subSwScaleFilter = GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subSwScaleFilter);
+ subFilters.Add("format=bgra");
+ }
+ else if (hasTextSubs)
+ {
+ var alphaSrcFilter = GetAlphaSrcFilter(state, inW, inH, reqW, reqH, reqMaxW, reqMaxH, hasAssSubs ? 10 : 5);
+ var subTextSubtitlesFilter = GetTextSubtitlesFilter(state, true, true);
+ subFilters.Add(alphaSrcFilter);
+ subFilters.Add("format=bgra");
+ subFilters.Add(subTextSubtitlesFilter);
+ }
+
+ subFilters.Add("hwupload=derive_device=vulkan:extra_hw_frames=16");
+
+ overlayFilters.Add("overlay_vulkan=eof_action=endall:shortest=1:repeatlast=0");
+
+ // explicitly sync using libplacebo.
+ overlayFilters.Add("libplacebo=format=nv12:upscaler=none:downscaler=none");
+
+ // OUTPUT vaapi(nv12/bgra) surface(vram)
+ // reverse-mapping via vaapi-vulkan interop.
+ overlayFilters.Add("hwmap=derive_device=vaapi:reverse=1");
+ overlayFilters.Add("format=vaapi");
+ }
+ }
+ else if (memoryOutput)
+ {
+ if (hasGraphicalSubs)
+ {
+ var subSwScaleFilter = isSwDecoder
+ ? GetSwScaleFilter(state, options, vidEncoder, inW, inH, threeDFormat, reqW, reqH, reqMaxW, reqMaxH)
+ : GetCustomSwScaleFilter(inW, inH, reqW, reqH, reqMaxW, reqMaxH);
+ subFilters.Add(subSwScaleFilter);
+ overlayFilters.Add("overlay=eof_action=pass:shortest=1:repeatlast=0");
+
+ if (isVaapiEncoder)
+ {
+ overlayFilters.Add("hwupload_vaapi");
+ }
+ }
+ }
+
+ return (mainFilters, subFilters, overlayFilters);
+ }
+
public (List<string> MainFilters, List<string> SubFilters, List<string> OverlayFilters) GetVaapiLimitedVidFiltersPrefered(
EncodingJobInfo state,
EncodingOptions options,
@@ -4976,13 +5258,13 @@ namespace MediaBrowser.Controller.MediaEncoding
// The default value of -probesize is more than enough, so leave it as is.
var ffmpegAnalyzeDuration = _config.GetFFmpegAnalyzeDuration() ?? string.Empty;
- if (!string.IsNullOrEmpty(ffmpegAnalyzeDuration))
+ if (state.MediaSource.AnalyzeDurationMs > 0)
{
- analyzeDurationArgument = "-analyzeduration " + ffmpegAnalyzeDuration;
+ analyzeDurationArgument = "-analyzeduration " + (state.MediaSource.AnalyzeDurationMs.Value * 1000).ToString(CultureInfo.InvariantCulture);
}
- else if (state.MediaSource.AnalyzeDurationMs.HasValue)
+ else if (!string.IsNullOrEmpty(ffmpegAnalyzeDuration))
{
- analyzeDurationArgument = "-analyzeduration " + (state.MediaSource.AnalyzeDurationMs.Value * 1000).ToString(CultureInfo.InvariantCulture);
+ analyzeDurationArgument = "-analyzeduration " + ffmpegAnalyzeDuration;
}
if (!string.IsNullOrEmpty(analyzeDurationArgument))
@@ -5001,6 +5283,15 @@ namespace MediaBrowser.Controller.MediaEncoding
inputModifier = inputModifier.Trim();
+ var refererParam = GetRefererParam(state);
+
+ if (!string.IsNullOrEmpty(refererParam))
+ {
+ inputModifier += " " + refererParam;
+ }
+
+ inputModifier = inputModifier.Trim();
+
inputModifier += " " + GetFastSeekCommandLineParameter(state, encodingOptions, segmentContainer);
inputModifier = inputModifier.Trim();
@@ -5081,15 +5372,9 @@ namespace MediaBrowser.Controller.MediaEncoding
MediaSourceInfo mediaSource,
string requestedUrl)
{
- if (state == null)
- {
- throw new ArgumentNullException(nameof(state));
- }
+ ArgumentNullException.ThrowIfNull(state);
- if (mediaSource == null)
- {
- throw new ArgumentNullException(nameof(mediaSource));
- }
+ ArgumentNullException.ThrowIfNull(mediaSource);
var path = mediaSource.Path;
var protocol = mediaSource.Protocol;
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index 491662861..c9625cf1d 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -6,7 +6,6 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using System.Text.Json.Serialization;
using Jellyfin.Data.Entities;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
diff --git a/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs b/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
index a4869cb67..b1d319d21 100644
--- a/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
+++ b/MediaBrowser.Controller/MediaEncoding/FilterOptionType.cs
@@ -28,6 +28,11 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <summary>
/// The overlay_vaapi_framesync.
/// </summary>
- OverlayVaapiFrameSync = 4
+ OverlayVaapiFrameSync = 4,
+
+ /// <summary>
+ /// The overlay_vulkan_framesync.
+ /// </summary>
+ OverlayVulkanFrameSync = 5
}
}
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index dae30cd8b..52c57b906 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -38,6 +38,12 @@ namespace MediaBrowser.Controller.MediaEncoding
Version EncoderVersion { get; }
/// <summary>
+ /// Whether p key pausing is supported.
+ /// </summary>
+ /// <value><c>true</c> if p key pausing is supported, <c>false</c> otherwise.</value>
+ bool IsPkeyPauseSupported { get; }
+
+ /// <summary>
/// Gets a value indicating whether the configured Vaapi device is from AMD(radeonsi/r600 Mesa driver).
/// </summary>
/// <value><c>true</c> if the Vaapi device is an AMD(radeonsi/r600 Mesa driver) GPU, <c>false</c> otherwise.</value>
@@ -56,6 +62,12 @@ namespace MediaBrowser.Controller.MediaEncoding
bool IsVaapiDeviceInteli965 { get; }
/// <summary>
+ /// Gets a value indicating whether the configured Vaapi device supports vulkan drm format modifier.
+ /// </summary>
+ /// <value><c>true</c> if the Vaapi device supports vulkan drm format modifier, <c>false</c> otherwise.</value>
+ bool IsVaapiDeviceSupportVulkanFmtModifier { get; }
+
+ /// <summary>
/// Whether given encoder codec is supported.
/// </summary>
/// <param name="encoder">The encoder.</param>
diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
index eadc09fd4..647de5003 100644
--- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
+++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs
@@ -38,10 +38,7 @@ namespace MediaBrowser.Controller.Net
protected BasePeriodicWebSocketListener(ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> logger)
{
- if (logger == null)
- {
- throw new ArgumentNullException(nameof(logger));
- }
+ ArgumentNullException.ThrowIfNull(logger);
Logger = logger;
}
@@ -77,10 +74,7 @@ namespace MediaBrowser.Controller.Net
/// <returns>Task.</returns>
public Task ProcessMessageAsync(WebSocketMessageInfo message)
{
- if (message == null)
- {
- throw new ArgumentNullException(nameof(message));
- }
+ ArgumentNullException.ThrowIfNull(message);
if (message.MessageType == StartType)
{
diff --git a/MediaBrowser.Controller/Net/ISessionContext.cs b/MediaBrowser.Controller/Net/ISessionContext.cs
deleted file mode 100644
index b48181b3f..000000000
--- a/MediaBrowser.Controller/Net/ISessionContext.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Threading.Tasks;
-using Jellyfin.Data.Entities;
-using MediaBrowser.Controller.Session;
-using Microsoft.AspNetCore.Http;
-
-namespace MediaBrowser.Controller.Net
-{
- public interface ISessionContext
- {
- Task<SessionInfo> GetSession(object requestContext);
-
- Task<User?> GetUser(object requestContext);
-
- Task<SessionInfo> GetSession(HttpContext requestContext);
-
- Task<User?> GetUser(HttpContext requestContext);
- }
-}
diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
index 43c7ce370..4f2492b89 100644
--- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs
+++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs
@@ -6,7 +6,6 @@ using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Net;
-using Microsoft.AspNetCore.Http;
namespace MediaBrowser.Controller.Net
{
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index 828ecb2c5..7ae9ff746 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
diff --git a/MediaBrowser.Controller/Properties/AssemblyInfo.cs b/MediaBrowser.Controller/Properties/AssemblyInfo.cs
index 60e792309..534dec8d2 100644
--- a/MediaBrowser.Controller/Properties/AssemblyInfo.cs
+++ b/MediaBrowser.Controller/Properties/AssemblyInfo.cs
@@ -1,5 +1,6 @@
using System.Reflection;
using System.Resources;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@@ -14,6 +15,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
+[assembly: InternalsVisibleTo("Jellyfin.Controller.Tests")]
+[assembly: InternalsVisibleTo("Jellyfin.Server.Implementations.Tests")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
diff --git a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
index 32a9cbef2..14428df5b 100644
--- a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs
@@ -18,9 +18,9 @@ namespace MediaBrowser.Controller.Providers
/// Fetches the metadata asynchronously.
/// </summary>
/// <param name="item">The item.</param>
- /// <param name="options">The options.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{ItemUpdateType}.</returns>
+ /// <param name="options">The <see cref="MetadataRefreshOptions"/>.</param>
+ /// <param name="cancellationToken">The <see cref="CancellationToken"/>.</param>
+ /// <returns>A <see cref="Task"/> fetching the <see cref="ItemUpdateType"/>.</returns>
Task<ItemUpdateType> FetchAsync(TItemType item, MetadataRefreshOptions options, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/Resolvers/ItemResolver.cs b/MediaBrowser.Controller/Resolvers/ItemResolver.cs
index 7fd54fcc6..e7bf013fa 100644
--- a/MediaBrowser.Controller/Resolvers/ItemResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/ItemResolver.cs
@@ -23,7 +23,7 @@ namespace MediaBrowser.Controller.Resolvers
/// </summary>
/// <param name="args">The args.</param>
/// <returns>`0.</returns>
- public virtual T Resolve(ItemResolveArgs args)
+ protected internal virtual T Resolve(ItemResolveArgs args)
{
return null;
}
@@ -42,7 +42,7 @@ namespace MediaBrowser.Controller.Resolvers
/// </summary>
/// <param name="args">The args.</param>
/// <returns>BaseItem.</returns>
- BaseItem IItemResolver.ResolvePath(ItemResolveArgs args)
+ public BaseItem ResolvePath(ItemResolveArgs args)
{
var item = Resolve(args);
diff --git a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs
index a0c38b309..216494556 100644
--- a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs
+++ b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs
@@ -549,7 +549,7 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates
if (InitialState.Equals(GroupStateType.Playing))
{
- // Group went from playing to waiting state and a pause request occured while waiting.
+ // Group went from playing to waiting state and a pause request occurred while waiting.
var pauseRequest = new PauseGroupRequest();
pausedState.HandleRequest(pauseRequest, context, Type, session, cancellationToken);
}
diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs
index 2f38d6adc..619294e95 100644
--- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs
+++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs
@@ -27,9 +27,9 @@ namespace MediaBrowser.Controller.SyncPlay.PlaybackRequests
}
/// <summary>
- /// Gets the playlist identifiers ot the items.
+ /// Gets the playlist identifiers of the items.
/// </summary>
- /// <value>The playlist identifiers ot the items.</value>
+ /// <value>The playlist identifiers of the items.</value>
public IReadOnlyList<Guid> PlaylistItemIds { get; }
/// <summary>
diff --git a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs
index f49876cca..3a7685f34 100644
--- a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs
+++ b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs
@@ -102,7 +102,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
}
/// <summary>
- /// Appends new items to the playlist. The specified order is mantained.
+ /// Appends new items to the playlist. The specified order is maintained.
/// </summary>
/// <param name="items">The items to add to the playlist.</param>
public void Queue(IReadOnlyList<Guid> items)
@@ -197,7 +197,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
}
/// <summary>
- /// Adds new items to the playlist right after the playing item. The specified order is mantained.
+ /// Adds new items to the playlist right after the playing item. The specified order is maintained.
/// </summary>
/// <param name="items">The items to add to the playlist.</param>
public void QueueNext(IReadOnlyList<Guid> items)